From c4d07d72d4da9a00c2e4332186d532b7894e9347 Mon Sep 17 00:00:00 2001 From: Avijit Mondal Date: Mon, 16 Jul 2018 18:18:18 +0530 Subject: [PATCH] Removed files to initialize branch for abot release --- abot-clearwater-ims-bundle/README.md | 99 ----- abot-clearwater-ims-bundle/bundle.yaml | 69 --- abot-clearwater-ims-bundle/bundle.yaml.orig | 77 ---- abot-epc-basic/README.md | 91 ---- abot-epc-basic/actions.yaml | 62 --- abot-epc-basic/actions/add-tags | 54 --- abot-epc-basic/actions/delete-all-tags | 26 -- abot-epc-basic/actions/delete-tags | 51 --- abot-epc-basic/actions/list-tags | 31 -- abot-epc-basic/actions/run | 84 ---- abot-epc-basic/actions/run_local | 79 ---- abot-epc-basic/actions/update-diameter-params | 46 -- abot-epc-basic/config.yaml | 14 - abot-epc-basic/copyright | 8 - abot-epc-basic/hooks/config-changed | 41 -- abot-epc-basic/hooks/epc-relation-changed | 34 -- abot-epc-basic/hooks/epc-relation-departed | 11 - abot-epc-basic/hooks/epc-relation-joined | 9 - abot-epc-basic/hooks/install | 24 -- .../hooks/ssh-abot-relation-changed | 10 - abot-epc-basic/hooks/ssh-abot-relation-joined | 39 -- abot-epc-basic/hooks/start | 30 -- abot-epc-basic/hooks/stop | 7 - abot-epc-basic/hooks/upgrade-charm | 28 -- abot-epc-basic/icon.svg | 26 -- abot-epc-basic/install-abot.sh | 362 ---------------- abot-epc-basic/metadata.yaml | 21 - abot-epc-basic/revision | 1 - abot-ims-basic/README.md | 118 ----- abot-ims-basic/actions.yaml | 91 ---- abot-ims-basic/actions/add-tags | 55 --- abot-ims-basic/actions/benchmark | 160 ------- abot-ims-basic/actions/configure-cx-client | 33 -- abot-ims-basic/actions/delete-all-tags | 26 -- abot-ims-basic/actions/delete-tags | 51 --- abot-ims-basic/actions/list-tags | 31 -- abot-ims-basic/actions/run | 84 ---- abot-ims-basic/actions/run_local | 89 ---- abot-ims-basic/actions/update-cx-params | 45 -- abot-ims-basic/config.yaml | 18 - abot-ims-basic/copyright | 8 - abot-ims-basic/hooks/abot-es-relation-changed | 21 - abot-ims-basic/hooks/config-changed | 104 ----- .../hooks/hss-abot-relation-changed | 18 - abot-ims-basic/hooks/hss-abot-relation-joined | 15 - .../hooks/hss-prov-relation-changed | 37 -- abot-ims-basic/hooks/install | 30 -- .../programmable-multiple-relation-changed | 75 ---- .../programmable-multiple-relation-departed | 11 - abot-ims-basic/hooks/start | 54 --- abot-ims-basic/hooks/stop | 7 - abot-ims-basic/hooks/ue-abot-relation-changed | 29 -- .../hooks/ue-abot-relation-departed | 16 - abot-ims-basic/hooks/ue-abot-relation-joined | 18 - abot-ims-basic/hooks/upgrade-charm | 29 -- abot-ims-basic/icon.svg | 26 -- abot-ims-basic/install-abot.sh | 362 ---------------- abot-ims-basic/metadata.yaml | 24 -- abot-ims-basic/revision | 1 - abot-ims-basic/tests/01-setup | 5 - abot-ims-basic/tests/02-standard | 67 --- abot-ims-basic/tests/03-action | 95 ---- abot-oai-epc-bundle/README.md | 95 ---- abot-oai-epc-bundle/bundle.yaml | 63 --- abot-volte-basic/README.md | 121 ------ abot-volte-basic/actions.yaml | 85 ---- abot-volte-basic/actions/add-tags | 55 --- abot-volte-basic/actions/benchmark | 157 ------- abot-volte-basic/actions/configure-cx-client | 28 -- abot-volte-basic/actions/delete-all-tags | 26 -- abot-volte-basic/actions/delete-tags | 51 --- abot-volte-basic/actions/list-tags | 31 -- abot-volte-basic/actions/run | 105 ----- abot-volte-basic/actions/update-cx-params | 44 -- abot-volte-basic/actions/update-framework | 27 -- .../actions/update-test-scenarios | 19 - abot-volte-basic/config.yaml | 47 -- abot-volte-basic/copyright | 8 - .../hooks/abot-mme-relation-changed | 29 -- .../hooks/abot-mme-relation-departed | 17 - .../hooks/abot-mme-relation-joined | 10 - abot-volte-basic/hooks/config-changed | 116 ----- .../hooks/hss-abot-relation-changed | 10 - .../hooks/hss-prov-relation-changed | 37 -- abot-volte-basic/hooks/install | 151 ------- .../programmable-multiple-relation-changed | 76 ---- .../programmable-multiple-relation-departed | 11 - .../hooks/ssh-abot-relation-changed | 10 - .../hooks/ssh-abot-relation-joined | 39 -- abot-volte-basic/hooks/start | 28 -- abot-volte-basic/hooks/stop | 7 - abot-volte-basic/hooks/upgrade-charm | 60 --- abot-volte-basic/icon.svg | 26 -- .../featureFiles/000-local-commands.feature | 12 - .../lib/featureFiles/ResourceBundle.xml | 4 - abot-volte-basic/lib/scripts/add-keys.sh | 64 --- abot-volte-basic/lib/scripts/config_script | 65 --- .../lib/scripts/configure_is_cscf | 31 -- abot-volte-basic/lib/scripts/epc_setup.sh | 48 --- .../lib/scripts/gen_multiple_ue.sh | 68 --- .../lib/scripts/generate_dns_records | 27 -- abot-volte-basic/lib/scripts/install_java | 7 - abot-volte-basic/lib/scripts/oaisim.conf | 11 - abot-volte-basic/lib/scripts/order_series | 19 - abot-volte-basic/lib/scripts/run_oaisim.sh | 4 - .../lib/scripts/security_setup.sh | 40 -- abot-volte-basic/lib/scripts/stop_oaisim.sh | 4 - abot-volte-basic/lib/scripts/tags_update | 15 - abot-volte-basic/lib/scripts/ue_setup.sh | 43 -- abot-volte-basic/lib/scripts/usim_setup.sh | 43 -- abot-volte-basic/metadata.yaml | 27 -- abot-volte-basic/revision | 1 - abot-volte-basic/tests/01-setup | 5 - abot-volte-basic/tests/02-standard | 67 --- abot-volte-basic/tests/03-action | 95 ---- abot-volte-bundle/README.md | 95 ---- abot-volte-bundle/bundle.yaml | 125 ------ clearwater-bono/README.md | 50 --- clearwater-bono/config.yaml | 29 -- clearwater-bono/copyright | 31 -- clearwater-bono/hooks/config-changed | 9 - clearwater-bono/hooks/cscf-relation-changed | 8 - clearwater-bono/hooks/install | 37 -- .../programmable-multiple-relation-changed | 61 --- .../programmable-multiple-relation-departed | 11 - .../hooks/ralf-ctf-relation-changed | 6 - clearwater-bono/hooks/start | 15 - clearwater-bono/hooks/stop | 12 - clearwater-bono/hooks/ue-relation-changed | 7 - clearwater-bono/hooks/ue-relation-joined | 5 - clearwater-bono/hooks/upgrade-charm | 8 - clearwater-bono/icon.svg | 407 ------------------ clearwater-bono/lib/chef_solo_install | 38 -- clearwater-bono/lib/config_script | 63 --- clearwater-bono/lib/generate_dns_records | 63 --- clearwater-bono/lib/node_json_script | 58 --- clearwater-bono/lib/node_json_template | 18 - clearwater-bono/lib/restart | 6 - clearwater-bono/metadata.yaml | 18 - clearwater-bono/revision | 1 - clearwater-homestead/README.md | 88 ---- clearwater-homestead/config.yaml | 16 - clearwater-homestead/copyright | 31 -- clearwater-homestead/hooks/config-changed | 12 - .../hooks/homestead-cscf-relation-joined | 4 - .../hooks/homestead-prov-user-relation-joined | 4 - .../hooks/homestead-relation-changed | 22 - .../hooks/homestead-relation-departed | 25 -- .../hooks/homestead-relation-joined | 33 -- .../hooks/hss-relation-changed | 13 - .../hooks/hss-relation-departed | 6 - clearwater-homestead/hooks/install | 40 -- .../programmable-multiple-relation-changed | 104 ----- .../programmable-multiple-relation-departed | 11 - .../ssh-abot-volte-homestead-relation-changed | 10 - clearwater-homestead/hooks/start | 20 - clearwater-homestead/hooks/stop | 13 - clearwater-homestead/hooks/upgrade-charm | 9 - clearwater-homestead/icon.svg | 407 ------------------ clearwater-homestead/lib/chef_solo_install | 38 -- clearwater-homestead/lib/config_script | 75 ---- clearwater-homestead/lib/generate_dns_records | 63 --- .../lib/node_json_clustered_script | 58 --- .../lib/node_json_clustered_template | 20 - clearwater-homestead/lib/node_json_script | 57 --- clearwater-homestead/lib/node_json_template | 17 - clearwater-homestead/lib/restart | 7 - clearwater-homestead/metadata.yaml | 23 - clearwater-homestead/revision | 1 - clearwater-sprout/README.md | 88 ---- clearwater-sprout/config.yaml | 28 -- clearwater-sprout/copyright | 31 -- clearwater-sprout/hooks/as-relation-joined | 8 - clearwater-sprout/hooks/config-changed | 9 - .../hooks/homestead-hss-relation-changed | 6 - clearwater-sprout/hooks/install | 38 -- clearwater-sprout/hooks/pcscf-relation-joined | 4 - .../programmable-multiple-relation-changed | 70 --- .../programmable-multiple-relation-departed | 11 - .../hooks/ralf-ctf-relation-changed | 6 - .../hooks/sprout-relation-changed | 16 - .../hooks/sprout-relation-departed | 19 - .../hooks/sprout-relation-joined | 26 -- clearwater-sprout/hooks/start | 15 - clearwater-sprout/hooks/stop | 12 - clearwater-sprout/hooks/upgrade-charm | 9 - clearwater-sprout/hooks/xdms-relation-changed | 6 - clearwater-sprout/icon.svg | 407 ------------------ clearwater-sprout/lib/chef_solo_install | 38 -- clearwater-sprout/lib/config_script | 68 --- clearwater-sprout/lib/finish_scale | 10 - clearwater-sprout/lib/generate_dns_records | 67 --- .../lib/node_json_clustered_script | 58 --- .../lib/node_json_clustered_template | 20 - clearwater-sprout/lib/node_json_script | 57 --- clearwater-sprout/lib/node_json_template | 17 - clearwater-sprout/lib/restart | 6 - clearwater-sprout/metadata.yaml | 27 -- clearwater-sprout/revision | 1 - dns/.coveragerc | 2 - dns/.drone.yml | 5 - dns/.gitignore | 10 - dns/Makefile | 14 - dns/README.md | 102 ----- dns/config.yaml | 17 - dns/contrib/__init__.py | 0 dns/contrib/bind/__init__.py | 0 dns/contrib/bind/install.py | 47 -- dns/contrib/bind/provider.py | 63 --- dns/contrib/bind/provider.py.orig | 82 ---- .../bind/templates/named.conf.options.jinja2 | 21 - dns/contrib/bind/templates/zone.jinja2 | 24 -- dns/contrib/bind/zone.py | 155 ------- dns/contrib/bind/zoneparser.py | 317 -------------- dns/contrib/common.py | 118 ----- dns/contrib/consul_client.py | 61 --- dns/contrib/requirements.txt | 2 - dns/contrib/rt53/README.md | 64 --- dns/contrib/rt53/__init__.py | 0 dns/contrib/rt53/install.py | 21 - dns/contrib/rt53/provider.py | 100 ----- dns/contrib/rt53/rt53-requirements.txt | 1 - dns/contrib/tests/fixtures/db.orangebox.com | 9 - dns/contrib/tests/fixtures/sample_bind_parsed | 5 - dns/contrib/tests/test_bind_provider.py | 34 -- dns/contrib/tests/test_common.py | 98 ----- dns/contrib/tests/test_rt53_install.py | 24 -- dns/contrib/tests/test_rt53_provider.py | 61 --- dns/contrib/tests/test_zone.py | 167 ------- dns/contrib/tests/test_zoneparser.py | 231 ---------- dns/copyright | 21 - dns/docs/HACKING.md | 56 --- dns/docs/provider.md | 71 --- dns/docs/spec-diagrams.pdf | Bin 333335 -> 0 bytes dns/docs/spec-document.pdf | Bin 133929 -> 0 bytes dns/hooks/__init__.py | 0 dns/hooks/config-changed | 22 - dns/hooks/consul-relation-changed | 49 --- dns/hooks/install | 9 - dns/hooks/install_core | 61 --- .../programmable-multiple-relation-changed | 50 --- dns/hooks/programmable-relation-changed | 47 -- dns/hooks/programmable-relation-departed | 40 -- dns/hooks/public-website-relation-changed | 62 --- dns/hooks/public-website-relation-departed | 33 -- dns/icon.svg | 287 ------------ dns/local-dev-standup.yaml | 14 - dns/metadata.yaml | 21 - dns/pytest.ini | 2 - dns/tests/00-setup | 5 - dns/tests/local-bundle.yaml | 13 - dns/tests/local_formation_test | 82 ---- dns/tox.ini | 25 -- functest-abot-epc-bundle/README.md | 95 ---- functest-abot-epc-bundle/bundle.yaml | 52 --- oai-epc/README.md | 361 ---------------- oai-epc/actions.yaml | 4 - oai-epc/actions/restart | 6 - oai-epc/actions/stop | 5 - oai-epc/config.yaml | 89 ---- oai-epc/copyright | 25 -- oai-epc/files/mme_gw | 19 - oai-epc/hooks/config-changed | 175 -------- oai-epc/hooks/epc-relation-broken | 38 -- oai-epc/hooks/epc-relation-changed | 201 --------- oai-epc/hooks/epc-relation-departed | 113 ----- oai-epc/hooks/epc-relation-joined | 72 ---- oai-epc/hooks/hss-relation-broken | 38 -- oai-epc/hooks/hss-relation-changed | 144 ------- oai-epc/hooks/hss-relation-departed | 65 --- oai-epc/hooks/hss-relation-joined | 56 --- oai-epc/hooks/install | 215 --------- oai-epc/hooks/opc-key-relation-changed | 6 - oai-epc/hooks/ssh-abot-epc-relation-changed | 10 - oai-epc/hooks/start | 51 --- oai-epc/hooks/stop | 71 --- oai-epc/hooks/update-status | 50 --- oai-epc/hooks/upgrade-charm | 74 ---- oai-epc/icon.svg | 298 ------------- oai-epc/metadata.yaml | 24 -- oai-epc/revision | 1 - oai-epc/utils/common | 206 --------- oai-hss/README.md | 285 ------------ oai-hss/actions.yaml | 4 - oai-hss/actions/restart | 6 - oai-hss/actions/stop | 5 - oai-hss/config.yaml | 30 -- oai-hss/copyright | 24 -- oai-hss/files/CMakeLists.txt | 347 --------------- oai-hss/files/oai_hss | 19 - oai-hss/hooks/config-changed | 96 ----- oai-hss/hooks/db-relation-broken | 1 - oai-hss/hooks/db-relation-changed | 79 ---- oai-hss/hooks/db-relation-departed | 23 - oai-hss/hooks/hss-relation-broken | 38 -- oai-hss/hooks/hss-relation-changed | 104 ----- oai-hss/hooks/hss-relation-departed | 73 ---- oai-hss/hooks/hss-relation-joined | 71 --- oai-hss/hooks/install | 224 ---------- oai-hss/hooks/opc-relation-changed | 58 --- oai-hss/hooks/opc-relation-joined | 54 --- oai-hss/hooks/start | 53 --- oai-hss/hooks/stop | 71 --- oai-hss/hooks/update-status | 54 --- oai-hss/hooks/upgrade-charm | 62 --- oai-hss/icon.svg | 299 ------------- oai-hss/metadata.yaml | 21 - oai-hss/revision | 1 - oai-hss/utils/common | 224 ---------- oai-mme/README.md | 361 ---------------- oai-mme/actions.yaml | 4 - oai-mme/actions/restart | 6 - oai-mme/actions/stop | 5 - oai-mme/config.yaml | 73 ---- oai-mme/config/mme.yaml | 5 - oai-mme/config/mme_config.sh | 2 - oai-mme/copyright | 25 -- oai-mme/files/dhclient_hook_tpl | 11 - oai-mme/files/eth1_tpl.cfg | 3 - oai-mme/files/mme.service | 14 - oai-mme/files/mme_fd.conf | 103 ----- oai-mme/files/mme_gw.service | 14 - oai-mme/hooks/config-changed | 135 ------ oai-mme/hooks/hss-relation-broken | 38 -- oai-mme/hooks/hss-relation-changed | 144 ------- oai-mme/hooks/hss-relation-departed | 66 --- oai-mme/hooks/hss-relation-joined | 65 --- oai-mme/hooks/install | 212 --------- oai-mme/hooks/mme-relation-broken | 38 -- oai-mme/hooks/mme-relation-changed | 211 --------- oai-mme/hooks/mme-relation-departed | 113 ----- oai-mme/hooks/mme-relation-joined | 84 ---- oai-mme/hooks/spgw-relation-changed | 12 - oai-mme/hooks/spgw-relation-departed | 47 -- oai-mme/hooks/ssh-abot-mme-relation-changed | 15 - oai-mme/hooks/start | 68 --- oai-mme/hooks/stop | 71 --- oai-mme/hooks/update-status | 47 -- oai-mme/hooks/upgrade-charm | 45 -- oai-mme/icon.svg | 298 ------------- oai-mme/metadata.yaml | 26 -- oai-mme/revision | 1 - oai-mme/utils/common | 267 ------------ oai-spgw/README.md | 361 ---------------- oai-spgw/config.yaml | 52 --- oai-spgw/config/spgw.dns.yaml | 5 - oai-spgw/config/spgw_config.sh | 7 - oai-spgw/copyright | 25 -- oai-spgw/files/dhclient_hook_tpl | 11 - oai-spgw/files/eth1_tpl.cfg | 3 - oai-spgw/files/spgw.service | 13 - oai-spgw/hooks/config-changed | 84 ---- oai-spgw/hooks/install | 231 ---------- .../programmable-multiple-relation-changed | 76 ---- .../programmable-multiple-relation-departed | 11 - oai-spgw/hooks/spgw-relation-departed | 47 -- oai-spgw/hooks/spgw-relation-joined | 60 --- oai-spgw/hooks/ssh-abot-spgw-relation-changed | 9 - oai-spgw/hooks/start | 62 --- oai-spgw/hooks/stop | 62 --- oai-spgw/hooks/ue-abot-relation-changed | 32 -- oai-spgw/hooks/update-status | 45 -- oai-spgw/icon.svg | 298 ------------- oai-spgw/metadata.yaml | 26 -- oai-spgw/revision | 1 - oai-spgw/utils/common | 230 ---------- oai-spgw/utils/generate_dns_records | 27 -- 367 files changed, 21284 deletions(-) delete mode 100644 abot-clearwater-ims-bundle/README.md delete mode 100644 abot-clearwater-ims-bundle/bundle.yaml delete mode 100644 abot-clearwater-ims-bundle/bundle.yaml.orig delete mode 100644 abot-epc-basic/README.md delete mode 100644 abot-epc-basic/actions.yaml delete mode 100755 abot-epc-basic/actions/add-tags delete mode 100755 abot-epc-basic/actions/delete-all-tags delete mode 100755 abot-epc-basic/actions/delete-tags delete mode 100755 abot-epc-basic/actions/list-tags delete mode 100755 abot-epc-basic/actions/run delete mode 100755 abot-epc-basic/actions/run_local delete mode 100755 abot-epc-basic/actions/update-diameter-params delete mode 100644 abot-epc-basic/config.yaml delete mode 100644 abot-epc-basic/copyright delete mode 100755 abot-epc-basic/hooks/config-changed delete mode 100755 abot-epc-basic/hooks/epc-relation-changed delete mode 100755 abot-epc-basic/hooks/epc-relation-departed delete mode 100755 abot-epc-basic/hooks/epc-relation-joined delete mode 100755 abot-epc-basic/hooks/install delete mode 100755 abot-epc-basic/hooks/ssh-abot-relation-changed delete mode 100755 abot-epc-basic/hooks/ssh-abot-relation-joined delete mode 100755 abot-epc-basic/hooks/start delete mode 100755 abot-epc-basic/hooks/stop delete mode 100755 abot-epc-basic/hooks/upgrade-charm delete mode 100644 abot-epc-basic/icon.svg delete mode 100755 abot-epc-basic/install-abot.sh delete mode 100644 abot-epc-basic/metadata.yaml delete mode 100644 abot-epc-basic/revision delete mode 100644 abot-ims-basic/README.md delete mode 100644 abot-ims-basic/actions.yaml delete mode 100755 abot-ims-basic/actions/add-tags delete mode 100755 abot-ims-basic/actions/benchmark delete mode 100755 abot-ims-basic/actions/configure-cx-client delete mode 100755 abot-ims-basic/actions/delete-all-tags delete mode 100755 abot-ims-basic/actions/delete-tags delete mode 100755 abot-ims-basic/actions/list-tags delete mode 100755 abot-ims-basic/actions/run delete mode 100755 abot-ims-basic/actions/run_local delete mode 100755 abot-ims-basic/actions/update-cx-params delete mode 100644 abot-ims-basic/config.yaml delete mode 100644 abot-ims-basic/copyright delete mode 100755 abot-ims-basic/hooks/abot-es-relation-changed delete mode 100755 abot-ims-basic/hooks/config-changed delete mode 100755 abot-ims-basic/hooks/hss-abot-relation-changed delete mode 100755 abot-ims-basic/hooks/hss-abot-relation-joined delete mode 100755 abot-ims-basic/hooks/hss-prov-relation-changed delete mode 100755 abot-ims-basic/hooks/install delete mode 100755 abot-ims-basic/hooks/programmable-multiple-relation-changed delete mode 100755 abot-ims-basic/hooks/programmable-multiple-relation-departed delete mode 100755 abot-ims-basic/hooks/start delete mode 100755 abot-ims-basic/hooks/stop delete mode 100755 abot-ims-basic/hooks/ue-abot-relation-changed delete mode 100755 abot-ims-basic/hooks/ue-abot-relation-departed delete mode 100755 abot-ims-basic/hooks/ue-abot-relation-joined delete mode 100755 abot-ims-basic/hooks/upgrade-charm delete mode 100644 abot-ims-basic/icon.svg delete mode 100755 abot-ims-basic/install-abot.sh delete mode 100644 abot-ims-basic/metadata.yaml delete mode 100644 abot-ims-basic/revision delete mode 100755 abot-ims-basic/tests/01-setup delete mode 100755 abot-ims-basic/tests/02-standard delete mode 100755 abot-ims-basic/tests/03-action delete mode 100644 abot-oai-epc-bundle/README.md delete mode 100644 abot-oai-epc-bundle/bundle.yaml delete mode 100644 abot-volte-basic/README.md delete mode 100644 abot-volte-basic/actions.yaml delete mode 100755 abot-volte-basic/actions/add-tags delete mode 100755 abot-volte-basic/actions/benchmark delete mode 100755 abot-volte-basic/actions/configure-cx-client delete mode 100755 abot-volte-basic/actions/delete-all-tags delete mode 100755 abot-volte-basic/actions/delete-tags delete mode 100755 abot-volte-basic/actions/list-tags delete mode 100755 abot-volte-basic/actions/run delete mode 100755 abot-volte-basic/actions/update-cx-params delete mode 100755 abot-volte-basic/actions/update-framework delete mode 100755 abot-volte-basic/actions/update-test-scenarios delete mode 100644 abot-volte-basic/config.yaml delete mode 100644 abot-volte-basic/copyright delete mode 100755 abot-volte-basic/hooks/abot-mme-relation-changed delete mode 100755 abot-volte-basic/hooks/abot-mme-relation-departed delete mode 100755 abot-volte-basic/hooks/abot-mme-relation-joined delete mode 100755 abot-volte-basic/hooks/config-changed delete mode 100755 abot-volte-basic/hooks/hss-abot-relation-changed delete mode 100755 abot-volte-basic/hooks/hss-prov-relation-changed delete mode 100755 abot-volte-basic/hooks/install delete mode 100755 abot-volte-basic/hooks/programmable-multiple-relation-changed delete mode 100755 abot-volte-basic/hooks/programmable-multiple-relation-departed delete mode 100755 abot-volte-basic/hooks/ssh-abot-relation-changed delete mode 100755 abot-volte-basic/hooks/ssh-abot-relation-joined delete mode 100755 abot-volte-basic/hooks/start delete mode 100755 abot-volte-basic/hooks/stop delete mode 100755 abot-volte-basic/hooks/upgrade-charm delete mode 100644 abot-volte-basic/icon.svg delete mode 100644 abot-volte-basic/lib/featureFiles/000-local-commands.feature delete mode 100644 abot-volte-basic/lib/featureFiles/ResourceBundle.xml delete mode 100755 abot-volte-basic/lib/scripts/add-keys.sh delete mode 100755 abot-volte-basic/lib/scripts/config_script delete mode 100755 abot-volte-basic/lib/scripts/configure_is_cscf delete mode 100755 abot-volte-basic/lib/scripts/epc_setup.sh delete mode 100755 abot-volte-basic/lib/scripts/gen_multiple_ue.sh delete mode 100755 abot-volte-basic/lib/scripts/generate_dns_records delete mode 100755 abot-volte-basic/lib/scripts/install_java delete mode 100755 abot-volte-basic/lib/scripts/oaisim.conf delete mode 100755 abot-volte-basic/lib/scripts/order_series delete mode 100755 abot-volte-basic/lib/scripts/run_oaisim.sh delete mode 100755 abot-volte-basic/lib/scripts/security_setup.sh delete mode 100755 abot-volte-basic/lib/scripts/stop_oaisim.sh delete mode 100755 abot-volte-basic/lib/scripts/tags_update delete mode 100755 abot-volte-basic/lib/scripts/ue_setup.sh delete mode 100755 abot-volte-basic/lib/scripts/usim_setup.sh delete mode 100644 abot-volte-basic/metadata.yaml delete mode 100644 abot-volte-basic/revision delete mode 100755 abot-volte-basic/tests/01-setup delete mode 100755 abot-volte-basic/tests/02-standard delete mode 100755 abot-volte-basic/tests/03-action delete mode 100644 abot-volte-bundle/README.md delete mode 100644 abot-volte-bundle/bundle.yaml delete mode 100644 clearwater-bono/README.md delete mode 100644 clearwater-bono/config.yaml delete mode 100644 clearwater-bono/copyright delete mode 100755 clearwater-bono/hooks/config-changed delete mode 100755 clearwater-bono/hooks/cscf-relation-changed delete mode 100755 clearwater-bono/hooks/install delete mode 100755 clearwater-bono/hooks/programmable-multiple-relation-changed delete mode 100755 clearwater-bono/hooks/programmable-multiple-relation-departed delete mode 100755 clearwater-bono/hooks/ralf-ctf-relation-changed delete mode 100755 clearwater-bono/hooks/start delete mode 100755 clearwater-bono/hooks/stop delete mode 100755 clearwater-bono/hooks/ue-relation-changed delete mode 100755 clearwater-bono/hooks/ue-relation-joined delete mode 100755 clearwater-bono/hooks/upgrade-charm delete mode 100644 clearwater-bono/icon.svg delete mode 100755 clearwater-bono/lib/chef_solo_install delete mode 100755 clearwater-bono/lib/config_script delete mode 100755 clearwater-bono/lib/generate_dns_records delete mode 100755 clearwater-bono/lib/node_json_script delete mode 100755 clearwater-bono/lib/node_json_template delete mode 100755 clearwater-bono/lib/restart delete mode 100644 clearwater-bono/metadata.yaml delete mode 100644 clearwater-bono/revision delete mode 100644 clearwater-homestead/README.md delete mode 100644 clearwater-homestead/config.yaml delete mode 100644 clearwater-homestead/copyright delete mode 100755 clearwater-homestead/hooks/config-changed delete mode 100755 clearwater-homestead/hooks/homestead-cscf-relation-joined delete mode 100755 clearwater-homestead/hooks/homestead-prov-user-relation-joined delete mode 100755 clearwater-homestead/hooks/homestead-relation-changed delete mode 100755 clearwater-homestead/hooks/homestead-relation-departed delete mode 100755 clearwater-homestead/hooks/homestead-relation-joined delete mode 100755 clearwater-homestead/hooks/hss-relation-changed delete mode 100755 clearwater-homestead/hooks/hss-relation-departed delete mode 100755 clearwater-homestead/hooks/install delete mode 100755 clearwater-homestead/hooks/programmable-multiple-relation-changed delete mode 100755 clearwater-homestead/hooks/programmable-multiple-relation-departed delete mode 100755 clearwater-homestead/hooks/ssh-abot-volte-homestead-relation-changed delete mode 100755 clearwater-homestead/hooks/start delete mode 100755 clearwater-homestead/hooks/stop delete mode 100755 clearwater-homestead/hooks/upgrade-charm delete mode 100644 clearwater-homestead/icon.svg delete mode 100755 clearwater-homestead/lib/chef_solo_install delete mode 100755 clearwater-homestead/lib/config_script delete mode 100755 clearwater-homestead/lib/generate_dns_records delete mode 100755 clearwater-homestead/lib/node_json_clustered_script delete mode 100755 clearwater-homestead/lib/node_json_clustered_template delete mode 100755 clearwater-homestead/lib/node_json_script delete mode 100755 clearwater-homestead/lib/node_json_template delete mode 100755 clearwater-homestead/lib/restart delete mode 100644 clearwater-homestead/metadata.yaml delete mode 100644 clearwater-homestead/revision delete mode 100644 clearwater-sprout/README.md delete mode 100644 clearwater-sprout/config.yaml delete mode 100644 clearwater-sprout/copyright delete mode 100755 clearwater-sprout/hooks/as-relation-joined delete mode 100755 clearwater-sprout/hooks/config-changed delete mode 100755 clearwater-sprout/hooks/homestead-hss-relation-changed delete mode 100755 clearwater-sprout/hooks/install delete mode 100755 clearwater-sprout/hooks/pcscf-relation-joined delete mode 100755 clearwater-sprout/hooks/programmable-multiple-relation-changed delete mode 100755 clearwater-sprout/hooks/programmable-multiple-relation-departed delete mode 100755 clearwater-sprout/hooks/ralf-ctf-relation-changed delete mode 100755 clearwater-sprout/hooks/sprout-relation-changed delete mode 100755 clearwater-sprout/hooks/sprout-relation-departed delete mode 100755 clearwater-sprout/hooks/sprout-relation-joined delete mode 100755 clearwater-sprout/hooks/start delete mode 100755 clearwater-sprout/hooks/stop delete mode 100755 clearwater-sprout/hooks/upgrade-charm delete mode 100755 clearwater-sprout/hooks/xdms-relation-changed delete mode 100644 clearwater-sprout/icon.svg delete mode 100755 clearwater-sprout/lib/chef_solo_install delete mode 100755 clearwater-sprout/lib/config_script delete mode 100755 clearwater-sprout/lib/finish_scale delete mode 100755 clearwater-sprout/lib/generate_dns_records delete mode 100755 clearwater-sprout/lib/node_json_clustered_script delete mode 100755 clearwater-sprout/lib/node_json_clustered_template delete mode 100755 clearwater-sprout/lib/node_json_script delete mode 100755 clearwater-sprout/lib/node_json_template delete mode 100755 clearwater-sprout/lib/restart delete mode 100644 clearwater-sprout/metadata.yaml delete mode 100644 clearwater-sprout/revision delete mode 100644 dns/.coveragerc delete mode 100644 dns/.drone.yml delete mode 100644 dns/.gitignore delete mode 100644 dns/Makefile delete mode 100644 dns/README.md delete mode 100644 dns/config.yaml delete mode 100644 dns/contrib/__init__.py delete mode 100644 dns/contrib/bind/__init__.py delete mode 100644 dns/contrib/bind/install.py delete mode 100644 dns/contrib/bind/provider.py delete mode 100644 dns/contrib/bind/provider.py.orig delete mode 100644 dns/contrib/bind/templates/named.conf.options.jinja2 delete mode 100644 dns/contrib/bind/templates/zone.jinja2 delete mode 100644 dns/contrib/bind/zone.py delete mode 100644 dns/contrib/bind/zoneparser.py delete mode 100644 dns/contrib/common.py delete mode 100644 dns/contrib/consul_client.py delete mode 100644 dns/contrib/requirements.txt delete mode 100644 dns/contrib/rt53/README.md delete mode 100644 dns/contrib/rt53/__init__.py delete mode 100644 dns/contrib/rt53/install.py delete mode 100644 dns/contrib/rt53/provider.py delete mode 100644 dns/contrib/rt53/rt53-requirements.txt delete mode 100644 dns/contrib/tests/fixtures/db.orangebox.com delete mode 100644 dns/contrib/tests/fixtures/sample_bind_parsed delete mode 100644 dns/contrib/tests/test_bind_provider.py delete mode 100644 dns/contrib/tests/test_common.py delete mode 100644 dns/contrib/tests/test_rt53_install.py delete mode 100644 dns/contrib/tests/test_rt53_provider.py delete mode 100644 dns/contrib/tests/test_zone.py delete mode 100644 dns/contrib/tests/test_zoneparser.py delete mode 100644 dns/copyright delete mode 100644 dns/docs/HACKING.md delete mode 100644 dns/docs/provider.md delete mode 100644 dns/docs/spec-diagrams.pdf delete mode 100644 dns/docs/spec-document.pdf delete mode 100644 dns/hooks/__init__.py delete mode 100755 dns/hooks/config-changed delete mode 100755 dns/hooks/consul-relation-changed delete mode 100755 dns/hooks/install delete mode 100755 dns/hooks/install_core delete mode 100755 dns/hooks/programmable-multiple-relation-changed delete mode 100755 dns/hooks/programmable-relation-changed delete mode 100755 dns/hooks/programmable-relation-departed delete mode 100755 dns/hooks/public-website-relation-changed delete mode 100755 dns/hooks/public-website-relation-departed delete mode 100644 dns/icon.svg delete mode 100644 dns/local-dev-standup.yaml delete mode 100644 dns/metadata.yaml delete mode 100644 dns/pytest.ini delete mode 100755 dns/tests/00-setup delete mode 100644 dns/tests/local-bundle.yaml delete mode 100755 dns/tests/local_formation_test delete mode 100644 dns/tox.ini delete mode 100644 functest-abot-epc-bundle/README.md delete mode 100644 functest-abot-epc-bundle/bundle.yaml delete mode 100644 oai-epc/README.md delete mode 100644 oai-epc/actions.yaml delete mode 100644 oai-epc/actions/restart delete mode 100644 oai-epc/actions/stop delete mode 100644 oai-epc/config.yaml delete mode 100644 oai-epc/copyright delete mode 100644 oai-epc/files/mme_gw delete mode 100755 oai-epc/hooks/config-changed delete mode 100755 oai-epc/hooks/epc-relation-broken delete mode 100755 oai-epc/hooks/epc-relation-changed delete mode 100755 oai-epc/hooks/epc-relation-departed delete mode 100755 oai-epc/hooks/epc-relation-joined delete mode 100755 oai-epc/hooks/hss-relation-broken delete mode 100755 oai-epc/hooks/hss-relation-changed delete mode 100755 oai-epc/hooks/hss-relation-departed delete mode 100755 oai-epc/hooks/hss-relation-joined delete mode 100755 oai-epc/hooks/install delete mode 100755 oai-epc/hooks/opc-key-relation-changed delete mode 100755 oai-epc/hooks/ssh-abot-epc-relation-changed delete mode 100755 oai-epc/hooks/start delete mode 100755 oai-epc/hooks/stop delete mode 100755 oai-epc/hooks/update-status delete mode 100755 oai-epc/hooks/upgrade-charm delete mode 100644 oai-epc/icon.svg delete mode 100644 oai-epc/metadata.yaml delete mode 100644 oai-epc/revision delete mode 100644 oai-epc/utils/common delete mode 100755 oai-hss/README.md delete mode 100755 oai-hss/actions.yaml delete mode 100755 oai-hss/actions/restart delete mode 100755 oai-hss/actions/stop delete mode 100755 oai-hss/config.yaml delete mode 100755 oai-hss/copyright delete mode 100755 oai-hss/files/CMakeLists.txt delete mode 100755 oai-hss/files/oai_hss delete mode 100755 oai-hss/hooks/config-changed delete mode 100755 oai-hss/hooks/db-relation-broken delete mode 100755 oai-hss/hooks/db-relation-changed delete mode 100755 oai-hss/hooks/db-relation-departed delete mode 100755 oai-hss/hooks/hss-relation-broken delete mode 100755 oai-hss/hooks/hss-relation-changed delete mode 100755 oai-hss/hooks/hss-relation-departed delete mode 100755 oai-hss/hooks/hss-relation-joined delete mode 100755 oai-hss/hooks/install delete mode 100755 oai-hss/hooks/opc-relation-changed delete mode 100755 oai-hss/hooks/opc-relation-joined delete mode 100755 oai-hss/hooks/start delete mode 100755 oai-hss/hooks/stop delete mode 100755 oai-hss/hooks/update-status delete mode 100755 oai-hss/hooks/upgrade-charm delete mode 100755 oai-hss/icon.svg delete mode 100755 oai-hss/metadata.yaml delete mode 100644 oai-hss/revision delete mode 100755 oai-hss/utils/common delete mode 100755 oai-mme/README.md delete mode 100755 oai-mme/actions.yaml delete mode 100755 oai-mme/actions/restart delete mode 100755 oai-mme/actions/stop delete mode 100755 oai-mme/config.yaml delete mode 100644 oai-mme/config/mme.yaml delete mode 100755 oai-mme/config/mme_config.sh delete mode 100755 oai-mme/copyright delete mode 100755 oai-mme/files/dhclient_hook_tpl delete mode 100644 oai-mme/files/eth1_tpl.cfg delete mode 100755 oai-mme/files/mme.service delete mode 100644 oai-mme/files/mme_fd.conf delete mode 100755 oai-mme/files/mme_gw.service delete mode 100755 oai-mme/hooks/config-changed delete mode 100755 oai-mme/hooks/hss-relation-broken delete mode 100755 oai-mme/hooks/hss-relation-changed delete mode 100755 oai-mme/hooks/hss-relation-departed delete mode 100755 oai-mme/hooks/hss-relation-joined delete mode 100755 oai-mme/hooks/install delete mode 100755 oai-mme/hooks/mme-relation-broken delete mode 100755 oai-mme/hooks/mme-relation-changed delete mode 100755 oai-mme/hooks/mme-relation-departed delete mode 100755 oai-mme/hooks/mme-relation-joined delete mode 100755 oai-mme/hooks/spgw-relation-changed delete mode 100755 oai-mme/hooks/spgw-relation-departed delete mode 100755 oai-mme/hooks/ssh-abot-mme-relation-changed delete mode 100755 oai-mme/hooks/start delete mode 100755 oai-mme/hooks/stop delete mode 100755 oai-mme/hooks/update-status delete mode 100755 oai-mme/hooks/upgrade-charm delete mode 100755 oai-mme/icon.svg delete mode 100755 oai-mme/metadata.yaml delete mode 100644 oai-mme/revision delete mode 100755 oai-mme/utils/common delete mode 100755 oai-spgw/README.md delete mode 100755 oai-spgw/config.yaml delete mode 100644 oai-spgw/config/spgw.dns.yaml delete mode 100755 oai-spgw/config/spgw_config.sh delete mode 100755 oai-spgw/copyright delete mode 100755 oai-spgw/files/dhclient_hook_tpl delete mode 100644 oai-spgw/files/eth1_tpl.cfg delete mode 100755 oai-spgw/files/spgw.service delete mode 100755 oai-spgw/hooks/config-changed delete mode 100755 oai-spgw/hooks/install delete mode 100755 oai-spgw/hooks/programmable-multiple-relation-changed delete mode 100755 oai-spgw/hooks/programmable-multiple-relation-departed delete mode 100755 oai-spgw/hooks/spgw-relation-departed delete mode 100755 oai-spgw/hooks/spgw-relation-joined delete mode 100755 oai-spgw/hooks/ssh-abot-spgw-relation-changed delete mode 100755 oai-spgw/hooks/start delete mode 100755 oai-spgw/hooks/stop delete mode 100755 oai-spgw/hooks/ue-abot-relation-changed delete mode 100755 oai-spgw/hooks/update-status delete mode 100755 oai-spgw/icon.svg delete mode 100755 oai-spgw/metadata.yaml delete mode 100644 oai-spgw/revision delete mode 100755 oai-spgw/utils/common delete mode 100755 oai-spgw/utils/generate_dns_records diff --git a/abot-clearwater-ims-bundle/README.md b/abot-clearwater-ims-bundle/README.md deleted file mode 100644 index 45aa11e..0000000 --- a/abot-clearwater-ims-bundle/README.md +++ /dev/null @@ -1,99 +0,0 @@ -# Overview - -Abot-Clearwater-ims bundle deploys abot-ims-basic along with Clearwater IMS solution, which supports deployment and testing of a [Project Clearwater](http://www.projectclearwater.org) IMS core using the Test Orchestration solution ABOT developed by Rebaca. - -# Usage - -Before deploying this bundle, you should bootstrap a Juju environment, as documented in the https://jujucharms.com/docs/stable/getting-started. The testing of this bundle on MAAS as well as OpenStack is complete. If you get it working on a different cloud service, please let us know! - -Clearwater is a reasonably complex system (especially compared to typical beginner Juju examples like Wordpress + MySQL) - deploying a Clearwater IMS core involves deploying and configuring six or seven charms, and adding relations between them. You can find the Clearwater IMS bundle at [https://github.com/Metaswitch/clearwater-juju.git]. -To know more about ABOT please visit charmstore (https://jujucharms.com/u/abotcharm/abot-ims-basic/trusty). - -We have now created a Juju bundle that will tie all these charms and their relations together, allowing the deployment of a full system in a single step. - ->juju deploy [bundle_name] - -Please wait for all the associated charms to be readiy by issuing the following command ->watch juju status - - -# Configuration - -See [bundle.yaml](bundle.yaml), which lists the main configuration fields with comments about their meanings. - -# Testing - -To test the Clearwater IMS setup using ABOT run the following actions from Juju client(once all the charms are in ready state). - -####Configure ABOT to run IMS in Cache mode: - ->juju run-action [abot-ims-basic unit name] configure-cx-client auth-type=cache - -####Add user: - ->juju run-action [abot-ims-basic unit name] run tagnames=ims-user-add - -####Run a generic single test: - ->juju run-action [abot-ims-basic unit name] run tagnames=sip-register - -####Run a generic test suite: - ->juju run-action [abot-ims-basic unit name] run tagnames=sip-tests - - -#Running a benchmark -List of benchmarks available at any time by executing juju run-action benchmark. To run benchmark action execute the following command from juju client. - -##Run benchmark action with default parameter. - ->juju run-action [abot-ims-basic-unit-name] benchmark - -##Run benchmark action with configurable parameters - -There are some configurable parameter in benchmark action, which are number of calls (num-calls), call rate per second (call-rate), sip scenario (sip-scenario) and max call duration in minutes (timeout-in-minutes). Currently number of calls is limited to 10000 and calls per second is limited to 500. -Benchmark action supports two sip scenarios, sip register and sip invite. - -Default values are as follows: - -- `number of calls(num-calls)`:1000 -- `calls per second(call-rate)`:5 -- `sip scenario(sip-scenario)`:register -- `max call duration in minutes(timeout-in-minutes)`:10 minutes. -**The timeout-in-minutes value is not expected to be specified as in most of the cases it is expected that the calls will get over within 10 minutes.** - -Command to execute benchmark action with 500 number of calls and 50 calls per second, sip register as sip scenario. - ->juju run-action [abot-ims-basic-unit-name] benchmark num-calls=500 call-rate=50 sip-scenario=register - -Command to execute benchmark action with 500 number of calls and 50 calls per second, sip invite as sip scenario. - ->juju run-action [abot-ims-basic-unit-name] benchmark num-calls=500 call-rate=50 sip-scenario=invite - -Command to execute benchmark action with 1000 number of calls and 100 calls per second, sip register as sip scenario. - ->juju run-action [abot-ims-basic-unit-name] benchmark num-calls=1000 call-rate=100 - -Command to execute benchmark action with 2000 number of calls and 200 calls per second, sip register as sip scenario. - ->juju run-action [abot-ims-basic-unit-name] benchmark num-calls=2000 call-rate=200 - -Command to execute benchmark action with 5000 number of calls and 500 calls per second, sip register as sip scenario. - ->juju run-action [abot-ims-basic-unit-name] benchmark num-calls=5000 call-rate=500 - -Command to execute benchmark action with 5000 number of calls and 50 calls per second, sip invite as sip scenario, for a max call duration of 15 minutes. - ->juju run-action [abot-ims-basic-unit-name] benchmark num-calls=5000 call-rate=50 sip-scenario=invite timeout-in-minutes=15 - -# Contact and Upstream Project Information - -Project Clearwater is an open-source IMS core, developed by [Metaswitch Networks](http://www.metaswitch.com) and released under the [GNU GPLv3](http://www.projectclearwater.org/download/license/). You can find more information about it on [our website](http://www.projectclearwater.org/) or [our documentation site](https://clearwater.readthedocs.org). - -Clearwater source code and issue trackers is present at https://github.com/Metaswitch/. - -If you have problems when using Project Clearwater, read [our troubleshooting documentation](http://clearwater.readthedocs.org/en/latest/Troubleshooting_and_Recovery/index.html) for help, or see [our support page](http://clearwater.readthedocs.org/en/latest/Support/index.html) to find out how to ask mailing list questions or raise issues. - -Abot-ims-basic is a Test Automation Framework for network services that uses the behavior-oriented testing model. -This enables users to define test cases as feature files based on a domain-specific language. These feature files could subsequently be executed in automated manner on multiple environments. To reach us please visit https://www.rebaca.com/contact-us. - diff --git a/abot-clearwater-ims-bundle/bundle.yaml b/abot-clearwater-ims-bundle/bundle.yaml deleted file mode 100644 index 31d1d8a..0000000 --- a/abot-clearwater-ims-bundle/bundle.yaml +++ /dev/null @@ -1,69 +0,0 @@ - services: - "clearwater-bono": - charm: cs:~abotcharm/trusty/clearwater-bono - num_units: 1 - constraints: "arch=amd64" - options: - zone: "rebaca.local" - expose: true - annotations: - "gui-x": "400" - "gui-y": "900" - "clearwater-sprout": - charm: cs:~abotcharm/trusty/clearwater-sprout - num_units: 1 - constraints: "arch=amd64" - options: - zone: "rebaca.local" - annotations: - "gui-x": "400" - "gui-y": "600" - "clearwater-homestead": - charm: cs:~abotcharm/trusty/clearwater-homestead - num_units: 1 - constraints: "arch=amd64" - options: - zone: "rebaca.local" - annotations: - "gui-x": "200" - "gui-y": "300" - dns: - charm: cs:~abotcharm/trusty/dns - num_units: 1 - constraints: "arch=amd64" - options: - domain: "rebaca.local" - annotations: - "gui-x": "1000" - "gui-y": "450" - abot-ims-basic: - charm: cs:~abotcharm/trusty/abot-ims-basic - constraints: "arch=amd64" - num_units: 1 - options: - zone: "rebaca.local" - expose: true - annotations: - "gui-x": "400" - "gui-y": "700" - relations: - - - "clearwater-homestead:programmable-multiple" - - "dns:programmable-multiple" - - - "clearwater-sprout:programmable-multiple" - - "dns:programmable-multiple" - - - "clearwater-bono:programmable-multiple" - - "dns:programmable-multiple" - - - "abot-ims-basic:programmable-multiple" - - "dns:programmable-multiple" - - - "clearwater-bono:cscf" - - "clearwater-sprout:pcscf" - - - "clearwater-homestead:homestead-cscf" - - "clearwater-sprout:homestead-hss" - - - "abot-ims-basic:hss-prov" - - "clearwater-homestead:homestead-prov-user" - - - "clearwater-bono:ue" - - "abot-ims-basic:ue-abot" - - - "abot-ims-basic:hss-abot" - - "clearwater-homestead:hss" - series: trusty - diff --git a/abot-clearwater-ims-bundle/bundle.yaml.orig b/abot-clearwater-ims-bundle/bundle.yaml.orig deleted file mode 100644 index 659e835..0000000 --- a/abot-clearwater-ims-bundle/bundle.yaml.orig +++ /dev/null @@ -1,77 +0,0 @@ - services: - "clearwater-bono": - charm: cs:~abotcharm/trusty/clearwater-bono-0 - num_units: 1 - constraints: "arch=amd64" - options: - zone: "rebaca.local" - expose: true - annotations: - "gui-x": "400" - "gui-y": "900" - "clearwater-sprout": - charm: cs:~abotcharm/trusty/clearwater-sprout-0 - num_units: 1 - constraints: "arch=amd64" - options: - zone: "rebaca.local" - annotations: - "gui-x": "400" - "gui-y": "600" - "clearwater-homestead": - charm: cs:~abotcharm/trusty/clearwater-homestead-1 - num_units: 1 - constraints: "arch=amd64" - options: - zone: "rebaca.local" - annotations: - "gui-x": "200" - "gui-y": "300" - # "clearwater-sipp": - # charm: "/home/rebaca/abot_bundles/clearwater-juju-trusty-with-abot/charms/trusty/clearwater-sipp" - # num_units: 1 - # constraints: "arch=amd64 tags=sipp2" - #options: - # zone: "clearwater.local" - #user_count: 40000 - # annotations: - # "gui-x": "400" - # "gui-y": "1200" - dns: - charm: cs:~abotcharm/trusty/dns-0 - num_units: 1 - constraints: "arch=amd64" - options: - domain: "rebaca.local" - annotations: - "gui-x": "1000" - "gui-y": "450" - abot-ims-basic: - charm: cs:~abotcharm/trusty/abot-ims-basic-6 - constraints: "arch=amd64" - num_units: 1 - options: - zone: "rebaca.local" - annotations: - "gui-x": "400" - "gui-y": "700" - relations: - - - "clearwater-homestead:programmable-multiple" - - "dns:programmable-multiple" - - - "clearwater-sprout:programmable-multiple" - - "dns:programmable-multiple" - - - "clearwater-bono:programmable-multiple" - - "dns:programmable-multiple" - - - "abot-ims-basic:programmable-multiple" - - "dns:programmable-multiple" - - - "clearwater-bono:cscf" - - "clearwater-sprout:pcscf" - - - "clearwater-homestead:homestead-cscf" - - "clearwater-sprout:homestead-hss" - - - "abot-ims-basic:hss-prov" - - "clearwater-homestead:homestead-prov-user" - - - "clearwater-bono:ue" - - "abot-ims-basic:ue-abot" - - - "abot-ims-basic:hss-abot" - - "clearwater-homestead:hss" - series: trusty diff --git a/abot-epc-basic/README.md b/abot-epc-basic/README.md deleted file mode 100644 index 5eea1a7..0000000 --- a/abot-epc-basic/README.md +++ /dev/null @@ -1,91 +0,0 @@ -# Charms Overview - -Abot-OAI-EPC bundle deploys abot-epc-basic along-with Oai-hss, Oai-epc and Mysql charms, which support deployment and scaling of OAI charms along with ABot developed by Rebaca. - -ABot should typically be deployed alongside the System Under Test (SUT) - such that it can execute tests against the latter. One of the possible SUTs is the OAI EPC, which is an open source initiative of Eurecom. - -ABot enables users to create **feature files** based on a **Cucumber** framework. Cucumber is a Behavior Driven Development (BDD) framework, which is used to write tests for different types of applications. It allows automation of functional validation in an easily readable and understandable format (like plain English). It is used to test the system rather than testing the particular piece of code. - -The feature files authored by the user(s) can subsequently be used for testing different types of applications/protocols that are running on the System Under Test (SUT). The feature files follow a specified grammar and include instructions that translate into sending messages to the SUT, and receiving back responses using different protocols and verification of the results as defined. - -The current protocol drivers (adapters) provided with ABot are SSH, SFTP, HTTP as well as S1AP. - -Each feature file can be associated with one (or more) tags; thereby enabling it to be selectively invoked. **Execution of feature file(s) associated with one (or more) specified tags is driven through a Juju action after deployment of the ABot charm.** - -The result of each test execution is displayed in a web-based reporting UI; this is currently accessible on port 80. - -# Usage - -Before deploying this bundle, you should bootstrap a Juju environment, as documented in the https://jujucharms.com/docs/stable/getting-started. The testing of this charm on MAAS cloud is complete, and there are plans to test it on Amazon and OpenStack in the near future. If you get it working on a different cloud service, please let us know! - -OAI is a relatively simpler bundle consisting of Mysql, Oai-hss, Oai-epc and Abot-EPC-basic, together forming the SUT for feature file execution. The ABot OAI EPC bundle has been integrated and tested on MAAS cloud. You can find the ABot OAI EPC bundle at [https://jujucharms.com/u/abotcharm/abot-oai-epc-bundle/]. To know more about ABot please visit charmstore (https://jujucharms.com/u/abotcharm/abot-epc-basic/xenial/). - -The ABot OAI EPC bundle ties up all the above-mentioned charms and their relations together, allowing the deployment of a full system in a single step. - ->juju deploy [bundle_name] - - -# Configuration - -See [bundle.yaml](bundle.yaml), which lists the main configuration fields with comments about their meanings. - -# Files downloaded - -When the above-mentioned charms are being installed, the following files are downloaded from their respected paths: - -- The ABot Debian package, from the public package repository server hosted at Rebaca. -- Any dependencies related to Debian packages (particularly Java and Maven), are downloaded from the standard Ubuntu repository servers. -- Any dependency related to OAI charms are downloaded from their respective repositories. - -# Test Case Execution - -- After deployment of the ABot OAI EPC bundle, the following actions can be quickly executed to verify whether the bundle works fine: -> juju run-action abot-epc-basic/[abot-unit-number] run - -- The following script needs to be executed from the Juju controller in order to generate and add the ssh keys to all the ABot OAI EPC bundle units. -> charm pull cs:~abotcharm/xenial/abot-epc-basic -> ./abot-epc-basic/lib/scripts/add-keys.sh - -- In order to execute a particular feature file or a set of feature files we need to execute the following commands mentioned below, replacing the tag name mentioned below with the required tag name. Also in the following commands we need to replace the [abot-unit-number] with the current abot-unit-number. -> juju run-action abot-epc-basic/[abot-unit-number] run tagnames=[tag name] - -- In order to run EPC attach test case feature file: -> juju run-action abot-epc-basic/8 run tagnames=Attach_Procedure_AttachWithIMSI - -- In order to run EPC MAC Failure test case feature file: -> juju run-action abot-epc-basic/8 run tagnames=Auth_NotAccept_by_UE_GUTIattach_MAC_code_failure - -- In order to run EPC SQN Failure test case feature file: -> juju run-action abot-epc-basic/8 run tagnames=Auth_NotAccept_by_UE_SQN_failure - -- In order to run EPC negative test cases in multiple feature files: -> juju run-action abot-epc-basic/8 run tagnames=negTCs - -- In order to run 3GPP TS 24.301 test cases in multiple feature files: -> juju run-action abot-epc-basic/8 run tagnames=TS_24_301 - -- The following action will execute all test cases (feature files) for which the tags have been added through the previous command. Please note that the tags can be run from a specified filename as well. -> juju run-action abot-epc-basic/[abot-unit-number] run tagnames=[feature file name without extension] - -- Execute tags from the file titled ***negTCs***. This file includes tags for negative call scenarios; such as *sync failure*, *MAC failure* and so on. -> juju run-action abot-epc-basic/[abot-unit-number] run tagnames=negTCs - -#Adding new Test Cases (Feature Files) - -- **New Feature Files should be added into the following folder** on the Juju controller node ->charm pull cs:~abotcharm/trusty/abot-ims-basic -> $PWD/abot-epc-basic/lib/featureFiles - -- The Juju charm upgrade action should be invoked to propagate these new feature files onto the deployed service node. ->juju upgrade-charm --path=$PWD/abot-epc-basic abot-epc-basic - -- Once the upgrade is successful, the test case execution steps can be followed to execute one or more feature files. - -# Viewing Test Report - -- The report UI can then be viewed by accessing the below mentioned URL. - http://[abot-ip]/app - E.g.: http://192.168.15.87/app/ - -- This pulls up the Cucumber report of the latest execution. -- Accessing any of the feature files present in the report shows the step-wise execution status for that feature file. diff --git a/abot-epc-basic/actions.yaml b/abot-epc-basic/actions.yaml deleted file mode 100644 index ddc4c15..0000000 --- a/abot-epc-basic/actions.yaml +++ /dev/null @@ -1,62 +0,0 @@ -# actions.yaml - -run: - description: Run the Automation framework for the specified tags(or filename) - params: - tagnames: - type: string - filename: - type: string - additionalProperties: false - -add-tags: - description: Add the tag(s) to the default file or specific file - params: - tagnames: - type: string - filename: - type: string - required: [tagnames] - additionalProperties: false - -delete-tags: - description: Delete the tag(s) from all files or specific file - params: - tagnames: - type: string - filename: - type: string - required: [tagnames] - additionalProperties: false - -list-tags: - description: List the tags from all files or specific file - params: - filename: - type: string - additionalProperties: false - -delete-all-tags: - description: Delete all tags from the default file or specific file - params: - filename: - type: string - additionalProperties: false - -update-test-scenarios: - description: Incorporate new test scenarios onto the charm; based on new content previously copied onto Abot node. - -update-framework: - description: Incorporate new framework binaries onto the charm; based on new content previously copied onto Abot node. - -update-diameter-params: - description: Update different diamater-related parameters into the Abot system - params: - cxserver-originhost: - type: string - cxserver_originrealm: - type: string - cxserver_desthost: - type: string - cxserver_clientIp: - type: string diff --git a/abot-epc-basic/actions/add-tags b/abot-epc-basic/actions/add-tags deleted file mode 100755 index d17bde3..0000000 --- a/abot-epc-basic/actions/add-tags +++ /dev/null @@ -1,54 +0,0 @@ -#!/bin/bash -# Action -Add Tags -set -eu -DIR=/etc/rebaca-test-suite/tags - -# Add tags specified by TAG_NAMES to the file specified by TAG_FILE_NAME -function add_tags_to_file() -{ - #Initialize new_tags variable with blank value. - new_tags= - - if [ -f $DIR/$TAG_FILE_NAME ]; then - new_tags=`cat $DIR/$TAG_FILE_NAME` - fi - - if [ -z "$TAG_NAMES" ]; then - juju-log "No tag names specified - none added" - else - juju-log "Tag names to be added: $TAG_NAMES" - - #Split the tags and add the @ symbols - IFS=', ' read -r -a array <<< "$TAG_NAMES" - for index in "${!array[@]}" - do - if [ -z "$new_tags" ]; then - new_tags="@${array[index]}" - else - #Add current tag only if not duplicate - current_tag="${array[index]}" - if [ `echo $new_tags | grep -c $current_tag` -eq 0 ]; then - new_tags="$new_tags,@$current_tag" - fi - fi - done - - #Add to new file if filename defined - echo -n "$new_tags" > $DIR/$TAG_FILE_NAME - - juju-log "Filename:$TAG_FILE_NAME, Tags:$new_tags" - fi -} - -TAG_NAMES=$(action-get tagnames) -TAG_FILE_NAME=$(action-get filename) - -if [ ! -z "$TAG_FILE_NAME" ]; then - juju-log "Tag file specified: $TAG_FILE_NAME" -else - #Assign default file name - TAG_FILE_NAME=tags -fi - -add_tags_to_file - diff --git a/abot-epc-basic/actions/delete-all-tags b/abot-epc-basic/actions/delete-all-tags deleted file mode 100755 index 96f00a6..0000000 --- a/abot-epc-basic/actions/delete-all-tags +++ /dev/null @@ -1,26 +0,0 @@ -#!/bin/bash -# Action -Delete all tags -set -e -DIR=/etc/rebaca-test-suite/tags - -# Delete multiple tags from a given file -function delete_all_tags_from_file() -{ - FILE_NAME=$1 - - #Delete the specified file - rm -f $DIR/$FILE_NAME - juju-log "Cleared all tags from file: $FILE_NAME" -} - - -TAG_FILE_NAME=$(action-get filename) - -if [ ! -z "$TAG_FILE_NAME" ]; then - juju-log "Tag file specified: $TAG_FILE_NAME" -else - #Assign default file name - TAG_FILE_NAME=tags -fi - -delete_all_tags_from_file $TAG_FILE_NAME diff --git a/abot-epc-basic/actions/delete-tags b/abot-epc-basic/actions/delete-tags deleted file mode 100755 index 969ceab..0000000 --- a/abot-epc-basic/actions/delete-tags +++ /dev/null @@ -1,51 +0,0 @@ -#!/bin/bash -# Action -Delete tags -set -e -DIR=/etc/rebaca-test-suite/tags - -# Delete multiple tags from a given file -function delete_tags_from_file() -{ - TAG_NAMES=$1 - FILE_NAME=$2 - - if [ ! -f $DIR/$FILE_NAME ]; then - juju-log "Filename($FILE_NAME) does not exist" - return - fi - - IFS=', ' read -r -a array <<< "$TAG_NAMES" - for index in "${!array[@]}" - do - tagname="${array[index]}" - - cat $DIR/$FILE_NAME | sed "s/,@$tagname//g" > $DIR/tmp - cp $DIR/tmp $DIR/$FILE_NAME - cat $DIR/$FILE_NAME | sed "s/@$tagname,//g" > $DIR/tmp - cp $DIR/tmp $DIR/$FILE_NAME - cat $DIR/$FILE_NAME | sed "s/@$tagname//g" > $DIR/tmp - cp $DIR/tmp $DIR/$FILE_NAME - - rm $DIR/tmp - done - - UPDATED_TAGS=`cat $DIR/$FILE_NAME` - juju-log "Filename:$FILE_NAME, Tags:$UPDATED_TAGS" -} - -TAG_NAMES=$(action-get tagnames) -TAG_FILE_NAME=$(action-get filename) - -if [ -z "$TAG_FILE_NAME" ] ; then - juju-log "No filename specified; will delete specified tags from all files" - - for filename in `ls -t $DIR` - do - delete_tags_from_file $TAG_NAMES $filename - done -else - juju-log "Deleting specified tags from $TAG_FILE_NAME" - delete_tags_from_file $TAG_NAMES $TAG_FILE_NAME -fi - - diff --git a/abot-epc-basic/actions/list-tags b/abot-epc-basic/actions/list-tags deleted file mode 100755 index e6078c0..0000000 --- a/abot-epc-basic/actions/list-tags +++ /dev/null @@ -1,31 +0,0 @@ -#!/bin/bash -# Action -List tags -set -eu - -DIR=/etc/rebaca-test-suite/tags -TAG_FILE_NAME=$(action-get filename) - - -function list_tags_from_file() -{ - tagFile=$1 - - if [ -f $DIR/$tagFile ]; then - tags=`cat $DIR/$tagFile` - juju-log "Filename:$tagFile, Tags:$tags" - fi -} - - -if [ ! -z "$TAG_FILE_NAME" ]; then - juju-log "Tag file specified: $TAG_FILE_NAME" - list_tags_from_file $TAG_FILE_NAME -else - juju-log "Tag file not specified, will list tags from all files" - for tagFile in `ls -t $DIR` - do - list_tags_from_file $tagFile - done -fi - - diff --git a/abot-epc-basic/actions/run b/abot-epc-basic/actions/run deleted file mode 100755 index 9d2f48b..0000000 --- a/abot-epc-basic/actions/run +++ /dev/null @@ -1,84 +0,0 @@ -#!/bin/bash -# Action -Run -set -eu - -ABOT_DEPLOY_DIR=/etc/rebaca-test-suite -ELASTIC_FILE_NAME=${ABOT_DEPLOY_DIR}/es-conf -ABOT_TAGS_DIR=${ABOT_DEPLOY_DIR}/tags -tags_defined=1 - -TAG_NAMES=$(action-get tagnames) -TAG_FILE_NAME=$(action-get filename) - -if [ ! -z "$TAG_FILE_NAME" ]; then - juju-log "Tag file specified: $TAG_FILE_NAME" -else - #Assign default file name - TAG_FILE_NAME=tags -fi - -new_tags= - -if [ -z "${TAG_NAMES}" ]; then - juju-log "No tag names specified" - tags_defined=0 - #Check if filename specified and file exists - if [ ! -z "${TAG_FILE_NAME}" ] && [ -f ${ABOT_TAGS_DIR}/${TAG_FILE_NAME} ] ; then - tags=`cat ${ABOT_TAGS_DIR}/${TAG_FILE_NAME}` - fi -else - juju-log "Tag names: ${TAG_NAMES}" - - #Split the tags and add the @ symbols - IFS=', ' read -r -a array <<< "${TAG_NAMES}" - for index in "${!array[@]}" - do - if [ $index -eq 0 ]; then - new_tags="@${array[0]}" - else - #Add current tag only if not duplicate - current_tag="${array[index]}" - if [ `echo ${new_tags} | grep -c ${current_tag}` -eq 0 ]; then - new_tags="${new_tags},@${current_tag}" - fi - fi - done - - #Assign the tags variable accordingly. - tags=${new_tags} -fi - -if [ -z "${tags}" ]; then - juju-log "No tags found for execution" -else - juju-log "Running the test with the tags ${tags}" - - ${ABOT_DEPLOY_DIR}/bin/tags_update ${tags} - - #Removing previous run artifacts - rm -rf ${ABOT_DEPLOY_DIR}/log/*.* - - (cd ${ABOT_DEPLOY_DIR}; \ - #perform mvn clean first - mvn clean; \ - #Run the install target now - mvn install -Dtest=RunScenarios -DrootDir=com/rebaca/abot || true) - - #Copying log artifacts to appropriate artifacts folder - LAST_REPORT_DIR="$(ls -td -- ${ABOT_DEPLOY_DIR}/artifacts/*/ | head -n 1)" - cp -R ${ABOT_DEPLOY_DIR}/log/*.* ${LAST_REPORT_DIR} - - #Process Test results and post to ElasticSearch - touch ${ELASTIC_FILE_NAME} - ES_CONF=`cat ${ELASTIC_FILE_NAME}` - if [ -z "${ES_CONF}" ]; then - echo "No ES conf defined - hence result not pushed to ES" - else - echo "Pushing Test result to ES" - LAST_REPORT_FILE_NAME=`basename ${LAST_REPORT_DIR}` - echo "Renaming Test result to ${LAST_REPORT_FILE_NAME}.json" - cp ${ABOT_DEPLOY_DIR}/artifacts/TestResults.json ${ABOT_DEPLOY_DIR}/artifacts/${LAST_REPORT_FILE_NAME}.json - python ${ABOT_DEPLOY_DIR}/bin/es-feed.py --elastic ${ES_CONF} --mappings ${ABOT_DEPLOY_DIR}/bin/mappings.json \ - --filepath ${ABOT_DEPLOY_DIR}/artifacts/${LAST_REPORT_FILE_NAME}.json - fi -fi diff --git a/abot-epc-basic/actions/run_local b/abot-epc-basic/actions/run_local deleted file mode 100755 index b9d5bbb..0000000 --- a/abot-epc-basic/actions/run_local +++ /dev/null @@ -1,79 +0,0 @@ -#!/bin/bash -# Action -Run -set -eu - -ABOT_DEPLOY_DIR=/etc/rebaca-test-suite -ELASTIC_FILE_NAME=${ABOT_DEPLOY_DIR}/es-conf - -tags_defined=1 -no_report_needed=0 - - -new_tags= - -if [ $# -gt 0 ]; then - TAG_NAMES="$1" - # echo "Tag(s) = ${TAG_NAMES}" -else - # echo "No tags specified - exiting" - exit 1 -fi - -if [ ! -z "${TAG_NAMES}" ]; then - # echo "Tag names: ${TAG_NAMES}" - - #Split the tags and add the @ symbols - IFS=', ' read -r -a array <<< "${TAG_NAMES}" - for index in "${!array[@]}" - do - if [ ${index} -eq 0 ]; then - new_tags="@${array[0]}" - else - #Add current tag only if not duplicate - current_tag="${array[index]}" - if [ `echo ${new_tags} | grep -c ${current_tag}` -eq 0 ]; then - new_tags="${new_tags},@${current_tag}" - fi - fi - done - - #Assign the tags variable accordingly. - tags=$new_tags -fi - -if [ -z "$tags" ]; then - echo "No tags found for execution" -else - echo "Running the test with the tags $tags" - - # status-set active "Executing run action" - ${ABOT_DEPLOY_DIR}/bin/tags_update_local $tags - - # Removing previous run artifacts - rm -rf ${ABOT_DEPLOY_DIR}/log/*.* - - (cd ${ABOT_DEPLOY_DIR}; \ - # Perform mvn clean first - mvn clean; \ - # Run the install target now - mvn install -Dtest=RunScenarios -DrootDir=com/rebaca/abot || true) - - # Copying log artifacts to appropriate artifacts folder - LAST_REPORT_DIR="$(ls -td -- ${ABOT_DEPLOY_DIR}/artifacts/*/ | head -n 1)" - - cp -R ${ABOT_DEPLOY_DIR}/log/*.* ${LAST_REPORT_DIR} - - # Process Test results and post to ElasticSearch - touch ${ELASTIC_FILE_NAME} - ES_CONF=`cat ${ELASTIC_FILE_NAME}` - if [ -z "${ES_CONF}" ]; then - echo "No ES conf defined - hence result not pushed to ES" - else - echo "Pushing Test result to ES" - LAST_REPORT_FILE_NAME=`basename ${LAST_REPORT_DIR}` - echo "Renaming Test result to ${LAST_REPORT_FILE_NAME}.json" - cp ${ABOT_DEPLOY_DIR}/artifacts/TestResults.json ${ABOT_DEPLOY_DIR}/artifacts/${LAST_REPORT_FILE_NAME}.json - python ${ABOT_DEPLOY_DIR}/bin/es-feed.py --elastic ${ES_CONF} --mappings ${ABOT_DEPLOY_DIR}/bin/mappings.json \ - --filepath ${ABOT_DEPLOY_DIR}/artifacts/${LAST_REPORT_FILE_NAME}.json - fi -fi diff --git a/abot-epc-basic/actions/update-diameter-params b/abot-epc-basic/actions/update-diameter-params deleted file mode 100755 index 39ebf55..0000000 --- a/abot-epc-basic/actions/update-diameter-params +++ /dev/null @@ -1,46 +0,0 @@ -#!/bin/bash -# Action -Run -set -eu - -ABOT_DIR=/var/lib/abot-epc-basic - -S6ASERVER_ORIGINHOST=$(action-get s6aserver-originhost) -S6ASERVER_ORIGINREALM=$(action-get s6aServer-originrealm) -S6ASERVER_DESTHOST=$(action-get s6aserver-desthost) -S6ASERVER_CLIENT_IP=$(action-get s6aserver-clientip) - - -if [ ! -z "$S6ASERVER_ORIGINHOST" ]; then - juju-log "Setting S6aServer_OriginHost=$S6ASERVER_ORIGINHOST" - sed -i "s/S6aServer.OriginHost=[^ ]*/S6aServer.OriginHost=$S6ASERVER_ORIGINHOST/" ${ABOT_DIR}/config/ABotConfig.properties - if [ `grep -c $S6ASERVER_ORIGINHOST /etc/hosts` == "0" ]; then - public_hostname=$(unit-get private-address) - juju-log "Public Hostname: $public_hostname" - public_ip_address=`dig +short $public_hostname` - juju-log "Inserting originhost($S6ASERVER_ORIGINHOST) into /etc/hosts file for IP $public_ip_address" - echo "$public_ip_address $S6ASERVER_ORIGINHOST" >> /etc/hosts - fi -fi - -if [ ! -z "$S6ASERVER_ORIGINREALM" ]; then - juju-log "S6aServer_OriginRealm=$S6ASERVER_ORIGINREALM" - sed -i "s/S6aServer.OriginRealm=[^ ]*/S6aServer.OriginRealm=$S6ASERVER_ORIGINREALM/" ${ABOT_DIR}/config/ABotConfig.properties -fi - - -if [ ! -z "$S6ASERVER_DESTHOST" ]; then - juju-log "S6aServer_DestHost=$S6ASERVER_DESTHOST" - sed -i "s/S6aServer.DestHost=[^ ]*/S6aServer.DestHost=$S6ASERVER_DESTHOST/" ${ABOT_DIR}/config/ABotConfig.properties -fi - -if [ ! -z "$S6ASERVER_CLIENT_IP" ]; then - juju-log "S6aServer_Client_IP=$S6ASERVER_CLIENT_IP" - #Get the destination host - S6ASERVER_DEST_HOST=`grep "S6aServer.DestHost" ${ABOT_DIR}/config/config.properties | cut -d= -f2` - if [ ! -z $S6ASERVER_DEST_HOST ]; then - if [ `grep -c $S6ASERVER_CLIENT_IP /etc/hosts` == "0" ]; then - juju-log "Setting $S6ASERVER_CLIENT_IP against $S6ASERVER_DEST_HOST in /etc/hosts" - echo "$S6ASERVER_CLIENT_IP $S6ASERVER_DEST_HOST" >> /etc/hosts - fi - fi -fi diff --git a/abot-epc-basic/config.yaml b/abot-epc-basic/config.yaml deleted file mode 100644 index ee92c09..0000000 --- a/abot-epc-basic/config.yaml +++ /dev/null @@ -1,14 +0,0 @@ -options: - tags: - type: string - default: "local-commands" - description: "Tag for the feature file to run" - abot_app: - type: string - default: "abot-epc-basic" - description: "The ABot Application" - abot_version: - type: string - default: "3.1.0" - description: "The ABot Version" - diff --git a/abot-epc-basic/copyright b/abot-epc-basic/copyright deleted file mode 100644 index 883a93a..0000000 --- a/abot-epc-basic/copyright +++ /dev/null @@ -1,8 +0,0 @@ -Project Automated Behaviour-Oriented Tester(ABOT) - -Copyright (C) 2016 Rebaca Technologies Inc. - - This package contains software that is confidential and proprietary to - Rebaca Technologies. Any reproduction, disclosure, or use in whole or - in part is expressly prohibited, except as may be specifically - authorized by prior written agreement. diff --git a/abot-epc-basic/hooks/config-changed b/abot-epc-basic/hooks/config-changed deleted file mode 100755 index 33ffe68..0000000 --- a/abot-epc-basic/hooks/config-changed +++ /dev/null @@ -1,41 +0,0 @@ -#!/bin/bash -# config-changed occurs everytime a new configuration value is updated (juju set) -set -e - -ABOT_DEPLOY_DIR=/etc/rebaca-test-suite -TAG_DIR=${ABOT_DEPLOY_DIR}/tags - -tags=$(config-get tags) - -if [ -n $TAG_DIR/tags ]; then - # Install the user's supplied hashes - if [ -f $TAG_DIR/tags ]; then - existing_tags=`cat $TAG_DIR/tags` - juju-log "Current tags: $existing_tags" - fi - juju-log "Installing the tags obtained from config: $tags" - echo -n "@$tags" > $TAG_DIR/tags -fi - -private_address=$(unit-get private-address) -if [[ `dig +short $private_address` =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]] -then - juju-log "Verifying $private_address in dns " - DIGSHORT="dig +short" -elif [[ "$private_address" =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]] -then - DIGSHORT=echo -else - juju-log "Unable to fetch private_address ..." - exit 1 -fi -ip=`$DIGSHORT $private_address` - -if [ ! -z $ip ]; then - sed -i "s/ABOT.SecureShell.IPAddress=[^ ]*/ABOT.SecureShell.IPAddress=$ip/" ${ABOT_DEPLOY_DIR}/config/ABotConfig.properties - sed -i "s#ABOT.ENB.ENB_IPV4_ADDRESS_FOR_S1_MME=[^ ]*#ABOT.ENB.ENB_IPV4_ADDRESS_FOR_S1_MME=$ip/24#" ${ABOT_DEPLOY_DIR}/config/abot-epc-basic_defaults.conf - sed -i "s#ABOT.ENB.ENB_IPV4_ADDRESS_FOR_S1U=[^ ]*#ABOT.ENB.ENB_IPV4_ADDRESS_FOR_S1U=$ip/24#" ${ABOT_DEPLOY_DIR}/config/abot-epc-basic_defaults.conf -else - juju-log "IP Address of local unit not available - exiting with error" - exit 2 -fi diff --git a/abot-epc-basic/hooks/epc-relation-changed b/abot-epc-basic/hooks/epc-relation-changed deleted file mode 100755 index f35a032..0000000 --- a/abot-epc-basic/hooks/epc-relation-changed +++ /dev/null @@ -1,34 +0,0 @@ -#!/bin/bash - -#called on every epc relation change - -ABOT_DEPLOY_DIR=/etc/rebaca-test-suite -set -ex - -mobile_country_code=`relation-get MCC` -mobile_network_code=`relation-get MNC` -mme_ip_address_ipv4=`relation-get mme_ip` -epc_running=`relation-get epc_running` -mme_running=`relation-get mme_running` -spgw_ip_address_ipv4=`relation-get spgw_ip_address_ipv4` -unhex_key=`relation-get opc-key` - - -sed -i "s/ABOT.ENB.mobile_country_code=[^ ]*/ABOT.ENB.mobile_country_code=$mobile_country_code/" ${ABOT_DEPLOY_DIR}/config/abot-epc-basic_defaults.conf -sed -i "s/ABOT.ENB.mobile_network_code=[^ ]*/ABOT.ENB.mobile_network_code=$mobile_network_code/" ${ABOT_DEPLOY_DIR}/config/abot-epc-basic_defaults.conf -sed -i "s/ABOT.ENB.mme_ip_address_ipv4=[^ ]*/ABOT.ENB.mme_ip_address_ipv4=$mme_ip_address_ipv4/" ${ABOT_DEPLOY_DIR}/config/abot-epc-basic_defaults.conf -sed -i "s/MME.SecureShell.IPAddress=[^ ]*/MME.SecureShell.IPAddress=$mme_ip_address_ipv4/" ${ABOT_DEPLOY_DIR}/config/ABotConfig.properties -sed -i -e "s/OPC=\".*\";/OPC=\"$unhex_key\";/" ${ABOT_DEPLOY_DIR}/oaisim/config/ue_eurecom_test_sfr.conf - -if [ "$spgw_ip_address_ipv4" != "" ]; then -sed -i "s/SPGW.SecureShell.IPAddress=[^ ]*/SPGW.SecureShell.IPAddress=${spgw_ip_address_ipv4}/" ${ABOT_DEPLOY_DIR}/config/ABotConfig.properties -fi - -if [ "$epc_running" == "yes" ]; then - status-set active "ABot ready! EPC relation established, proceed to run tests..." - exit 0 -elif [ "$mme_running" == "yes" ]; then - status-set active "ABot ready! MME relation established, proceed to run tests..." - exit 0 -fi -status-set blocked "ABot blocked! EPC relation not established, unable to get to active state" diff --git a/abot-epc-basic/hooks/epc-relation-departed b/abot-epc-basic/hooks/epc-relation-departed deleted file mode 100755 index 2e9a02c..0000000 --- a/abot-epc-basic/hooks/epc-relation-departed +++ /dev/null @@ -1,11 +0,0 @@ -#!/bin/bash - -ABOT_DEPLOY_DIR=/etc/rebaca-test-suite -juju-log "Removing relation with epc" - -sed -i "s/ABOT.ENB.mobile_country_code=[^ ]*/ABOT.ENB.mobile_country_code=/" ${ABOT_DEPLOY_DIR}/config/ABotConfig.properties -sed -i "s/ABOT.ENB.mobile_network_code=[^ ]*/ABOT.ENB.mobile_network_code=/" ${ABOT_DEPLOY_DIR}/config/ABotConfig.properties -sed -i "s/ABOT.ENB.mme_ip_address_ipv4=[^ ]*/ABOT.ENB.mme_ip_address_ipv4=192.168.255.255/" ${ABOT_DEPLOY_DIR}/config/ABotConfig.properties -sed -i "s/MME.SecureShell.IPAddress=[^ ]*/MME.SecureShell.IPAddress=192.168.255.255/" ${ABOT_DEPLOY_DIR}/config/ABotConfig.properties - -status-set blocked "ABot blocked! Waiting for epc relation" diff --git a/abot-epc-basic/hooks/epc-relation-joined b/abot-epc-basic/hooks/epc-relation-joined deleted file mode 100755 index a8e3803..0000000 --- a/abot-epc-basic/hooks/epc-relation-joined +++ /dev/null @@ -1,9 +0,0 @@ -#!/bin/bash - -set -eux - -ABOT_DEPLOY_DIR=/etc/rebaca-test-suite -juju-log "Setting EPC charm relation variables" -relation-set TAC=1 -sed -i "s/ABOT.ENB.tracking_area_code=[^ ]*/ABOT.ENB.tracking_area_code=1/" ${ABOT_DEPLOY_DIR}/config/ABotConfig.properties - diff --git a/abot-epc-basic/hooks/install b/abot-epc-basic/hooks/install deleted file mode 100755 index 70e22a7..0000000 --- a/abot-epc-basic/hooks/install +++ /dev/null @@ -1,24 +0,0 @@ -#!/bin/bash -# Here do anything needed to install the service -# i.e. apt-get install -y foo or bzr branch http://myserver/mycode /srv/webroot -# Make sure this hook exits cleanly and is idempotent, common problems here are -# failing to account for a debconf question on a dependency, or trying to pull -# from github without installing git first. - -set -eu - -abot_app=`config-get abot_app` -abot_version=`config-get abot_version` - -ABOT_DEPLOY_DIR=/etc/rebaca-test-suite - -(cd ${CHARM_DIR}; ./install-abot.sh -a ${abot_app} -v ${abot_version} -nc) - -# Change permissions of the ABOT_DEPLOY_DIR folders -chown -R ubuntu:ubuntu `readlink ${ABOT_DEPLOY_DIR}` - -open-port 80/tcp -open-port 5000/tcp - -juju-log "ABot Install complete!" -status-set maintenance "ABot installed! Waiting to start..." diff --git a/abot-epc-basic/hooks/ssh-abot-relation-changed b/abot-epc-basic/hooks/ssh-abot-relation-changed deleted file mode 100755 index 85ac998..0000000 --- a/abot-epc-basic/hooks/ssh-abot-relation-changed +++ /dev/null @@ -1,10 +0,0 @@ -#!/bin/bash -set -e - -if [ -f /home/ubuntu/.ssh/id_rsa.pub ]; - then - ssh_key=`cat /home/ubuntu/.ssh/id_rsa.pub` - ssh_key=`echo "$ssh_key" | sed 's/\r//'` - relation-set ssh-key-abotepc="$ssh_key" -fi - diff --git a/abot-epc-basic/hooks/ssh-abot-relation-joined b/abot-epc-basic/hooks/ssh-abot-relation-joined deleted file mode 100755 index 4fc9194..0000000 --- a/abot-epc-basic/hooks/ssh-abot-relation-joined +++ /dev/null @@ -1,39 +0,0 @@ -#!/bin/bash -set -e -generate-ssh_key(){ - -if [ ! -f /home/ubuntu/.ssh/id_rsa.pub ]; - then - ssh-keygen -t rsa -N "" -f /root/.ssh/id_rsa - pub_key=`cat /root/.ssh/id_rsa.pub` - echo "$pub_key" | sed 's/\r//' >> /root/.ssh/authorized_keys - else - pub_key=`cat /root/.ssh/id_rsa.pub` - echo "$pub_key" | sed 's/\r//' >> /root/.ssh/authorized_keys -fi - -if [ ! -f /home/ubuntu/.ssh/id_rsa.pub ]; - then - sudo -u ubuntu bash << EOF - ssh-keygen -t rsa -N "" -f /home/ubuntu/.ssh/id_rsa -EOF - pub_key=`cat /home/ubuntu/.ssh/id_rsa.pub` - echo "$pub_key" | sed 's/\r//' >> /home/ubuntu/.ssh/authorized_keys - else - pub_key=`cat /home/ubuntu/.ssh/id_rsa.pub` - echo "$pub_key" | sed 's/\r//' >> /home/ubuntu/.ssh/authorized_keys -fi -} - -if [ -f /home/ubuntu/.ssh/id_rsa.pub ]; - then - ssh_key=`cat /home/ubuntu/.ssh/id_rsa.pub` - ssh_key=`echo "$ssh_key" | sed 's/\r//'` - relation-set ssh-key-abotepc="$ssh_key" - else - generate-ssh_key - ssh_key=`cat /home/ubuntu/.ssh/id_rsa.pub` - ssh_key=`echo "$ssh_key" | sed 's/\r//'` - relation-set ssh-key-abotepc="$ssh_key" -fi - diff --git a/abot-epc-basic/hooks/start b/abot-epc-basic/hooks/start deleted file mode 100755 index 48458c8..0000000 --- a/abot-epc-basic/hooks/start +++ /dev/null @@ -1,30 +0,0 @@ -#!/bin/bash -# Here put anything that is needed to start the service. -# Note that currently this is run directly after install -set -eu - -# Generate ssh key for root user -if [ ! -f /root/.ssh/id_rsa.pub ]; - then - ssh-keygen -t rsa -N "" -f /root/.ssh/id_rsa - pub_key=`cat /root/.ssh/id_rsa.pub` - echo "${pub_key}" | sed 's/\r//' >> /root/.ssh/authorized_keys - else - pub_key=`cat /root/.ssh/id_rsa.pub` - echo "${pub_key}" | sed 's/\r//' >> /root/.ssh/authorized_keys -fi - -# Generate ssh key for ubuntu user -if [ ! -f /home/ubuntu/.ssh/id_rsa.pub ]; - then - sudo -u ubuntu bash << EOF - ssh-keygen -t rsa -N "" -f /home/ubuntu/.ssh/id_rsa -EOF - pub_key=`cat /home/ubuntu/.ssh/id_rsa.pub` - echo "${pub_key}" | sed 's/\r//' >> /home/ubuntu/.ssh/authorized_keys - else - pub_key=`cat /home/ubuntu/.ssh/id_rsa.pub` - echo "${pub_key}" | sed 's/\r//' >> /home/ubuntu/.ssh/authorized_keys -fi - -status-set maintenance "ABot updated! Setup relations to get into active state..." diff --git a/abot-epc-basic/hooks/stop b/abot-epc-basic/hooks/stop deleted file mode 100755 index 1068769..0000000 --- a/abot-epc-basic/hooks/stop +++ /dev/null @@ -1,7 +0,0 @@ -#!/bin/bash -# This will be run when the service is being torn down, allowing you to disable -# it in various ways.. -# For example, if your web app uses a text file to signal to the load balancer -# that it is live... you could remove it and sleep for a bit to allow the load -# balancer to stop sending traffic. -# rm /srv/webroot/server-live.txt && sleep 30 diff --git a/abot-epc-basic/hooks/upgrade-charm b/abot-epc-basic/hooks/upgrade-charm deleted file mode 100755 index 5413ad9..0000000 --- a/abot-epc-basic/hooks/upgrade-charm +++ /dev/null @@ -1,28 +0,0 @@ -#!/bin/bash -# This hook is executed each time a charm is upgraded after the new charm -# contents have been unpacked -# Best practice suggests you execute the hooks/install and -# hooks/config-changed to ensure all updates are processed -set -eu - -abot_app=`config-get abot_app` -abot_version=`config-get abot_version` - -ABOT_DEPLOY_DIR=/etc/rebaca-test-suite - -(cd ${CHARM_DIR}; ./install-abot.sh -a ${abot_app} -v ${abot_version} -nc) - -# Change permissions of the ABOT_DEPLOY_DIR folders -chown -R ubuntu:ubuntu `readlink ${ABOT_DEPLOY_DIR}` - -if [ ! -f ~/.ssh/id_rsa.pub ]; - then - ssh-keygen -t rsa -N "" -f ~/.ssh/id_rsa - pub_key=`cat ~/.ssh/id_rsa.pub` - echo "$pub_key" | sed 's/\r//' >> ~/.ssh/authorized_keys -fi - -open-port 80/tcp - -juju-log "ABot update complete!" -status-set maintenance "ABot updated! Reset relations to get into active state" diff --git a/abot-epc-basic/icon.svg b/abot-epc-basic/icon.svg deleted file mode 100644 index 51b1d87..0000000 --- a/abot-epc-basic/icon.svg +++ /dev/null @@ -1,26 +0,0 @@ - - - - - Created by potrace 1.10, written by Peter Selinger 2001-2011 - - - - - - - - - - - - - - - - - - Layer 2 - - - \ No newline at end of file diff --git a/abot-epc-basic/install-abot.sh b/abot-epc-basic/install-abot.sh deleted file mode 100755 index ca01283..0000000 --- a/abot-epc-basic/install-abot.sh +++ /dev/null @@ -1,362 +0,0 @@ -#!/bin/bash - -ABOT_DEPLOY_DIR=/etc/rebaca-test-suite - -red='\E[31m' -green='\E[32m' -blue='\E[1;34m' -reset_color='\E[00m' - -# Green echo -function echo_green { - message=$1 - echo -e "${green}${message}${reset_color}" -} - -# Red echo -function echo_red { - message=$1 - echo -e "${red}${message}${reset_color}" -} - -# Blue echo -function echo_blue { - message=$1 - echo -e "${blue}${message}${reset_color}" -} - -function exit_on_error { - LAST_RESULT=$? - MESSAGE=$1 - if [ ${LAST_RESULT} -eq 0 ] - then - if [ "$2" != "ECHO=ON_FAILURE_ONLY" ]; then - echo_green "<< ABOT Installer - Success: ${MESSAGE} >>" - fi - else - if [ "$2" != "ECHO=ON_WARNING_ONLY" ]; then - echo_red "<< ABOT Installer - Error: ${MESSAGE} >>" - exit 1 - else - echo_blue "<< ABOT Installer - Warning: ${MESSAGE} >>" - fi - fi -} - - -function usage { - echo_blue "./install-abot " - echo_blue "Run with sudo privileges" - echo_blue "Example: sudo ./install-abot.sh -a abot-ims-basic -v 3.1.0 -nc" - echo "" - echo "options" - echo "" - echo "-a | --app) application name: abot-ims-basic | abot-epc-basic | abot-functest-basic | abot-volte-basic" - echo "-v | --version) version: 3.1.0" - echo "-et | --external-tester) external tester: ixia | dsTest" - echo "-nc | --null-configuration) No configuration done during deployment of system" - echo "" - exit 0 -} - -function check_input { - case $APP_NAME in - abot-ims-basic|abot-epc-basic|abot-functest-basic|abot-volte-basic) true;; - *) usage;; - esac - exit_on_error "Application Name $APP_NAME" - - case $APP_VERSION in - 3.1.0) true;; - *) usage;; - esac - exit_on_error "Version $APP_VERSION" - - case $APP_EXTERNAL_TESTER in - none) true;; - ixia) true;; - *) usage;; - esac - exit_on_error "External Tester $APP_EXTERNAL_TESTER" -} - -function install_oai_packages { - source ${ABOT_DEPLOY_DIR}/oaisim/bin/build_helper_rebaca - set_openair_deploy_env - check_install_oai_software - - # ue_ip driver compilation - { - cd ${ABOT_DEPLOY_DIR}/oaisim/openair2/NETWORK_DRIVER/UE_IP; - cp Makefile.rebaca Makefile; - make clean &> /dev/null; - make; - cp -f ue_ip.ko ${ABOT_DEPLOY_DIR}/oaisim/bin; - } &> ${ABOT_DEPLOY_DIR}/oaisim/cmake_targets/log/ue_ip.txt - - exit_on_error "Installing oaisim" - - # Not installing additional tools - # for the time being - - # check_install_additional_tools -} - -function install_ixia_packages { - pip install pyyaml - pip install paramiko - pip install dnspython - pip install scp - pip install jinja2 - pip install httplib2 - - exit_on_error "Installing Ixia python packages" -} - -function set_ixia_ims_information { - PRIVATE_IP=`ip addr | grep 'state UP' -A2 | tail -n1 | awk '{print $2}' | cut -f1 -d'/'` - sed -i "s/ABOT.SecureShell.IPAddress=[^ ]*/ABOT.SecureShell.IPAddress=${PRIVATE_IP}/" ${ABOT_DEPLOY_DIR}/config/ABotConfig.properties - - read -p "IMS DNS IP Address (e.g. IMS DNS Server): " DNS_IP; DNS_IP=${DNS_IP:="192.168.255.255"} - echo $DNS_IP > ${ABOT_DEPLOY_DIR}/dns_ip - read -p "IMS Clearwater Ellis IP Address (e.g. Clearwater Ellis IP): " ELLIS_IP; ELLIS_IP=${ELLIS_IP:=192.168.255.255} - echo $ELLIS_IP > ${ABOT_DEPLOY_DIR}/ellis_ip - read -p "IMS IxLoad IP Address: " IXLOAD_IP; IXLOAD_IP=${IXLOAD_IP:=192.168.255.255} - echo $IXLOAD_IP > ${ABOT_DEPLOY_DIR}/ixload_ip - read -p "IMS IxChassis IP Address: " IXCHASSIS_IP; IXCHASSIS_IP=${IXCHASSIS_IP:=192.168.255.255} - echo $IXCHASSIS_IP > ${ABOT_DEPLOY_DIR}/ixchassis_ip - read -p "IMS IxCard1 IP Address: " IXCARD1_IP; IXCARD1_IP=${IXCARD1_IP:=192.168.255.255} - echo $IXCARD1_IP > ${ABOT_DEPLOY_DIR}/ixcard1_ip - read -p "IMS IxCard2 IP Address: " IXCARD2_IP; IXCARD2_IP=${IXCARD2_IP:=192.168.255.255} - echo $IXCARD2_IP > ${ABOT_DEPLOY_DIR}/ixcard2_ip - - exit_on_error "Setting Ixia Information" -} - -function set_sut_ims_information { - PRIVATE_IP=`ip addr | grep 'state UP' -A2 | tail -n1 | awk '{print $2}' | cut -f1 -d'/'` - sed -i "s/ABOT.SecureShell.IPAddress=[^ ]*/ABOT.SecureShell.IPAddress=${PRIVATE_IP}/" ${ABOT_DEPLOY_DIR}/config/ABotConfig.properties - sed -i "/ip=/s/[^ ]*/ip=${PRIVATE_IP}/" ${ABOT_DEPLOY_DIR}/bin/sip - sed -i "/ip=/s/[^ ]*/ip=${PRIVATE_IP}/" ${ABOT_DEPLOY_DIR}/bin/sip_imsi_domain - sed -i "/ip=/s/[^ ]*/ip=${PRIVATE_IP}/" ${ABOT_DEPLOY_DIR}/bin/sip-stress - - PUBLIC_HOSTNAME=`hostname | cut -d"." -f1` - - read -p "IMS DNS IP Address (e.g. IMS DNS Server): " DNS_IP; DNS_IP=${DNS_IP:="192.168.255.255"} - - read -p "IMS SIP Domain (default: rebaca.local): " ZONE; ZONE=${ZONE:="rebaca.local"} - echo $ZONE > ${ABOT_DEPLOY_DIR}/sip_domain - - read -p "IMS HSS Mirror IP Address: " IMS_HSS_MIRROR_IP; IMS_HSS_MIRROR_IP=${IMS_HSS_MIRROR_IP:="192.168.255.255"} - sed -i "s/HSS_MIRROR.SecureShell.IPAddress=[^ ]*/HSS_MIRROR.SecureShell.IPAddress=${IMS_HSS_MIRROR_IP}/" \ - ${ABOT_DEPLOY_DIR}/config/ABotConfig.properties - - read -p "IMS P-CSCF IP Address: " IMS_PCSCF_IP; IMS_PCSCF_IP=${IMS_PCSCF_IP:="192.168.255.255"} - sed -i "s/P_CSCF.SecureShell.IPAddress=[^ ]*/P_CSCF.SecureShell.IPAddress=${IMS_PCSCF_IP}/" \ - ${ABOT_DEPLOY_DIR}/config/ABotConfig.properties - echo -n ${IMS_PCSCF_IP} > ${ABOT_DEPLOY_DIR}/sip_server - - exit_on_error "Setting IMS SUT Information" -} - -function set_sut_epc_information { - PRIVATE_IP=`ip addr | grep 'state UP' -A2 | tail -n1 | awk '{print $2}' | cut -f1 -d'/'` - - read -p "EPC Mobile Network Code (default: 208): " MCC; MCC=${MCC:="208"} - sed -i "s/ABOT.ENB.mobile_country_code=[^ ]*/ABOT.ENB.mobile_country_code=${MCC}/" \ - ${ABOT_DEPLOY_DIR}/config/abot-epc-basic_defaults.conf - - read -p "EPC Mobile Network Code (default: 93): " MNC; MNC=${MNC:="93"} - sed -i "s/ABOT.ENB.mobile_network_code=[^ ]*/ABOT.ENB.mobile_network_code=${MNC}/" \ - ${ABOT_DEPLOY_DIR}/config/abot-epc-basic_defaults.conf - - read -p "MME IP Address: " EPC_MME_IP; EPC_MME_IP=${EPC_MME_IP:="192.168.255.255"} - sed -i "s/ABOT.ENB.mme_ip_address_ipv4=[^ ]*/ABOT.ENB.mme_ip_address_ipv4=${EPC_MME_IP}/" \ - ${ABOT_DEPLOY_DIR}/config/abot-epc-basic_defaults.conf - sed -i "s/MME.SecureShell.IPAddress=[^ ]*/MME.SecureShell.IPAddress=${EPC_MME_IP}/" \ - ${ABOT_DEPLOY_DIR}/config/ABotConfig.properties - - read -p "EPC Subscriber OPC (e.g. 79477a68055263cb0c3052057e0d20b8): " EPC_OPC; EPC_OPC=${EPC_OPC:="79477a68055263cb0c3052057e0d20b8"} - sed -i -e "s/OPC=\".*\";/OPC=\"${EPC_OPC}\";/" \ - ${ABOT_DEPLOY_DIR}/oaisim/config/ue_eurecom_test_sfr.conf - -} - -function configure_dnsmasq { - # Update DNS server using DNSMASQ - echo nameserver ${DNS_IP} > /etc/dnsmasq.resolv.conf - grep -v ^RESOLV_CONF= /etc/default/dnsmasq > /tmp/dnsmasq.$$ - mv /tmp/dnsmasq.$$ /etc/default/dnsmasq - echo RESOLV_CONF=/etc/dnsmasq.resolv.conf >> /etc/default/dnsmasq - service dnsmasq restart - sleep 5 - # Change Resolvconf order and put dnsmasq on top - dnsmasq=lo.dnsmasq - resolvconf=/etc/resolvconf - loinet=`grep ^[a-z] /etc/resolvconf/interface-order | head -n1` - if [ $loinet != $dnsmasq ] - then - cp /etc/resolvconf/interface-order /etc/resolvconf/interface-order.orig - sed '/'$dnsmasq'/d' /etc/resolvconf/interface-order.orig > /etc/resolvconf/interface-order - sed -i '/'$loinet'/i lo.dnsmasq' /etc/resolvconf/interface-order - service dnsmasq restart - resolvconf -u - else - false - exit_on_error "dnsmasq interface order $loinet $dnsmasq" - fi - exit_on_error "Setup dnsmasq" -} - -function verify_dns_records { - if [[ `dig +short ${PRIVATE_IP}` =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]] - then - exit_on_error "Verifying ${PRIVATE_IP} in dns" - elif [[ "${PRIVATE_IP}" =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]] - then - exit_on_error "Verifying ${PRIVATE_IP} in dns" - else - cp /etc/resolvconf/interface-order.orig /etc/resolvconf/interface-order - resolvconf -u - false - exit_on_error "Unable to fetch ${PRIVATE_IP} dns order changed to original" - fi -} - -function set_keys { - # Generate ssh key for root user - if [ ! -f /root/.ssh/id_rsa.pub ]; - then - ssh-keygen -t rsa -N "" -f /root/.ssh/id_rsa - pub_key=`cat /root/.ssh/id_rsa.pub` - echo "${pub_key}" | sed 's/\r//' >> /root/.ssh/authorized_keys - else - pub_key=`cat /root/.ssh/id_rsa.pub` - echo "${pub_key}" | sed 's/\r//' >> /root/.ssh/authorized_keys - fi -} - -function configure_volte { - configure_epc - configure_ims -} - -function configure_epc { - install_oai_packages - - if [ ! $APP_NULL_CONFIG ]; then - set_sut_epc_information - set_keys - fi -} - -function configure_ixia { - install_ixia_packages - - if [ ! $APP_NULL_CONFIG ]; then - set_ixia_ims_information - set_keys - fi -} - -function configure_ims { - if [ ! $APP_NULL_CONFIG ]; then - set_sut_ims_information - configure_dnsmasq - verify_dns_records - set_keys - fi -} - - -function configure_abot { - - # If there is an external tester, - # then only configure tester and return - - case $APP_EXTERNAL_TESTER in - none) true;; - ixia) - configure_ixia - exit_on_error "Configuring $APP_EXTERNAL_TESTER" - return - ;; - *) false;; - esac - - # If there is no external tester, - # then configure application - - case $APP_NAME in - none) false;; - abot-ims-basic) configure_ims;; - abot-epc-basic) configure_epc;; - abot-functest-basic) configure_epc;; - abot-volte-basic) configure_volte;; - *) false;; - esac - exit_on_error "Configuring $APP_NAME" - -} - -APP_NAME=none -APP_VERSION=none -APP_EXTERNAL_TESTER=none -APP_NULL_CONFIG=false - -sudo -v -exit_on_error "Checking sudo privileges" - -while [ $# -gt 0 ] -do - - case $1 in - -a | --app) - APP_NAME=$2 - shift - ;; - -v | --version) - APP_VERSION=$2 - shift - ;; - -h | --help) - usage - ;; - -et | --external-tester) - APP_EXTERNAL_TESTER=$2 - shift - ;; - -nc | --null-config) - APP_NULL_CONFIG=true - ;; - *) - usage - ;; - esac - shift -done - -check_input - -ABOT_DIR=/var/lib/${APP_NAME} -ABOT_URL="http://115.249.81.187:8080/rebaca" -ABOT_DEBIAN=${APP_NAME}_${APP_VERSION}_all.deb - -# Installing dependencies for abot-volte-basic -echo_blue "Oracle Java Prerequisites..." -add-apt-repository -y ppa:webupd8team/java -apt-get update -echo debconf shared/accepted-oracle-license-v1-1 select true | sudo debconf-set-selections -echo debconf shared/accepted-oracle-license-v1-1 seen true | sudo debconf-set-selections -apt-get -y install oracle-java8-installer - -# Download ABot Debian and Install -mkdir -p ${ABOT_DIR}/payload -wget ${ABOT_URL}/${ABOT_DEBIAN} -O ${ABOT_DIR}/payload/${ABOT_DEBIAN} -exit_on_error "Download Debian $ABOT_DEBIAN" -dpkg --install ${ABOT_DIR}/payload/${ABOT_DEBIAN} || true -apt-get -f -y install -exit_on_error "ABot Debian Installed!!!" - -# Configure ABot -configure_abot diff --git a/abot-epc-basic/metadata.yaml b/abot-epc-basic/metadata.yaml deleted file mode 100644 index aa0766f..0000000 --- a/abot-epc-basic/metadata.yaml +++ /dev/null @@ -1,21 +0,0 @@ -name: abot-epc-basic -summary: An Automation Framework built on behaviour-oriented testing model -maintainer: Rebaca Technologies -description: | - A Test Automation Framework for network services that is built on the behaviour-oriented testing model. - This enables users to define test cases as feature files based on domain-specific language. - These feature files could subsequently be executed in automated manner on multiple environments. -tags: - - misc - - test-automation - - epc - - lte - - ue - - enb -subordinate: false -requires: - epc: - interface: S1-C -provides: - ssh-abot: - interface: ssh diff --git a/abot-epc-basic/revision b/abot-epc-basic/revision deleted file mode 100644 index d00491f..0000000 --- a/abot-epc-basic/revision +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/abot-ims-basic/README.md b/abot-ims-basic/README.md deleted file mode 100644 index 7f2a4dc..0000000 --- a/abot-ims-basic/README.md +++ /dev/null @@ -1,118 +0,0 @@ -# Charms Overview - -This is a [Juju charm](https://jujucharms.com/about), which allows deployment of the Rebaca Automation Behaviour-oriented Testing Framework(ABOT). - -ABOT should typically be deployed alongside the System Under Test(SUT) - such that it can execute tests against the latter. One of the possible SUTs is the ClearWater IMS developed by Metaswitch. - -ABOT enables users to create **feature files** based on **Cucumber** framework. Cucumber is a Behavior Driven Development (BDD) framework which is used to write tests for different types of applications. It allows automation of functional validation in easily readable and understandable format (like plain English). It is used to test the system rather than testing the particular piece of code. - -The feature files authored by user(s) can subsequently be used for testing different types of applications/protocols that are running on the System Under Test(SUT). The feature files follows a specified grammar and includes instructions that translate into sending messages to the SUT and receiving back responses using different protocols and verification of the results as defined. - -The current protocol drivers(adapters) provided with Abot are SSH, SFTP, HTTP as well as SIP. As such, it should be possible to execute tests on any SUT that supports the above protocols. - -In the absence of an SUT, it is also possible to write feature files for executing local commands on the deployed unit and validating the return values. - -Each feature file can be associated with one(or more) tags; thereby enabling it to be selectively invoked. **Execution of feature file(s) associated with one(or more) specified tags is driven through a Juju action after deployment of the Abot charm.** - -The result of each test execution is displayed in a web-based reporting UI; which is currently accessible on port 80. - - -## Deployment - -# Initial deployment - -The Abot service should be deployed as a standalone charm on a single unit; typically after the deployment of the System Under Test(SUT). - -As such, assuming that the SUT is the Clearwater IMS( https://github.com/thomnico/juju-nfv-clearwater-restcomm/tree/trusty), the Abot charm should be deployed after the deployment of the Clearwater IMS bundle, following the instructions provided in the ClearWater github repository. - -The typical command for deploying Abot is as follows: - - -> juju deploy cs:~abotcharm/trusty/abot-ims-basic - -Subsequently, it needs to be exposed using the following command. - -> juju expose abot-ims-basic - -## Adding Relations with ClearWater IMS charms - -The following relations should be added if the ClearWater IMS bundle is also installed in the setup. -> juju add-relation abot-ims-basic dns -> juju add-relation abot-ims-basic clearwater-bono -> juju add-relation abot-ims-basic:hss-prov clearwater-homestead:homestead-prov-user -> juju add-relation clearwater-homestead:hss abot-ims-basic:hss-abot - - -# Using Abot - -Once installed, Abot comes with a set of feature files that can be run against an existing Clearwater IMS setup(if it exists); after setting a few configuration parameters. - - -# Configuration - -These parameters are specific to the IMS Test System; and do not need to be specified if we are not validating an IMS. - -- `sip_server`: SIP proxy server FQDN of the IMS Test System. An example value would be bono.clearwater.local. **This is automatically set once the relation with ClearWater bono(SIP proxy server) is invoked.** -- `sip_domain`: SIP domain associated with the IMS Test System. An example value would be clearwater.local. **This is automatically set once the relation with ClearWater bono(SIP proxy server) is invoked.** -- `ims_hss_mirror_ip`: The IP address of the HSS Mirror of the IMS Test System. **This is automatically set once the relation with ClearWater Homestead is invoked.** -- `ims_cscf_ip`: The IP address of the P-CSCF.component of the IMS Test System. In the context of Clearwater IMS, this is the IP address of the node running clearwater-bono service. **This is automatically set once the relation with ClearWater bono(SIP proxy server) is invoked.** -- `clearwater_sipp_ip`: (optional) The IP address of the SIPP component of the IMS Test System(if any). In the context of Clearwater IMS, this is the IP address of the node running clearwater-sipp service. - -# Files downloaded - -When the charm is being installed, several files are downloaded: - -- The ABOT Debian package, from our public package repository server hosted at Rebaca. -- Any dependencies of those Debian packages (particularly Java and Maven), from the standard Ubuntu repository servers. - -# Test Case Execution - - - After deployment of Abot charm, the following actions can be quickly executed to verify whether the charm works fine. -> juju action do abot-ims-basic/[abot-unit-number] run - - - The report UI can then be viewed by clicking on the link that appears under the specific unit of the service in the Juju GUI - - - -- The following action(s) will add new tags for subsequent execution. Pls. note that the unit number below should be replaced with the proper value from the given deployment. Also, pls. note that the tag-names specified below are sample ones and should be replaced by the actual tags associated with newly authored feature files. - -> juju run-action abot-ims-basic/[abot-unit-number] configure-cx-client auth-type=cache -> juju run-action abot-ims-basic/[abot-unit-number] add-tags tagnames=sip-single-call -> juju run-action abot-ims-basic/[abot-unit-number] add-tags tagnames=sip-subscribe-notify,sip-messaging - -- Pls. note that the new tags may also be added to specific filenames; for future retrieval. -> juju run-action abot-ims-basic/[abot-unit-number] add-tags tagnames=sip-register,sip-ssh-test,ims-user-add,sip-single-call,sip-multi-call,sip-unregister filename=normal_cases -> juju run-action abot-ims-basic/[abot-unit-number] add-tags tagnames=sip-call-busy,sip-call-no-answer,sip-call-reject,sip-call-unavailable,sip-call-cancel,sip-malformed_request,sip-invalid-register,sip-single-call-error filename=negative_cases - - -- The following action will execute all test cases(feature files) for which the tags have been added through the previous command. Pls. note that the tags can be run from a specified filename as well. - -> juju run-action abot-ims-basic/[abot-unit-number] run - -- Execute tags from the file titled ***normal_cases***. This file includes tags for normal/successful call scenarios. -> juju run-action abot-ims-basic/[abot-unit-number] run filename=normal_cases - -- Execute tags from the file titled ***normal_cases***. This file includes tags for normal/successful call scenarios. -> juju run-action abot-ims-basic/[abot-unit-number] run filename=normal_cases - -- Execute tags from the file titled ***negative_cases***. This file includes tags for negative call scenarios; such as *user busy*, *no answer*, *cancel* and so on. -> juju action do abot/[abot-unit-number] run filename=negative_cases - -# Adding new Test Cases(Feature Files) - -- **New Feature Files should be added into the following folder** on the Juju controller node - ~/charms/trusty/abot/lib/featureFiles - -- The Juju charm upgrade action should be invoked to propagate these new feature files onto the deployed service node. - - -> juju upgrade-charm abot-ims-absic - -- Once the upgrade is successful, the test case execution steps can be followed to execute one or more feature files. - -# Viewing Test Report - -- Login to the Juju GUI and select Abot service -- Select the specific unit of the service and click on the link. -- Click of the test report link that appears under the IP Address header for the given Abot unit. -- This should show up the Cucumber report of the last executed test. diff --git a/abot-ims-basic/actions.yaml b/abot-ims-basic/actions.yaml deleted file mode 100644 index 64c790e..0000000 --- a/abot-ims-basic/actions.yaml +++ /dev/null @@ -1,91 +0,0 @@ -# actions.yaml - -run: - description: Run the Automation framework for the specified tags(or filename) - params: - tagnames: - type: string - filename: - type: string - additionalProperties: false - -add-tags: - description: Add the tag(s) to the default file or specific file - params: - tagnames: - type: string - filename: - type: string - required: [tagnames] - additionalProperties: false - -delete-tags: - description: Delete the tag(s) from all files or specific file - params: - tagnames: - type: string - filename: - type: string - required: [tagnames] - additionalProperties: false - -list-tags: - description: List the tags from all files or specific file - params: - filename: - type: string - additionalProperties: false - -delete-all-tags: - description: Delete all tags from the default file or specific file - params: - filename: - type: string - additionalProperties: false - -update-test-scenarios: - description: Incorporate new test scenarios onto the charm; based on new content previously copied onto Abot node. - -update-framework: - description: Incorporate new framework binaries onto the charm; based on new content previously copied onto Abot node. - -update-diameter-params: - description: Update different diamater-related parameters into the Abot system - params: - cxserver-originhost: - type: string - cxserver_originrealm: - type: string - cxserver_desthost: - type: string - cxserver_clientIp: - type: string - -configure-cx-client: - description: Configure the Cx client to point to Abot(diameter) or to authenticate from cache. Possible values of auth-type parameter are hss and cache. - params: - auth-type: - type: string - required: [auth-type] - additionalProperties: false - -benchmark: - description: To run benchmark as an action. - params: - num-calls: - type: integer - default: 1000 - constraints: - - range: - min: 1 - max: 1000 - call-rate: - type: integer - default: 5 - sip-scenario: - type: string - default: register - timeout-in-minutes: - type: integer - default: 10 - diff --git a/abot-ims-basic/actions/add-tags b/abot-ims-basic/actions/add-tags deleted file mode 100755 index 83ab067..0000000 --- a/abot-ims-basic/actions/add-tags +++ /dev/null @@ -1,55 +0,0 @@ -#!/bin/bash -# Action -Add Tags -set -eu -DIR=/etc/rebaca-test-suite/tags - -# Add tags specified by TAG_NAMES to the file specified by TAG_FILE_NAME -function add_tags_to_file() -{ - #Initialize new_tags variable with blank value. - new_tags= - - if [ -f $DIR/$TAG_FILE_NAME ]; then - new_tags=`cat $DIR/$TAG_FILE_NAME` - fi - - if [ -z "$TAG_NAMES" ]; then - juju-log "No tag names specified - none added" - else - juju-log "Tag names to be added: $TAG_NAMES" - - #Split the tags and add the @ symbols - IFS=', ' read -r -a array <<< "$TAG_NAMES" - for index in "${!array[@]}" - do - if [ -z "$new_tags" ]; then - new_tags="@${array[index]}" - else - #Add current tag only if not duplicate - current_tag="${array[index]}" - if [ `echo $new_tags | grep -c $current_tag` -eq 0 ]; then - new_tags="$new_tags,@$current_tag" - fi - fi - done - - #Add to new file if filename defined - echo -n "$new_tags" > $DIR/$TAG_FILE_NAME - - juju-log "Filename:$TAG_FILE_NAME, Tags:$new_tags" - fi -} - -TAG_NAMES=$(action-get tagnames) -TAG_FILE_NAME=$(action-get filename) - -if [ ! -z "$TAG_FILE_NAME" ]; then - juju-log "Tag file specified: $TAG_FILE_NAME" -else - #Assign default file name - TAG_FILE_NAME=tags -fi -status-set active "Executing - Adding Tags" -add_tags_to_file -status-set active "Ready" - diff --git a/abot-ims-basic/actions/benchmark b/abot-ims-basic/actions/benchmark deleted file mode 100755 index 6eb0357..0000000 --- a/abot-ims-basic/actions/benchmark +++ /dev/null @@ -1,160 +0,0 @@ -#!/bin/bash -# Action -Run -set -eux -# Make sure charm-benchmark is installed -if ! hash benchmark-start 2>/dev/null; then -apt-get install -y python-pip -pip install -U charm-benchmark -fi - -#Benchmark Start -status-set active "Executing benchmark action" -benchmark-start -featurefile_dir="/etc/rebaca-test-suite/featureFiles" -configfile_dir="/etc/rebaca-test-suite/config" -sipcommand="/etc/rebaca-test-suite/bin/sip" -sip_users="/etc/rebaca-test-suite/scenarios/user_bulk.csv" -num_calls=`action-get num-calls` -callrate=`action-get call-rate` -sipscenario=`action-get sip-scenario` -user_range_start=2010000000 -user_range_end=$((user_range_start+100+num_calls*2)) -max_recv_loops=2000 -max_sched_loops=2000 -recv_timeout=20000 -iter_start=0 -total_timeout=`action-get timeout-in-minutes` -sleep_for_completion=30 - - -system_kpi() -{ - echo "System_KPI $1 $(date)###################" >> ~/systemkpi.log - echo "-----------------------------" >> ~/systemkpi.log - echo " CPU Usage - $(grep 'cpu ' /proc/stat | awk '{usage=($2+$4)*100/($2+$4+$5)} END {print usage "%"}')" >> ~/systemkpi.log - echo "-----------------------------" >> ~/systemkpi.log - free -m >> ~/systemkpi.log - echo "-----------------------------" >> ~/systemkpi.log - df -h >> ~/systemkpi.log - echo "-----------------------------" >> ~/systemkpi.log - -} - -kill_running_processes() -{ - num_process=`ps -ef | grep sipp | grep -v grep | wc -l` - if [ ${num_process} -gt 0 ] ; then - sudo killall sipp - sleep 2 - fi - - java_process=`ps -ef | grep java | grep -v grep | wc -l` - if [ ${java_process} -gt 0 ] ; then - sudo killall java - sleep 2 - fi - - tcpdump_process=`ps -ef | grep tcpdump | grep -v grep | wc -l` - if [ ${tcpdump_process} -gt 0 ] ; then - sudo killall tcpdump - sleep 2 - fi -} - -#Load-test command - -system_kpi IMS-BENCHMARK_START - -PACKET_CAPTURE_ENABLE=`grep "ABOT.PacketCapture=" ${configfile_dir}/ABotConfig.properties | awk -F"=" '{print $2}'` -juju-log "PACKET_CAPTURE_ENABLE=${PACKET_CAPTURE_ENABLE}" - - -kill_running_processes - -./actions/configure-cx-client cache - -sed -e "s/USER_RANGE_START/${user_range_start}/g" -e "s/USER_RANGE_END/${user_range_end}/g" <${featurefile_dir}/001-clearwater-ims-bulk-user-add.feature.template>${featurefile_dir}/001-clearwater-ims-bulk-user-add.feature - - -if [ "${PACKET_CAPTURE_ENABLE}" == "true" ]; then - sed -ie "s|ABOT\.PacketCapture=.*|ABOT\.PacketCapture=false|" ${configfile_dir}/ABotConfig.properties - ./actions/run_local ims-bulk-user-add no-report - sed -ie "s|ABOT\.PacketCapture=.*|ABOT\.PacketCapture=true|" ${configfile_dir}/ABotConfig.properties -else - ./actions/run_local ims-bulk-user-add no-report -fi - - -if [ ${sipscenario} = invite ]; then - initial_wait=15 - scenarioxml="/etc/rebaca-test-suite/scenarios/sip_invite_load.xml" -else - initial_wait=15 - scenarioxml="/etc/rebaca-test-suite/scenarios/register.xml" -fi - -status-set active "Executing benchmark action" - -touch ~/result.log - - -${sipcommand} -sf ${scenarioxml} -inf ${sip_users} -m ${num_calls} -r ${callrate} -max_recv_loops ${max_recv_loops} -max_sched_loops ${max_sched_loops} -recv_timeout ${recv_timeout} -t tn > ~/result.log 2>&1 & - - -loop_count=$((total_timeout*2)) -juju-log "resultant loop count "${loop_count} -sleep ${initial_wait} -while [ ${iter_start} -lt ${loop_count} ] -do - num_process=`ps -ef | grep sipp | grep -v grep | wc -l` - juju-log "number of SIP process "${num_process} - if [ ${num_process} -lt 1 ] ; then - break - else - echo "process present" - sleep ${sleep_for_completion} - fi - iter_start=`expr ${iter_start} + 1` -done - -num_process=`ps -ef | grep sipp | grep -v grep | wc -l` -if [ ${num_process} -gt 0 ] ; then - sudo killall sipp -fi - - -# Grep/awk/parse the results - -callrate=`awk '/Call\ Rate/ { print $7 }' ~/result.log` -totalcall=`awk '/Total\ Call/ { print $6 }' ~/result.log` -successfulcall=`awk '/Successful\ call/ { print $6 }' ~/result.log` -regresponsetime=`awk '/Response\ Time\ register/ { print $7 }' ~/result.log` -calllength=`awk '/Call\ Length/ { print $6 }' ~/result.log` -multiply=$((successfulcall*100)) -passpercentage=$((multiply/totalcall)) -failedcalls=$((totalcall-successfulcall)) - -if [ ${sipscenario} = invite ]; then - csresponsetime=`awk '/Response\ Time\ call\-set/ { print $7 }' ~/result.log` -fi -#set benchmark data - -benchmark-data 'callrate' ${callrate} 'CPS' -benchmark-data 'totalcalls' ${totalcall} 'nos' -benchmark-data 'successfulcalls' ${successfulcall} 'nos' -benchmark-data 'failedcalls' ${failedcalls} 'nos' -benchmark-data 'regresponsetime' ${regresponsetime} 'HH:mm:ss:SSS' -benchmark-data 'calllength' ${calllength} 'HH:mm:ss:SSS' -if [ ${sipscenario} = invite ]; then - benchmark-data 'csresponsetime' ${csresponsetime} 'HH:mm:ss:SSS' -fi - - -# Set the composite, which is the single most important score - -benchmark-composite ${passpercentage} 'percent' 'desc' - -#Benchchmark finish -benchmark-finish || true - -status-set active "Ready" diff --git a/abot-ims-basic/actions/configure-cx-client b/abot-ims-basic/actions/configure-cx-client deleted file mode 100755 index c2e38ce..0000000 --- a/abot-ims-basic/actions/configure-cx-client +++ /dev/null @@ -1,33 +0,0 @@ -#!/bin/bash -set -e -ABOT_DEPLOY_DIR=/etc/rebaca-test-suite - -juju-log "Configuring Cx client" - -if [ "$#" -eq "0" ]; then - AUTH_TYPE=$(action-get auth-type) -else - AUTH_TYPE="$1" - juju-log " AUTH_TYPE = $1" -fi - -CX_CLIENT_IP=`grep "^HSS_MIRROR.SecureShell.IPAddress" ${ABOT_DEPLOY_DIR}/config/ABotConfig.properties | cut -d= -f2` -if [ -z "${CX_CLIENT_IP}" ]; then - juju-log "HSS hostname not found - exiting" - exit 0 -fi - -juju-log "CX_CLIENT_IP=${CX_CLIENT_IP}" - -ABOT_IP=`grep "^ABOT.SecureShell.IPAddress=" ${ABOT_DEPLOY_DIR}/config/ABotConfig.properties | cut -d= -f2` -juju-log "ABOT_IP=${ABOT_IP}" - -CX_CONFIGURATION_SCRIPT=configure_is_cscf -juju-log "Copying configuration script ${CX_CONFIGURATION_SCRIPT} to ${CX_CLIENT_IP}" - -sed -i "s/HSS_HOSTNAME=[^ ]*/HSS_HOSTNAME=${ABOT_IP}/" ${ABOT_DEPLOY_DIR}/bin/${CX_CONFIGURATION_SCRIPT} -sed -i "s/HSS_TAG=[^ ]*/HSS_TAG=${AUTH_TYPE}/" ${ABOT_DEPLOY_DIR}/bin/${CX_CONFIGURATION_SCRIPT} - -(cd ${ABOT_DEPLOY_DIR}/bin - scp -i /home/ubuntu/.ssh/id_rsa -o StrictHostKeyChecking=no ${CX_CONFIGURATION_SCRIPT} ubuntu@${CX_CLIENT_IP}:/home/ubuntu - ssh -i /home/ubuntu/.ssh/id_rsa -o StrictHostKeyChecking=no ubuntu@${CX_CLIENT_IP} /home/ubuntu/${CX_CONFIGURATION_SCRIPT}) diff --git a/abot-ims-basic/actions/delete-all-tags b/abot-ims-basic/actions/delete-all-tags deleted file mode 100755 index 96f00a6..0000000 --- a/abot-ims-basic/actions/delete-all-tags +++ /dev/null @@ -1,26 +0,0 @@ -#!/bin/bash -# Action -Delete all tags -set -e -DIR=/etc/rebaca-test-suite/tags - -# Delete multiple tags from a given file -function delete_all_tags_from_file() -{ - FILE_NAME=$1 - - #Delete the specified file - rm -f $DIR/$FILE_NAME - juju-log "Cleared all tags from file: $FILE_NAME" -} - - -TAG_FILE_NAME=$(action-get filename) - -if [ ! -z "$TAG_FILE_NAME" ]; then - juju-log "Tag file specified: $TAG_FILE_NAME" -else - #Assign default file name - TAG_FILE_NAME=tags -fi - -delete_all_tags_from_file $TAG_FILE_NAME diff --git a/abot-ims-basic/actions/delete-tags b/abot-ims-basic/actions/delete-tags deleted file mode 100755 index 969ceab..0000000 --- a/abot-ims-basic/actions/delete-tags +++ /dev/null @@ -1,51 +0,0 @@ -#!/bin/bash -# Action -Delete tags -set -e -DIR=/etc/rebaca-test-suite/tags - -# Delete multiple tags from a given file -function delete_tags_from_file() -{ - TAG_NAMES=$1 - FILE_NAME=$2 - - if [ ! -f $DIR/$FILE_NAME ]; then - juju-log "Filename($FILE_NAME) does not exist" - return - fi - - IFS=', ' read -r -a array <<< "$TAG_NAMES" - for index in "${!array[@]}" - do - tagname="${array[index]}" - - cat $DIR/$FILE_NAME | sed "s/,@$tagname//g" > $DIR/tmp - cp $DIR/tmp $DIR/$FILE_NAME - cat $DIR/$FILE_NAME | sed "s/@$tagname,//g" > $DIR/tmp - cp $DIR/tmp $DIR/$FILE_NAME - cat $DIR/$FILE_NAME | sed "s/@$tagname//g" > $DIR/tmp - cp $DIR/tmp $DIR/$FILE_NAME - - rm $DIR/tmp - done - - UPDATED_TAGS=`cat $DIR/$FILE_NAME` - juju-log "Filename:$FILE_NAME, Tags:$UPDATED_TAGS" -} - -TAG_NAMES=$(action-get tagnames) -TAG_FILE_NAME=$(action-get filename) - -if [ -z "$TAG_FILE_NAME" ] ; then - juju-log "No filename specified; will delete specified tags from all files" - - for filename in `ls -t $DIR` - do - delete_tags_from_file $TAG_NAMES $filename - done -else - juju-log "Deleting specified tags from $TAG_FILE_NAME" - delete_tags_from_file $TAG_NAMES $TAG_FILE_NAME -fi - - diff --git a/abot-ims-basic/actions/list-tags b/abot-ims-basic/actions/list-tags deleted file mode 100755 index e6078c0..0000000 --- a/abot-ims-basic/actions/list-tags +++ /dev/null @@ -1,31 +0,0 @@ -#!/bin/bash -# Action -List tags -set -eu - -DIR=/etc/rebaca-test-suite/tags -TAG_FILE_NAME=$(action-get filename) - - -function list_tags_from_file() -{ - tagFile=$1 - - if [ -f $DIR/$tagFile ]; then - tags=`cat $DIR/$tagFile` - juju-log "Filename:$tagFile, Tags:$tags" - fi -} - - -if [ ! -z "$TAG_FILE_NAME" ]; then - juju-log "Tag file specified: $TAG_FILE_NAME" - list_tags_from_file $TAG_FILE_NAME -else - juju-log "Tag file not specified, will list tags from all files" - for tagFile in `ls -t $DIR` - do - list_tags_from_file $tagFile - done -fi - - diff --git a/abot-ims-basic/actions/run b/abot-ims-basic/actions/run deleted file mode 100755 index 9d2f48b..0000000 --- a/abot-ims-basic/actions/run +++ /dev/null @@ -1,84 +0,0 @@ -#!/bin/bash -# Action -Run -set -eu - -ABOT_DEPLOY_DIR=/etc/rebaca-test-suite -ELASTIC_FILE_NAME=${ABOT_DEPLOY_DIR}/es-conf -ABOT_TAGS_DIR=${ABOT_DEPLOY_DIR}/tags -tags_defined=1 - -TAG_NAMES=$(action-get tagnames) -TAG_FILE_NAME=$(action-get filename) - -if [ ! -z "$TAG_FILE_NAME" ]; then - juju-log "Tag file specified: $TAG_FILE_NAME" -else - #Assign default file name - TAG_FILE_NAME=tags -fi - -new_tags= - -if [ -z "${TAG_NAMES}" ]; then - juju-log "No tag names specified" - tags_defined=0 - #Check if filename specified and file exists - if [ ! -z "${TAG_FILE_NAME}" ] && [ -f ${ABOT_TAGS_DIR}/${TAG_FILE_NAME} ] ; then - tags=`cat ${ABOT_TAGS_DIR}/${TAG_FILE_NAME}` - fi -else - juju-log "Tag names: ${TAG_NAMES}" - - #Split the tags and add the @ symbols - IFS=', ' read -r -a array <<< "${TAG_NAMES}" - for index in "${!array[@]}" - do - if [ $index -eq 0 ]; then - new_tags="@${array[0]}" - else - #Add current tag only if not duplicate - current_tag="${array[index]}" - if [ `echo ${new_tags} | grep -c ${current_tag}` -eq 0 ]; then - new_tags="${new_tags},@${current_tag}" - fi - fi - done - - #Assign the tags variable accordingly. - tags=${new_tags} -fi - -if [ -z "${tags}" ]; then - juju-log "No tags found for execution" -else - juju-log "Running the test with the tags ${tags}" - - ${ABOT_DEPLOY_DIR}/bin/tags_update ${tags} - - #Removing previous run artifacts - rm -rf ${ABOT_DEPLOY_DIR}/log/*.* - - (cd ${ABOT_DEPLOY_DIR}; \ - #perform mvn clean first - mvn clean; \ - #Run the install target now - mvn install -Dtest=RunScenarios -DrootDir=com/rebaca/abot || true) - - #Copying log artifacts to appropriate artifacts folder - LAST_REPORT_DIR="$(ls -td -- ${ABOT_DEPLOY_DIR}/artifacts/*/ | head -n 1)" - cp -R ${ABOT_DEPLOY_DIR}/log/*.* ${LAST_REPORT_DIR} - - #Process Test results and post to ElasticSearch - touch ${ELASTIC_FILE_NAME} - ES_CONF=`cat ${ELASTIC_FILE_NAME}` - if [ -z "${ES_CONF}" ]; then - echo "No ES conf defined - hence result not pushed to ES" - else - echo "Pushing Test result to ES" - LAST_REPORT_FILE_NAME=`basename ${LAST_REPORT_DIR}` - echo "Renaming Test result to ${LAST_REPORT_FILE_NAME}.json" - cp ${ABOT_DEPLOY_DIR}/artifacts/TestResults.json ${ABOT_DEPLOY_DIR}/artifacts/${LAST_REPORT_FILE_NAME}.json - python ${ABOT_DEPLOY_DIR}/bin/es-feed.py --elastic ${ES_CONF} --mappings ${ABOT_DEPLOY_DIR}/bin/mappings.json \ - --filepath ${ABOT_DEPLOY_DIR}/artifacts/${LAST_REPORT_FILE_NAME}.json - fi -fi diff --git a/abot-ims-basic/actions/run_local b/abot-ims-basic/actions/run_local deleted file mode 100755 index 6c88653..0000000 --- a/abot-ims-basic/actions/run_local +++ /dev/null @@ -1,89 +0,0 @@ -#!/bin/bash -# Action -Run -set -eu - -ABOT_DEPLOY_DIR=/etc/rebaca-test-suite -ELASTIC_FILE_NAME=${ABOT_DEPLOY_DIR}/es-conf - -tags_defined=1 -no_report_needed=0 - - -new_tags= - -if [ $# -gt 0 ]; then - TAG_NAMES="$1" - - if [ $# -gt 1 ] && [ "$2" == "no-report" ]; then - no_report_needed=1 - fi -else - # echo "No tags specified - exiting" - exit 1 -fi - -if [ ! -z "${TAG_NAMES}" ]; then - # echo "Tag names: ${TAG_NAMES}" - - #Split the tags and add the @ symbols - IFS=', ' read -r -a array <<< "${TAG_NAMES}" - for index in "${!array[@]}" - do - if [ ${index} -eq 0 ]; then - new_tags="@${array[0]}" - else - #Add current tag only if not duplicate - current_tag="${array[index]}" - if [ `echo ${new_tags} | grep -c ${current_tag}` -eq 0 ]; then - new_tags="${new_tags},@${current_tag}" - fi - fi - done - - #Assign the tags variable accordingly. - tags=$new_tags -fi - -if [ -z "$tags" ]; then - echo "No tags found for execution" -else - echo "Running the test with the tags $tags" - - # status-set active "Executing run action" - ${ABOT_DEPLOY_DIR}/bin/tags_update_local $tags - - # Removing previous run artifacts - rm -rf ${ABOT_DEPLOY_DIR}/log/*.* - - (cd ${ABOT_DEPLOY_DIR}; \ - # Perform mvn clean first - mvn clean; \ - # Run the install target now - mvn install -Dtest=RunScenarios -DrootDir=com/rebaca/abot || true) - - # Copying log artifacts to appropriate artifacts folder - LAST_REPORT_DIR="$(ls -td -- ${ABOT_DEPLOY_DIR}/artifacts/*/ | head -n 1)" - - if [ $no_report_needed -eq 0 ]; then - cp -R ${ABOT_DEPLOY_DIR}/log/*.* ${LAST_REPORT_DIR} - - # Process Test results and post to ElasticSearch - touch ${ELASTIC_FILE_NAME} - ES_CONF=`cat ${ELASTIC_FILE_NAME}` - if [ -z "${ES_CONF}" ]; then - echo "No ES conf defined - hence result not pushed to ES" - else - echo "Pushing Test result to ES" - LAST_REPORT_FILE_NAME=`basename ${LAST_REPORT_DIR}` - echo "Renaming Test result to ${LAST_REPORT_FILE_NAME}.json" - cp ${ABOT_DEPLOY_DIR}/artifacts/TestResults.json ${ABOT_DEPLOY_DIR}/artifacts/${LAST_REPORT_FILE_NAME}.json - python ${ABOT_DEPLOY_DIR}/bin/es-feed.py --elastic ${ES_CONF} --mappings ${ABOT_DEPLOY_DIR}/bin/mappings.json \ - --filepath ${ABOT_DEPLOY_DIR}/artifacts/${LAST_REPORT_FILE_NAME}.json - fi - else - LAST_REPORT_DATETIME=`basename $LAST_REPORT_DIR` - cd ${ABOT_DEPLOY_DIR}/artifacts - tar czf ${LAST_REPORT_DATETIME}.tgz $LAST_REPORT_DATETIME - rm -rf $LAST_REPORT_DATETIME - fi -fi diff --git a/abot-ims-basic/actions/update-cx-params b/abot-ims-basic/actions/update-cx-params deleted file mode 100755 index f47e8da..0000000 --- a/abot-ims-basic/actions/update-cx-params +++ /dev/null @@ -1,45 +0,0 @@ -#!/bin/bash -# Action -Run -set -eu -ABOT_DEPLOY_DIR=/etc/rebaca-test-suite - -CXSERVER_ORIGINHOST=$(action-get cxserver-originhost) -CXSERVER_ORIGINREALM=$(action-get cxServer-originrealm) -CXSERVER_DESTHOST=$(action-get cxserver-desthost) -CXSERVER_CLIENT_IP=$(action-get cxserver-clientip) - - -if [ ! -z "${CXSERVER_ORIGINHOST}" ]; then - juju-log "Setting CxServer_OriginHost=${CXSERVER_ORIGINHOST}" - sed -i "s/CxServer.OriginHost=[^ ]*/CxServer.OriginHost=${CXSERVER_ORIGINHOST}/" ${ABOT_DEPLOY_DIR}/config/ABotConfig.properties - if [ `grep -c ${CXSERVER_ORIGINHOST} /etc/hosts` == "0" ]; then - public_hostname=$(unit-get public-address) - juju-log "Public Hostname: ${public_hostname}" - public_ip_address=`dig +short ${public_hostname}` - juju-log "Inserting originhost(${CXSERVER_ORIGINHOST}) into /etc/hosts file for IP ${public_ip_address}" - echo "${public_ip_address} ${CXSERVER_ORIGINHOST}" >> /etc/hosts - fi -fi - -if [ ! -z "${CXSERVER_ORIGINREALM}" ]; then - juju-log "CxServer_OriginRealm=${CXSERVER_ORIGINREALM}" - sed -i "s/CxServer.OriginRealm=[^ ]*/CxServer.OriginRealm=${CXSERVER_ORIGINREALM}/" ${ABOT_DEPLOY_DIR}/config/ABotConfig.properties -fi - - -if [ ! -z "${CXSERVER_DESTHOST}" ]; then - juju-log "CxServer_DestHost=${CXSERVER_DESTHOST}" - sed -i "s/CxServer.DestHost=[^ ]*/CxServer.DestHost=${CXSERVER_DESTHOST}/" ${ABOT_DEPLOY_DIR}/config/ABotConfig.properties -fi - -if [ ! -z "${CXSERVER_CLIENT_IP}" ]; then - juju-log "CxServer_Client_IP=${CXSERVER_CLIENT_IP}" - #Get the destination host - CXSERVER_DEST_HOST=`grep "CxServer.DestHost" ${ABOT_DEPLOY_DIR}/config/ABotConfig.properties | cut -d= -f2` - if [ ! -z ${CXSERVER_DEST_HOST} ]; then - if [ `grep -c $CXSERVER_CLIENT_IP /etc/hosts` == "0" ]; then - juju-log "Setting ${CXSERVER_CLIENT_IP} against ${CXSERVER_DEST_HOST} in /etc/hosts" - echo "${CXSERVER_CLIENT_IP} ${CXSERVER_DEST_HOST}" >> /etc/hosts - fi - fi -fi diff --git a/abot-ims-basic/config.yaml b/abot-ims-basic/config.yaml deleted file mode 100644 index adec2c1..0000000 --- a/abot-ims-basic/config.yaml +++ /dev/null @@ -1,18 +0,0 @@ -options: - zone: - default: "rebaca.local" - description: "The DNS root zone for this service" - type: string - tags: - type: string - default: "local-commands" - description: "Tag for the feature file to run" - abot_app: - type: string - default: "abot-ims-basic" - description: "The ABot Application" - abot_version: - type: string - default: "3.1.0" - description: "The ABot Version" - diff --git a/abot-ims-basic/copyright b/abot-ims-basic/copyright deleted file mode 100644 index 883a93a..0000000 --- a/abot-ims-basic/copyright +++ /dev/null @@ -1,8 +0,0 @@ -Project Automated Behaviour-Oriented Tester(ABOT) - -Copyright (C) 2016 Rebaca Technologies Inc. - - This package contains software that is confidential and proprietary to - Rebaca Technologies. Any reproduction, disclosure, or use in whole or - in part is expressly prohibited, except as may be specifically - authorized by prior written agreement. diff --git a/abot-ims-basic/hooks/abot-es-relation-changed b/abot-ims-basic/hooks/abot-es-relation-changed deleted file mode 100755 index b45e2f6..0000000 --- a/abot-ims-basic/hooks/abot-es-relation-changed +++ /dev/null @@ -1,21 +0,0 @@ -#!/bin/bash -set -e -ABOT_DEPLOY_DIR=/etc/rebaca-test-suite - -juju-log "Invoking abot-es-relation" -es_url_val="$(relation-get es-url)" -if [ -z "${es_url_val}" ]; then - juju-log "No data sent yet from abot-analytics" - exit 0 -fi - -juju-log "ES_URL=${es_url_val}" -echo -n ${es_url_val} > ${ABOT_DEPLOY_DIR}/es-conf - -#Get the Kibana URL Value -kibana_url_val="$(relation-get kibana-url)" - -if [ ! -z ${kibana_url_val} ]; then - juju-log "KIBANA_URL=${kibana_url_val}" - echo -n ${kibana_url_val} > ${ABOT_DEPLOY_DIR}/kibana-conf -fi diff --git a/abot-ims-basic/hooks/config-changed b/abot-ims-basic/hooks/config-changed deleted file mode 100755 index 89aa498..0000000 --- a/abot-ims-basic/hooks/config-changed +++ /dev/null @@ -1,104 +0,0 @@ -#!/bin/bash -# config-changed occurs everytime a new configuration value is updated (juju set) -set -e - -ABOT_DEPLOY_DIR=/etc/rebaca-test-suite -TAG_DIR=${ABOT_DEPLOY_DIR}/tags -MAVEN_USER_DIR=/root/.m2 -PROXY_ADDRESS=`echo ${http_proxy} | cut -d'/' -f3 | cut -d':' -f2` -PROXY_PORT=`echo ${http_proxy} | cut -d'/' -f3 | cut -d':' -f1` - -tags=$(config-get tags) - -if [ -n ${TAG_DIR}/tags ]; then - # Install the user's supplied hashes - if [ -f ${TAG_DIR}/tags ]; then - existing_tags=`cat ${TAG_DIR}/tags` - juju-log "Current tags: ${existing_tags}" - fi - juju-log "Installing the tags obtained from config: ${tags}" - echo -n "@$tags" > ${TAG_DIR}/tags -fi - -touch ${ABOT_DEPLOY_DIR}/sip_server - -sip_domain=$(config-get zone) -touch ${ABOT_DEPLOY_DIR}/sip_domain -if [ ! -z ${sip_domain} ] ; then - juju-log "Setting sip_domain=${sip_domain}" - # Install the user's supplied hashes - echo -n "${sip_domain}" > ${ABOT_DEPLOY_DIR}/sip_domain -fi - - -diameter_origin_realm=$(config-get zone) -if [ ! -z ${diameter_origin_realm} ]; then - juju-log "Setting origin realm=${diameter_origin_realm}" - sed -i "s/ABOT_HSS.OriginRealm=[^ ]*/ABOT_HSS.OriginRealm=${diameter_origin_realm}/" ${ABOT_DEPLOY_DIR}/config/ABotConfig.properties -fi - -touch ${ABOT_DEPLOY_DIR}/ims_hss_mirror_ip -if [ ! -z `cat ${ABOT_DEPLOY_DIR}/ims_hss_mirror_ip` ]; then - ims_hss_mirror_ip=`cat ${ABOT_DEPLOY_DIR}/ims_hss_mirror_ip` - sed -i "s/HSS_MIRROR.SecureShell.IPAddress=[^ ]*/HSS_MIRROR.SecureShell.IPAddress=${ims_hss_mirror_ip}/" ${ABOT_DEPLOY_DIR}/config/ABotConfig.properties -fi - -touch ${ABOT_DEPLOY_DIR}/ims_cscf_ip -if [ ! -z `cat ${ABOT_DEPLOY_DIR}/ims_cscf_ip` ]; then - ims_cscf_ip=`cat ${ABOT_DEPLOY_DIR}/ims_cscf_ip` - sed -i "s/P_CSCF.SecureShell.IPAddress=[^ ]*/P_CSCF.SecureShell.IPAddress=${ims_cscf_ip}/" ${ABOT_DEPLOY_DIR}/config/ABotConfig.properties -fi - -private_address=$(unit-get private-address) -if [[ `dig +short ${private_address}` =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]] - then - juju-log "Verifying ${private_address} in dns " - DIGSHORT="dig +short" - elif [[ "${private_address}" =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]] - then - DIGSHORT=echo - else - juju-log "Unable to fetch private_address ..." - exit 1 -fi - -ip=`${DIGSHORT} ${private_address}` - -if [ ! -z ${ip} ]; then - sed -i "s/ABOT.SecureShell.IPAddress=[^ ]*/ABOT.SecureShell.IPAddress=${ip}/" ${ABOT_DEPLOY_DIR}/config/ABotConfig.properties -else - juju-log "IP Address of local unit not available - exiting with error" - exit 2 -fi - -#Replace realm-name in diameter-specific feature files -cd ${ABOT_DEPLOY_DIR}/featureFiles -DOMAIN_NAME="${diameter_origin_realm}" -grep -rl rebaca.local . | xargs sed -i "s|rebaca\.local|${DOMAIN_NAME}|g" || true - -#Configure MAVEN proxy -if [ -z ${http_proxy} ];then - juju-log "No proxy found" -else - cat < /root/.m2/settings.xml - - - - optional - true - http - proxyuser - proxypass - ${PROXY_ADDRESS} - ${PROXY_PORT} - local.net|some.host.com - - - -EOT -fi - -cd ${CHARM_DIR} - -juju-log "Calling start hook from config-changed hook ..." -. ${CHARM_DIR}/hooks/start diff --git a/abot-ims-basic/hooks/hss-abot-relation-changed b/abot-ims-basic/hooks/hss-abot-relation-changed deleted file mode 100755 index b28b2a8..0000000 --- a/abot-ims-basic/hooks/hss-abot-relation-changed +++ /dev/null @@ -1,18 +0,0 @@ -#!/bin/bash - -set -e -ABOT_DEPLOY_DIR=/etc/rebaca-test-suite - -hss_host=`grep "^ABOT.SecureShell.IPAddress" ${ABOT_DEPLOY_DIR}/config/ABotConfig.properties | cut -d= -f2` -hss_realm=`grep "^ABOT_HSS.OriginRealm" ${ABOT_DEPLOY_DIR}/config/ABotConfig.properties | cut -d= -f2` - -relation-set public-address=$hss_host -relation-set hss-realm=$hss_realm -relation-set hss-port=3868 - -#if [ -f ~/.ssh/id_rsa.pub ]; -# then -# ssh_key=`cat ~/.ssh/id_rsa.pub` -# ssh_key=`echo "$ssh_key" | sed 's/\r//'` -# relation-set ssh-key=$ssh_key -#fi diff --git a/abot-ims-basic/hooks/hss-abot-relation-joined b/abot-ims-basic/hooks/hss-abot-relation-joined deleted file mode 100755 index 6bee0f3..0000000 --- a/abot-ims-basic/hooks/hss-abot-relation-joined +++ /dev/null @@ -1,15 +0,0 @@ -#!/bin/bash -set -e - -dns_ip=$(relation-get private-address) -if [ -z "${dns_ip}" ]; then - juju-log "No data sent yet from DNS" - exit 0 -fi - -if [ -f /home/ubuntu/.ssh/id_rsa.pub ]; -then - ssh_key=`cat /home/ubuntu/.ssh/id_rsa.pub` - ssh_key=`echo "${ssh_key}" | sed 's/\r//'` - relation-set ssh-key="${ssh_key}" -fi diff --git a/abot-ims-basic/hooks/hss-prov-relation-changed b/abot-ims-basic/hooks/hss-prov-relation-changed deleted file mode 100755 index 36f067e..0000000 --- a/abot-ims-basic/hooks/hss-prov-relation-changed +++ /dev/null @@ -1,37 +0,0 @@ -#!/bin/bash -set -e -ABOT_DEPLOY_DIR=/etc/rebaca-test-suite - -juju-log "Invoking hss-relation" -#ims_hss_mirror_ip=$(relation-get public-address) -ims_hss_mirror_ip=$(relation-get private-address) -if [ -z "${ims_hss_mirror_ip}" ]; then - juju-log "No data sent yet from HSS" - exit 0 -fi - -juju-log "HSS FQDN ${ims_hss_mirror_ip}" -if [[ `dig +short ${ims_hss_mirror_ip}` =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]] -then - juju-log "Verifying ${ims_hss_mirror_ip} in dns " - DIGSHORT="dig +short" -elif [[ "${ims_hss_mirror_ip}" =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]] -then - DIGSHORT=echo -else - juju-log "Unable to fetch public_address changing dns order to original" - exit 1 -fi - -ims_hss_mirror_ip=`${DIGSHORT} ${ims_hss_mirror_ip}` -juju-log "Ims hss mirror IP ${ims_hss_mirror_ip}" - -if [ -z ${ims_hss_mirror_ip} ]; then - juju-log "No IP Address found for HSS_FQDN: ${ims_hss_mirror_ip}" - exit 1 -fi - -#Update HSS Mirror IP -echo -n "${ims_hss_mirror_ip}" > ${ABOT_DEPLOY_DIR}/ims_hss_mirror_ip - -sed -i "s/HSS_MIRROR.SecureShell.IPAddress=[^ ]*/HSS_MIRROR.SecureShell.IPAddress=${ims_hss_mirror_ip}/" ${ABOT_DEPLOY_DIR}/config/ABotConfig.properties diff --git a/abot-ims-basic/hooks/install b/abot-ims-basic/hooks/install deleted file mode 100755 index 06aa180..0000000 --- a/abot-ims-basic/hooks/install +++ /dev/null @@ -1,30 +0,0 @@ -#!/bin/bash -# Here do anything needed to install the service -# i.e. apt-get install -y foo or bzr branch http://myserver/mycode /srv/webroot -# Make sure this hook exits cleanly and is idempotent, common problems here are -# failing to account for a debconf question on a dependency, or trying to pull -# from github without installing git first. - -set -eu - -abot_app=`config-get abot_app` -abot_version=`config-get abot_version` - -ABOT_DEPLOY_DIR=/etc/rebaca-test-suite - -if ! grep "# added by ABot-*/hooks/install" /etc/hosts ; then - public_ip=$(unit-get public-address) - public_ip_1=`dig +short ${public_ip}` - echo "${public_ip_1} abot-$(cut -d/ -f2 <<< ${JUJU_UNIT_NAME}).$(config-get zone) # added by ABot-*/hooks/install" >>/etc/hosts -fi - -(cd ${CHARM_DIR}; ./install-abot.sh -a ${abot_app} -v ${abot_version} -nc) - -# Change permissions of the ABOT_DEPLOY_DIR folders -chown -R ubuntu:ubuntu `readlink ${ABOT_DEPLOY_DIR}` - -open-port 80/tcp -open-port 5000/tcp - -juju-log "ABot Install complete!" -status-set maintenance "ABot installed! Waiting to start..." diff --git a/abot-ims-basic/hooks/programmable-multiple-relation-changed b/abot-ims-basic/hooks/programmable-multiple-relation-changed deleted file mode 100755 index 9f46759..0000000 --- a/abot-ims-basic/hooks/programmable-multiple-relation-changed +++ /dev/null @@ -1,75 +0,0 @@ -#!/bin/bash -set -e -ABOT_DEPLOY_DIR=/etc/rebaca-test-suite - -juju-log "Invoking dns-relation" - -dns_ip=$(relation-get private-address) -if [ -z "${dns_ip}" ]; then - juju-log "No data sent yet from DNS" - exit 0 -fi - -juju-log "DNS IP: ${dns_ip}" - -# Set our DNS requirements -id=$(cut -d/ -f2 <<< ${JUJU_UNIT_NAME}) -public_fqdn=$(unit-get public-address) - -public_hostname=`hostname | cut -d"." -f1` -juju-log "Public Hostname: ${public_hostname}, public_address: ${public_fqdn}" - -if [[ ${public_fqdn} =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; -then - ip=${public_fqdn} - juju-log "Unit get address - ${ip}" - relation-set domain=$(config-get zone) - DNS_JSON=$(${ABOT_DEPLOY_DIR}/bin/generate_dns_records ${ip} ${public_hostname} abot-ims-basic-${id}) - juju-log "DNS_JSON:${DNS_JSON}" - relation-set resources="${DNS_JSON}" -else - ip=`dig +short ${public_fqdn}` - juju-log "After dig and short unit address is ${ip}" - relation-set domain=$(config-get zone) - DNS_JSON=$(${ABOT_DEPLOY_DIR}/bin/generate_dns_records ${ip} ${public_hostname} abot-ims-basic-${id}) - juju-log "DNS_JSON:${DNS_JSON}" - relation-set resources="${DNS_JSON}" -fi - - -#update DNS server using DNSMASQ -echo nameserver ${dns_ip} > /etc/dnsmasq.resolv.conf -grep -v ^RESOLV_CONF= /etc/default/dnsmasq > /tmp/dnsmasq.$$ -mv /tmp/dnsmasq.$$ /etc/default/dnsmasq -echo RESOLV_CONF=/etc/dnsmasq.resolv.conf >> /etc/default/dnsmasq -service dnsmasq restart -sleep 5 -# Change Resolvconf order and make dnsmasq on top -dnsmasq=lo.dnsmasq -resolvconf=/etc/resolvconf -loinet=`grep ^[a-z] /etc/resolvconf/interface-order | head -n1` -if [ $loinet != $dnsmasq ] -then - cp /etc/resolvconf/interface-order /etc/resolvconf/interface-order.orig - sed '/'$dnsmasq'/d' /etc/resolvconf/interface-order.orig > /etc/resolvconf/interface-order - sed -i '/'$loinet'/i lo.dnsmasq' /etc/resolvconf/interface-order - service dnsmasq restart - resolvconf -u -else - exit 0 -fi - -# Verify DNS record to make sure that IMS dns is working properly -if [[ `dig +short ${public_fqdn}` =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]] -then - juju-log "Verifying ${public_fqdn} in dns " - exit -elif [[ "${public_fqdn}" =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]] -then - juju-log "Verifying ${public_fqdn} in dns " - exit -else - juju-log "Unable to fetch ${public_fqdn} changing dns order to original" - cp /etc/resolvconf/interface-order.orig /etc/resolvconf/interface-order - resolvconf -u -fi diff --git a/abot-ims-basic/hooks/programmable-multiple-relation-departed b/abot-ims-basic/hooks/programmable-multiple-relation-departed deleted file mode 100755 index c51502b..0000000 --- a/abot-ims-basic/hooks/programmable-multiple-relation-departed +++ /dev/null @@ -1,11 +0,0 @@ -#!/bin/bash - -set -e - -INTR_ORDER_DIR="/etc/resolvconf" - -if [ -f "${INTR_ORDER_DIR}/interface-order.orig" ]; then - cp ${INTR_ORDER_DIR}/interface-order.orig ${INTR_ORDER_DIR}/interface-order - resolvconf -u -fi - diff --git a/abot-ims-basic/hooks/start b/abot-ims-basic/hooks/start deleted file mode 100755 index d8206b0..0000000 --- a/abot-ims-basic/hooks/start +++ /dev/null @@ -1,54 +0,0 @@ -#!/bin/bash -# Here put anything that is needed to start the service. -# Note that currently this is run directly after install -set -eu -ABOT_DEPLOY_DIR=/etc/rebaca-test-suite - -# Generate ssh key for root user -if [ ! -f /home/ubuntu/.ssh/id_rsa.pub ]; -then - ssh-keygen -t rsa -N "" -f /root/.ssh/id_rsa - pub_key=`cat /root/.ssh/id_rsa.pub` - echo "${pub_key}" | sed 's/\r//' >> /root/.ssh/authorized_keys -else - pub_key=`cat /root/.ssh/id_rsa.pub` - echo "${pub_key}" | sed 's/\r//' >> /root/.ssh/authorized_keys -fi - -# Generate ssh key for ubuntu user -if [ ! -f /home/ubuntu/.ssh/id_rsa.pub ]; -then - sudo -u ubuntu bash << EOF - ssh-keygen -t rsa -N "" -f /home/ubuntu/.ssh/id_rsa -EOF - pub_key=`cat /home/ubuntu/.ssh/id_rsa.pub` - echo "${pub_key}" | sed 's/\r//' >> /home/ubuntu/.ssh/authorized_keys -else - pub_key=`cat /home/ubuntu/.ssh/id_rsa.pub` - echo "${pub_key}" | sed 's/\r//' >> /home/ubuntu/.ssh/authorized_keys -fi - -# Set private address -ip=$(unit-get private-address) -if [[ "$ip" =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]] -then - juju-log "Private adrress is an IP address - ${ip} . " - DIGSHORT=echo -elif [[ "${ip}" =~ ^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9])$ ]] -then - juju-log "Private address is a FQDN - ${ip} " - DIGSHORT="dig +short" -else - juju-log "Unable to find private address " - exit -fi - -ip=`${DIGSHORT} ${ip}` -echo "${ip}" -sed -i "/ip=/s/[^ ]*/ip=${ip}/" ${ABOT_DEPLOY_DIR}/bin/sip - -sed -i "/ip=/s/[^ ]*/ip=${ip}/" ${ABOT_DEPLOY_DIR}/bin/sip_imsi_domain - -sed -i "/ip=/s/[^ ]*/ip=${ip}/" ${ABOT_DEPLOY_DIR}/bin/sip-stress - -status-set maintenance "ABot updated! Setup relations to get into active state..." diff --git a/abot-ims-basic/hooks/stop b/abot-ims-basic/hooks/stop deleted file mode 100755 index 1068769..0000000 --- a/abot-ims-basic/hooks/stop +++ /dev/null @@ -1,7 +0,0 @@ -#!/bin/bash -# This will be run when the service is being torn down, allowing you to disable -# it in various ways.. -# For example, if your web app uses a text file to signal to the load balancer -# that it is live... you could remove it and sleep for a bit to allow the load -# balancer to stop sending traffic. -# rm /srv/webroot/server-live.txt && sleep 30 diff --git a/abot-ims-basic/hooks/ue-abot-relation-changed b/abot-ims-basic/hooks/ue-abot-relation-changed deleted file mode 100755 index 42ca107..0000000 --- a/abot-ims-basic/hooks/ue-abot-relation-changed +++ /dev/null @@ -1,29 +0,0 @@ -#!/bin/bash -set -e -ABOT_DEPLOY_DIR=/etc/rebaca-test-suite - -juju-log "Invoking cscf-relation" -ims_cscf_zone=$(relation-get public-address) -if [ -z "${ims_cscf_zone}" ]; then - juju-log "No data sent yet from Cscf" - exit 0 -fi - -ims_cscf_hostname=$(dig +short _sip._tcp.${ims_cscf_zone} SRV | awk '{print $4}') - -juju-log "CSFP FQDN ${ims_cscf_hostname}" - -ims_cscf_ip=`dig +short ${ims_cscf_hostname}` - -sed -i "s/P_CSCF.SecureShell.IPAddress=[^ ]*/P_CSCF.SecureShell.IPAddress=${ims_cscf_ip}/" ${ABOT_DEPLOY_DIR}/config/ABotConfig.properties - -echo -n ${ims_cscf_hostname} > ${ABOT_DEPLOY_DIR}/sip_server -echo -n ${ims_cscf_ip} > ${ABOT_DEPLOY_DIR}/ims_cscf_ip -echo -n ${ims_cscf_zone} > ${ABOT_DEPLOY_DIR}/sip_domain - -if [ -f /home/ubuntu/.ssh/id_rsa.pub ]; -then - ssh_key=`cat /home/ubuntu/.ssh/id_rsa.pub` - ssh_key=`echo "$ssh_key" | sed 's/\r//'` - relation-set ssh-key="${ssh_key}" -fi diff --git a/abot-ims-basic/hooks/ue-abot-relation-departed b/abot-ims-basic/hooks/ue-abot-relation-departed deleted file mode 100755 index b1f9202..0000000 --- a/abot-ims-basic/hooks/ue-abot-relation-departed +++ /dev/null @@ -1,16 +0,0 @@ -#!/bin/bash -set -e -ABOT_DEPLOY_DIR=/etc/rebaca-test-suite - -juju-log "Invoking cscf-departed relation" - -ims_cscf_hostname=cscf_host.rebaca.local - -juju-log "CSFP FQDN ${ims_cscf_hostname}" - -ims_cscf_ip=192.168.255.255 - -sed -i "s/P_CSCF.Host.IPAddress=[^ ]*/P_CSCF.Host.IPAddress=${ims_cscf_ip}/" ${ABOT_DEPLOY_DIR}/config/ABotConfig.properties - -echo -n ${ims_cscf_hostname} > ${ABOT_DEPLOY_DIR}/sip_server -echo -n ${ims_cscf_ip} > ${ABOT_DEPLOY_DIR}/ims_cscf_ip diff --git a/abot-ims-basic/hooks/ue-abot-relation-joined b/abot-ims-basic/hooks/ue-abot-relation-joined deleted file mode 100755 index 06088a1..0000000 --- a/abot-ims-basic/hooks/ue-abot-relation-joined +++ /dev/null @@ -1,18 +0,0 @@ -#!/bin/bash - -set -e - -ims_cscf_zone=$(relation-get public-address) -if [ -z "${ims_cscf_zone}" ]; then - juju-log "No data sent yet from Cscf" - exit 0 -fi - -if [ -f /home/ubuntu/.ssh/id_rsa.pub ]; - then - ssh_key=`cat /home/ubuntu/.ssh/id_rsa.pub` - ssh_key=`echo "${ssh_key}" | sed 's/\r//'` - relation-set ssh-key="${ssh_key}" - juju-log "SSH KEY of ABot-ims-basic ${ssh-key}" -fi - diff --git a/abot-ims-basic/hooks/upgrade-charm b/abot-ims-basic/hooks/upgrade-charm deleted file mode 100755 index d6be954..0000000 --- a/abot-ims-basic/hooks/upgrade-charm +++ /dev/null @@ -1,29 +0,0 @@ -#!/bin/bash -# This hook is executed each time a charm is upgraded after the new charm -# contents have been unpacked -# Best practice suggests you execute the hooks/install and -# hooks/config-changed to ensure all updates are processed -set -eu - -abot_app=`config-get abot_app` -abot_version=`config-get abot_version` - -ABOT_DEPLOY_DIR=/etc/rebaca-test-suite - -(cd ${CHARM_DIR}; ./install-abot.sh -a ${abot_app} -v ${abot_version} -nc) - -# Change permissions of the ABOT_DEPLOY_DIR folders -chown -R ubuntu:ubuntu `readlink ${ABOT_DEPLOY_DIR}` - -if [ ! -f ~/.ssh/id_rsa.pub ]; -then - ssh-keygen -t rsa -N "" -f ~/.ssh/id_rsa - pub_key=`cat ~/.ssh/id_rsa.pub` - echo "${pub_key}" | sed 's/\r//' >> ~/.ssh/authorized_keys -fi - -open-port 80/tcp -open-port 5000/tcp - -juju-log "ABot update complete!" -status-set active "ABot updated! Proceed to run tests..." diff --git a/abot-ims-basic/icon.svg b/abot-ims-basic/icon.svg deleted file mode 100644 index 51b1d87..0000000 --- a/abot-ims-basic/icon.svg +++ /dev/null @@ -1,26 +0,0 @@ - - - - - Created by potrace 1.10, written by Peter Selinger 2001-2011 - - - - - - - - - - - - - - - - - - Layer 2 - - - \ No newline at end of file diff --git a/abot-ims-basic/install-abot.sh b/abot-ims-basic/install-abot.sh deleted file mode 100755 index ca01283..0000000 --- a/abot-ims-basic/install-abot.sh +++ /dev/null @@ -1,362 +0,0 @@ -#!/bin/bash - -ABOT_DEPLOY_DIR=/etc/rebaca-test-suite - -red='\E[31m' -green='\E[32m' -blue='\E[1;34m' -reset_color='\E[00m' - -# Green echo -function echo_green { - message=$1 - echo -e "${green}${message}${reset_color}" -} - -# Red echo -function echo_red { - message=$1 - echo -e "${red}${message}${reset_color}" -} - -# Blue echo -function echo_blue { - message=$1 - echo -e "${blue}${message}${reset_color}" -} - -function exit_on_error { - LAST_RESULT=$? - MESSAGE=$1 - if [ ${LAST_RESULT} -eq 0 ] - then - if [ "$2" != "ECHO=ON_FAILURE_ONLY" ]; then - echo_green "<< ABOT Installer - Success: ${MESSAGE} >>" - fi - else - if [ "$2" != "ECHO=ON_WARNING_ONLY" ]; then - echo_red "<< ABOT Installer - Error: ${MESSAGE} >>" - exit 1 - else - echo_blue "<< ABOT Installer - Warning: ${MESSAGE} >>" - fi - fi -} - - -function usage { - echo_blue "./install-abot " - echo_blue "Run with sudo privileges" - echo_blue "Example: sudo ./install-abot.sh -a abot-ims-basic -v 3.1.0 -nc" - echo "" - echo "options" - echo "" - echo "-a | --app) application name: abot-ims-basic | abot-epc-basic | abot-functest-basic | abot-volte-basic" - echo "-v | --version) version: 3.1.0" - echo "-et | --external-tester) external tester: ixia | dsTest" - echo "-nc | --null-configuration) No configuration done during deployment of system" - echo "" - exit 0 -} - -function check_input { - case $APP_NAME in - abot-ims-basic|abot-epc-basic|abot-functest-basic|abot-volte-basic) true;; - *) usage;; - esac - exit_on_error "Application Name $APP_NAME" - - case $APP_VERSION in - 3.1.0) true;; - *) usage;; - esac - exit_on_error "Version $APP_VERSION" - - case $APP_EXTERNAL_TESTER in - none) true;; - ixia) true;; - *) usage;; - esac - exit_on_error "External Tester $APP_EXTERNAL_TESTER" -} - -function install_oai_packages { - source ${ABOT_DEPLOY_DIR}/oaisim/bin/build_helper_rebaca - set_openair_deploy_env - check_install_oai_software - - # ue_ip driver compilation - { - cd ${ABOT_DEPLOY_DIR}/oaisim/openair2/NETWORK_DRIVER/UE_IP; - cp Makefile.rebaca Makefile; - make clean &> /dev/null; - make; - cp -f ue_ip.ko ${ABOT_DEPLOY_DIR}/oaisim/bin; - } &> ${ABOT_DEPLOY_DIR}/oaisim/cmake_targets/log/ue_ip.txt - - exit_on_error "Installing oaisim" - - # Not installing additional tools - # for the time being - - # check_install_additional_tools -} - -function install_ixia_packages { - pip install pyyaml - pip install paramiko - pip install dnspython - pip install scp - pip install jinja2 - pip install httplib2 - - exit_on_error "Installing Ixia python packages" -} - -function set_ixia_ims_information { - PRIVATE_IP=`ip addr | grep 'state UP' -A2 | tail -n1 | awk '{print $2}' | cut -f1 -d'/'` - sed -i "s/ABOT.SecureShell.IPAddress=[^ ]*/ABOT.SecureShell.IPAddress=${PRIVATE_IP}/" ${ABOT_DEPLOY_DIR}/config/ABotConfig.properties - - read -p "IMS DNS IP Address (e.g. IMS DNS Server): " DNS_IP; DNS_IP=${DNS_IP:="192.168.255.255"} - echo $DNS_IP > ${ABOT_DEPLOY_DIR}/dns_ip - read -p "IMS Clearwater Ellis IP Address (e.g. Clearwater Ellis IP): " ELLIS_IP; ELLIS_IP=${ELLIS_IP:=192.168.255.255} - echo $ELLIS_IP > ${ABOT_DEPLOY_DIR}/ellis_ip - read -p "IMS IxLoad IP Address: " IXLOAD_IP; IXLOAD_IP=${IXLOAD_IP:=192.168.255.255} - echo $IXLOAD_IP > ${ABOT_DEPLOY_DIR}/ixload_ip - read -p "IMS IxChassis IP Address: " IXCHASSIS_IP; IXCHASSIS_IP=${IXCHASSIS_IP:=192.168.255.255} - echo $IXCHASSIS_IP > ${ABOT_DEPLOY_DIR}/ixchassis_ip - read -p "IMS IxCard1 IP Address: " IXCARD1_IP; IXCARD1_IP=${IXCARD1_IP:=192.168.255.255} - echo $IXCARD1_IP > ${ABOT_DEPLOY_DIR}/ixcard1_ip - read -p "IMS IxCard2 IP Address: " IXCARD2_IP; IXCARD2_IP=${IXCARD2_IP:=192.168.255.255} - echo $IXCARD2_IP > ${ABOT_DEPLOY_DIR}/ixcard2_ip - - exit_on_error "Setting Ixia Information" -} - -function set_sut_ims_information { - PRIVATE_IP=`ip addr | grep 'state UP' -A2 | tail -n1 | awk '{print $2}' | cut -f1 -d'/'` - sed -i "s/ABOT.SecureShell.IPAddress=[^ ]*/ABOT.SecureShell.IPAddress=${PRIVATE_IP}/" ${ABOT_DEPLOY_DIR}/config/ABotConfig.properties - sed -i "/ip=/s/[^ ]*/ip=${PRIVATE_IP}/" ${ABOT_DEPLOY_DIR}/bin/sip - sed -i "/ip=/s/[^ ]*/ip=${PRIVATE_IP}/" ${ABOT_DEPLOY_DIR}/bin/sip_imsi_domain - sed -i "/ip=/s/[^ ]*/ip=${PRIVATE_IP}/" ${ABOT_DEPLOY_DIR}/bin/sip-stress - - PUBLIC_HOSTNAME=`hostname | cut -d"." -f1` - - read -p "IMS DNS IP Address (e.g. IMS DNS Server): " DNS_IP; DNS_IP=${DNS_IP:="192.168.255.255"} - - read -p "IMS SIP Domain (default: rebaca.local): " ZONE; ZONE=${ZONE:="rebaca.local"} - echo $ZONE > ${ABOT_DEPLOY_DIR}/sip_domain - - read -p "IMS HSS Mirror IP Address: " IMS_HSS_MIRROR_IP; IMS_HSS_MIRROR_IP=${IMS_HSS_MIRROR_IP:="192.168.255.255"} - sed -i "s/HSS_MIRROR.SecureShell.IPAddress=[^ ]*/HSS_MIRROR.SecureShell.IPAddress=${IMS_HSS_MIRROR_IP}/" \ - ${ABOT_DEPLOY_DIR}/config/ABotConfig.properties - - read -p "IMS P-CSCF IP Address: " IMS_PCSCF_IP; IMS_PCSCF_IP=${IMS_PCSCF_IP:="192.168.255.255"} - sed -i "s/P_CSCF.SecureShell.IPAddress=[^ ]*/P_CSCF.SecureShell.IPAddress=${IMS_PCSCF_IP}/" \ - ${ABOT_DEPLOY_DIR}/config/ABotConfig.properties - echo -n ${IMS_PCSCF_IP} > ${ABOT_DEPLOY_DIR}/sip_server - - exit_on_error "Setting IMS SUT Information" -} - -function set_sut_epc_information { - PRIVATE_IP=`ip addr | grep 'state UP' -A2 | tail -n1 | awk '{print $2}' | cut -f1 -d'/'` - - read -p "EPC Mobile Network Code (default: 208): " MCC; MCC=${MCC:="208"} - sed -i "s/ABOT.ENB.mobile_country_code=[^ ]*/ABOT.ENB.mobile_country_code=${MCC}/" \ - ${ABOT_DEPLOY_DIR}/config/abot-epc-basic_defaults.conf - - read -p "EPC Mobile Network Code (default: 93): " MNC; MNC=${MNC:="93"} - sed -i "s/ABOT.ENB.mobile_network_code=[^ ]*/ABOT.ENB.mobile_network_code=${MNC}/" \ - ${ABOT_DEPLOY_DIR}/config/abot-epc-basic_defaults.conf - - read -p "MME IP Address: " EPC_MME_IP; EPC_MME_IP=${EPC_MME_IP:="192.168.255.255"} - sed -i "s/ABOT.ENB.mme_ip_address_ipv4=[^ ]*/ABOT.ENB.mme_ip_address_ipv4=${EPC_MME_IP}/" \ - ${ABOT_DEPLOY_DIR}/config/abot-epc-basic_defaults.conf - sed -i "s/MME.SecureShell.IPAddress=[^ ]*/MME.SecureShell.IPAddress=${EPC_MME_IP}/" \ - ${ABOT_DEPLOY_DIR}/config/ABotConfig.properties - - read -p "EPC Subscriber OPC (e.g. 79477a68055263cb0c3052057e0d20b8): " EPC_OPC; EPC_OPC=${EPC_OPC:="79477a68055263cb0c3052057e0d20b8"} - sed -i -e "s/OPC=\".*\";/OPC=\"${EPC_OPC}\";/" \ - ${ABOT_DEPLOY_DIR}/oaisim/config/ue_eurecom_test_sfr.conf - -} - -function configure_dnsmasq { - # Update DNS server using DNSMASQ - echo nameserver ${DNS_IP} > /etc/dnsmasq.resolv.conf - grep -v ^RESOLV_CONF= /etc/default/dnsmasq > /tmp/dnsmasq.$$ - mv /tmp/dnsmasq.$$ /etc/default/dnsmasq - echo RESOLV_CONF=/etc/dnsmasq.resolv.conf >> /etc/default/dnsmasq - service dnsmasq restart - sleep 5 - # Change Resolvconf order and put dnsmasq on top - dnsmasq=lo.dnsmasq - resolvconf=/etc/resolvconf - loinet=`grep ^[a-z] /etc/resolvconf/interface-order | head -n1` - if [ $loinet != $dnsmasq ] - then - cp /etc/resolvconf/interface-order /etc/resolvconf/interface-order.orig - sed '/'$dnsmasq'/d' /etc/resolvconf/interface-order.orig > /etc/resolvconf/interface-order - sed -i '/'$loinet'/i lo.dnsmasq' /etc/resolvconf/interface-order - service dnsmasq restart - resolvconf -u - else - false - exit_on_error "dnsmasq interface order $loinet $dnsmasq" - fi - exit_on_error "Setup dnsmasq" -} - -function verify_dns_records { - if [[ `dig +short ${PRIVATE_IP}` =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]] - then - exit_on_error "Verifying ${PRIVATE_IP} in dns" - elif [[ "${PRIVATE_IP}" =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]] - then - exit_on_error "Verifying ${PRIVATE_IP} in dns" - else - cp /etc/resolvconf/interface-order.orig /etc/resolvconf/interface-order - resolvconf -u - false - exit_on_error "Unable to fetch ${PRIVATE_IP} dns order changed to original" - fi -} - -function set_keys { - # Generate ssh key for root user - if [ ! -f /root/.ssh/id_rsa.pub ]; - then - ssh-keygen -t rsa -N "" -f /root/.ssh/id_rsa - pub_key=`cat /root/.ssh/id_rsa.pub` - echo "${pub_key}" | sed 's/\r//' >> /root/.ssh/authorized_keys - else - pub_key=`cat /root/.ssh/id_rsa.pub` - echo "${pub_key}" | sed 's/\r//' >> /root/.ssh/authorized_keys - fi -} - -function configure_volte { - configure_epc - configure_ims -} - -function configure_epc { - install_oai_packages - - if [ ! $APP_NULL_CONFIG ]; then - set_sut_epc_information - set_keys - fi -} - -function configure_ixia { - install_ixia_packages - - if [ ! $APP_NULL_CONFIG ]; then - set_ixia_ims_information - set_keys - fi -} - -function configure_ims { - if [ ! $APP_NULL_CONFIG ]; then - set_sut_ims_information - configure_dnsmasq - verify_dns_records - set_keys - fi -} - - -function configure_abot { - - # If there is an external tester, - # then only configure tester and return - - case $APP_EXTERNAL_TESTER in - none) true;; - ixia) - configure_ixia - exit_on_error "Configuring $APP_EXTERNAL_TESTER" - return - ;; - *) false;; - esac - - # If there is no external tester, - # then configure application - - case $APP_NAME in - none) false;; - abot-ims-basic) configure_ims;; - abot-epc-basic) configure_epc;; - abot-functest-basic) configure_epc;; - abot-volte-basic) configure_volte;; - *) false;; - esac - exit_on_error "Configuring $APP_NAME" - -} - -APP_NAME=none -APP_VERSION=none -APP_EXTERNAL_TESTER=none -APP_NULL_CONFIG=false - -sudo -v -exit_on_error "Checking sudo privileges" - -while [ $# -gt 0 ] -do - - case $1 in - -a | --app) - APP_NAME=$2 - shift - ;; - -v | --version) - APP_VERSION=$2 - shift - ;; - -h | --help) - usage - ;; - -et | --external-tester) - APP_EXTERNAL_TESTER=$2 - shift - ;; - -nc | --null-config) - APP_NULL_CONFIG=true - ;; - *) - usage - ;; - esac - shift -done - -check_input - -ABOT_DIR=/var/lib/${APP_NAME} -ABOT_URL="http://115.249.81.187:8080/rebaca" -ABOT_DEBIAN=${APP_NAME}_${APP_VERSION}_all.deb - -# Installing dependencies for abot-volte-basic -echo_blue "Oracle Java Prerequisites..." -add-apt-repository -y ppa:webupd8team/java -apt-get update -echo debconf shared/accepted-oracle-license-v1-1 select true | sudo debconf-set-selections -echo debconf shared/accepted-oracle-license-v1-1 seen true | sudo debconf-set-selections -apt-get -y install oracle-java8-installer - -# Download ABot Debian and Install -mkdir -p ${ABOT_DIR}/payload -wget ${ABOT_URL}/${ABOT_DEBIAN} -O ${ABOT_DIR}/payload/${ABOT_DEBIAN} -exit_on_error "Download Debian $ABOT_DEBIAN" -dpkg --install ${ABOT_DIR}/payload/${ABOT_DEBIAN} || true -apt-get -f -y install -exit_on_error "ABot Debian Installed!!!" - -# Configure ABot -configure_abot diff --git a/abot-ims-basic/metadata.yaml b/abot-ims-basic/metadata.yaml deleted file mode 100644 index 71601eb..0000000 --- a/abot-ims-basic/metadata.yaml +++ /dev/null @@ -1,24 +0,0 @@ -name: abot-ims-basic -summary: An Automation Framework built on behaviour-oriented testing model -maintainer: Rebaca Technologies -description: | - A Test Automation Framework for network services that is built on the behaviour-oriented testing model. - This enables users to define test cases as feature files based on domain-specific language. - These feature files could subsequently be executed in automated manner on multiple environments. -tags: - - misc - - test-automation - - ims -subordinate: false -requires: - programmable-multiple: - interface: dns-multi - ue-abot: - interface: 3GPP-Gm - hss-prov: - interface: homestead-prov-interface - abot-es: - interface: abot-es -provides: - hss-abot: - interface: 3GPP-Cx diff --git a/abot-ims-basic/revision b/abot-ims-basic/revision deleted file mode 100644 index 56a6051..0000000 --- a/abot-ims-basic/revision +++ /dev/null @@ -1 +0,0 @@ -1 \ No newline at end of file diff --git a/abot-ims-basic/tests/01-setup b/abot-ims-basic/tests/01-setup deleted file mode 100755 index cd5b3a2..0000000 --- a/abot-ims-basic/tests/01-setup +++ /dev/null @@ -1,5 +0,0 @@ -#!/bin/bash - -sudo add-apt-repository ppa:juju/stable -y -sudo apt-get update -sudo apt-get install amulet python3-requests -y diff --git a/abot-ims-basic/tests/02-standard b/abot-ims-basic/tests/02-standard deleted file mode 100755 index c3da949..0000000 --- a/abot-ims-basic/tests/02-standard +++ /dev/null @@ -1,67 +0,0 @@ -#!/usr/bin/env python3 - -import amulet -import requests -import unittest - - -class TestAbotimsbasicDeployment(unittest.TestCase): - @classmethod - def setUpClass(cls): - cls.deployment = amulet.Deployment(series='trusty') - cls.deployment.add('abot-ims-basic', charm='~debayan-ch/trusty/abot-ims-basic-33') - cls.deployment.configure('abot-ims-basic', {'clearwater_sipp_ip' : '172.16.255.255'}) - - try: - cls.deployment.setup(timeout=9000) - cls.deployment.sentry.wait() - except amulet.helpers.TimeoutError: - amulet.raise_status(amulet.SKIP, msg="Environment wasn't stood up in time") - except: - raise - - def test_homepage(self): - # Now you can use self.deployment.sentry.unit[UNIT] to address each of - # the units and perform more in-depth steps. You can also reference - # the first unit as self.unit. - # There are three test statuses that can be triggered with - # amulet.raise_status(): - # - amulet.PASS - # - amulet.FAIL - # - amulet.SKIP - # Each unit has the following methods: - # - .info - An array of the information of that unit from Juju - # - .file(PATH) - Get the details of a file on that unit - # - .file_contents(PATH) - Get plain text output of PATH file from that unit - # - .directory(PATH) - Get details of directory - # - .directory_contents(PATH) - List files and folders in PATH on that unit - # - .relation(relation, service:rel) - Get relation data from return service - # add tests here to confirm service is up and working properly - # For example, to confirm that it has a functioning HTTP server: - # page = requests.get('http://{}'.format(self.unit.info['public-address'])) - # page.raise_for_status() - # More information on writing Amulet tests can be found at: - # https://jujucharms.com/docs/stable/tools-amulet - # Abot-ims-basic sentry unit - abotimsbasic_unit = self.deployment.sentry['abot-ims-basic'] [0] - # Abot-ims-basic home page URL - abotimsbasic_url = 'http://{public-address}'.format(**abotimsbasic_unit.info) - # Request for home page of abot ims basic - homepage = requests.get(abotimsbasic_url) - # Status of Homepage - homepage.raise_for_status() - pass - def test_config(self): - #Abot-ims-basic sentry unit - abotimsbasic_unit = self.deployment.sentry['abot-ims-basic'] [0] - #Sip server config file - sip_ip_config = abotimsbasic_unit.file_contents('/etc/rebaca-test-suite/clearwater_sipp_ip') - #Sip server content - sip_ip_content = "172.16.255.255" - #Compare the configuration change - self.assertTrue(sip_ip_content in sip_ip_config) - pass -#if __name__ == '__main__': -# unittest.main() -suite = unittest.TestLoader().loadTestsFromTestCase(TestAbotimsbasicDeployment) -unittest.TextTestRunner(verbosity=2).run(suite) diff --git a/abot-ims-basic/tests/03-action b/abot-ims-basic/tests/03-action deleted file mode 100755 index a5ed586..0000000 --- a/abot-ims-basic/tests/03-action +++ /dev/null @@ -1,95 +0,0 @@ -#!/usr/bin/env python3 - -import amulet -import requests -import unittest - - -class TestAbotimsbasicDeployment(unittest.TestCase): - @classmethod - def setUpClass(cls): - cls.deployment = amulet.Deployment(series='trusty') - cls.deployment.add('abot-ims-basic', charm='~debayan-ch/trusty/abot-ims-basic-33') - cls.deployment.configure('abot-ims-basic', {'clearwater_sipp_ip' : '172.16.255.255'}) - - try: - cls.deployment.setup(timeout=9000) - cls.deployment.sentry.wait() - except amulet.helpers.TimeoutError: - amulet.raise_status(amulet.SKIP, msg="Environment wasn't stood up in time") - except: - raise - - def test_homepage(self): - # Now you can use self.deployment.sentry.unit[UNIT] to address each of - # the units and perform more in-depth steps. You can also reference - # the first unit as self.unit. - # There are three test statuses that can be triggered with - # amulet.raise_status(): - # - amulet.PASS - # - amulet.FAIL - # - amulet.SKIP - # Each unit has the following methods: - # - .info - An array of the information of that unit from Juju - # - .file(PATH) - Get the details of a file on that unit - # - .file_contents(PATH) - Get plain text output of PATH file from that unit - # - .directory(PATH) - Get details of directory - # - .directory_contents(PATH) - List files and folders in PATH on that unit - # - .relation(relation, service:rel) - Get relation data from return service - # add tests here to confirm service is up and working properly - # For example, to confirm that it has a functioning HTTP server: - # page = requests.get('http://{}'.format(self.unit.info['public-address'])) - # page.raise_for_status() - # More information on writing Amulet tests can be found at: - # https://jujucharms.com/docs/stable/tools-amulet - # Abot-ims-basic sentry unit - abotimsbasic_unit = self.deployment.sentry['abot-ims-basic'] [0] - # Abot-ims-basic home page URL - abotimsbasic_url = 'http://{public-address}'.format(**abotimsbasic_unit.info) - # Request for home page of abot ims basic - homepage = requests.get(abotimsbasic_url) - # Status of Homepage - homepage.raise_for_status() - pass - def test_config(self): - #Deployment sentry for Abot-ims-basic - abotimsbasic_unit = self.deployment.sentry['abot-ims-basic'] [0] - #SIP server config file - sip_ip_config = abotimsbasic_unit.file_contents('/etc/rebaca-test-suite/clearwater_sipp_ip') - #Verify SIP server name with - sip_ip_content = "172.16.255.255" - #Asssertion for verification - self.assertTrue(sip_ip_content in sip_ip_config) - - def test_action_add_tags(self): - #abot_unit = self.deployment.sentry['abot'] [0] - abotimsbasic_unit = self.deployment.sentry['abot-ims-basic'] [0] - #Add tags using action command - abotimsbasic_unit.run_action('add-tags',{'tagnames' : 'sip-subscribe-notify'}) - # Tag addition verification - tag_file_content = abotimsbasic_unit.file_contents('/etc/rebaca-test-suite/tags/tags') - # Verify tag name with - tag_name = 'sip-subscribe-notify' - # - self.deployment.sentry.wait_for_messages({'abot-ims-basic':'Ready'}, timeout=300) - # Asssertion for verification - self.assertTrue(tag_name in tag_file_content) - pass - - def test_action_run(self): - #Sentry for Abot-ims-basic unit - abotimsbasic_unit = self.deployment.sentry['abot-ims-basic'] [0] - #Run the action hook of list-tags - abotimsbasic_unit.run_action('run', {'tagnames' : 'local-commands'}) - #Wait for time to complete run action - self.deployment.sentry.wait_for_messages({'abot-ims-basic':'Ready'}, timeout=300) - # Check the file statistics to check successful execution of run action - abotimsbasic_unit.file_stat('lib/testReports/report-*/*local-commands*.html') - #Raise status on status of action_check - pass - - -#if __name__ == '__main__': -# unittest.main() -suite = unittest.TestLoader().loadTestsFromTestCase(TestAbotimsbasicDeployment) -unittest.TextTestRunner(verbosity=2).run(suite) diff --git a/abot-oai-epc-bundle/README.md b/abot-oai-epc-bundle/README.md deleted file mode 100644 index b6f3f96..0000000 --- a/abot-oai-epc-bundle/README.md +++ /dev/null @@ -1,95 +0,0 @@ -# Charms Overview - -Abot-OAI-EPC bundle deploys abot-epc-basic along-with Oai-hss, Oai-epc and Mysql charms, which support deployment and scaling of OAI charms along with ABot developed by Rebeca. - -ABot should typically be deployed alongside the System Under Test(SUT) - such that it can execute tests against the latter. One of the possible SUTs is the OAI EPC, which is an open source initiative of Eurecom. - -ABot enables users to create **feature files** based on a **Cucumber** framework. Cucumber is a Behavior Driven Development (BDD) framework which is used to write tests for different types of applications. It allows automation of functional validation in an easily readable and understandable format (like plain English). It is used to test the system rather than testing the particular piece of code. - -The feature files authored by the user(s) can subsequently be used for testing different types of applications/protocols that are running on the System Under Test(SUT). The feature files follow a specified grammar and include instructions that translate into sending messages to the SUT, and receiving back responses using different protocols and verification of the results as defined. - -The current protocol drivers(adapters) provided with ABot are SSH, SFTP, HTTP as well as S1AP. - -Each feature file can be associated with one (or more) tags; thereby enabling it to be selectively invoked. **Execution of feature file(s) associated with one (or more) specified tags is driven through a Juju action after deployment of the ABot charm.** - -The result of each test execution is displayed in a web-based reporting UI; this is currently accessible on port 80. - -# Usage - -Before deploying this bundle, you should bootstrap a Juju environment, as documented in the https://jujucharms.com/docs/stable/getting-started. The testing of this charm on MAAS cloud is complete, and there are plans to test it on Amazon and OpenStack in the near future. If you get it working on a different cloud service, please let us know! - -OAI is a relatively simpler bundle consisting of Mysql, Oai-hss, Oai-epc and Abot-EPC-basic, together forming the SUT for feature file execution. The ABot OAI EPC bundle has been integrated and tested on MAAS cloud. You can find the ABot OAI EPC bundle at [ ]. To know more about ABot please visit charmstore (https://jujucharms.com/u/abotcharm/abot-epc-basic/xenial/). - -The ABot OAI EPC bundle ties up all the above mentioned charms and their relations together, allowing the deployment of a full system in a single step. - ->juju deploy [bundle_name] - - -# Configuration - -See [bundle.yaml](bundle.yaml), which lists the main configuration fields with comments about their meanings. - -# Files downloaded - -When the above mentioned charms are being installed, the following files are downloaded from their respected paths: - -- The ABot Debian package, from the public package repository server hosted at Rebaca. -- Any dependencies related to Debian packages (particularly Java and Maven), are downloaded from the standard Ubuntu repository servers. -- Any dependency related to OAI charms are downloaded from their respective repositories. - -# Test Case Execution - - - After deployment of the ABot OAI EPC bundle, the following actions can be quickly executed to verify whether the bundle works fine: -> juju run-action abot-epc-basic/[abot-unit-number] run - - - The following script needs to be executed from the Juju controller in order to generate and add the ssh keys to all the ABot OAI EPC bundle units. - -> charm pull cs:~abotcharm/xenial/abot-epc-basic -> ./abot-epc-basic/lib/scripts/add-keys.sh - -- In order to execute a particular feature file or a set of feature files we need to execute the following commands mentioned below, replacing the tag name mentioned below with the required tag name. Also in the following commands we need to replace the [abot-unit-number] with the current abot-unit-number. - -> juju run-action abot-epc-basic/[abot-unit-number] run tagnames=[tag name] - -In order to run EPC attach test case feature file: -> juju run-action abot-epc-basic/8 run tagnames=Attach_Procedure_AttachWithIMSI - -In order to run EPC MAC Failure test case feature file: -> juju run-action abot-epc-basic/8 run tagnames=Auth_NotAccept_by_UE_GUTIattach_MAC_code_failure - -In order to run EPC SQN Failure test case feature file: -> juju run-action abot-epc-basic/8 run tagnames=Auth_NotAccept_by_UE_SQN_failure - -In order to run EPC negative test cases in multiple feature files: -> juju run-action abot-epc-basic/8 run tagnames=negTCs - -In order to run 3GPP TS 24.301 test cases in multiple feature files: -> juju run-action abot-epc-basic/8 run tagnames=TS_24_301 - -- The following action will execute all test cases (feature files) for which the tags have been added through the previous command. Please note that the tags can be run from a specified filename as well. - -> juju run-action abot-epc-basic/[abot-unit-number] run tagnames=[feature file name without extension] - -- Execute tags from the file titled ***negTCs***. This file includes tags for negative call scenarios; such as *sync failure*, *MAC failure* and so on. -> juju run-action abot-epc-basic/[abot-unit-number] run tagnames=negTCs - -# Adding new Test Cases (Feature Files) - -- **New Feature Files should be added into the following folder** on the Juju controller node -> charm pull cs:~abotcharm/trusty/abot-ims-basic -> $PWD/abot-epc-basic/lib/featureFiles - -- The Juju charm upgrade action should be invoked to propagate these new feature files onto the deployed service node. - -> juju upgrade-charm --path=$PWD/abot-epc-basic abot-epc-basic - -- Once the upgrade is successful, the test case execution steps can be followed to execute one or more feature files. - -# Viewing Test Report - - - The report UI can then be viewed by accessing the below mentioned URL. - http://[abot-ip]/app - Eg : http://192.168.15.87/app/ - -- This pulls up the Cucumber report of the latest execution. -- Accessing any of the feature files present in the report shows the step-wise execution status for that feature file. diff --git a/abot-oai-epc-bundle/bundle.yaml b/abot-oai-epc-bundle/bundle.yaml deleted file mode 100644 index 5b1433f..0000000 --- a/abot-oai-epc-bundle/bundle.yaml +++ /dev/null @@ -1,63 +0,0 @@ - services: - "oai-hss": - charm: cs:~abotcharm/trusty/oai-hss - num_units: 1 - constraints: "arch=amd64 mem=2G" - series: trusty - options: - operator-key: "1006020f0a478bf6b699f15c062e42b3" - random: "true" - annotations: - "gui-x": "400" - "gui-y": "900" - "oai-spgw": - charm: cs:~abotcharm/xenial/oai-spgw - num_units: 1 - constraints: "arch=amd64 mem=4G" - series: xenial - options: - zone: "rebaca.local" - annotations: - "gui-x": "550" - "gui-y": "900" - "oai-mme": - charm: cs:~abotcharm/xenial/oai-mme - num_units: 1 - constraints: "arch=amd64 mem=4G" - series: xenial - options: - gummei_tai_mnc: "93" - annotations: - "gui-x": "500" - "gui-y": "900" - "mysql": - charm: cs:mysql-55 - num_units: 1 - constraints: "arch=amd64 mem=1G" - series: trusty - annotations: - "gui-x": "200" - "gui-y": "300" - abot-epc-basic: - charm: $PWD/abot/charm/abot-epc-basic - constraints: "arch=amd64 mem=6G" - series: xenial - expose: true - num_units: 1 - annotations: - "gui-x": "400" - "gui-y": "700" - relations: - - - "mysql:db" - - "oai-hss:db" - - - "oai-hss:hss" - - "oai-mme:hss" - - - "oai-mme:spgw" - - "oai-spgw:spgw" - - - "oai-mme:mme" - - "abot-epc-basic:epc" - - - "abot-epc-basic:ssh-abot" - - "oai-mme:ssh-abot-mme" -# - - "oai-hss:opc" -# - "oai-mme:opc-key" - series: xenial diff --git a/abot-volte-basic/README.md b/abot-volte-basic/README.md deleted file mode 100644 index 0ff0c8f..0000000 --- a/abot-volte-basic/README.md +++ /dev/null @@ -1,121 +0,0 @@ -# Charms Overview - -This is a [Juju charm](https://jujucharms.com/about), which allows deployment of the Rebaca Automation Behaviour-oriented Testing Framework(ABOT). - -ABOT should typically be deployed alongside the System Under Test(SUT) - such that it can execute tests against the latter. One of the possible SUTs is the ClearWater IMS developed by Metaswitch. - -ABOT enables users to create **feature files** based on **Cucumber** framework. Cucumber is a Behavior Driven Development (BDD) framework which is used to write tests for different types of applications. It allows automation of functional validation in easily readable and understandable format (like plain English). It is used to test the system rather than testing the particular piece of code. - -The feature files authored by user(s) can subsequently be used for testing different types of applications/protocols that are running on the System Under Test(SUT). The feature files follows a specified grammar and includes instructions that translate into sending messages to the SUT and receiving back responses using different protocols and verification of the results as defined. - -The current protocol drivers(adapters) provided with Abot are SSH, SFTP, HTTP as well as SIP. As such, it should be possible to execute tests on any SUT that supports the above protocols. - -In the absence of an SUT, it is also possible to write feature files for executing local commands on the deployed unit and validating the return values. - -Each feature file can be associated with one(or more) tags; thereby enabling it to be selectively invoked. **Execution of feature file(s) associated with one(or more) specified tags is driven through a Juju action after deployment of the Abot charm.** - -The result of each test execution is displayed in a web-based reporting UI; which is currently accessible on port 80. - - -## Deployment - -# Initial deployment - -The Abot service should be deployed as a standalone charm on a single unit; typically after the deployment of the System Under Test(SUT). - -As such, assuming that the SUT is the Clearwater IMS( https://github.com/thomnico/juju-nfv-clearwater-restcomm/tree/trusty), the Abot charm should be deployed after the deployment of the Clearwater IMS bundle, following the instructions provided in the ClearWater github repository. - -The typical command for deploying Abot is as follows: - - -> juju deploy --repository=~/charms local:trusty/abot - -Subsequently, it needs to be exposed using the following command. - -> juju expose abot - -## Adding Relations with ClearWater IMS charms - -The following relations should be added if the ClearWater IMS bundle is also installed in the setup. -> juju add-relation abot dns -> juju add-relation abot clearwater-bono -> juju add-relation abot clearwater-homestead - - -# Using Abot - -Once installed, Abot comes with a set of feature files that can be run against an existing Clearwater IMS setup(if it exists); after setting a few configuration parameters. - - -# Configuration - -These parameters are specific to the IMS Test System; and do not need to be specified if we are not validating an IMS. - -- `sip_server`: SIP proxy server FQDN of the IMS Test System. An example value would be bono.clearwater.local. **This is automatically set once the relation with ClearWater bono(SIP proxy server) is invoked.** -- `sip_domain`: SIP domain associated with the IMS Test System. An example value would be clearwater.local. **This is automatically set once the relation with ClearWater bono(SIP proxy server) is invoked.** -- `ims_hss_mirror_ip`: The IP address of the HSS Mirror of the IMS Test System. **This is automatically set once the relation with ClearWater Homestead is invoked.** -- `ims_cscf_ip`: The IP address of the P-CSCF.component of the IMS Test System. In the context of Clearwater IMS, this is the IP address of the node running clearwater-bono service. **This is automatically set once the relation with ClearWater bono(SIP proxy server) is invoked.** -- `clearwater_sipp_ip`: (optional) The IP address of the SIPP component of the IMS Test System(if any). In the context of Clearwater IMS, this is the IP address of the node running clearwater-sipp service. - -# Files downloaded - -When the charm is being installed, several files are downloaded: - -- The ABOT Debian package, from our public package repository server hosted at Rebaca. -- Any dependencies of those Debian packages (particularly Java and Maven), from the standard Ubuntu repository servers. - -# Test Case Execution - - - After deployment of Abot charm, the following actions can be quickly executed to verify whether the charm works fine. -> juju action do abot/[abot-unit-number] run - - - The report UI can then be viewed by clicking on the link that appears under the specific unit of the service in the Juju GUI - - - When testing against the ClearWater IMS, the following script needs to be invoked from the Juju controller machine in order to create the ssh keys on the Test Framework and distribute them on the relevant IMS nodes - - -> ~/charms/trusty/abot/lib/scripts/add-keys.sh - - -- The following action(s) will add new tags for subsequent execution. Pls. note that the unit number below should be replaced with the proper value from the given deployment. Also, pls. note that the tag-names specified below are sample ones and should be replaced by the actual tags associated with newly authored feature files. - - -> juju action do abot/[abot-unit-number] add-tags tagnames=sip-single-call -> juju action do abot/[abot-unit-number] add-tags tagnames=sip-subscribe-notify,sip-messaging - -- Pls. note that the new tags may also be added to specific filenames; for future retrieval. -> juju action do abot/[abot-unit-number] add-tags tagnames=sip-register,sip-ssh-test,ims-user-add,sip-single-call,sip-multi-call,sip-unregister filename=normal_cases -> juju action do abot/[abot-unit-number] add-tags tagnames=sip-call-busy,sip-call-no-answer,sip-call-reject,sip-call-unavailable,sip-call-cancel,sip-malformed_request,sip-invalid-register,sip-single-call-error filename=negative_cases - - -- The following action will execute all test cases(feature files) for which the tags have been added through the previous command. Pls. note that the tags can be run from a specified filename as well. - -> juju action do abot/[abot-unit-number] run - -- Execute tags from the file titled ***normal_cases***. This file includes tags for normal/successful call scenarios. -> juju action do abot/[abot-unit-number] run filename=normal_cases - -- Execute tags from the file titled ***normal_cases***. This file includes tags for normal/successful call scenarios. -> juju action do abot/[abot-unit-number] run filename=normal_cases - -- Execute tags from the file titled ***negative_cases***. This file includes tags for negative call scenarios; such as *user busy*, *no answer*, *cancel* and so on. -> juju action do abot/[abot-unit-number] run filename=negative_cases - -# Adding new Test Cases(Feature Files) - -- **New Feature Files should be added into the following folder** on the Juju controller node - ~/charms/trusty/abot/lib/featureFiles - -- The Juju charm upgrade action should be invoked to propagate these new feature files onto the deployed service node. - - -> juju upgrade-charm --repository=~/charms abot - -- Once the upgrade is successful, the test case execution steps can be followed to execute one or more feature files. - -# Viewing Test Report - -- Login to the Juju GUI and select Abot service -- Select the specific unit of the service and click on the link. -- Click of the test report link that appears under the IP Address header for the given Abot unit. -- This should show up the Cucumber report of the last executed test. diff --git a/abot-volte-basic/actions.yaml b/abot-volte-basic/actions.yaml deleted file mode 100644 index 9bebeba..0000000 --- a/abot-volte-basic/actions.yaml +++ /dev/null @@ -1,85 +0,0 @@ -# actions.yaml - -run: - description: Run the Automation framework for the specified tags(or filename) - params: - tagnames: - type: string - filename: - type: string - additionalProperties: false - -add-tags: - description: Add the tag(s) to the default file or specific file - params: - tagnames: - type: string - filename: - type: string - required: [tagnames] - additionalProperties: false - -delete-tags: - description: Delete the tag(s) from all files or specific file - params: - tagnames: - type: string - filename: - type: string - required: [tagnames] - additionalProperties: false - -list-tags: - description: List the tags from all files or specific file - params: - filename: - type: string - additionalProperties: false - -delete-all-tags: - description: Delete all tags from the default file or specific file - params: - filename: - type: string - additionalProperties: false - -update-cx-params: - description: Update different diamater-related parameters into the Abot system - params: - cxserver-originhost: - type: string - cxserver_originrealm: - type: string - cxserver_desthost: - type: string - cxserver_clientIp: - type: string - -configure-cx-client: - description: Configure the HSS client to point to Abot(diameter) or to authenticate from cache. Possible values of auth-type parameter are hss and cache. - params: - auth-type: - type: string - required: [auth-type] - additionalProperties: false - -ims-benchmark: - description: To run benchmark as an action. - params: - num-calls: - type: integer - default: 1000 - constraints: - - range: - min: 1 - max: 1000 - call-rate: - type: integer - default: 5 - sip-scenario: - type: string - default: register - timeout-in-minutes: - type: integer - default: 10 - diff --git a/abot-volte-basic/actions/add-tags b/abot-volte-basic/actions/add-tags deleted file mode 100755 index 83ab067..0000000 --- a/abot-volte-basic/actions/add-tags +++ /dev/null @@ -1,55 +0,0 @@ -#!/bin/bash -# Action -Add Tags -set -eu -DIR=/etc/rebaca-test-suite/tags - -# Add tags specified by TAG_NAMES to the file specified by TAG_FILE_NAME -function add_tags_to_file() -{ - #Initialize new_tags variable with blank value. - new_tags= - - if [ -f $DIR/$TAG_FILE_NAME ]; then - new_tags=`cat $DIR/$TAG_FILE_NAME` - fi - - if [ -z "$TAG_NAMES" ]; then - juju-log "No tag names specified - none added" - else - juju-log "Tag names to be added: $TAG_NAMES" - - #Split the tags and add the @ symbols - IFS=', ' read -r -a array <<< "$TAG_NAMES" - for index in "${!array[@]}" - do - if [ -z "$new_tags" ]; then - new_tags="@${array[index]}" - else - #Add current tag only if not duplicate - current_tag="${array[index]}" - if [ `echo $new_tags | grep -c $current_tag` -eq 0 ]; then - new_tags="$new_tags,@$current_tag" - fi - fi - done - - #Add to new file if filename defined - echo -n "$new_tags" > $DIR/$TAG_FILE_NAME - - juju-log "Filename:$TAG_FILE_NAME, Tags:$new_tags" - fi -} - -TAG_NAMES=$(action-get tagnames) -TAG_FILE_NAME=$(action-get filename) - -if [ ! -z "$TAG_FILE_NAME" ]; then - juju-log "Tag file specified: $TAG_FILE_NAME" -else - #Assign default file name - TAG_FILE_NAME=tags -fi -status-set active "Executing - Adding Tags" -add_tags_to_file -status-set active "Ready" - diff --git a/abot-volte-basic/actions/benchmark b/abot-volte-basic/actions/benchmark deleted file mode 100755 index 9e08576..0000000 --- a/abot-volte-basic/actions/benchmark +++ /dev/null @@ -1,157 +0,0 @@ -#!/bin/bash -# Action -Run -set -eux -# Make sure charm-benchmark is installed -if ! hash benchmark-start 2>/dev/null; then -apt-get install -y python-pip -pip install -U charm-benchmark -fi - -#Benchmark Start -status-set active "Executing benchmark action" -benchmark-start -featurefile_dir="/etc/rebaca-test-suite/featureFiles" -configfile_dir="/etc/rebaca-test-suite/config" -sipcommand="/etc/rebaca-test-suite/bin/sip" -sip_users="/etc/rebaca-test-suite/scenarios/user_bulk.csv" -num_calls=`action-get num-calls` -callrate=`action-get call-rate` -sipscenario=`action-get sip-scenario` -user_range_start=2010000000 -user_range_end=$((user_range_start+100+num_calls*2)) -max_recv_loops=2000 -max_sched_loops=2000 -recv_timeout=20000 -iter_start=0 -total_timeout=`action-get timeout-in-minutes` -sleep_for_completion=30 - -system_kpi() -{ - echo "System_KPI $1 $(date)###################" >> ~/systemkpi.log - echo "-----------------------------" >> ~/systemkpi.log - echo " CPU Usage - $(grep 'cpu ' /proc/stat | awk '{usage=($2+$4)*100/($2+$4+$5)} END {print usage "%"}')" >> ~/systemkpi.log - echo "-----------------------------" >> ~/systemkpi.log - free -m >> ~/systemkpi.log - echo "-----------------------------" >> ~/systemkpi.log - df -h >> ~/systemkpi.log - echo "-----------------------------" >> ~/systemkpi.log - -} - -kill_running_processes() -{ - num_process=`ps -ef | grep sipp | grep -v grep | wc -l` - if [ $num_process -gt 0 ] ; then - sudo killall sipp - sleep 2 - fi - - java_process=`ps -ef | grep java | grep -v grep | wc -l` - if [ $java_process -gt 0 ] ; then - sudo killall java - sleep 2 - fi - - tcpdump_process=`ps -ef | grep tcpdump | grep -v grep | wc -l` - if [ $tcpdump_process -gt 0 ] ; then - sudo killall tcpdump - sleep 2 - fi -} - -#Load-test command - -system_kpi IMS-BENCHMARK_START - -PACKET_CAPTURE_ENABLE=`grep "ABOT.PacketCapture=" /etc/rebaca-test-suite/config/config.properties | awk -F"=" '{print $2}'` -juju-log "PACKET_CAPTURE_ENABLE=$PACKET_CAPTURE_ENABLE" - - -kill_running_processes - -sed -e "s/USER_RANGE_START/$user_range_start/g" -e "s/USER_RANGE_END/$user_range_end/g" <$featurefile_dir/001-clearwater-ims-bulk-user-add.feature.template>$featurefile_dir/001-clearwater-ims-bulk-user-add.feature - - -if [ "$PACKET_CAPTURE_ENABLE" == "true" ]; then - sed -ie "s|ABOT\.PacketCapture=.*|ABOT\.PacketCapture=false|" $configfile_dir/config.properties - ./actions/run ims-bulk-user-add no-report - sed -ie "s|ABOT\.PacketCapture=.*|ABOT\.PacketCapture=true|" $configfile_dir/config.properties -else - ./actions/run ims-bulk-user-add no-report -fi - - -if [ $sipscenario = invite ]; then - initial_wait=15 - scenarioxml="/etc/rebaca-test-suite/scenarios/sip_invite_load.xml" -else - initial_wait=15 - scenarioxml="/etc/rebaca-test-suite/scenarios/register.xml" -fi - -status-set active "Executing benchmark action" - -touch ~/result.log - - -$sipcommand -sf $scenarioxml -inf $sip_users -m $num_calls -r $callrate -max_recv_loops $max_recv_loops -max_sched_loops $max_sched_loops -recv_timeout $recv_timeout -t tn > ~/result.log 2>&1 & - - -loop_count=$((total_timeout*2)) -juju-log "resultant loop count "$loop_count -sleep $initial_wait -while [ $iter_start -lt $loop_count ] -do - num_process=`ps -ef | grep sipp | grep -v grep | wc -l` - juju-log "number of SIP process "$num_process - if [ $num_process -lt 1 ] ; then - break - else - echo "process present" - sleep $sleep_for_completion - fi - iter_start=`expr $iter_start + 1` -done - -num_process=`ps -ef | grep sipp | grep -v grep | wc -l` -if [ $num_process -gt 0 ] ; then - sudo killall sipp -fi - - -# Grep/awk/parse the results - -callrate=`awk '/Call\ Rate/ { print $7 }' ~/result.log` -totalcall=`awk '/Total\ Call/ { print $6 }' ~/result.log` -successfulcall=`awk '/Successful\ call/ { print $6 }' ~/result.log` -regresponsetime=`awk '/Response\ Time\ register/ { print $7 }' ~/result.log` -calllength=`awk '/Call\ Length/ { print $6 }' ~/result.log` -multiply=$((successfulcall*100)) -passpercentage=$((multiply/totalcall)) -failedcalls=$((totalcall-successfulcall)) - -if [ $sipscenario = invite ]; then - csresponsetime=`awk '/Response\ Time\ call\-set/ { print $7 }' ~/result.log` -fi -#set benchmark data - -benchmark-data 'callrate' $callrate 'CPS' -benchmark-data 'totalcalls' $totalcall 'nos' -benchmark-data 'successfulcalls' $successfulcall 'nos' -benchmark-data 'failedcalls' $failedcalls 'nos' -benchmark-data 'regresponsetime' $regresponsetime 'HH:mm:ss:SSS' -benchmark-data 'calllength' $calllength 'HH:mm:ss:SSS' -if [ $sipscenario = invite ]; then - benchmark-data 'csresponsetime' $csresponsetime 'HH:mm:ss:SSS' -fi - - -# Set the composite, which is the single most important score - -benchmark-composite $passpercentage 'percent' 'desc' - -#Benchchmark finish -benchmark-finish || true - -status-set active "Ready" diff --git a/abot-volte-basic/actions/configure-cx-client b/abot-volte-basic/actions/configure-cx-client deleted file mode 100755 index 1c2c9a5..0000000 --- a/abot-volte-basic/actions/configure-cx-client +++ /dev/null @@ -1,28 +0,0 @@ -#!/bin/bash -set -e - -juju-log "Configuring Cx client" - -AUTH_TYPE=$(action-get auth-type) - -CX_CLIENT_IP=`grep "^HSS_MIRROR.Host.IPAddress" $CHARM_DIR/lib/config/config.properties | cut -d= -f2` -if [ -z "$CX_CLIENT_IP" ]; then - juju-log "HSS hostname not found - exiting" - exit 0 -fi - -juju-log "CX_CLIENT_IP=$CX_CLIENT_IP" - -ABOT_IP=`grep "^ABOT.Host.IPAddress=" $CHARM_DIR/lib/config/config.properties | cut -d= -f2` -juju-log "ABOT_IP=$ABOT_IP" - -CX_CONFIGURATION_SCRIPT=configure_is_cscf -juju-log "Copying configuration script $CX_CONFIGURATION_SCRIPT to $CX_CLIENT_IP" - -sed -i "s/HSS_HOSTNAME=[^ ]*/HSS_HOSTNAME=$ABOT_IP/" $CHARM_DIR/lib/scripts/$CX_CONFIGURATION_SCRIPT -sed -i "s/HSS_TAG=[^ ]*/HSS_TAG=$AUTH_TYPE/" $CHARM_DIR/lib/scripts/$CX_CONFIGURATION_SCRIPT - -cd $CHARM_DIR/lib/scripts -scp -i /home/ubuntu/.ssh/id_rsa -o StrictHostKeyChecking=no $CX_CONFIGURATION_SCRIPT ubuntu@$CX_CLIENT_IP:/home/ubuntu - -ssh -i /home/ubuntu/.ssh/id_rsa -o StrictHostKeyChecking=no ubuntu@$CX_CLIENT_IP /home/ubuntu/$CX_CONFIGURATION_SCRIPT diff --git a/abot-volte-basic/actions/delete-all-tags b/abot-volte-basic/actions/delete-all-tags deleted file mode 100755 index 96f00a6..0000000 --- a/abot-volte-basic/actions/delete-all-tags +++ /dev/null @@ -1,26 +0,0 @@ -#!/bin/bash -# Action -Delete all tags -set -e -DIR=/etc/rebaca-test-suite/tags - -# Delete multiple tags from a given file -function delete_all_tags_from_file() -{ - FILE_NAME=$1 - - #Delete the specified file - rm -f $DIR/$FILE_NAME - juju-log "Cleared all tags from file: $FILE_NAME" -} - - -TAG_FILE_NAME=$(action-get filename) - -if [ ! -z "$TAG_FILE_NAME" ]; then - juju-log "Tag file specified: $TAG_FILE_NAME" -else - #Assign default file name - TAG_FILE_NAME=tags -fi - -delete_all_tags_from_file $TAG_FILE_NAME diff --git a/abot-volte-basic/actions/delete-tags b/abot-volte-basic/actions/delete-tags deleted file mode 100755 index 969ceab..0000000 --- a/abot-volte-basic/actions/delete-tags +++ /dev/null @@ -1,51 +0,0 @@ -#!/bin/bash -# Action -Delete tags -set -e -DIR=/etc/rebaca-test-suite/tags - -# Delete multiple tags from a given file -function delete_tags_from_file() -{ - TAG_NAMES=$1 - FILE_NAME=$2 - - if [ ! -f $DIR/$FILE_NAME ]; then - juju-log "Filename($FILE_NAME) does not exist" - return - fi - - IFS=', ' read -r -a array <<< "$TAG_NAMES" - for index in "${!array[@]}" - do - tagname="${array[index]}" - - cat $DIR/$FILE_NAME | sed "s/,@$tagname//g" > $DIR/tmp - cp $DIR/tmp $DIR/$FILE_NAME - cat $DIR/$FILE_NAME | sed "s/@$tagname,//g" > $DIR/tmp - cp $DIR/tmp $DIR/$FILE_NAME - cat $DIR/$FILE_NAME | sed "s/@$tagname//g" > $DIR/tmp - cp $DIR/tmp $DIR/$FILE_NAME - - rm $DIR/tmp - done - - UPDATED_TAGS=`cat $DIR/$FILE_NAME` - juju-log "Filename:$FILE_NAME, Tags:$UPDATED_TAGS" -} - -TAG_NAMES=$(action-get tagnames) -TAG_FILE_NAME=$(action-get filename) - -if [ -z "$TAG_FILE_NAME" ] ; then - juju-log "No filename specified; will delete specified tags from all files" - - for filename in `ls -t $DIR` - do - delete_tags_from_file $TAG_NAMES $filename - done -else - juju-log "Deleting specified tags from $TAG_FILE_NAME" - delete_tags_from_file $TAG_NAMES $TAG_FILE_NAME -fi - - diff --git a/abot-volte-basic/actions/list-tags b/abot-volte-basic/actions/list-tags deleted file mode 100755 index e6078c0..0000000 --- a/abot-volte-basic/actions/list-tags +++ /dev/null @@ -1,31 +0,0 @@ -#!/bin/bash -# Action -List tags -set -eu - -DIR=/etc/rebaca-test-suite/tags -TAG_FILE_NAME=$(action-get filename) - - -function list_tags_from_file() -{ - tagFile=$1 - - if [ -f $DIR/$tagFile ]; then - tags=`cat $DIR/$tagFile` - juju-log "Filename:$tagFile, Tags:$tags" - fi -} - - -if [ ! -z "$TAG_FILE_NAME" ]; then - juju-log "Tag file specified: $TAG_FILE_NAME" - list_tags_from_file $TAG_FILE_NAME -else - juju-log "Tag file not specified, will list tags from all files" - for tagFile in `ls -t $DIR` - do - list_tags_from_file $tagFile - done -fi - - diff --git a/abot-volte-basic/actions/run b/abot-volte-basic/actions/run deleted file mode 100755 index b2a7a0f..0000000 --- a/abot-volte-basic/actions/run +++ /dev/null @@ -1,105 +0,0 @@ -#!/bin/bash -# Action -Run -set -eu - -DIR=/etc/rebaca-test-suite/tags -ELASTIC_FILE_NAME=/etc/rebaca-test-suite/es-conf - -tags_defined=1 -no_report_needed=0 - -TAG_NAMES=$(action-get tagnames) -TAG_FILE_NAME=$(action-get filename) - -if [ ! -z "$TAG_FILE_NAME" ]; then - juju-log "Tag file specified: $TAG_FILE_NAME" -else - #Assign default file name - TAG_FILE_NAME=tags -fi - -new_tags= - -if [ "$#" -eq "0" ]; then - juju-log "No Script input found - may have been called through Juju action" -else - TAG_NAMES="$1" - juju-log " Tag = $1" - - if [ $# -gt 1 ] && [ "$2" == "no-report" ]; then - no_report_needed=1 - fi -fi - -if [ -z "$TAG_NAMES" ]; then - juju-log "No tag names specified" - tags_defined=0 - #Check if filename specified and file exists - if [ ! -z "$TAG_FILE_NAME" ] && [ -f $DIR/$TAG_FILE_NAME ] ; then - tags=`cat $DIR/$TAG_FILE_NAME` - fi -else - juju-log "Tag names: $TAG_NAMES" - - #Split the tags and add the @ symbols - IFS=', ' read -r -a array <<< "$TAG_NAMES" - for index in "${!array[@]}" - do - if [ $index -eq 0 ]; then - new_tags="@${array[0]}" - else - #Add current tag only if not duplicate - current_tag="${array[index]}" - if [ `echo $new_tags | grep -c $current_tag` -eq 0 ]; then - new_tags="$new_tags,@$current_tag" - fi - fi - done - - #Assign the tags variable accordingly. - tags=$new_tags -fi - -if [ -z "$tags" ]; then - juju-log "No tags found for execution" -else - juju-log "Running the test with the tags $tags" - - cd $CHARM_DIR/lib/ - status-set active "Executing run action" - . scripts/tags_update $tags - - #Removing previous run artifacts - rm -rf $CHARM_DIR/lib/log/*.* - - #Run the install target now - mvn install -Dtest=RunScenarios -DrootDir=com/rebaca/abot || true - - #Copying log artifacts to appropriate artifacts folder - LAST_REPORT_DIR="$(ls -td -- ${CHARM_DIR}/lib/artifacts/*/ | head -n 1)" - - if [ $no_report_needed -eq 0 ]; then - cp -R $CHARM_DIR/lib/log/*.* $LAST_REPORT_DIR - - #Process Test results and post to ElasticSearch - touch $ELASTIC_FILE_NAME - ES_CONF=`cat $ELASTIC_FILE_NAME` - if [ -z "$ES_CONF" ]; then - juju-log "No ES conf defined - hence result not pushed to ES" - else - juju-log "Pushing Test result to ES" - LAST_REPORT_FILE_NAME=`basename $LAST_REPORT_DIR` - juju-log "Renaming Test result to $LAST_REPORT_FILE_NAME.json" - cp ${CHARM_DIR}/lib/artifacts/TestResults.json ${CHARM_DIR}/lib/artifacts/$LAST_REPORT_FILE_NAME.json - python ${CHARM_DIR}/lib/bin/es-feed.py --elastic $ES_CONF --filepath ${CHARM_DIR}/lib/artifacts/$LAST_REPORT_FILE_NAME.json - fi - else - LAST_REPORT_DATETIME=`basename $LAST_REPORT_DIR` - cd ${CHARM_DIR}/lib/artifacts - tar czf ${LAST_REPORT_DATETIME}.tgz $LAST_REPORT_DATETIME - rm -rf $LAST_REPORT_DATETIME - fi - - - status-set active "Ready" -fi diff --git a/abot-volte-basic/actions/update-cx-params b/abot-volte-basic/actions/update-cx-params deleted file mode 100755 index 27ab6d3..0000000 --- a/abot-volte-basic/actions/update-cx-params +++ /dev/null @@ -1,44 +0,0 @@ -#!/bin/bash -# Action -Run -set -eu - -CXSERVER_ORIGINHOST=$(action-get cxserver-originhost) -CXSERVER_ORIGINREALM=$(action-get cxServer-originrealm) -CXSERVER_DESTHOST=$(action-get cxserver-desthost) -CXSERVER_CLIENT_IP=$(action-get cxserver-clientip) - - -if [ ! -z "$CXSERVER_ORIGINHOST" ]; then - juju-log "Setting CxServer_OriginHost=$CXSERVER_ORIGINHOST" - sed -i "s/CxServer.OriginHost=[^ ]*/CxServer.OriginHost=$CXSERVER_ORIGINHOST/" $CHARM_DIR/lib/config/config.properties - if [ `grep -c $CXSERVER_ORIGINHOST /etc/hosts` == "0" ]; then - public_hostname=$(unit-get public-address) - juju-log "Public Hostname: $public_hostname" - public_ip_address=`dig +short $public_hostname` - juju-log "Inserting originhost($CXSERVER_ORIGINHOST) into /etc/hosts file for IP $public_ip_address" - echo "$public_ip_address $CXSERVER_ORIGINHOST" >> /etc/hosts - fi -fi - -if [ ! -z "$CXSERVER_ORIGINREALM" ]; then - juju-log "CxServer_OriginRealm=$CXSERVER_ORIGINREALM" - sed -i "s/CxServer.OriginRealm=[^ ]*/CxServer.OriginRealm=$CXSERVER_ORIGINREALM/" $CHARM_DIR/lib/config/config.properties -fi - - -if [ ! -z "$CXSERVER_DESTHOST" ]; then - juju-log "CxServer_DestHost=$CXSERVER_DESTHOST" - sed -i "s/CxServer.DestHost=[^ ]*/CxServer.DestHost=$CXSERVER_DESTHOST/" $CHARM_DIR/lib/config/config.properties -fi - -if [ ! -z "$CXSERVER_CLIENT_IP" ]; then - juju-log "CxServer_Client_IP=$CXSERVER_CLIENT_IP" - #Get the destination host - CXSERVER_DEST_HOST=`grep "CxServer.DestHost" $CHARM_DIR/lib/config/config.properties | cut -d= -f2` - if [ ! -z $CXSERVER_DEST_HOST ]; then - if [ `grep -c $CXSERVER_CLIENT_IP /etc/hosts` == "0" ]; then - juju-log "Setting $CXSERVER_CLIENT_IP against $CXSERVER_DEST_HOST in /etc/hosts" - echo "$CXSERVER_CLIENT_IP $CXSERVER_DEST_HOST" >> /etc/hosts - fi - fi -fi diff --git a/abot-volte-basic/actions/update-framework b/abot-volte-basic/actions/update-framework deleted file mode 100755 index 7150a0e..0000000 --- a/abot-volte-basic/actions/update-framework +++ /dev/null @@ -1,27 +0,0 @@ -#!/bin/bash -# Action -Update Framework -set -eu - -juju-log "Incorporating new binary files into the Abot framework" -#Copy jar files to the relevant folder. -if [ `ls -1 ~ubuntu/framework-binaries | grep jar | wc -l` -gt 0 ]; then - mv -f ~ubuntu/framework-binaries/*.jar $CHARM_DIR/lib/jar/ - - cd $CHARM_DIR/lib/jar - - echo "Invoking mvn on jar files" - #Obtain version of base jar - ABOT_BASE_JAR_FILE=`ls abot-base*.jar` - ABOT_BASE_VERSION=`ls abot-base*.jar | sed -e "s/abot-base-//g" -e "s/.jar//g"` - mvn install:install-file -DgroupId=com.rebaca.abot.base -DartifactId=abot-base -Dversion=$ABOT_BASE_VERSION -Dpackaging=jar -Dfile=$CHARM_DIR/lib/jar/$ABOT_BASE_JAR_FILE - - #Obtain version of web jar - ABOT_WEB_JAR_FILE=`ls abot-web*.jar` - ABOT_WEB_VERSION=`ls abot-web*.jar | sed -e "s/abot-web-//g" -e "s/.jar//g"` - mvn install:install-file -DgroupId=com.rebaca.abot.adapters -DartifactId=abot-web -Dversion=$ABOT_WEB_VERSION -Dpackaging=jar -Dfile=$CHARM_DIR/lib/jar/$ABOT_WEB_JAR_FILE - - #Obtain version of diameter jar - ABOT_DIAMETER_JAR_FILE=`ls abot-diameter*.jar` - ABOT_DIAMETER_VERSION=`ls abot-diameter*.jar | sed -e "s/abot-diameter-//g" -e "s/.jar//g"` - mvn install:install-file -DgroupId=com.rebaca.abot.adapters -DartifactId=abot-diameter -Dversion=$ABOT_DIAMETER_VERSION -Dpackaging=jar -Dfile=$CHARM_DIR/lib/jar/$ABOT_DIAMETER_JAR_FILE -fi diff --git a/abot-volte-basic/actions/update-test-scenarios b/abot-volte-basic/actions/update-test-scenarios deleted file mode 100755 index 5b28e92..0000000 --- a/abot-volte-basic/actions/update-test-scenarios +++ /dev/null @@ -1,19 +0,0 @@ -#!/bin/bash -# Action -Update Test Scenarios -set -eu - -XML_SCENARIO_DIR=/etc/rebaca-test-suite/scenarios - -juju-log "Adding new test scenarios into the charm" -#Copy feature files to the relevant folder. -if [ `ls -1 ~ubuntu/test-scenarios | grep feature | wc -l` -gt 0 ]; then - mv -f ~ubuntu/test-scenarios/*.* $CHARM_DIR/lib/featureFiles/ -fi - -#Copy SIPP scenario files to the relevant folder. -juju-log "Adding new xml scenarios into the charm" -if [ `ls -1 ~ubuntu/xml-scenarios | grep xml | wc -l` -gt 0 ]; then - mkdir -p $XML_SCENARIO_DIR - mv -f ~ubuntu/xml-scenarios/*.* $CHARM_DIR/lib/scenarios/ - cp -f $CHARM_DIR/lib/scenarios/* $XML_SCENARIO_DIR/ -fi diff --git a/abot-volte-basic/config.yaml b/abot-volte-basic/config.yaml deleted file mode 100644 index 0b2f752..0000000 --- a/abot-volte-basic/config.yaml +++ /dev/null @@ -1,47 +0,0 @@ -options: - ims_apn_host: - default: "bono.rebaca.local" - description: "The P-CSCF hostname" - type: string - ims_apn_domain: - default: "rebaca.local" - description: "The DNS name for ims service" - type: string - tac: - default: "1" - description: "The default tracking area code for the eNodeB" - type: string - mcc: - default: "208" - description: "The default broadcast PLMN MCC" - type: string - mnc: - default: "93" - description: "The default broadcast PLMN MCC" - type: string - ue_ip: - default: "172.16.0.2" - description: "The SPGW IP Address" - type: string - tags: - type: string - default: "local-commands" - description: "Tag for the feature file to run" - abot_volte_basic_repo_url: - type: string - default: "http://115.113.43.202:8090/rebaca" - description: "URL of abot-volte-basic repo " - sipp_repo_url: - type: string - default: "https://sourceforge.net/projects/sipp/files/sipp/3.3/sipp-3.3.tar.gz" - description: "URL of sipp repo" - abot_volte_basic_package: - type: string - default: "abot-volte-basic_3.0.0_all.deb" - description: "The ABot VoLTE Basic package" - zone: - default: "rebaca.local" - description: The DNS root zone for this service - type: string - - diff --git a/abot-volte-basic/copyright b/abot-volte-basic/copyright deleted file mode 100644 index 883a93a..0000000 --- a/abot-volte-basic/copyright +++ /dev/null @@ -1,8 +0,0 @@ -Project Automated Behaviour-Oriented Tester(ABOT) - -Copyright (C) 2016 Rebaca Technologies Inc. - - This package contains software that is confidential and proprietary to - Rebaca Technologies. Any reproduction, disclosure, or use in whole or - in part is expressly prohibited, except as may be specifically - authorized by prior written agreement. diff --git a/abot-volte-basic/hooks/abot-mme-relation-changed b/abot-volte-basic/hooks/abot-mme-relation-changed deleted file mode 100755 index 043f4e8..0000000 --- a/abot-volte-basic/hooks/abot-mme-relation-changed +++ /dev/null @@ -1,29 +0,0 @@ -#!/bin/bash - -#called on every epc relation change - -set -ex -DIR=/etc/rebaca-test-suite -mobile_country_code=`relation-get MCC` -mobile_network_code=`relation-get MNC` -mme_ip_address_ipv4=`relation-get mme_ip` -mme_running=`relation-get mme_running` -unhex_key=`relation-get opc-key` -spgw_ip_address_ipv4=`relation-get spgw_ip_address_ipv4` - -sed -i "s/ABOT.ENB.mobile_country_code=[^ ]*/ABOT.ENB.mobile_country_code=$mobile_country_code/" $CHARM_DIR/lib/config/volte_defaults.conf -sed -i "s/ABOT.ENB.mobile_network_code=[^ ]*/ABOT.ENB.mobile_network_code=$mobile_network_code/" $CHARM_DIR/lib/config/volte_defaults.conf -sed -i "s/ABOT.ENB.mme_ip_address_ipv4=[^ ]*/ABOT.ENB.mme_ip_address_ipv4=$mme_ip_address_ipv4/" $CHARM_DIR/lib/config/volte_defaults.conf -sed -i "s/MME.SecureShell.IPAddress=[^ ]*/MME.SecureShell.IPAddress=$mme_ip_address_ipv4/" $CHARM_DIR/lib/config/ABotConfig.properties -sed -i "s/SPGW.SecureShell.IPAddress=[^ ]*/SPGW.SecureShell.IPAddress=$spgw_ip_address_ipv4/" $CHARM_DIR/lib/config/ABotConfig.properties -sed -i -e "s/OPC=\".*\";/OPC=\"$unhex_key\";/" /srv/openair5G/openair3/NAS/TOOLS/ue_eurecom_test_sfr.conf - -echo -n "$mme_ip_address_ipv4" > /root/.mme_ip_address_ipv4 -echo -n "$spgw_ip_address_ipv4" > /root/.spgw_ip_address_ipv4 -echo -n "$unhex_key" > /root/.opc_val - -if [ "$mme_running" == "yes" ]; then - status-set active "Abot ready! MME relation established, proceed to run tests..." - exit 0 -fi -status-set blocked "Abot blocked! MME relation not established, unable to get to active state" diff --git a/abot-volte-basic/hooks/abot-mme-relation-departed b/abot-volte-basic/hooks/abot-mme-relation-departed deleted file mode 100755 index 5279b4e..0000000 --- a/abot-volte-basic/hooks/abot-mme-relation-departed +++ /dev/null @@ -1,17 +0,0 @@ -#!/bin/bash - -juju-log "Removing relation with epc" - -sed -i "s/ABOT.ENB.mobile_country_code=[^ ]*/ABOT.ENB.mobile_country_code=/" $CHARM_DIR/lib/config/volte_defaults.conf -sed -i "s/ABOT.ENB.mobile_network_code=[^ ]*/ABOT.ENB.mobile_network_code=/" $CHARM_DIR/lib/config/volte_defaults.conf -sed -i "s/ABOT.ENB.mme_ip_address_ipv4=[^ ]*/ABOT.ENB.mme_ip_address_ipv4=192.168.255.255/" $CHARM_DIR/lib/config/volte_defaults.conf -sed -i "s/MME.SecureShell.IPAddress=[^ ]*/MME.SecureShell.IPAddress=192.168.255.255/" $CHARM_DIR/lib/config/ABotConfig.properties -sed -i "s/SPGW.SecureShell.IPAddress=[^ ]*/SPGW.SecureShell.IPAddress=192.168.255.255/" $CHARM_DIR/lib/config/ABotConfig.properties -sed -i -e "s/OPC=\".*\";/OPC=\"\";/" /srv/openair5G/openair3/NAS/TOOLS/ue_eurecom_test_sfr.conf - -#Remove the stored value files -rm -f /root/.mme_ip_address_ipv4 -rm -f /root/.spgw_ip_address_ipv4 -rm -f /root/.opc_val - -status-set blocked "Abot blocked! Waiting for epc relation" diff --git a/abot-volte-basic/hooks/abot-mme-relation-joined b/abot-volte-basic/hooks/abot-mme-relation-joined deleted file mode 100755 index db28a89..0000000 --- a/abot-volte-basic/hooks/abot-mme-relation-joined +++ /dev/null @@ -1,10 +0,0 @@ -#!/bin/bash - -set -eux - -juju-log "Setting EPC charm relation variables" -relation-set TAC=1 -sed -i "s/ABOT.ENB.tracking_area_code=[^ ]*/ABOT.ENB.tracking_area_code=1/" $CHARM_DIR/lib/config/ABotConfig.properties - -ip rule add from 172.16.0.2/32 table 200 -ip rule add to 172.16.0.2/32 table 200 diff --git a/abot-volte-basic/hooks/config-changed b/abot-volte-basic/hooks/config-changed deleted file mode 100755 index 25d9d86..0000000 --- a/abot-volte-basic/hooks/config-changed +++ /dev/null @@ -1,116 +0,0 @@ -#!/bin/bash -# config-changed occurs everytime a new configuration value is updated (juju set) -set -e - -DIR=/etc/rebaca-test-suite -TAG_DIR=/etc/rebaca-test-suite/tags - -#rm -f $DIR/tags -tags=$(config-get tags) - -if [ -n $TAG_DIR/tags ]; then - # Install the user's supplied hashes - if [ -f $TAG_DIR/tags ]; then - existing_tags=`cat $TAG_DIR/tags` - juju-log "Current tags: $existing_tags" - fi - juju-log "Installing the tags obtained from config: $tags" - echo -n "@$tags" > $TAG_DIR/tags -fi - -diameter_origin_realm=$(config-get ims_apn_domain) -if [ ! -z $diameter_origin_realm ]; then - juju-log "Setting origin realm=$diameter_origin_realm" - sed -i "s/ABOT_HSS.OriginRealm=[^ ]*/ABOT_HSS.OriginRealm=$diameter_origin_realm/" $CHARM_DIR/lib/config/ABotConfig.properties -fi - -ims_apn_host=$(config-get ims_apn_host) -tac=$(config-get tac) -mobile_country_code=$(config-get mcc) -mobile_network_code=$(config-get mnc) - -sed -i "s/ABOT.ENB.mobile_country_code=[^ ]*/ABOT.ENB.mobile_country_code=$mobile_country_code/" $CHARM_DIR/lib/config/volte_defaults.conf -sed -i "s/ABOT.ENB.mobile_network_code=[^ ]*/ABOT.ENB.mobile_network_code=$mobile_network_code/" $CHARM_DIR/lib/config/volte_defaults.conf -sed -i "s/ABOT.ENB.tracking_area_code=[^ ]*/ABOT.ENB.tracking_area_code=$tac/" $CHARM_DIR/lib/config/volte_defaults.conf - -if [ ! -f /root/.mme_ip_address_ipv4 ];then - touch /root/.mme_ip_address_ipv4 -fi -if [ ! -z `cat /root/.mme_ip_address_ipv4` ]; then - mme_ip_address_ipv4=`cat /root/.mme_ip_address_ipv4` - sed -i "s/MME.SecureShell.IPAddress=[^ ]*/MME.SecureShell.IPAddress=$mme_ip_address_ipv4/" $CHARM_DIR/lib/config/ABotConfig.properties - sed -i "s/ABOT.ENB.mme_ip_address_ipv4=[^ ]*/ABOT.ENB.mme_ip_address_ipv4=$mme_ip_address_ipv4/" $CHARM_DIR/lib/config/volte_defaults.conf -fi - - -if [ ! -f /root/.spgw_ip_address_ipv4 ];then - touch /root/.spgw_ip_address_ipv4 -fi - -if [ ! -z `cat /root/.spgw_ip_address_ipv4` ]; then - spgw_ip_address_ipv4=`cat /root/.spgw_ip_address_ipv4` - sed -i "s/SPGW.SecureShell.IPAddress=[^ ]*/SPGW.SecureShell.IPAddress=$spgw_ip_address_ipv4/" $CHARM_DIR/lib/config/ABotConfig.properties -fi - -if [ ! -f /root/.ims_hss_mirror_ip ];then - touch /root/.ims_hss_mirror_ip -fi - -if [ ! -z `cat /root/.ims_hss_mirror_ip` ]; then - ims_hss_mirror_ip=`cat /root/.ims_hss_mirror_ip` - sed -i "s/HSS_MIRROR.SecureShell.IPAddress=[^ ]*/HSS_MIRROR.SecureShell.IPAddress=$ims_hss_mirror_ip/" $CHARM_DIR/lib/config/ABotConfig.properties -fi - -if [ ! -f /root/.opc_val ];then - touch /root/.opc_val -fi - -if [ ! -z `cat /root/.opc_val` ]; then - opc_val=`cat /root/.opc_val` - sed -i -e "s/OPC=\".*\";/OPC=\"$opc_val\";/" /srv/openair5G/openair3/NAS/TOOLS/ue_eurecom_test_sfr.conf -fi - -private_address=$(unit-get private-address) -if [[ `dig +short $private_address` =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]] -then - juju-log "Verifying $private_address in dns " - DIGSHORT="dig +short" -elif [[ "$private_address" =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]] -then - DIGSHORT=echo -else - juju-log "Unable to fetch private_address ..." - exit 1 -fi - -ip=`$DIGSHORT $private_address` - -if [ ! -z $ip ]; then - sed -i "s/ABOT.SecureShell.IPAddress=[^ ]*/ABOT.SecureShell.IPAddress=$ip/" $CHARM_DIR/lib/config/ABotConfig.properties - sed -i "s#ABOT.ENB.ENB_IPV4_ADDRESS_FOR_S1_MME=[^ ]*#ABOT.ENB.ENB_IPV4_ADDRESS_FOR_S1_MME=$ip/24#" $CHARM_DIR/lib/config/volte_defaults.conf - sed -i "s#ABOT.ENB.ENB_IPV4_ADDRESS_FOR_S1U=[^ ]*#ABOT.ENB.ENB_IPV4_ADDRESS_FOR_S1U=$ip/24#" $CHARM_DIR/lib/config/volte_defaults.conf - sed -i "s/ABOT_IP_ADDRESS/$ip/" $CHARM_DIR/lib/abot/static/js/apps/config/appconfig.js -else - juju-log "IP Address of local unit not available - exiting with error" - exit 2 -fi - -#Replace realm-name in diameter-specific feature files -cd $DIR/featureFiles -DOMAIN_NAME="$ims_apn_domain" -grep -rl rebaca.local . | xargs sed -i "s|rebaca\.local|${DOMAIN_NAME}|g" || true - -echo -n $ims_apn_host > $DIR/sip_server -echo -n $ims_apn_domain > $DIR/sip_domain - -#Set IP Address in sip scripts -echo "ue_ip=172.16.0.2" -ue_ip=$(config-get ue_ip) -sed -i "/ip=/s/[^ ]*/ip=$ue_ip/" $DIR/bin/sip -chmod +x $DIR/bin/sip - -sed -i "/ip=/s/[^ ]*/ip=$ue_ip/" $DIR/bin/sip_imsi_domain -chmod +x $DIR/bin/sip_imsi_domain - -sed -i "/ip=/s/[^ ]*/ip=$ue_ip/" $DIR/bin/sip-stress -chmod +x $DIR/bin/sip-stress diff --git a/abot-volte-basic/hooks/hss-abot-relation-changed b/abot-volte-basic/hooks/hss-abot-relation-changed deleted file mode 100755 index c6020c6..0000000 --- a/abot-volte-basic/hooks/hss-abot-relation-changed +++ /dev/null @@ -1,10 +0,0 @@ -#!/bin/bash - -set -e - -hss_host=`grep "^ABOT.SecureShell.IPAddress" $CHARM_DIR/lib/config/ABotConfig.properties | cut -d= -f2` -hss_realm=`grep "^ABOT_HSS.OriginRealm" $CHARM_DIR/lib/config/ABotConfig.properties | cut -d= -f2` - -relation-set public-address=$hss_host -relation-set hss-realm=$hss_realm -relation-set hss-port=3868 diff --git a/abot-volte-basic/hooks/hss-prov-relation-changed b/abot-volte-basic/hooks/hss-prov-relation-changed deleted file mode 100755 index 397091c..0000000 --- a/abot-volte-basic/hooks/hss-prov-relation-changed +++ /dev/null @@ -1,37 +0,0 @@ -#!/bin/bash -set -e -DIR=/etc/rebaca-test-suite - -juju-log "Invoking hss-relation" -ims_hss_mirror_ip=$(relation-get public-address) -if [ -z "$ims_hss_mirror_ip" ]; then - juju-log "No data sent yet from HSS" - exit 0 -fi - -juju-log "HSS FQDN $ims_hss_mirror_ip" -if [[ `dig +short $ims_hss_mirror_ip` =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]] - then - juju-log "Verifying $ims_hss_mirror_ip in dns " - DIGSHORT="dig +short" - elif [[ "$ims_hss_mirror_ip" =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]] - then - DIGSHORT=echo - else - juju-log "Unable to fetch public_address changing dns order to original" - exit 1 -fi - -ims_hss_mirror_ip=`$DIGSHORT $ims_hss_mirror_ip` -juju-log "Ims hss mirror IP $ims_hss_mirror_ip" - -if [ -z $ims_hss_mirror_ip ]; then - juju-log "No IP Address found for HSS_FQDN: $ims_hss_mirror_ip" - exit 1 -fi - -#Update HSS Mirror IP -echo -n "$ims_hss_mirror_ip" > $DIR/ims_hss_mirror_ip -echo -n "$ims_hss_mirror_ip" > /root/.ims_hss_mirror_ip - -sed -i "s/HSS_MIRROR.SecureShell.IPAddress=[^ ]*/HSS_MIRROR.SecureShell.IPAddress=$ims_hss_mirror_ip/" $CHARM_DIR/lib/config/ABotConfig.properties diff --git a/abot-volte-basic/hooks/install b/abot-volte-basic/hooks/install deleted file mode 100755 index 2452102..0000000 --- a/abot-volte-basic/hooks/install +++ /dev/null @@ -1,151 +0,0 @@ -#!/bin/bash -# Here do anything needed to install the service -# i.e. apt-get install -y foo or bzr branch http://myserver/mycode /srv/webroot -# Make sure this hook exits cleanly and is idempotent, common problems here are -# failing to account for a debconf question on a dependency, or trying to pull -# from github without installing git first. - -set -eu - -abot_volte_basic_url=`config-get abot_volte_basic_repo_url` -abot_volte_basic_package_name=`config-get abot_volte_basic_package` -sipp_url=`config-get sipp_repo_url` -DIR=/etc/rebaca-test-suite -mkdir -p $DIR - -juju-log "Updating existing Ubuntu libraries..." -apt-get update -juju-log "Existing Ubuntu libraries update done." - -juju-log "Installing Java." -cd $CHARM_DIR/lib/ -. scripts/install_java -juju-log "Java Installed." - -#Installing dependencies for abot-volte-basic -apt-get -y install apache2 - -juju-log "Installing Maven..." -apt-get update -apt-get -y install maven -juju-log "Maven installation done." - -#Download the ABOT package and install it - -#Install the package using dpkg -juju-log "Install abot package completed ..." - -if [[ ! -d "$CHARM_DIR/payload" ]]; then - mkdir $CHARM_DIR/payload -fi - -#Download the ABOT package and install it -if [[ ! -f "$CHARM_DIR/payload/$abot_volte_basic_package_name" ]]; then - wget $abot_volte_basic_url/$abot_volte_basic_package_name -O $CHARM_DIR/payload/$abot_volte_basic_package_name -fi - -dpkg --install $CHARM_DIR/payload/$abot_volte_basic_package_name - -#Install libjson -apt-get -y install libjson0 - -#Install RabbitMQ Server -apt-get -y install librabbitmq-dev -apt-get -y install rabbitmq-server - -#Install the PHP plugin -apt-get -y install libapache2-mod-php - -juju-log "SIPP dependency" -apt-get -y install pcaputils libncursesw5-dev libncurses5-dev libpcap-dev libssl-dev libssl1.0.0 openssl libssl-dev build-essential make -juju-log "SIPP installation done." - -juju-log "Downloading Sipp" - -cd /home/ubuntu -wget $sipp_url - -tar -xvzf sipp-3.3.tar.gz - -cd sipp-3.3 - -juju-log "Compiling Sipp" -make pcapplay_ossl - -juju-log "Completed installing Sipp" - -juju-log "installing dnsmasq" -apt-get -q -y --force-yes install dnsmasq - -juju-log "Completed installing dnsmasq" - -#Install python pip -apt-get -y install python-pip - -#Install the elasticSearch python libraries -pip install elasticsearch - -#Install flask python libraries and initialize GUI -pip install flask - -#Check for flask process and stop it if it it running -flask_process_id=`ps -ef | grep python | grep app.py | awk '{print $2}'` -if [ ! -z $flask_process_id ]; then - juju-log "Stopping flask process with id $flask_process_id" - kill -9 $flask_process_id -fi - -cd $CHARM_DIR/lib/abot -juju-log "Starting flask ..." -python app.py& - -#Clean out the symlinks from the DIR folder -find $DIR/ -maxdepth 1 -type l -delete - -#Create symlinks to installation -mkdir -p $DIR/tags - -ln -s $CHARM_DIR/lib/bin $DIR/bin -ln -s $CHARM_DIR/lib/log $DIR/log -ln -s $CHARM_DIR/lib/featureFiles $DIR/featureFiles -ln -s $CHARM_DIR/lib/config $DIR/config -ln -s $CHARM_DIR/lib/jar $DIR/jar -ln -s $CHARM_DIR/lib/scenarios $DIR/scenarios - -#Copy init script of oaisim -cp $DIR/bin/oaisim.conf /etc/init - -#Change permissions of the charm/lib folders -chown -R ubuntu:ubuntu $CHARM_DIR/lib - -#Change permissions of the openair5G folders -chown -R ubuntu:ubuntu /srv - -#Generate ssh key for root user -if [ ! -f /home/ubuntu/.ssh/id_rsa.pub ]; - then - ssh-keygen -t rsa -N "" -f /root/.ssh/id_rsa - pub_key=`cat /root/.ssh/id_rsa.pub` - echo "$pub_key" | sed 's/\r//' >> /root/.ssh/authorized_keys - else - pub_key=`cat /root/.ssh/id_rsa.pub` - echo "$pub_key" | sed 's/\r//' >> /root/.ssh/authorized_keys -fi - -if [ ! -f /home/ubuntu/.ssh/id_rsa.pub ]; - then - sudo -u ubuntu bash << EOF - ssh-keygen -t rsa -N "" -f /home/ubuntu/.ssh/id_rsa -EOF - pub_key=`cat /home/ubuntu/.ssh/id_rsa.pub` - echo "$pub_key" | sed 's/\r//' >> /home/ubuntu/.ssh/authorized_keys - else - pub_key=`cat /home/ubuntu/.ssh/id_rsa.pub` - echo "$pub_key" | sed 's/\r//' >> /home/ubuntu/.ssh/authorized_keys -fi - - -open-port 80/tcp - -juju-log "ABot Install complete!" -status-set maintenance "ABot installed! Waiting to start..." diff --git a/abot-volte-basic/hooks/programmable-multiple-relation-changed b/abot-volte-basic/hooks/programmable-multiple-relation-changed deleted file mode 100755 index e1d2bac..0000000 --- a/abot-volte-basic/hooks/programmable-multiple-relation-changed +++ /dev/null @@ -1,76 +0,0 @@ -#!/bin/bash -set -e - -juju-log "Invoking dns-relation" - -dns_ip=$(relation-get private-address) -if [ -z "$dns_ip" ]; then - juju-log "No data sent yet from DNS" - exit 0 -fi - -juju-log "DNS IP: $dns_ip" - -# Set our DNS requirements -id=$(cut -d/ -f2 <<< $JUJU_UNIT_NAME) -public_fqdn=$(unit-get public-address) - -public_hostname=`hostname | cut -d"." -f1` -#public_hostname=`echo $public_fqdn | cut -d. -f1` -juju-log "Public Hostname: $public_hostname, public_address: $public_fqdn" - -#added on 31 aug by soumaya -if [[ $public_fqdn =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; - then - ip=$public_fqdn - juju-log "Unit get address - $ip" - relation-set domain=$(config-get zone) - DNS_JSON=$($CHARM_DIR/lib/scripts/generate_dns_records $ip $public_hostname abot-volte-basic-$id) - juju-log "DNS_JSON:$DNS_JSON" - relation-set resources="$DNS_JSON" - else - ip=`dig +short $public_fqdn` - juju-log "After dig and short unit address is $ip" - relation-set domain=$(config-get zone) - DNS_JSON=$($CHARM_DIR/lib/scripts/generate_dns_records $ip $public_hostname abot-volte-basic-$id) - juju-log "DNS_JSON:$DNS_JSON" - relation-set resources="$DNS_JSON" -fi - - -#update DNS server using DNSMASQ -echo nameserver $dns_ip > /etc/dnsmasq.resolv.conf -grep -v ^RESOLV_CONF= /etc/default/dnsmasq > /tmp/dnsmasq.$$ -mv /tmp/dnsmasq.$$ /etc/default/dnsmasq -echo RESOLV_CONF=/etc/dnsmasq.resolv.conf >> /etc/default/dnsmasq -service dnsmasq restart -sleep 5 -# Change Resolvconf order and make dnsmasq on top -dnsmasq=lo.dnsmasq -resolvconf=/etc/resolvconf -loinet=`grep ^[a-z] /etc/resolvconf/interface-order | head -n1` - if [ $loinet != $dnsmasq ] - then - cp /etc/resolvconf/interface-order /etc/resolvconf/interface-order.orig - sed '/'$dnsmasq'/d' /etc/resolvconf/interface-order.orig > /etc/resolvconf/interface-order - sed -i '/'$loinet'/i lo.dnsmasq' /etc/resolvconf/interface-order - service dnsmasq restart - resolvconf -u - else - exit 0 - fi - -# Verify DNS record to make sure that IMS dns is working properly -if [[ `dig +short $public_fqdn` =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]] - then - juju-log "Verifying $public_fqdn in dns " - exit - elif [[ "$public_fqdn" =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]] - then - juju-log "Verifying $public_fqdn in dns " - exit - else - juju-log "Unable to fetch $public_fqdn changing dns order to original" - cp /etc/resolvconf/interface-order.orig /etc/resolvconf/interface-order - resolvconf -u -fi diff --git a/abot-volte-basic/hooks/programmable-multiple-relation-departed b/abot-volte-basic/hooks/programmable-multiple-relation-departed deleted file mode 100755 index fd94890..0000000 --- a/abot-volte-basic/hooks/programmable-multiple-relation-departed +++ /dev/null @@ -1,11 +0,0 @@ -#!/bin/bash - -set -e - -INTR_ORDER_DIR="/etc/resolvconf" - -if [ -f "$INTR_ORDER_DIR/interface-order.orig" ]; then - cp $INTR_ORDER_DIR/interface-order.orig $INTR_ORDER_DIR/interface-order - resolvconf -u -fi - diff --git a/abot-volte-basic/hooks/ssh-abot-relation-changed b/abot-volte-basic/hooks/ssh-abot-relation-changed deleted file mode 100755 index a1a2a6e..0000000 --- a/abot-volte-basic/hooks/ssh-abot-relation-changed +++ /dev/null @@ -1,10 +0,0 @@ -#!/bin/bash -set -e - -if [ -f /home/ubuntu/.ssh/id_rsa.pub ]; - then - ssh_key=`cat /home/ubuntu/.ssh/id_rsa.pub` - ssh_key=`echo "$ssh_key" | sed 's/\r//'` - relation-set ssh-key="$ssh_key" -fi - diff --git a/abot-volte-basic/hooks/ssh-abot-relation-joined b/abot-volte-basic/hooks/ssh-abot-relation-joined deleted file mode 100755 index 3e056d1..0000000 --- a/abot-volte-basic/hooks/ssh-abot-relation-joined +++ /dev/null @@ -1,39 +0,0 @@ -#!/bin/bash -set -e -generate-ssh_key(){ - -if [ ! -f /home/ubuntu/.ssh/id_rsa.pub ]; - then - ssh-key-abotvoltegen -t rsa -N "" -f /root/.ssh/id_rsa - pub_key=`cat /root/.ssh/id_rsa.pub` - echo "$pub_key" | sed 's/\r//' >> /root/.ssh/authorized_keys - else - pub_key=`cat /root/.ssh/id_rsa.pub` - echo "$pub_key" | sed 's/\r//' >> /root/.ssh/authorized_keys -fi - -if [ ! -f /home/ubuntu/.ssh/id_rsa.pub ]; - then - sudo -u ubuntu bash << EOF - ssh-key-abotvoltegen -t rsa -N "" -f /home/ubuntu/.ssh/id_rsa -EOF - pub_key=`cat /home/ubuntu/.ssh/id_rsa.pub` - echo "$pub_key" | sed 's/\r//' >> /home/ubuntu/.ssh/authorized_keys - else - pub_key=`cat /home/ubuntu/.ssh/id_rsa.pub` - echo "$pub_key" | sed 's/\r//' >> /home/ubuntu/.ssh/authorized_keys -fi -} - -if [ -f /home/ubuntu/.ssh/id_rsa.pub ]; - then - ssh_key=`cat /home/ubuntu/.ssh/id_rsa.pub` - ssh_key=`echo "$ssh_key" | sed 's/\r//'` - relation-set ssh-key-abotvolte="$ssh_key" - else - generate-ssh_key - ssh_key=`cat /home/ubuntu/.ssh/id_rsa.pub` - ssh_key=`echo "$ssh_key" | sed 's/\r//'` - relation-set ssh-key-abotvolte="$ssh_key" -fi - diff --git a/abot-volte-basic/hooks/start b/abot-volte-basic/hooks/start deleted file mode 100755 index 5736692..0000000 --- a/abot-volte-basic/hooks/start +++ /dev/null @@ -1,28 +0,0 @@ -#!/bin/bash -# Here put anything that is needed to start the service. -# Note that currently this is run directly after install -set -eu -DIR=/etc/rebaca-test-suite -TAG_DIR=$DIR/tags - -tags=`cat $TAG_DIR/tags` - -juju-log "Running the test with the tags $tags" -cd $CHARM_DIR/lib/ -. scripts/tags_update - -#perform mvn clean - this copied the required libs to the mvn repo -mvn clean - -#Setup the link to testReports folder -rm -Rf /var/www/html 2> /dev/null -ln -s $CHARM_DIR/lib /var/www/html - -#Build the oaisim -juju-log "Build oaisim..." -(cd /srv/openair5G/cmake_targets; sudo ./build_oai_rebaca -I --oaisim || true) - -#Change permissions of the openair5G folders -chown -R ubuntu:ubuntu /srv - -juju-log "Abot updated! Setup relations to get into ready state..." diff --git a/abot-volte-basic/hooks/stop b/abot-volte-basic/hooks/stop deleted file mode 100755 index 1068769..0000000 --- a/abot-volte-basic/hooks/stop +++ /dev/null @@ -1,7 +0,0 @@ -#!/bin/bash -# This will be run when the service is being torn down, allowing you to disable -# it in various ways.. -# For example, if your web app uses a text file to signal to the load balancer -# that it is live... you could remove it and sleep for a bit to allow the load -# balancer to stop sending traffic. -# rm /srv/webroot/server-live.txt && sleep 30 diff --git a/abot-volte-basic/hooks/upgrade-charm b/abot-volte-basic/hooks/upgrade-charm deleted file mode 100755 index e6360cf..0000000 --- a/abot-volte-basic/hooks/upgrade-charm +++ /dev/null @@ -1,60 +0,0 @@ -#!/bin/bash -# This hook is executed each time a charm is upgraded after the new charm -# contents have been unpacked -# Best practice suggests you execute the hooks/install and -# hooks/config-changed to ensure all updates are processed -set -eu -DIR=/etc/rebaca-test-suite - -abot_volte_basic_url=`config-get abot_volte_basic_repo_url` -abot_volte_basic_package_name=`config-get abot_volte_basic_package` - -rm -rf $CHARM_DIR/lib/target -rm -rf $CHARM_DIR/lib/log/* - -rm -rf $DIR/* - -#Remove the existing version of the abot package -rm -f $CHARM_DIR/payload/abot*.deb - -#Download the ABOT package and install it -if [[ ! -d "$CHARM_DIR/payload" ]]; then - mkdir $CHARM_DIR/payload -fi - -wget $abot_volte_basic_url/$abot_volte_basic_package_name -O $CHARM_DIR/payload/$abot_volte_basic_package_name - -#Install the package using dpkg -dpkg --install $CHARM_DIR/payload/$abot_volte_basic_package_name -juju-log "Install abot package completed ..." - -#Clean out the symlinks from the DIR folder -find $DIR/ -maxdepth 1 -type l -delete - -#Create symlinks to installation -mkdir -p $DIR/tags - -ln -s $CHARM_DIR/lib/bin $DIR/bin -ln -s $CHARM_DIR/lib/log $DIR/log -ln -s $CHARM_DIR/lib/featureFiles $DIR/featureFiles -ln -s $CHARM_DIR/lib/config $DIR/config -ln -s $CHARM_DIR/lib/jar $DIR/jar -ln -s $CHARM_DIR/lib/scenarios $DIR/scenarios - -#Copy oaisim init script -cp $DIR/bin/oaisim.conf /etc/init - -#Change permissions of charm/lib folder -chown -R ubuntu:ubuntu $CHARM_DIR/lib - -#Build the oaisim -juju-log "Build oaisim..." -(cd /srv/openair5G/cmake_targets; sudo ./build_oai_rebaca -c -I --oaisim) || true - -#Change permissions of the openair5G folders -chown -R ubuntu:ubuntu /srv - -open-port 80/tcp - -juju-log "ABot update complete!" -status-set maintenance "ABot updated! Reset relations to get into ready state" diff --git a/abot-volte-basic/icon.svg b/abot-volte-basic/icon.svg deleted file mode 100644 index 51b1d87..0000000 --- a/abot-volte-basic/icon.svg +++ /dev/null @@ -1,26 +0,0 @@ - - - - - Created by potrace 1.10, written by Peter Selinger 2001-2011 - - - - - - - - - - - - - - - - - - Layer 2 - - - \ No newline at end of file diff --git a/abot-volte-basic/lib/featureFiles/000-local-commands.feature b/abot-volte-basic/lib/featureFiles/000-local-commands.feature deleted file mode 100644 index 60361d3..0000000 --- a/abot-volte-basic/lib/featureFiles/000-local-commands.feature +++ /dev/null @@ -1,12 +0,0 @@ -Feature: Local_Commands_Testing - - @local-commands - Scenario: Local Commands testing - Given all configured endpoints for SSH are connected successfully - When I execute the command ifconfig in this system and check the presence of following strings: - | string | occurrence | - | inet | PRESENT | - | UP | PRESENT | - | BROADCAST | PRESENT | - | RUNNING | PRESENT | - | MULTICAST | PRESENT | diff --git a/abot-volte-basic/lib/featureFiles/ResourceBundle.xml b/abot-volte-basic/lib/featureFiles/ResourceBundle.xml deleted file mode 100644 index 6429722..0000000 --- a/abot-volte-basic/lib/featureFiles/ResourceBundle.xml +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file diff --git a/abot-volte-basic/lib/scripts/add-keys.sh b/abot-volte-basic/lib/scripts/add-keys.sh deleted file mode 100755 index 37a96e0..0000000 --- a/abot-volte-basic/lib/scripts/add-keys.sh +++ /dev/null @@ -1,64 +0,0 @@ -#!/bin/bash - -########################################################################## - # - # @!$ Rebaca Technologies - # __________________ - # - # 2016 Rebaca Technologies - # - # NOTICE: All information contained herein is, and remains - # the property of Rebaca Technologies and its suppliers, if any. - # The intellectual and technical concepts contained herein are - # proprietary to Rebaca Technologies and its suppliers and may be - # covered by Indian and Foreign Patents, patents in process, and - # are protected by trade secret or copyright law. Dissemination - # of this information or reproduction of this material is strictly - # forbidden unless prior written permission is obtained from - # Rebaca Technologies. - # - # @!$Id$ - # - -#Get all units of abot -ABOT_UNITS=`juju status --format=short abot-volte-basic | grep abot-volte-basic | awk -F'[: ]' '{print $2}'` -MME_UNITS=`juju status --format=short oai-mme | grep oai-mme | awk -F'[: ]' '{print $2}'` -SPGW_UNITS=`juju status --format=short oai-spgw | grep oai-spgw | awk -F'[: ]' '{print $2}'` -HSS_MIRROR_UNITS=`juju status --format=short clearwater-homestead | grep clearwater-homestead | awk -F'[: ]' '{print $2}'` - -if [ ! -z "$ABOT_UNITS" ]; then - for abot_unit in $ABOT_UNITS - do - echo "Circulating public key of Abot unit $abot_unit" - - juju ssh $abot_unit "if [ ! -f ~/.ssh/id_rsa.pub ]; then ssh-keygen -t rsa -N \"\" -f ~/.ssh/id_rsa; fi" - key=`juju ssh $abot_unit "cat ~/.ssh/id_rsa.pub"` - key=`echo "$key" | sed 's/\r//'` - echo "Key($abot_unit): $key" - - #Distribute to current Abot unit - juju ssh $abot_unit "echo $key >> ~/.ssh/authorized_keys" - - #Distribute to all MME units - for mme_unit in $MME_UNITS - do - echo "Publishing key to $mme_unit" - juju ssh $mme_unit "echo $key >> ~/.ssh/authorized_keys" - done - - #Distribute to all SPGW units - for spgw_unit in $SPGW_UNITS - do - echo "Publishing key to $spgw_unit" - juju ssh $spgw_unit "echo $key >> ~/.ssh/authorized_keys" - done - - #Distribute to all HSS MIRROR units - for hss_mirror_unit in $HSS_MIRROR_UNITS - do - echo "Publishing key to $hss_mirror_unit" - juju ssh $hss_mirror_unit "echo $key >> ~/.ssh/authorized_keys" - done - - done #For all abot units -fi diff --git a/abot-volte-basic/lib/scripts/config_script b/abot-volte-basic/lib/scripts/config_script deleted file mode 100755 index 318f486..0000000 --- a/abot-volte-basic/lib/scripts/config_script +++ /dev/null @@ -1,65 +0,0 @@ -#!/bin/bash - -set -e - -relation_hook=$1 - -# Defaults -sprout_hostname=sprout.$(config-get zone) -hs_hostname=homestead.$(config-get zone):8888 -hs_provisioning_hostname=homestead.$(config-get zone):8889 -stress_target=blackhole.example.com -xdms_hostname= -ralf_hostname= -sas_server= -enum_server= - -# Import existing configuration -[ ! -f /etc/clearwater/config ] || . /etc/clearwater/config - -# Apply new configuration -home_domain=$(config-get zone) -local_ip=$(unit-get private-address) -public_ip=$(unit-get private-address) -public_hostname=sipp-$(cut -d/ -f2 <<< $JUJU_UNIT_NAME).$(config-get zone) -node_idx=$(($(cut -d/ -f2 <<< $JUJU_UNIT_NAME) + 1)) -user_count=$(config-get user_count) -if [[ $relation_hook == "pcscf-changed" ]] -then - juju-log "Setting stress target to $home_domain" - stress_target=$home_domain -fi - -# Write configuration back -mkdir -p /etc/clearwater -cat >/etc/clearwater/local_config </etc/clearwater/shared_config < tmp -sudo cp tmp /etc/clearwater/shared_config -cat /etc/clearwater/shared_config | grep -v hss_realm > tmp -sudo cp tmp /etc/clearwater/shared_config -cat /etc/clearwater/shared_config | grep -v hss_port > tmp -sudo cp tmp /etc/clearwater/shared_config - -if [ "$HSS_TAG" == "hss" ]; then - echo "hss_hostname=$HSS_HOSTNAME" >> tmp -else - echo "hss_hostname=0.0.0.0" >> tmp -fi - -echo "hss_realm=" >> tmp -echo "hss_port=$HSS_PORT" >> tmp -sudo cp tmp /etc/clearwater/shared_config - -rm tmp - -sudo service homestead stop -sleep 5 - diff --git a/abot-volte-basic/lib/scripts/epc_setup.sh b/abot-volte-basic/lib/scripts/epc_setup.sh deleted file mode 100755 index 6006b05..0000000 --- a/abot-volte-basic/lib/scripts/epc_setup.sh +++ /dev/null @@ -1,48 +0,0 @@ -#!/bin/bash -search_and_replace() -{ - LHS=$1 - RHS=$2 - - #echo "$LHS=$RHS" - - str=$(grep -w "$LHS\s*=" $ENBCONFIGFILE) - - #echo "str=:"$str - - IFS='=' read -ra WORDSARR <<< $str - - #echo "${WORDSARR[0]}=${WORDSARR[1]}" - - OLD_RHS=`echo ${WORDSARR[1]} | tr -d '"' | tr -d ';'` - - #echo "OLD_RHS:"$OLD_RHS"; RHS:"$RHS"; LHS:"$LHS - - sed -i -e "s?\(\s*{*\)\(${LHS}\)\(\s*= *\)\(\"\{0,1\}\)\(${OLD_RHS}\)\(\"\{0,1\}\)\(;\)?\1\2\3\4${RHS}\6\7?g" $ENBCONFIGFILE -} - - -LINE=$1 -ENBCONFIGFILE=$2 - -if [ "$ENBCONFIGFILE" = "" ] -then - ENBCONFIGFILE="/srv/openair5G/targets/PROJECTS/GENERIC-LTE-EPC/CONF/enb.band7.generic.conf" -fi - -#echo $LINE - -if [[ $LINE == *"ABOT.ENB"* ]] -then - IFS='=' read -ra WORDS <<< $LINE - IFS='.' read -ra LeftString <<< ${WORDS[0]} - LHS=${LeftString[-1]} - RHS=${WORDS[1]} - - #Special handling for mme_ip_address in ipv4 - echo $LHS|grep "mme_ip_address_ipv4" >> /dev/null - - if [ $? -eq 0 ]; then LHS=ipv4 ; fi - - search_and_replace $LHS $RHS -fi diff --git a/abot-volte-basic/lib/scripts/gen_multiple_ue.sh b/abot-volte-basic/lib/scripts/gen_multiple_ue.sh deleted file mode 100755 index 2d0650e..0000000 --- a/abot-volte-basic/lib/scripts/gen_multiple_ue.sh +++ /dev/null @@ -1,68 +0,0 @@ -#!/bin/bash - -while [ $# -gt 0 ] -do -i=$1 - case $i in - -c) - conf=$2 - shift - ;; - -o) - out=$2 - shift - ;; - -u) - numUE=$2 - shift - ;; - *) - exit 1 - ;; - esac - shift -done - -msin=0000000001 -msin_prefix_part=0000000 -msin_add_part=$(echo -n $msin | tail -c 3) - -imei=356113022094149 -imei_prefix_part=356113022094 -imei_add_part=$(echo -n $imei | tail -c 3) - -msisdn=33611123456 -msisdn_prefix_part=33611123 -msisdn_add_part=$(echo -n $msisdn | tail -c 3) - -sed -e '/UE0/,$d' $conf > ${conf}.final - -for (( count=0 ; count<$numUE; count++ )) -do - cp $conf ${conf}.bak - - sed -i "s/UE0/UE${count}/g" $conf - - new_msin=$(($msin_add_part + $count)) - (( $count < 9 )) && new_msin="${msin_prefix_part}00${new_msin}" - (( $count < 99 && $count >= 9 )) && new_msin="${msin_prefix_part}0${new_msin}" - (( $count >= 99 )) && new_msin="${msin_prefix_part}${new_msin}" - sed -i "s/$msin/$new_msin/g" $conf - - new_imei=$(($imei_add_part + $count)) - new_imei="${imei_prefix_part}${new_imei}" - sed -i "s/$imei/$new_imei/g" $conf - - new_msisdn=$(($msisdn_add_part + $count)) - new_msisdn="${msisdn_prefix_part}${new_msisdn}" - sed -i "s/$msisdn/$new_msisdn/g" $conf - - sed -n -e "/UE${count}/,\$p" $conf >> ${conf}.final - mv ${conf}.bak $conf -done - -(cd /srv/openair5G/targets/bin; rm -rf .ue_emm.nvram*) -(cd /srv/openair5G/targets/bin; rm -rf .ue.nvram*) -(cd /srv/openair5G/targets/bin; rm -rf .usim.nvram*) - -(cd /srv/openair5G/targets/bin; ./conf2uedata -c ${conf}.final -o $out > /dev/null) diff --git a/abot-volte-basic/lib/scripts/generate_dns_records b/abot-volte-basic/lib/scripts/generate_dns_records deleted file mode 100755 index 9b97f9f..0000000 --- a/abot-volte-basic/lib/scripts/generate_dns_records +++ /dev/null @@ -1,27 +0,0 @@ -#!/usr/bin/python - -import sys -import json - -if len(sys.argv) < 4: - print "Usage: generate_dns_records.py IP CLUSTER_A_RECORD SPECIFIC_A_RECORD [SRV_PORT SRV_ADDR...]" - -ip = sys.argv[1] -cluster_a = sys.argv[2] -specific_a = sys.argv[3] -port = None -srv_addrs = [] - -ttl = "300" - -if len(sys.argv) > 5: - port = sys.argv[4] - srv_addrs = sys.argv[5:] - -ret = [{"rr":"A", "alias": cluster_a, "ttl": ttl,"addr": ip}, - {"rr":"A", "alias": specific_a, "ttl": ttl,"addr": ip}] - -for a in srv_addrs: - ret.append({"rr": "SRV", "alias": a, "ttl": ttl,"priority": "1", "weight": "1", "port": port, "target": specific_a}) - -print json.dumps(ret) diff --git a/abot-volte-basic/lib/scripts/install_java b/abot-volte-basic/lib/scripts/install_java deleted file mode 100755 index 6d91d6c..0000000 --- a/abot-volte-basic/lib/scripts/install_java +++ /dev/null @@ -1,7 +0,0 @@ -#!/bin/bash - -add-apt-repository ppa:webupd8team/java -apt-get update -echo debconf shared/accepted-oracle-license-v1-1 select true | sudo debconf-set-selections -echo debconf shared/accepted-oracle-license-v1-1 seen true | sudo debconf-set-selections -apt-get -y install oracle-java8-installer diff --git a/abot-volte-basic/lib/scripts/oaisim.conf b/abot-volte-basic/lib/scripts/oaisim.conf deleted file mode 100755 index d0ac81b..0000000 --- a/abot-volte-basic/lib/scripts/oaisim.conf +++ /dev/null @@ -1,11 +0,0 @@ -stop on shutdown - -script - -#exec source common ...set_envpaths - exec /srv/openair5G/cmake_targets/tools/run_enb_ue_virt_s1 -c /srv/openair5G/targets/PROJECTS/GENERIC-LTE-EPC/CONF/enb.band7.generic.conf -K /tmp/enb_ue_virt_s1_itti.log > /srv/.out 2> /srv/.err - #exec /srv/openair5G/cmake_targets/tools/run_enb_ue_virt_s1 -c /srv/openair5G/targets/PROJECTS/GENERIC-LTE-EPC/CONF/enb.band7.generic.oaisim.local_no_mme.conf -K /tmp/enb_ue_virt_s1_itti.log > /srv/.out 2> /srv/.err - - -end script - diff --git a/abot-volte-basic/lib/scripts/order_series b/abot-volte-basic/lib/scripts/order_series deleted file mode 100755 index 001beb0..0000000 --- a/abot-volte-basic/lib/scripts/order_series +++ /dev/null @@ -1,19 +0,0 @@ -#!/bin/bash - -count=0 -for name in `find . -type f -name '*.feature'`; -do - series=$(grep -i 'Series-[0-9]*' $name | sed 's/.*\([0-9][0-9][0-9][0-9]\).*/\1/') - if [ "$series" != "" ] - then - new_name=$(echo $name | sed 's?\(.*\)/[0-9][0-9][0-9][0-9]-?\1'/'?') - new_name=$(echo $new_name | sed 's?\(.*\)/?\1'/"${series}-"'?') - if [ "$name" != "$new_name" ] - then - echo "Renaming to series - [$series] $new_name" - mv "$name" "$new_name"; - (( count += 1 )) - fi - fi -done -echo "$count files renamed..." diff --git a/abot-volte-basic/lib/scripts/run_oaisim.sh b/abot-volte-basic/lib/scripts/run_oaisim.sh deleted file mode 100755 index 30ba450..0000000 --- a/abot-volte-basic/lib/scripts/run_oaisim.sh +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/bash - -#Run the oaisim service -/bin/sh -c "/srv/openair5G/cmake_targets/tools/run_enb_ue_virt_s1_rebaca -u $2 -c /srv/openair5G/targets/PROJECTS/GENERIC-LTE-EPC/CONF/enb.band7.generic.conf -K /tmp/enb_ue_virt_s1_itti.log > /srv/.out 2> /srv/.err" & diff --git a/abot-volte-basic/lib/scripts/security_setup.sh b/abot-volte-basic/lib/scripts/security_setup.sh deleted file mode 100755 index 7338c12..0000000 --- a/abot-volte-basic/lib/scripts/security_setup.sh +++ /dev/null @@ -1,40 +0,0 @@ -#!/bin/bash -search_and_replace() -{ - LHS=$1 - RHS=$2 - - #echo "$LHS=$RHS" - - str=$(grep -w "$LHS\s*=" $SECURITYCONFIGFILE) - - #echo "str=:"$str - - IFS='=' read -ra WORDSARR <<< $str - - #echo "${WORDSARR[0]}=${WORDSARR[1]}" - - OLD_RHS=`echo ${WORDSARR[1]} | tr -d '"' | tr -d ';'` - - #echo "OLD_RHS :"$OLD_RHS"----RHS :"$RHS"----LHS :"$LHS - - sed -i -e "s?\(\s*{*\)\(${LHS}\)\(\s*= *\)\(\"*\)\(${OLD_RHS}\)\(\"*\)\(;\)?\1\2\3\4${RHS}\6\7?g" $SECURITYCONFIGFILE -} - - -LINE=$1 -SECURITYCONFIGFILE=$2 - -if [ "$SECURITYCONFIGFILE" = "" ] -then - SECURITYCONFIGFILE="/srv/openair5G/targets/bin/security_config_rebaca.conf" -fi - -#echo $LINE - -IFS='=' read -ra WORDS <<< $LINE -IFS='.' read -ra LeftString <<< ${WORDS[0]} -LHS=${LeftString[-1]} -RHS=${WORDS[1]} - -search_and_replace $LHS $RHS diff --git a/abot-volte-basic/lib/scripts/stop_oaisim.sh b/abot-volte-basic/lib/scripts/stop_oaisim.sh deleted file mode 100755 index 7ebafd9..0000000 --- a/abot-volte-basic/lib/scripts/stop_oaisim.sh +++ /dev/null @@ -1,4 +0,0 @@ -#/bin/bash - -#Kill the oaisim service -pkill oaisim.Rel14 diff --git a/abot-volte-basic/lib/scripts/tags_update b/abot-volte-basic/lib/scripts/tags_update deleted file mode 100755 index 1dd281a..0000000 --- a/abot-volte-basic/lib/scripts/tags_update +++ /dev/null @@ -1,15 +0,0 @@ -#!/bin/bash - -filename="$CHARM_DIR/lib/src/test/java/com/rebaca/abot/RunScenarios" -TAG_DIR=/etc/rebaca-test-suite/tags - -if [ $# == 0 ]; then - tags=`cat $TAG_DIR/tags` -else - tags=$* -fi - -echo "Creating the Target JUnit file..."; -sed -i "s|.*tags=.*|\t\ttags={\"${tags}\"},|" ${filename}.java - -rm -f $CHARM_DIR/lib/target/test-classes/com/rebaca/automation/RunScenarios.class diff --git a/abot-volte-basic/lib/scripts/ue_setup.sh b/abot-volte-basic/lib/scripts/ue_setup.sh deleted file mode 100755 index 5ec9bfc..0000000 --- a/abot-volte-basic/lib/scripts/ue_setup.sh +++ /dev/null @@ -1,43 +0,0 @@ -#!/bin/bash -search_and_replace() -{ - LHS=$1 - RHS=$2 - - #echo "$LHS=$RHS" - - str=$(grep -w "$LHS\s*=" $UECONFIGFILE) - - #echo "str=:"$str - - IFS='=' read -ra WORDSARR <<< $str - - #echo "${WORDSARR[0]}=${WORDSARR[1]}" - - OLD_RHS=`echo ${WORDSARR[1]} | tr -d '"' | tr -d ';'` - - #echo "OLD_RHS :"$OLD_RHS"----RHS :"$RHS"----LHS :"$LHS - - sed -i -e "s?\(\s*{*\)\(${LHS}\)\(\s*= *\)\(\"*\)\(${OLD_RHS}\)\(\"*\)\(;\)?\1\2\3\4${RHS}\6\7?g" $UECONFIGFILE -} - - -LINE=$1 -UECONFIGFILE=$2 - -if [ "$UECONFIGFILE" = "" ] -then - UECONFIGFILE="/srv/openair5G/targets/bin/ue_config_rebaca.conf" -fi - -#echo $LINE - -if [[ $LINE == *"ABOT.UE.CONFIG"* ]] -then - IFS='=' read -ra WORDS <<< $LINE - IFS='.' read -ra LeftString <<< ${WORDS[0]} - LHS=${LeftString[-1]} - RHS=${WORDS[1]} - search_and_replace $LHS $RHS -fi - diff --git a/abot-volte-basic/lib/scripts/usim_setup.sh b/abot-volte-basic/lib/scripts/usim_setup.sh deleted file mode 100755 index 9eeef1e..0000000 --- a/abot-volte-basic/lib/scripts/usim_setup.sh +++ /dev/null @@ -1,43 +0,0 @@ -#!/bin/bash -search_and_replace() -{ - LHS=$1 - RHS=$2 - - #echo "$LHS=$RHS" - - str=$(grep -w "$LHS\s*=" $USIMCONFIGFILE) - - #echo "str=:"$str - - IFS='=' read -ra WORDSARR <<< $str - - #echo "${WORDSARR[0]}=${WORDSARR[1]}" - - OLD_RHS=`echo ${WORDSARR[1]} | tr -d '"' | tr -d ';'` - - #echo "OLD_RHS :"$OLD_RHS"----RHS :"$RHS"----LHS :"$LHS - - sed -i -e "s?\(\s*{*\)\(${LHS}\)\(\s*= *\)\(\"*\)\(${OLD_RHS}\)\(\"*\)\(;\)?\1\2\3\4${RHS}\6\7?g" $USIMCONFIGFILE -} - - -LINE=$1 -USIMCONFIGFILE=$2 - -if [ "$USIMCONFIGFILE" = "" ] -then - USIMCONFIGFILE="/srv/openair5G/targets/bin/usim_config_rebaca.conf" -fi - -#echo $LINE - -if [[ $LINE == *"ABOT.UE.USIM"* ]] -then - IFS='=' read -ra WORDS <<< $LINE - IFS='.' read -ra LeftString <<< ${WORDS[0]} - LHS=${LeftString[-1]} - RHS=${WORDS[1]} - - search_and_replace $LHS $RHS -fi diff --git a/abot-volte-basic/metadata.yaml b/abot-volte-basic/metadata.yaml deleted file mode 100644 index 8d7ab94..0000000 --- a/abot-volte-basic/metadata.yaml +++ /dev/null @@ -1,27 +0,0 @@ -name: abot-volte-basic -summary: An Automation Framework built on behaviour-oriented testing model -maintainer: Rebaca Technologies -description: | - A Test Automation Framework for network services that is built on the behaviour-oriented testing model. - This enables users to define test cases as feature files based on domain-specific language. - These feature files could subsequently be executed in automated manner on multiple environments. -tags: - - misc - - test-automation - - ims epc volte -subordinate: false -requires: - programmable-multiple: - interface: dns-multi - abot-es: - interface: abot-es - hss-prov: - interface: homestead-prov-interface - abot-mme: - interface: S1-C -provides: - hss-abot: - interface: 3GPP-Cx - ssh-abot: - interface: ssh - diff --git a/abot-volte-basic/revision b/abot-volte-basic/revision deleted file mode 100644 index 56a6051..0000000 --- a/abot-volte-basic/revision +++ /dev/null @@ -1 +0,0 @@ -1 \ No newline at end of file diff --git a/abot-volte-basic/tests/01-setup b/abot-volte-basic/tests/01-setup deleted file mode 100755 index cd5b3a2..0000000 --- a/abot-volte-basic/tests/01-setup +++ /dev/null @@ -1,5 +0,0 @@ -#!/bin/bash - -sudo add-apt-repository ppa:juju/stable -y -sudo apt-get update -sudo apt-get install amulet python3-requests -y diff --git a/abot-volte-basic/tests/02-standard b/abot-volte-basic/tests/02-standard deleted file mode 100755 index c3da949..0000000 --- a/abot-volte-basic/tests/02-standard +++ /dev/null @@ -1,67 +0,0 @@ -#!/usr/bin/env python3 - -import amulet -import requests -import unittest - - -class TestAbotimsbasicDeployment(unittest.TestCase): - @classmethod - def setUpClass(cls): - cls.deployment = amulet.Deployment(series='trusty') - cls.deployment.add('abot-ims-basic', charm='~debayan-ch/trusty/abot-ims-basic-33') - cls.deployment.configure('abot-ims-basic', {'clearwater_sipp_ip' : '172.16.255.255'}) - - try: - cls.deployment.setup(timeout=9000) - cls.deployment.sentry.wait() - except amulet.helpers.TimeoutError: - amulet.raise_status(amulet.SKIP, msg="Environment wasn't stood up in time") - except: - raise - - def test_homepage(self): - # Now you can use self.deployment.sentry.unit[UNIT] to address each of - # the units and perform more in-depth steps. You can also reference - # the first unit as self.unit. - # There are three test statuses that can be triggered with - # amulet.raise_status(): - # - amulet.PASS - # - amulet.FAIL - # - amulet.SKIP - # Each unit has the following methods: - # - .info - An array of the information of that unit from Juju - # - .file(PATH) - Get the details of a file on that unit - # - .file_contents(PATH) - Get plain text output of PATH file from that unit - # - .directory(PATH) - Get details of directory - # - .directory_contents(PATH) - List files and folders in PATH on that unit - # - .relation(relation, service:rel) - Get relation data from return service - # add tests here to confirm service is up and working properly - # For example, to confirm that it has a functioning HTTP server: - # page = requests.get('http://{}'.format(self.unit.info['public-address'])) - # page.raise_for_status() - # More information on writing Amulet tests can be found at: - # https://jujucharms.com/docs/stable/tools-amulet - # Abot-ims-basic sentry unit - abotimsbasic_unit = self.deployment.sentry['abot-ims-basic'] [0] - # Abot-ims-basic home page URL - abotimsbasic_url = 'http://{public-address}'.format(**abotimsbasic_unit.info) - # Request for home page of abot ims basic - homepage = requests.get(abotimsbasic_url) - # Status of Homepage - homepage.raise_for_status() - pass - def test_config(self): - #Abot-ims-basic sentry unit - abotimsbasic_unit = self.deployment.sentry['abot-ims-basic'] [0] - #Sip server config file - sip_ip_config = abotimsbasic_unit.file_contents('/etc/rebaca-test-suite/clearwater_sipp_ip') - #Sip server content - sip_ip_content = "172.16.255.255" - #Compare the configuration change - self.assertTrue(sip_ip_content in sip_ip_config) - pass -#if __name__ == '__main__': -# unittest.main() -suite = unittest.TestLoader().loadTestsFromTestCase(TestAbotimsbasicDeployment) -unittest.TextTestRunner(verbosity=2).run(suite) diff --git a/abot-volte-basic/tests/03-action b/abot-volte-basic/tests/03-action deleted file mode 100755 index a5ed586..0000000 --- a/abot-volte-basic/tests/03-action +++ /dev/null @@ -1,95 +0,0 @@ -#!/usr/bin/env python3 - -import amulet -import requests -import unittest - - -class TestAbotimsbasicDeployment(unittest.TestCase): - @classmethod - def setUpClass(cls): - cls.deployment = amulet.Deployment(series='trusty') - cls.deployment.add('abot-ims-basic', charm='~debayan-ch/trusty/abot-ims-basic-33') - cls.deployment.configure('abot-ims-basic', {'clearwater_sipp_ip' : '172.16.255.255'}) - - try: - cls.deployment.setup(timeout=9000) - cls.deployment.sentry.wait() - except amulet.helpers.TimeoutError: - amulet.raise_status(amulet.SKIP, msg="Environment wasn't stood up in time") - except: - raise - - def test_homepage(self): - # Now you can use self.deployment.sentry.unit[UNIT] to address each of - # the units and perform more in-depth steps. You can also reference - # the first unit as self.unit. - # There are three test statuses that can be triggered with - # amulet.raise_status(): - # - amulet.PASS - # - amulet.FAIL - # - amulet.SKIP - # Each unit has the following methods: - # - .info - An array of the information of that unit from Juju - # - .file(PATH) - Get the details of a file on that unit - # - .file_contents(PATH) - Get plain text output of PATH file from that unit - # - .directory(PATH) - Get details of directory - # - .directory_contents(PATH) - List files and folders in PATH on that unit - # - .relation(relation, service:rel) - Get relation data from return service - # add tests here to confirm service is up and working properly - # For example, to confirm that it has a functioning HTTP server: - # page = requests.get('http://{}'.format(self.unit.info['public-address'])) - # page.raise_for_status() - # More information on writing Amulet tests can be found at: - # https://jujucharms.com/docs/stable/tools-amulet - # Abot-ims-basic sentry unit - abotimsbasic_unit = self.deployment.sentry['abot-ims-basic'] [0] - # Abot-ims-basic home page URL - abotimsbasic_url = 'http://{public-address}'.format(**abotimsbasic_unit.info) - # Request for home page of abot ims basic - homepage = requests.get(abotimsbasic_url) - # Status of Homepage - homepage.raise_for_status() - pass - def test_config(self): - #Deployment sentry for Abot-ims-basic - abotimsbasic_unit = self.deployment.sentry['abot-ims-basic'] [0] - #SIP server config file - sip_ip_config = abotimsbasic_unit.file_contents('/etc/rebaca-test-suite/clearwater_sipp_ip') - #Verify SIP server name with - sip_ip_content = "172.16.255.255" - #Asssertion for verification - self.assertTrue(sip_ip_content in sip_ip_config) - - def test_action_add_tags(self): - #abot_unit = self.deployment.sentry['abot'] [0] - abotimsbasic_unit = self.deployment.sentry['abot-ims-basic'] [0] - #Add tags using action command - abotimsbasic_unit.run_action('add-tags',{'tagnames' : 'sip-subscribe-notify'}) - # Tag addition verification - tag_file_content = abotimsbasic_unit.file_contents('/etc/rebaca-test-suite/tags/tags') - # Verify tag name with - tag_name = 'sip-subscribe-notify' - # - self.deployment.sentry.wait_for_messages({'abot-ims-basic':'Ready'}, timeout=300) - # Asssertion for verification - self.assertTrue(tag_name in tag_file_content) - pass - - def test_action_run(self): - #Sentry for Abot-ims-basic unit - abotimsbasic_unit = self.deployment.sentry['abot-ims-basic'] [0] - #Run the action hook of list-tags - abotimsbasic_unit.run_action('run', {'tagnames' : 'local-commands'}) - #Wait for time to complete run action - self.deployment.sentry.wait_for_messages({'abot-ims-basic':'Ready'}, timeout=300) - # Check the file statistics to check successful execution of run action - abotimsbasic_unit.file_stat('lib/testReports/report-*/*local-commands*.html') - #Raise status on status of action_check - pass - - -#if __name__ == '__main__': -# unittest.main() -suite = unittest.TestLoader().loadTestsFromTestCase(TestAbotimsbasicDeployment) -unittest.TextTestRunner(verbosity=2).run(suite) diff --git a/abot-volte-bundle/README.md b/abot-volte-bundle/README.md deleted file mode 100644 index a477d36..0000000 --- a/abot-volte-bundle/README.md +++ /dev/null @@ -1,95 +0,0 @@ -# Charms Overview - -Abot-VOLTE bundle deploys abot-volte-basic along-with Oai-hss, Oai-epc , Mysql, Clearwater-homestead, Clearwater-bono, Clearwater-sprout, and dns charms, which support deployment and scaling of OAI charms and Clearwater charms along with ABot developed by Rebeca. - -ABot should typically be deployed alongside the System Under Test(SUT) - such that it can execute tests against the latter. One of the possible SUTs is the OAI EPC, which is an open source initiative of Eurecom. - -ABot enables users to create **feature files** based on a **Cucumber** framework. Cucumber is a Behavior Driven Development (BDD) framework which is used to write tests for different types of applications. It allows automation of functional validation in an easily readable and understandable format (like plain English). It is used to test the system rather than testing the particular piece of code. - -The feature files authored by the user(s) can subsequently be used for testing different types of applications/protocols that are running on the System Under Test(SUT). The feature files follow a specified grammar and include instructions that translate into sending messages to the SUT, and receiving back responses using different protocols and verification of the results as defined. - -The current protocol drivers(adapters) provided with ABot are SSH, SFTP, HTTP as well as S1AP. - -Each feature file can be associated with one (or more) tags; thereby enabling it to be selectively invoked. **Execution of feature file(s) associated with one (or more) specified tags is driven through a Juju action after deployment of the ABot charm.** - -The result of each test execution is displayed in a web-based reporting UI; this is currently accessible on port 80. - -# Usage - -Before deploying this bundle, you should bootstrap a Juju environment, as documented in the https://jujucharms.com/docs/stable/getting-started. The testing of this charm on MAAS cloud is complete, and there are plans to test it on Amazon and OpenStack in the near future. If you get it working on a different cloud service, please let us know! - -OAI is a relatively simpler bundle consisting of Mysql, Oai-hss, Oai-epc and Abot-EPC-basic, together forming the SUT for feature file execution. The ABot OAI EPC bundle has been integrated and tested on MAAS cloud. You can find the ABot OAI EPC bundle at [ ]. To know more about ABot please visit charmstore (https://jujucharms.com/u/abotcharm/abot-volte-basic/xenial/). - -The ABot OAI EPC bundle ties up all the above mentioned charms and their relations together, allowing the deployment of a full system in a single step. - ->juju deploy [bundle_name] - - -# Configuration - -See [bundle.yaml](bundle.yaml), which lists the main configuration fields with comments about their meanings. - -# Files downloaded - -When the above mentioned charms are being installed, the following files are downloaded from their respected paths: - -- The ABot Debian package, from the public package repository server hosted at Rebaca. -- Any dependencies related to Debian packages (particularly Java and Maven), are downloaded from the standard Ubuntu repository servers. -- Any dependency related to OAI charms are downloaded from their respective repositories. - -# Test Case Execution - - - After deployment of the ABot OAI EPC bundle, the following actions can be quickly executed to verify whether the bundle works fine: -> juju run-action abot-volte-basic/[abot-unit-number] run - - - The following script needs to be executed from the Juju controller in order to generate and add the ssh keys to all the ABot OAI EPC bundle units. - -> charm pull cs:~abotcharm/xenial/abot-volte-basic -> ./abot-volte-basic/lib/scripts/add-keys.sh - -- In order to execute a particular feature file or a set of feature files we need to execute the following commands mentioned below, replacing the tag name mentioned below with the required tag name. Also in the following commands we need to replace the [abot-unit-number] with the current abot-unit-number. - -> juju run-action abot-volte-basic/[abot-unit-number] run tagnames=[tag name] - -In order to run EPC attach test case feature file: -> juju run-action abot-volte-basic/8 run tagnames=Attach_Procedure_AttachWithIMSI - -In order to run EPC MAC Failure test case feature file: -> juju run-action abot-volte-basic/8 run tagnames=Auth_NotAccept_by_UE_GUTIattach_MAC_code_failure - -In order to run EPC SQN Failure test case feature file: -> juju run-action abot-volte-basic/8 run tagnames=Auth_NotAccept_by_UE_SQN_failure - -In order to run EPC negative test cases in multiple feature files: -> juju run-action abot-volte-basic/8 run tagnames=negTCs - -In order to run 3GPP TS 24.301 test cases in multiple feature files: -> juju run-action abot-volte-basic/8 run tagnames=TS_24_301 - -- The following action will execute all test cases (feature files) for which the tags have been added through the previous command. Please note that the tags can be run from a specified filename as well. - -> juju run-action abot-volte-basic/[abot-unit-number] run tagnames=[feature file name without extension] - -- Execute tags from the file titled ***negTCs***. This file includes tags for negative call scenarios; such as *sync failure*, *MAC failure* and so on. -> juju run-action abot-volte-basic/[abot-unit-number] run tagnames=negTCs - -# Adding new Test Cases (Feature Files) - -- **New Feature Files should be added into the following folder** on the Juju controller node -> charm pull cs:~abotcharm/trusty/abot-ims-basic -> $PWD/abot-volte-basic/lib/featureFiles - -- The Juju charm upgrade action should be invoked to propagate these new feature files onto the deployed service node. - -> juju upgrade-charm --path=$PWD/abot-volte-basic abot-epc-basic - -- Once the upgrade is successful, the test case execution steps can be followed to execute one or more feature files. - -# Viewing Test Report - - - The report UI can then be viewed by accessing the below mentioned URL. - http://[abot-ip]/app - Eg : http://192.168.15.87/app/ - -- This pulls up the Cucumber report of the latest execution. -- Accessing any of the feature files present in the report shows the step-wise execution status for that feature file. diff --git a/abot-volte-bundle/bundle.yaml b/abot-volte-bundle/bundle.yaml deleted file mode 100644 index 9ff0883..0000000 --- a/abot-volte-bundle/bundle.yaml +++ /dev/null @@ -1,125 +0,0 @@ - services: - "clearwater-bono": - charm: cs:~abotcharm/trusty/clearwater-bono - num_units: 1 - constraints: "arch=amd64 mem=2G" - options: - zone: "rebaca.local" - expose: true - annotations: - "gui-x": "400" - "gui-y": "900" - "clearwater-sprout": - charm: cs:~abotcharm/trusty/clearwater-sprout - num_units: 1 - constraints: "arch=amd64 mem=2G" - options: - zone: "rebaca.local" - annotations: - "gui-x": "400" - "gui-y": "600" - "clearwater-homestead": - charm: cs:~abotcharm/trusty/clearwater-homestead - num_units: 1 - constraints: "arch=amd64 mem=4G" - options: - zone: "rebaca.local" - annotations: - "gui-x": "200" - "gui-y": "300" - "dns": - charm: cs:~abotcharm/trusty/dns - num_units: 1 - constraints: "arch=amd64 mem=2G" - options: - domain: "rebaca.local" - annotations: - "gui-x": "1000" - "gui-y": "450" - "oai-hss": - charm: cs:~abotcharm/trusty/oai-hss - num_units: 1 - constraints: "arch=amd64 mem=2G" - series: trusty - options: - operator-key: "1006020f0a478bf6b699f15c062e42b3" - random: "true" - annotations: - "gui-x": "400" - "gui-y": "900" - "oai-spgw": - charm: cs:~abotcharm/xenial/oai-spgw - num_units: 1 - constraints: "arch=amd64 mem=4G" - series: xenial - options: - zone: "rebaca.local" - annotations: - "gui-x": "550" - "gui-y": "900" - "oai-mme": - charm: cs:~abotcharm/xenial/oai-mme - num_units: 1 - constraints: "arch=amd64 mem=4G" - series: xenial - options: - gummei_tai_mnc: "93" - annotations: - "gui-x": "500" - "gui-y": "900" - "mysql": - charm: cs:mysql-55 - num_units: 1 - constraints: "arch=amd64 mem=2G" - series: trusty - annotations: - "gui-x": "200" - "gui-y": "300" - "abot-volte-basic": - charm: cs:~abotcharm/xenial/abot-volte-basic - constraints: "arch=amd64 mem=8G" - series: xenial - num_units: 1 - options: - zone: "rebaca.local" - expose: true - annotations: - "gui-x": "400" - "gui-y": "700" - relations: - - - "clearwater-homestead:programmable-multiple" - - "dns:programmable-multiple" - - - "clearwater-sprout:programmable-multiple" - - "dns:programmable-multiple" - - - "clearwater-bono:programmable-multiple" - - "dns:programmable-multiple" - - - "abot-volte-basic:programmable-multiple" - - "dns:programmable-multiple" - - - "dns:programmable-multiple" - - "oai-spgw:programmable-multiple" - - - "clearwater-bono:cscf" - - "clearwater-sprout:pcscf" - - - "clearwater-homestead:homestead-cscf" - - "clearwater-sprout:homestead-hss" - - - "abot-volte-basic:ssh-abot" - - "oai-mme:ssh-abot-mme" - - - "abot-volte-basic:ssh-abot" - - "clearwater-homestead:ssh-abot-volte-homestead" - - - "abot-volte-basic:ssh-abot" - - "oai-spgw:ssh-abot-spgw" - - - "mysql:db" - - "oai-hss:db" - - - "oai-hss:hss" - - "oai-mme:hss" - - - "oai-mme:spgw" - - "oai-spgw:spgw" - - - "oai-spgw:ue-abot" - - "clearwater-bono:ue" - - - "abot-volte-basic:hss-prov" - - "clearwater-homestead:homestead-prov-user" - - - "clearwater-homestead:hss" - - "abot-volte-basic:hss-abot" - - - "abot-volte-basic:abot-mme" - - "oai-mme:mme" - series: trusty - diff --git a/clearwater-bono/README.md b/clearwater-bono/README.md deleted file mode 100644 index 338f990..0000000 --- a/clearwater-bono/README.md +++ /dev/null @@ -1,50 +0,0 @@ -# Overview - -This is a [Juju charm](https://jujucharms.com/about), which allows deployment and scaling of the [Bono](https://github.com/Metaswitch/sprout/#sprout-and-bono) component of a [Project Clearwater](http://projectclearwater.org) IMS core. - -Bono should only be deployed alongside the other Project Clearwater charms and a DNS server - see [our main Juju README](https://github.com/Metaswitch/clearwater-juju/blob/local_charms/README.md) for instructions on this, including a bundle that makes this deployment simple. - -# Deployment - -## Initial deployment - -The Bono service should be initially deployed as part of a Clearwater bundle, following the instructions in [our main README](https://github.com/Metaswitch/clearwater-juju/blob/local_charms/README.md). - -Note that the clearwater-bono charm can only be deployed on the `amd64` architecture (although the provided bundle already enforces this constraint). - -## Scale out Usage - -clearwater-bono can be scaled up through the normal Juju mechanism of `juju add-unit clearwater-bono`. - -This will create a new Bono instance, and trigger the DNS server charm to add a DNS record for this Bono. (Unlike Sprout, Homestead, Homer and Ralf nodes, Bono nodes don't have a shared datastore, so don't need any cluster configuration.) - -# Using Bono - -Once installed, Bono will listen for SIP traffic on port 5060 (both TCP and UDP). Once you have created a SIP subscriber through the Ellis UI, you can use a standard SIP client (e.g. Blink, Boghe or X-Lite) to register against Bono's public IP (which you can find with `juju status clearwater-bono`) and make calls. - -Our ["Making your first call" documentation](http://clearwater.readthedocs.org/en/latest/Making_your_first_call/index.html) has more information on this process. - -# Configuration - -- `zone:` The home domain of this IMS deployment - this could be a real domain that you own, or an internal-only name like "clearwater.local". -- `repo`: The URL of the Clearwater package repository server. Our latest relese, updated roughly every two weeks, is at http://repo.cw-ngv.com/juju-clearwater-2. -- `sas`: (optional) The location of a [Metaswitch SAS server](http://www.metaswitch.com/products/management-systems/service-assurance-server) for diagnostics and call flows. -- `trusted_peers`: (optional) Comma-separated list of IP addresses of trusted peers, for when Bono is used as an IBCF rather than a P-CSCF. - -The `zone`, `sas` and `repo` configuration options should be consistent across all Clearwater nodes in the deployment. - -# Files downloaded - -When the charm is being installed, several files are downloaded: - -- Our standard Chef setup scripts, checked out from https://github.com/Metaswitch/chef. -- The Bono Debian packages, from our package repository server at repo.cw-ngv.com. -- Any dependencies of those Debian packages, from the standard Ubuntu repository servers. - -# Contact and Upstream Project Information - -Project Clearwater is an open-source IMS core, developed by [Metaswitch Networks](http://www.metaswitch.com) and released under the [GNU GPLv3](http://www.projectclearwater.org/download/license/). You can find more information about it on [our website](http://www.projectclearwater.org/) or [our documentation site](https://clearwater.readthedocs.org). - -Clearwater source code and issue list can be found at https://github.com/Metaswitch/. Bono's source code can be found at https://github.com/Metaswitch/sprout/. - -If you have problems when using Project Clearwater, read [our troubleshooting documentation](http://clearwater.readthedocs.org/en/latest/Troubleshooting_and_Recovery/index.html) for help, or see [our support page](http://clearwater.readthedocs.org/en/latest/Support/index.html) to find out how to ask mailing list questions or raise issues. diff --git a/clearwater-bono/config.yaml b/clearwater-bono/config.yaml deleted file mode 100644 index 1e122a1..0000000 --- a/clearwater-bono/config.yaml +++ /dev/null @@ -1,29 +0,0 @@ -options: - zone: - default: "clearwater.local" - description: The DNS root zone for this service - type: string - sas: - default: "" - description: The location of the SAS server - type: string - cdf: - default: "" - description: The location of the CDF to bill to - type: string - turn_workaround: - default: "" - description: TURN workaround password, used by faulty WebRTC clients - type: string - trusted_peers: - default: "" - description: Comma-separated list of IP addresses of trusted peers - type: string - target_latency_us: - default: 100000 - description: Target request latency for overload control (microseconds) - type: int - repo: - default: http://repo.cw-ngv.com/archive/repo110/ - description: The location of the repo server - type: string diff --git a/clearwater-bono/copyright b/clearwater-bono/copyright deleted file mode 100644 index 39f1ec8..0000000 --- a/clearwater-bono/copyright +++ /dev/null @@ -1,31 +0,0 @@ -Project Clearwater - IMS in the Cloud -Copyright (C) 2016 Metaswitch Networks Ltd - -This program is free software: you can redistribute it and/or modify it -under the terms of the GNU General Public License as published by the -Free Software Foundation, either version 3 of the License, or (at your -option) any later version, along with the "Special Exception" for use of -the program along with SSL, set forth below. This program is distributed -in the hope that it will be useful, but WITHOUT ANY WARRANTY; -without even the implied warranty of MERCHANTABILITY or FITNESS FOR -A PARTICULAR PURPOSE. See the GNU General Public License for more -details. You should have received a copy of the GNU General Public -License along with this program. If not, see -. - -The author can be reached by email at clearwater@metaswitch.com or by -post at Metaswitch Networks Ltd, 100 Church St, Enfield EN2 6BQ, UK - -Special Exception -Metaswitch Networks Ltd grants you permission to copy, modify, -propagate, and distribute a work formed by combining OpenSSL with The -Software, or a work derivative of such a combination, even if such -copying, modification, propagation, or distribution would otherwise -violate the terms of the GPL. You must comply with the GPL in all -respects for all of the code used other than OpenSSL. -"OpenSSL" means OpenSSL toolkit software distributed by the OpenSSL -Project and licensed under the OpenSSL Licenses, or a work based on such -software and licensed under the OpenSSL Licenses. -"OpenSSL Licenses" means the OpenSSL License and Original SSLeay License -under which the OpenSSL Project distributes the OpenSSL toolkit software, -as those licenses appear in the file LICENSE-OPENSSL. diff --git a/clearwater-bono/hooks/config-changed b/clearwater-bono/hooks/config-changed deleted file mode 100755 index c658148..0000000 --- a/clearwater-bono/hooks/config-changed +++ /dev/null @@ -1,9 +0,0 @@ -#!/bin/bash -set -e - -# Update the /etc/clearwater/*_config files, the node.json, and re-run -# chef-solo -$CHARM_DIR/lib/config_script -$CHARM_DIR/lib/node_json_script -chef-solo -c /home/ubuntu/chef-solo/solo.rb -j /home/ubuntu/chef-solo/node.json -$CHARM_DIR/lib/restart diff --git a/clearwater-bono/hooks/cscf-relation-changed b/clearwater-bono/hooks/cscf-relation-changed deleted file mode 100755 index 6a33860..0000000 --- a/clearwater-bono/hooks/cscf-relation-changed +++ /dev/null @@ -1,8 +0,0 @@ -#!/bin/bash -set -e - -# Update Clearwater configuration and restart if anything changed -cp /etc/clearwater/shared_config /tmp/config.$$ -$CHARM_DIR/lib/config_script cscf -cmp /etc/clearwater/shared_config /tmp/config.$$ || $CHARM_DIR/lib/restart -rm -f /tmp/config.$$ diff --git a/clearwater-bono/hooks/install b/clearwater-bono/hooks/install deleted file mode 100755 index c9effa8..0000000 --- a/clearwater-bono/hooks/install +++ /dev/null @@ -1,37 +0,0 @@ -#!/bin/bash -set -e - -if ! grep "# added by clearwater-*/hooks/install" /etc/hosts ; then - public_ip=$(unit-get public-address) - echo "$public_ip bono-$(cut -d/ -f2 <<< $JUJU_UNIT_NAME).$(config-get zone) # added by clearwater-*/hooks/install" >>/etc/hosts -fi - -# Install chef solo, and create the node.json and config file -$CHARM_DIR/lib/chef_solo_install -$CHARM_DIR/lib/node_json_script -$CHARM_DIR/lib/config_script - -apt-get update - -# After we install dnsmasq, DNS might drop for a few seconds. Install it ahead of time -# and wait for DNS to recover. -apt-get -q -y --force-yes install dnsmasq -iterations=0 -while [ $(dig +short security.ubuntu.com | wc -l) == 0 ] && [ $iterations -lt 60 ] ; do - echo "Waiting for DNS to security.ubuntu.com to recover ($iterations/60)..." - sleep 1 - iterations=$((iterations + 1)) -done - -# Install the node -chef-solo -c /home/ubuntu/chef-solo/solo.rb -j /home/ubuntu/chef-solo/node.json - -# Expose the correct ports -open-port 22/tcp -open-port 123/udp -open-port 161/udp -open-port 3478/tcp -open-port 3478/udp -open-port 5060/tcp -open-port 5060/udp -open-port 5062/tcp diff --git a/clearwater-bono/hooks/programmable-multiple-relation-changed b/clearwater-bono/hooks/programmable-multiple-relation-changed deleted file mode 100755 index 314b5f6..0000000 --- a/clearwater-bono/hooks/programmable-multiple-relation-changed +++ /dev/null @@ -1,61 +0,0 @@ -#!/bin/bash -set -e - -juju-log "Invoking dns-relation" - -dns_ip=$(relation-get private-address) -if [ -z "$dns_ip" ]; then - juju-log "No data sent yet from DNS" - exit 0 -fi - -# Set our DNS requirements -id=$(cut -d/ -f2 <<< $JUJU_UNIT_NAME) -ip=$(unit-get public-address) -relation-set domain=$(config-get zone) -DNS_JSON=$($CHARM_DIR/lib/generate_dns_records $ip bono bono-$id 5060 _sip._tcp _sip._udp) -relation-set resources="$DNS_JSON" - -# Update our DNS server -if [ "$dns_ip" != "" ] -then - echo nameserver $dns_ip > /etc/dnsmasq.resolv.conf - grep -v ^RESOLV_CONF= /etc/default/dnsmasq > /tmp/dnsmasq.$$ - mv /tmp/dnsmasq.$$ /etc/default/dnsmasq - echo RESOLV_CONF=/etc/dnsmasq.resolv.conf >> /etc/default/dnsmasq - service dnsmasq restart -fi - -# Change Resolvconf order and make dnsmasq on top -dnsmasq=lo.dnsmasq -resolvconf=/etc/resolvconf -loinet=`grep ^[a-z] /etc/resolvconf/interface-order | head -n1` - if [ $loinet != $dnsmasq ] - then - cp /etc/resolvconf/interface-order /etc/resolvconf/interface-order.orig - sed '/'$dnsmasq'/d' /etc/resolvconf/interface-order.orig > /etc/resolvconf/interface-order - sed -i '/'$loinet'/i lo.dnsmasq' /etc/resolvconf/interface-order - service dnsmasq restart - resolvconf -u - else - exit 0 - fi - -# Verify DNS record to make sure that IMS dns is working properly -if [[ `dig +short $ip` =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]] - then - juju-log "Verifying $ip in dns " - exit - elif [[ "$ip" =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]] - then - juju-log "Verifying $ip in dns " - exit - else - juju-log "Unable to fetch $ip changing dns order to original" - cp /etc/resolvconf/interface-order.orig /etc/resolvconf/interface-order - resolvconf -u -fi - -# Update Clearwater configuration and restart -$CHARM_DIR/lib/config_script programmable-multiple -$CHARM_DIR/lib/restart diff --git a/clearwater-bono/hooks/programmable-multiple-relation-departed b/clearwater-bono/hooks/programmable-multiple-relation-departed deleted file mode 100755 index fd94890..0000000 --- a/clearwater-bono/hooks/programmable-multiple-relation-departed +++ /dev/null @@ -1,11 +0,0 @@ -#!/bin/bash - -set -e - -INTR_ORDER_DIR="/etc/resolvconf" - -if [ -f "$INTR_ORDER_DIR/interface-order.orig" ]; then - cp $INTR_ORDER_DIR/interface-order.orig $INTR_ORDER_DIR/interface-order - resolvconf -u -fi - diff --git a/clearwater-bono/hooks/ralf-ctf-relation-changed b/clearwater-bono/hooks/ralf-ctf-relation-changed deleted file mode 100755 index 7b5f0de..0000000 --- a/clearwater-bono/hooks/ralf-ctf-relation-changed +++ /dev/null @@ -1,6 +0,0 @@ -#!/bin/bash -set -e - -# Update Clearwater configuration and restart -$CHARM_DIR/lib/config_script ralf-ctf -$CHARM_DIR/lib/restart diff --git a/clearwater-bono/hooks/start b/clearwater-bono/hooks/start deleted file mode 100755 index 9eceaf9..0000000 --- a/clearwater-bono/hooks/start +++ /dev/null @@ -1,15 +0,0 @@ -#!/bin/bash -# Here put anything that is needed to start the service. -# Note that currently this is run directly after install -# i.e. 'service apache2 start' -set -e - -# This hook needs to be idempotent, so this could be run when bono is -# already running, when it's running but not monitored by monit, when -# it's stopped but being monitored (so it's about to start), or stopped -# and unmonitored. To cover all these cases, restart bono, reload monit, -# then finally have monit monitor bono. Note that reloading monit can -# fail and restarting bono can fail because monit will restart it. -service bono restart || true -reload clearwater-monit &> /dev/null || true -monit monitor -g bono diff --git a/clearwater-bono/hooks/stop b/clearwater-bono/hooks/stop deleted file mode 100755 index 1500c68..0000000 --- a/clearwater-bono/hooks/stop +++ /dev/null @@ -1,12 +0,0 @@ -#!/bin/bash -# This will be run when the service is being torn down, allowing you to disable -# it in various ways.. -# For example, if your web app uses a text file to signal to the load balancer -# that it is live... you could remove it and sleep for a bit to allow the load -# balancer to stop sending traffic. -# rm /srv/webroot/server-live.txt && sleep 30 -set -e - -# Stop bono via monit -reload clearwater-monit &> /dev/null || true -monit stop -g bono diff --git a/clearwater-bono/hooks/ue-relation-changed b/clearwater-bono/hooks/ue-relation-changed deleted file mode 100755 index 768e3d5..0000000 --- a/clearwater-bono/hooks/ue-relation-changed +++ /dev/null @@ -1,7 +0,0 @@ -#!/bin/bash - -set -e -pub_key=$(relation-get ssh-key) -echo "$pub_key" >> /home/ubuntu/.ssh/authorized_keys -echo "$pub_key" - diff --git a/clearwater-bono/hooks/ue-relation-joined b/clearwater-bono/hooks/ue-relation-joined deleted file mode 100755 index fafefe7..0000000 --- a/clearwater-bono/hooks/ue-relation-joined +++ /dev/null @@ -1,5 +0,0 @@ -#!/bin/bash -set -e - -relation-set public-address=$(config-get zone) - diff --git a/clearwater-bono/hooks/upgrade-charm b/clearwater-bono/hooks/upgrade-charm deleted file mode 100755 index fdb1f86..0000000 --- a/clearwater-bono/hooks/upgrade-charm +++ /dev/null @@ -1,8 +0,0 @@ -#!/bin/bash -# This hook is executed each time a charm is upgraded after the new charm -# contents have been unpacked -# Best practice suggests you execute the hooks/install to ensure all updates are processed - -# hooks/config_change is triggered automatically -set -e - -$CHARM_DIR/hooks/install diff --git a/clearwater-bono/icon.svg b/clearwater-bono/icon.svg deleted file mode 100644 index f9ac92c..0000000 --- a/clearwater-bono/icon.svg +++ /dev/null @@ -1,407 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - - - - - - - - diff --git a/clearwater-bono/lib/chef_solo_install b/clearwater-bono/lib/chef_solo_install deleted file mode 100755 index 0833f3d..0000000 --- a/clearwater-bono/lib/chef_solo_install +++ /dev/null @@ -1,38 +0,0 @@ -#!/bin/bash - -# Configure git proxy -if [[ ! -z $http_proxy ]]; then - juju-log "Configuring git proxy..." - git config --global http.proxy $http_proxy -fi - -# This covers installing chef-solo and the Clearwater chef recipes -# Install chef and needed tools/libraries -apt-get -y install chef git libxml2-dev libxslt1-dev wget curl - -# Pull down the Clearwater chef recipes -if [ ! -d /home/ubuntu/chef-solo ] -then - juju-log "Cloning Clearwater/chef recipes..." - mkdir /home/ubuntu/chef-solo - git clone --recursive https://github.com/Metaswitch/chef.git /home/ubuntu/chef-solo -fi - -# Update the chef recipes -juju-log "Updating chef recipes..." -cd /home/ubuntu/chef-solo -git checkout release-110 -git submodule update --init - -# Create the solo.rb file -cat > /home/ubuntu/chef-solo/solo.rb </etc/clearwater/local_config </etc/clearwater/shared_config <. -# -# The author can be reached by email at clearwater@metaswitch.com or by -# post at Metaswitch Networks Ltd, 100 Church St, Enfield EN2 6BQ, UK -# -# Special Exception -# Metaswitch Networks Ltd grants you permission to copy, modify, -# propagate, and distribute a work formed by combining OpenSSL with The -# Software, or a work derivative of such a combination, even if such -# copying, modification, propagation, or distribution would otherwise -# violate the terms of the GPL. You must comply with the GPL in all -# respects for all of the code used other than OpenSSL. -# "OpenSSL" means OpenSSL toolkit software distributed by the OpenSSL -# Project and licensed under the OpenSSL Licenses, or a work based on such -# software and licensed under the OpenSSL Licenses. -# "OpenSSL Licenses" means the OpenSSL License and Original SSLeay License -# under which the OpenSSL Project distributes the OpenSSL toolkit software, -# as those licenses appear in the file LICENSE-OPENSSL. - -# This script generates a JSON-ified set of DNS records based on an IP, A -# records and SRV records. These are suitable for the API to -# https://github.com/chuckbutler/DNS-Charm. - -import sys -import json - -if len(sys.argv) < 4: - print "Usage: generate_dns_records.py IP CLUSTER_A_RECORD SPECIFIC_A_RECORD [SRV_PORT SRV_ADDR...]" - -ip = sys.argv[1] -cluster_a = sys.argv[2] -specific_a = sys.argv[3] -port = None -srv_addrs = [] - -ttl = "300" - -if len(sys.argv) > 5: - port = sys.argv[4] - srv_addrs = sys.argv[5:] - -ret = [{"rr":"A", "alias": cluster_a, "ttl": ttl,"addr": ip}, - {"rr":"A", "alias": specific_a, "ttl": ttl,"addr": ip}] - -for a in srv_addrs: - ret.append({"rr": "SRV", "alias": a, "ttl": ttl,"priority": "1", "weight": "1", "port": port, "target": specific_a}) - -print json.dumps(ret) diff --git a/clearwater-bono/lib/node_json_script b/clearwater-bono/lib/node_json_script deleted file mode 100755 index 7009aed..0000000 --- a/clearwater-bono/lib/node_json_script +++ /dev/null @@ -1,58 +0,0 @@ -#!/usr/bin/python - -import subprocess -import string -import socket -import os -import argparse - -# Create the ~/chef-solo/node.json file. -# -# The node.json file contains the machines IP address, the machine ID (a number) -# and the repo server. -# -# If the node is being clustered, the role 'clustered' is added, and after -# clustering the tag 'clustered' is added (for homer/homestead) -# -# The node.json file is templated in lib/node_json_template. - -parser = argparse.ArgumentParser() - -parser.add_argument('--cluster', action='store_true') -parser.add_argument('--tag', action='store_true') - -args = parser.parse_args() - -# Populate the dictionary -d = {} - -d['LOCAL_IP'] = subprocess.check_output(["unit-get", "private-address"]).rstrip() -d['MACHINE_ID'] = str(int(os.environ['JUJU_UNIT_NAME'].split('/')[1]) + 1) -d['REPO'] = subprocess.check_output(["config-get", "repo"]).rstrip() -d['TRUSTED_PEERS'] = ','.join(['"' + ip + '"' for ip in subprocess.check_output(["config-get", "trusted_peers"]).rstrip().split(',')]) - -node_file = "/home/ubuntu/chef-solo/node.json" -if args.cluster or (os.path.exists(node_file) and "clustered" in open(node_file).read()): - d['CLUSTER'] = ",\n \"role[clustered]\"" - d['CLUSTERED'] = ", \"clustered\"" - d['TAG'] = "" - - if args.tag or (os.path.exists(node_file) and "tags" in open(node_file).read()): - d['TAG'] = ",\n \"tags\": [\"clustered\"]" - -else: - d['CLUSTER'] = "" - d['CLUSTERED'] = "" - d['TAG'] = "" - -# Open template file -charm_dir = os.environ['CHARM_DIR'] -with open( '%s/lib/node_json_template' % charm_dir ) as f: - src = string.Template( f.read() ) - -# Make the substitutions -result = src.substitute(d) - -# Write to ~/chef-solo/node.json -with open('/home/ubuntu/chef-solo/node.json', 'w') as f: - f.write(result) diff --git a/clearwater-bono/lib/node_json_template b/clearwater-bono/lib/node_json_template deleted file mode 100755 index f9605d4..0000000 --- a/clearwater-bono/lib/node_json_template +++ /dev/null @@ -1,18 +0,0 @@ -{ - "id": "$LOCAL_IP", - "name": "$LOCAL_IP", - "chef_environment": "_default", - "role": "bono", - "roles": ["clearwater-infrastructure", "bono"$CLUSTERED], - "cloud": { - "local_ipv4": "$LOCAL_IP" - }, - "clearwater": { - "index": $MACHINE_ID, - "repo_servers": ["$REPO"], - "trusted_peers": [$TRUSTED_PEERS] - }, - "run_list": [ - "role[bono]"$CLUSTER - ]$TAG -} diff --git a/clearwater-bono/lib/restart b/clearwater-bono/lib/restart deleted file mode 100755 index 07662e1..0000000 --- a/clearwater-bono/lib/restart +++ /dev/null @@ -1,6 +0,0 @@ -#!/bin/bash -set -e - -# Restart clearwater-infrastructure, and stop bono (to be restarted by monit) -service clearwater-infrastructure restart -service bono stop diff --git a/clearwater-bono/metadata.yaml b/clearwater-bono/metadata.yaml deleted file mode 100644 index 21c0b5e..0000000 --- a/clearwater-bono/metadata.yaml +++ /dev/null @@ -1,18 +0,0 @@ -name: clearwater-bono -summary: Bono charm for Project Clearwater -maintainer: Project Clearwater Maintainers -description: Bono charm for Project Clearwater -tags: - - misc -subordinate: false -provides: - ue: - interface: 3GPP-Gm -requires: - cscf: - interface: 3GPP-Mw - ralf-ctf: - interface: ralf-ctf-interface - optional: true - programmable-multiple: - interface: dns-multi diff --git a/clearwater-bono/revision b/clearwater-bono/revision deleted file mode 100644 index d00491f..0000000 --- a/clearwater-bono/revision +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/clearwater-homestead/README.md b/clearwater-homestead/README.md deleted file mode 100644 index fca1380..0000000 --- a/clearwater-homestead/README.md +++ /dev/null @@ -1,88 +0,0 @@ -# Overview - -This charm supports deployment and scaling of the Homestead component of a Project Clearwater system. See http://www.projectclearwater.org for more information on Project Clearwater. - -# Usage - -The Homestead service should be deployed as part of a Clearwater system. A Clearwater system can be deployed in a Juju environment by creating a config.yaml file then running the following commands. - - juju deploy --config config.yaml clearwater-route53 - juju deploy --config config.yaml --constraints arch=amd64 clearwater-ellis - juju deploy --config config.yaml --constraints arch=amd64 clearwater-bono - juju deploy --config config.yaml --constraints arch=amd64 clearwater-sprout - juju deploy --config config.yaml --constraints arch=amd64 clearwater-homestead - juju deploy --config config.yaml --constraints arch=amd64 clearwater-homer - juju add-relation clearwater-ellis clearwater-route53:register-ellis - juju add-relation clearwater-bono clearwater-route53:register-bono - juju add-relation clearwater-sprout clearwater-route53:register-sprout - juju add-relation clearwater-homestead clearwater-route53:register-homestead - juju add-relation clearwater-homer clearwater-route53:register-homer - juju expose clearwater-bono - juju expose clearwater-ellis - -The config.yaml configuration file takes the following format. - - clearwater-route53: - zone: - access_key: - secret_key: - sas: "0.0.0.0" - - clearwater-ellis: - smtp_server: smtp.cw-ngv.com - smtp_username: username - smtp_password: password - email_sender: blackhole@cw-ngv.com - signup_key: secret - base_number: "6505550000" - number_count: 1000 - repo: http://repo.cw-ngv.com/juju-clearwater-2 - - clearwater-bono: - turn_workaround: password - repo: http://repo.cw-ngv.com/juju-clearwater-2 - - clearwater-sprout: - reg_min_expires: 400 - session_max_expires: 900 - repo: http://repo.cw-ngv.com/juju-clearwater-2 - - clearwater-homestead: - repo: http://repo.cw-ngv.com/juju-clearwater-2 - - clearwater-homer: - repo: http://repo.cw-ngv.com/juju-clearwater-2 - -Note that the clearwater-homestead charm can only be deployed on the `amd64` architecture. - -## Scale out Usage - -## Known Limitations and Issues - -The only currently supported DNS service for Clearwater is clearwater-route53 (when deploying in Amazon EC2), so will currently only work on EC2. Additional DNS services will be added for other environments in future. - -clearwater-homestead currently only supports running as a single unit. Clustering support will be released shortly, which will allow units to be added to and removed from a clearwater-homestead deployment using juju add-unit and juju remove-unit commands. - -# Configuration - -Clearwater has a number of configuration fields which are non-defaultable. These are as follows. - -clearwater-route53 - -- `zone:` This must be set to a DNS zone name which is managed by the AWS Route53 service. -- `access_key:` and `secret_key:` These must be set to the AWS access key and secret key of the AWS account which owns the DNS zone name. - -clearwater-ellis - -- `signup_key:` This is used as a signup key on the Ellis self-provisioning portal, so should be set to a unique string for each installation. -- `base_number:` and `number_count:` These define the telephone number range assigned to the Clearwater system. - -# Contact Information - -## Upstream Project Name - -See http:www.projectclearwater.org and https://github.com/Metaswitch/clearwater-docs/wiki for information about clearwater. - -Clearwater source code and issue list can be found at https://github.com/Metaswitch/. - -The Clearwater mailing list is at lists.projectclearwater.org. diff --git a/clearwater-homestead/config.yaml b/clearwater-homestead/config.yaml deleted file mode 100644 index 4033e5d..0000000 --- a/clearwater-homestead/config.yaml +++ /dev/null @@ -1,16 +0,0 @@ -options: - zone: - description: The DNS root zone for this service - type: string - sas: - default: "" - description: The location of the SAS server - type: string - target_latency_us: - default: 100000 - description: Target request latency for overload control (microseconds) - type: int - repo: - default: http://repo.cw-ngv.com/archive/repo110/ - description: The location of the repo server - type: string diff --git a/clearwater-homestead/copyright b/clearwater-homestead/copyright deleted file mode 100644 index 39f1ec8..0000000 --- a/clearwater-homestead/copyright +++ /dev/null @@ -1,31 +0,0 @@ -Project Clearwater - IMS in the Cloud -Copyright (C) 2016 Metaswitch Networks Ltd - -This program is free software: you can redistribute it and/or modify it -under the terms of the GNU General Public License as published by the -Free Software Foundation, either version 3 of the License, or (at your -option) any later version, along with the "Special Exception" for use of -the program along with SSL, set forth below. This program is distributed -in the hope that it will be useful, but WITHOUT ANY WARRANTY; -without even the implied warranty of MERCHANTABILITY or FITNESS FOR -A PARTICULAR PURPOSE. See the GNU General Public License for more -details. You should have received a copy of the GNU General Public -License along with this program. If not, see -. - -The author can be reached by email at clearwater@metaswitch.com or by -post at Metaswitch Networks Ltd, 100 Church St, Enfield EN2 6BQ, UK - -Special Exception -Metaswitch Networks Ltd grants you permission to copy, modify, -propagate, and distribute a work formed by combining OpenSSL with The -Software, or a work derivative of such a combination, even if such -copying, modification, propagation, or distribution would otherwise -violate the terms of the GPL. You must comply with the GPL in all -respects for all of the code used other than OpenSSL. -"OpenSSL" means OpenSSL toolkit software distributed by the OpenSSL -Project and licensed under the OpenSSL Licenses, or a work based on such -software and licensed under the OpenSSL Licenses. -"OpenSSL Licenses" means the OpenSSL License and Original SSLeay License -under which the OpenSSL Project distributes the OpenSSL toolkit software, -as those licenses appear in the file LICENSE-OPENSSL. diff --git a/clearwater-homestead/hooks/config-changed b/clearwater-homestead/hooks/config-changed deleted file mode 100755 index 04c042e..0000000 --- a/clearwater-homestead/hooks/config-changed +++ /dev/null @@ -1,12 +0,0 @@ -#!/bin/bash -set -e - - -# Update the /etc/clearwater/*_config files, the node.json, and re-run -# chef-solo -$CHARM_DIR/lib/config_script -$CHARM_DIR/lib/node_json_script -chef-solo -c /home/ubuntu/chef-solo/solo.rb -j /home/ubuntu/chef-solo/node.json -$CHARM_DIR/lib/restart - - diff --git a/clearwater-homestead/hooks/homestead-cscf-relation-joined b/clearwater-homestead/hooks/homestead-cscf-relation-joined deleted file mode 100755 index e645a93..0000000 --- a/clearwater-homestead/hooks/homestead-cscf-relation-joined +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/bash -set -e - -relation-set public-address=homestead.$(config-get zone) diff --git a/clearwater-homestead/hooks/homestead-prov-user-relation-joined b/clearwater-homestead/hooks/homestead-prov-user-relation-joined deleted file mode 100755 index e645a93..0000000 --- a/clearwater-homestead/hooks/homestead-prov-user-relation-joined +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/bash -set -e - -relation-set public-address=homestead.$(config-get zone) diff --git a/clearwater-homestead/hooks/homestead-relation-changed b/clearwater-homestead/hooks/homestead-relation-changed deleted file mode 100755 index 349f373..0000000 --- a/clearwater-homestead/hooks/homestead-relation-changed +++ /dev/null @@ -1,22 +0,0 @@ -#!/bin/bash -set -e - -# Get the IP address of this unit and the remote unit -peer_ip=$(relation-get private-address) -this_ip=$(unit-get private-address) - -# Get the machine number of this unit and the remote unit. This -# takes the form '/ -peer_index=$(echo $JUJU_REMOTE_UNIT | cut -d'/' -f2) -this_index=$(echo $JUJU_UNIT_NAME | cut -d'/' -f2) - -# Re-create the node.json files in the data bags -$CHARM_DIR/lib/node_json_clustered_script --ip "$peer_ip" --index "$peer_index" --cluster -$CHARM_DIR/lib/node_json_clustered_script --ip "$this_ip" --index "$this_index" --cluster - -# Re-create the node.json file and re-run chef sole -$CHARM_DIR/lib/node_json_script --cluster -chef-solo -c /home/ubuntu/chef-solo/solo.rb -j /home/ubuntu/chef-solo/node.json - -# Re-create the node.json with the tag. -$CHARM_DIR/lib/node_json_script --cluster --tag diff --git a/clearwater-homestead/hooks/homestead-relation-departed b/clearwater-homestead/hooks/homestead-relation-departed deleted file mode 100755 index 5a0fd4f..0000000 --- a/clearwater-homestead/hooks/homestead-relation-departed +++ /dev/null @@ -1,25 +0,0 @@ -#!/bin/bash -set -e - -# Get the machine number of this unit and the remote unit. This -# takes the form '/ -peer_index=$(echo $JUJU_REMOTE_UNIT | cut -d'/' -f2) -this_index=$(echo $JUJU_UNIT_NAME | cut -d'/' -f2) - -if [ $this_index -gt $peer_index ] - then - # Remove this node. This relation can be called more than once, so check - # whether the node has already been decommissioned. - decommissioned=$(nodetool netstats | grep DECOMMISSIONED > /dev/null; echo $?) - if [[ $decommissioned == 1* ]] - then - monit stop -g homestead - monit unmonitor -g cassandra - nodetool decommission - fi - else - # Remove the node.json in databags that corresponds to the node being removed. - file_name="/home/ubuntu/chef-solo/data_bags/node/"$peer_index".json" - rm $file_name - chef-solo -c /home/ubuntu/chef-solo/solo.rb -j /home/ubuntu/chef-solo/node.json -fi diff --git a/clearwater-homestead/hooks/homestead-relation-joined b/clearwater-homestead/hooks/homestead-relation-joined deleted file mode 100755 index 9b12e97..0000000 --- a/clearwater-homestead/hooks/homestead-relation-joined +++ /dev/null @@ -1,33 +0,0 @@ -#!/bin/bash -set -e - -# Get the IP address of this unit and the remote unit -peer_ip=$(relation-get private-address) -this_ip=$(unit-get private-address) - -# Get the machine number of this unit and the remote unit. This -# takes the form '/ -peer_index=$(echo $JUJU_REMOTE_UNIT | cut -d'/' -f2) -this_index=$(echo $JUJU_UNIT_NAME | cut -d'/' -f2) - -# Re-create the node.json files in the data bags. Mark the new -# node as joining - the new node is the one with the higher index -if [ $this_index -gt $peer_index ] - then - $CHARM_DIR/lib/node_json_clustered_script --ip "$peer_ip" --index "$peer_index" --cluster - $CHARM_DIR/lib/node_json_clustered_script --ip "$this_ip" --index "$this_index" --cluster --joining - else - $CHARM_DIR/lib/node_json_clustered_script --ip "$peer_ip" --index "$peer_index" --cluster --joining - $CHARM_DIR/lib/node_json_clustered_script --ip "$this_ip" --index "$this_index" --cluster -fi - -# Re-create the node.json file and re-run chef solo -$CHARM_DIR/lib/node_json_script --cluster -chef-solo -c /home/ubuntu/chef-solo/solo.rb -j /home/ubuntu/chef-solo/node.json - -# Re-create the node.json with the tag. Only do this for the existing node, not the -# joining node -if [ $this_index -lt $peer_index ] - then - $CHARM_DIR/lib/node_json_script --cluster --tag -fi diff --git a/clearwater-homestead/hooks/hss-relation-changed b/clearwater-homestead/hooks/hss-relation-changed deleted file mode 100755 index 82a2094..0000000 --- a/clearwater-homestead/hooks/hss-relation-changed +++ /dev/null @@ -1,13 +0,0 @@ -#!/bin/bash -set -e - -# Update Clearwater configuration and restart -$CHARM_DIR/lib/config_script hss -pub_key=$(relation-get ssh-key) -echo "$pub_key" >> ~/.ssh/authorized_keys - -sudo -u ubuntu bash << EOF -echo "$pub_key" >> ~/.ssh/authorized_keys -EOF - -$CHARM_DIR/lib/restart diff --git a/clearwater-homestead/hooks/hss-relation-departed b/clearwater-homestead/hooks/hss-relation-departed deleted file mode 100755 index fd00bc8..0000000 --- a/clearwater-homestead/hooks/hss-relation-departed +++ /dev/null @@ -1,6 +0,0 @@ -#!/bin/bash -set -e - -# Update Clearwater configuration and restart -$CHARM_DIR/lib/config_script hss -$CHARM_DIR/lib/restart diff --git a/clearwater-homestead/hooks/install b/clearwater-homestead/hooks/install deleted file mode 100755 index 8818ffe..0000000 --- a/clearwater-homestead/hooks/install +++ /dev/null @@ -1,40 +0,0 @@ -#!/bin/bash -set -e - -if ! grep "# added by clearwater-*/hooks/install" /etc/hosts ; then - private_ip=$(unit-get private-address) - echo "$private_ip homestead-$(cut -d/ -f2 <<< $JUJU_UNIT_NAME).$(config-get zone) # added by clearwater-*/hooks/install" >>/etc/hosts -fi - -# Install chef solo -$CHARM_DIR/lib/chef_solo_install - -this_ip=$(unit-get private-address) -this_index=$(echo $JUJU_UNIT_NAME | cut -d'/' -f2) -$CHARM_DIR/lib/node_json_clustered_script --ip "$this_ip" --index "$this_index" -$CHARM_DIR/lib/node_json_script - -# Update the config file -$CHARM_DIR/lib/config_script - -apt-get update - -# After we install dnsmasq, DNS might drop for a few seconds. Install it ahead of time -# and wait for DNS to recover. -apt-get -q -y --force-yes install dnsmasq -iterations=0 -while [ $(dig +short security.ubuntu.com | wc -l) == 0 ] && [ $iterations -lt 60 ] ; do - echo "Waiting for DNS to security.ubuntu.com to recover ($iterations/60)..." - sleep 1 - iterations=$((iterations + 1)) -done - -# Install the node -chef-solo -c /home/ubuntu/chef-solo/solo.rb -j /home/ubuntu/chef-solo/node.json - -pip install service_identity - -# Expose the correct ports -open-port 22/tcp -open-port 123/udp -open-port 161/udp diff --git a/clearwater-homestead/hooks/programmable-multiple-relation-changed b/clearwater-homestead/hooks/programmable-multiple-relation-changed deleted file mode 100755 index 55fdb8a..0000000 --- a/clearwater-homestead/hooks/programmable-multiple-relation-changed +++ /dev/null @@ -1,104 +0,0 @@ -#!/bin/bash -set -e -juju-log "Invoking dns-relation" - -dns_ip=$(relation-get private-address) -if [ -z "$dns_ip" ]; then - juju-log "No data sent yet from DNS" - exit 0 -fi - -# Set our DNS requirements -id=$(cut -d/ -f2 <<< $JUJU_UNIT_NAME) -ip=$(unit-get private-address) -relation-set domain=$(config-get zone) -DNS_JSON=$($CHARM_DIR/lib/generate_dns_records $ip homestead homestead-$id) -relation-set resources="$DNS_JSON" - -# Update our DNS server -if [ "$dns_ip" != "" ] -then - echo nameserver $dns_ip > /etc/dnsmasq.resolv.conf - grep -v ^RESOLV_CONF= /etc/default/dnsmasq > /tmp/dnsmasq.$$ - mv /tmp/dnsmasq.$$ /etc/default/dnsmasq - echo RESOLV_CONF=/etc/dnsmasq.resolv.conf >> /etc/default/dnsmasq - service dnsmasq restart -fi - -# Change Resolvconf order and make dnsmasq on top -dnsmasq=lo.dnsmasq -resolvconf=/etc/resolvconf -loinet=`grep ^[a-z] /etc/resolvconf/interface-order | head -n1` - if [ $loinet != $dnsmasq ] - then - cp /etc/resolvconf/interface-order /etc/resolvconf/interface-order.orig - sed '/'$dnsmasq'/d' /etc/resolvconf/interface-order.orig > /etc/resolvconf/interface-order - sed -i '/'$loinet'/i lo.dnsmasq' /etc/resolvconf/interface-order - service dnsmasq restart - resolvconf -u - fi - -# Verify DNS record to make sure that IMS dns is working properly -if [[ `dig +short $ip` =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]] - then - juju-log "Verifying $ip in dns " - elif [[ "$ip" =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]] - then - juju-log "Verifying $ip in dns " - else - juju-log "Unable to fetch $ip changing dns order to original" - cp /etc/resolvconf/interface-order.orig /etc/resolvconf/interface-order - resolvconf -u -fi - -# Update Clearwater configuration and restart -$CHARM_DIR/lib/config_script programmable-multiple - -cassandra_util=/usr/share/clearwater -iter_start=0 -loop_count=20 -cassandra_change(){ - sed '71,75s/^/ #/' $cassandra_util/cassandra_schema_utils.sh.orig > $cassandra_util/cassandra_schema_utils.sh - sudo /usr/share/clearwater/cassandra-schemas/homestead_cache.sh - sudo /usr/share/clearwater/cassandra-schemas/homestead_provisioning.sh - $CHARM_DIR/lib/restart -} - - -while [ $iter_start -lt $loop_count ] -do - if monit status homestead_process | grep "\Execution failed | Does not exist\b" && monit status homestead-prov_process | grep "\Execution failed | Does not exist\b" ; then - break - elif monit status homestead_process | grep "\Running\b" && monit status homestead-prov_process | grep "\Running\b" ; then - break - else - echo "Waiting for monit to be intialized" - sleep 10 - fi - iter_start=`expr $iter_start + 1` -done - - - if monit status homestead_process | grep "\Running\b" && monit status homestead-prov_process | grep "\Running\b" ; - then - echo juju-log "homestead is running" - else - if ! -f "$cassandra_util/cassandra_schema_utils.sh.orig" ; - then - cp $cassandra_util/cassandra_schema_utils.sh $cassandra_util/cassandra_schema_utils.sh.orig - sed '71,75s/^/ #/' $cassandra_util/cassandra_schema_utils.sh.orig > $cassandra_util/cassandra_schema_utils.sh - /usr/share/clearwater/cassandra-schemas/homestead_cache.sh - /usr/share/clearwater/cassandra-schemas/homestead_provisioning.sh - service homestead stop - service homestead-prov stop - else - sed '71,75s/^/ #/' $cassandra_util/cassandra_schema_utils.sh.orig > $cassandra_util/cassandra_schema_utils.sh - /usr/share/clearwater/cassandra-schemas/homestead_cache.sh - /usr/share/clearwater/cassandra-schemas/homestead_provisioning.sh - service homestead stop - service homestead-prov stop - fi - fi - - -$CHARM_DIR/lib/restart diff --git a/clearwater-homestead/hooks/programmable-multiple-relation-departed b/clearwater-homestead/hooks/programmable-multiple-relation-departed deleted file mode 100755 index fd94890..0000000 --- a/clearwater-homestead/hooks/programmable-multiple-relation-departed +++ /dev/null @@ -1,11 +0,0 @@ -#!/bin/bash - -set -e - -INTR_ORDER_DIR="/etc/resolvconf" - -if [ -f "$INTR_ORDER_DIR/interface-order.orig" ]; then - cp $INTR_ORDER_DIR/interface-order.orig $INTR_ORDER_DIR/interface-order - resolvconf -u -fi - diff --git a/clearwater-homestead/hooks/ssh-abot-volte-homestead-relation-changed b/clearwater-homestead/hooks/ssh-abot-volte-homestead-relation-changed deleted file mode 100755 index c7f5365..0000000 --- a/clearwater-homestead/hooks/ssh-abot-volte-homestead-relation-changed +++ /dev/null @@ -1,10 +0,0 @@ -#!/bin/bash -set -e - -pub_key=$(relation-get ssh-key-abotvolte) -echo "$pub_key" >> ~/.ssh/authorized_keys - -sudo -u ubuntu bash << EOF -echo "$pub_key" >> ~/.ssh/authorized_keys -EOF - diff --git a/clearwater-homestead/hooks/start b/clearwater-homestead/hooks/start deleted file mode 100755 index 3dcda91..0000000 --- a/clearwater-homestead/hooks/start +++ /dev/null @@ -1,20 +0,0 @@ -#!/bin/bash -# Here put anything that is needed to start the service. -# Note that currently this is run directly after install -# i.e. 'service apache2 start' -set -e - - -# This hook needs to be idempotent, so this could be run when homestead is -# already running, when it's running but not monitored by monit, when -# it's stopped but being monitored (so it's about to start), or stopped -# and unmonitored. To cover all these cases, restart homestead, reload monit, -# then finally have monit monitor homestead. Note that restarting monit can -# fail and restarting homestead can fail because monit will restart it. -service homestead restart || /usr/bin/true -service homestead-prov restart || /usr/bin/true -reload clearwater-monit &> /dev/null || true -monit monitor -g homestead -monit monitor -g homestead-prov - - diff --git a/clearwater-homestead/hooks/stop b/clearwater-homestead/hooks/stop deleted file mode 100755 index 871559d..0000000 --- a/clearwater-homestead/hooks/stop +++ /dev/null @@ -1,13 +0,0 @@ -#!/bin/bash -# This will be run when the service is being torn down, allowing you to disable -# it in various ways.. -# For example, if your web app uses a text file to signal to the load balancer -# that it is live... you could remove it and sleep for a bit to allow the load -# balancer to stop sending traffic. -# rm /srv/webroot/server-live.txt && sleep 30 -set -e - -# Stop homestead via monit -reload clearwater-monit &> /dev/null || true -monit stop -g homestead -monit stop -g homestead-prov diff --git a/clearwater-homestead/hooks/upgrade-charm b/clearwater-homestead/hooks/upgrade-charm deleted file mode 100755 index 50bffee..0000000 --- a/clearwater-homestead/hooks/upgrade-charm +++ /dev/null @@ -1,9 +0,0 @@ -#!/bin/bash -# This hook is executed each time a charm is upgraded after the new charm -# contents have been unpacked -# Best practice suggests you execute the hooks/install and -# hooks/config-changed to ensure all updates are processed -set -e - -$CHARM_DIR/hooks/install -$CHARM_DIR/hooks/config-changed diff --git a/clearwater-homestead/icon.svg b/clearwater-homestead/icon.svg deleted file mode 100644 index f9ac92c..0000000 --- a/clearwater-homestead/icon.svg +++ /dev/null @@ -1,407 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - - - - - - - - diff --git a/clearwater-homestead/lib/chef_solo_install b/clearwater-homestead/lib/chef_solo_install deleted file mode 100755 index 0833f3d..0000000 --- a/clearwater-homestead/lib/chef_solo_install +++ /dev/null @@ -1,38 +0,0 @@ -#!/bin/bash - -# Configure git proxy -if [[ ! -z $http_proxy ]]; then - juju-log "Configuring git proxy..." - git config --global http.proxy $http_proxy -fi - -# This covers installing chef-solo and the Clearwater chef recipes -# Install chef and needed tools/libraries -apt-get -y install chef git libxml2-dev libxslt1-dev wget curl - -# Pull down the Clearwater chef recipes -if [ ! -d /home/ubuntu/chef-solo ] -then - juju-log "Cloning Clearwater/chef recipes..." - mkdir /home/ubuntu/chef-solo - git clone --recursive https://github.com/Metaswitch/chef.git /home/ubuntu/chef-solo -fi - -# Update the chef recipes -juju-log "Updating chef recipes..." -cd /home/ubuntu/chef-solo -git checkout release-110 -git submodule update --init - -# Create the solo.rb file -cat > /home/ubuntu/chef-solo/solo.rb </etc/clearwater/local_config </etc/clearwater/shared_config <. -# -# The author can be reached by email at clearwater@metaswitch.com or by -# post at Metaswitch Networks Ltd, 100 Church St, Enfield EN2 6BQ, UK -# -# Special Exception -# Metaswitch Networks Ltd grants you permission to copy, modify, -# propagate, and distribute a work formed by combining OpenSSL with The -# Software, or a work derivative of such a combination, even if such -# copying, modification, propagation, or distribution would otherwise -# violate the terms of the GPL. You must comply with the GPL in all -# respects for all of the code used other than OpenSSL. -# "OpenSSL" means OpenSSL toolkit software distributed by the OpenSSL -# Project and licensed under the OpenSSL Licenses, or a work based on such -# software and licensed under the OpenSSL Licenses. -# "OpenSSL Licenses" means the OpenSSL License and Original SSLeay License -# under which the OpenSSL Project distributes the OpenSSL toolkit software, -# as those licenses appear in the file LICENSE-OPENSSL. - -# This script generates a JSON-ified set of DNS records based on an IP, A -# records and SRV records. These are suitable for the API to -# https://github.com/chuckbutler/DNS-Charm. - -import sys -import json - -if len(sys.argv) < 4: - print "Usage: generate_dns_records.py IP CLUSTER_A_RECORD SPECIFIC_A_RECORD [SRV_PORT SRV_ADDR...]" - -ip = sys.argv[1] -cluster_a = sys.argv[2] -specific_a = sys.argv[3] -port = None -srv_addrs = [] - -ttl = "300" - -if len(sys.argv) > 5: - port = sys.argv[4] - srv_addrs = sys.argv[5:] - -ret = [{"rr":"A", "alias": cluster_a, "ttl": ttl,"addr": ip}, - {"rr":"A", "alias": specific_a, "ttl": ttl,"addr": ip}] - -for a in srv_addrs: - ret.append({"rr": "SRV", "alias": a, "ttl": ttl,"priority": "1", "weight": "1", "port": port, "target": specific_a}) - -print json.dumps(ret) diff --git a/clearwater-homestead/lib/node_json_clustered_script b/clearwater-homestead/lib/node_json_clustered_script deleted file mode 100755 index f063bd9..0000000 --- a/clearwater-homestead/lib/node_json_clustered_script +++ /dev/null @@ -1,58 +0,0 @@ -#!/usr/bin/python -import subprocess -import string -import socket -import os -import sys -import argparse - -# Create the ~/chef-solo/data_bags/node/.json files. -# -# The node.json file contains the machines IP address, the machine ID (a number) -# and the repo server. -# -# If the node is being clustered, the role 'clustered' is added, and if the -# node is joinging the attribute 'joining' is added -# -# The node.json file is templated in lib/node_json_clustered_template. - -parser = argparse.ArgumentParser() - -parser.add_argument('--cluster', action='store_true') -parser.add_argument('--joining', action='store_true') -parser.add_argument('--index', type=int, required=True) -parser.add_argument('--ip', type=str, required=True) - -args = parser.parse_args() - -# Populate the dictionary -d = {} - -d['LOCAL_IP'] = socket.gethostbyname(args.ip) -d['MACHINE_ID'] = str(args.index + 1) -d['REPO'] = subprocess.check_output(["config-get", "repo"]).rstrip() - -if args.cluster: - d['CLUSTER'] = ",\n \"role[clustered]\"" - d['CLUSTERED'] = ", \"clustered\"" -else: - d['CLUSTER'] = "" - d['CLUSTERED'] = "" - -if args.joining: - d['JOINING'] = ",\n \"joining\": true" -else: - d['JOINING'] = "" - -# Open template file -charm_dir = os.environ['CHARM_DIR'] -with open( '%s/lib/node_json_clustered_template' % charm_dir ) as f: - src = string.Template( f.read() ) - -# Make the substitutions -result = src.substitute(d) - -# Write to ~/chef-solo/data_bags/node/.json -node_file = str(args.index + 1) + ".json" -with open('/home/ubuntu/chef-solo/data_bags/node/%s' % node_file, 'w') as f: - f.write(result) diff --git a/clearwater-homestead/lib/node_json_clustered_template b/clearwater-homestead/lib/node_json_clustered_template deleted file mode 100755 index cd58203..0000000 --- a/clearwater-homestead/lib/node_json_clustered_template +++ /dev/null @@ -1,20 +0,0 @@ -{ - "id": "$LOCAL_IP", - "name": "$LOCAL_IP", - "chef_environment": "_default", - "json_class": "Chef::Node", - "automatic": { - "role": "homestead", - "roles": ["clearwater-infrastructure", "homestead"$CLUSTERED], - "cloud": { - "local_ipv4": "$LOCAL_IP" - }, - "clearwater": { - "index": $MACHINE_ID$JOINING - } - }, - "chef_type": "node", - "run_list": [ - "role[homestead]"$CLUSTER - ] -} diff --git a/clearwater-homestead/lib/node_json_script b/clearwater-homestead/lib/node_json_script deleted file mode 100755 index 4be4214..0000000 --- a/clearwater-homestead/lib/node_json_script +++ /dev/null @@ -1,57 +0,0 @@ -#!/usr/bin/python - -import subprocess -import string -import socket -import os -import argparse - -# Create the ~/chef-solo/node.json file. -# -# The node.json file contains the machines IP address, the machine ID (a number) -# and the repo server. -# -# If the node is being clustered, the role 'clustered' is added, and after -# clustering the tag 'clustered' is added (for homer/homestead) -# -# The node.json file is templated in lib/node_json_template. - -parser = argparse.ArgumentParser() - -parser.add_argument('--cluster', action='store_true') -parser.add_argument('--tag', action='store_true') - -args = parser.parse_args() - -# Populate the dictionary -d = {} - -d['LOCAL_IP'] = subprocess.check_output(["unit-get", "private-address"]).rstrip() -d['MACHINE_ID'] = str(int(os.environ['JUJU_UNIT_NAME'].split('/')[1]) + 1) -d['REPO'] = subprocess.check_output(["config-get", "repo"]).rstrip() - -node_file = "/home/ubuntu/chef-solo/node.json" -if args.cluster or (os.path.exists(node_file) and "clustered" in open(node_file).read()): - d['CLUSTER'] = ",\n \"role[clustered]\"" - d['CLUSTERED'] = ", \"clustered\"" - d['TAG'] = "" - - if args.tag or (os.path.exists(node_file) and "tags" in open(node_file).read()): - d['TAG'] = ",\n \"tags\": [\"clustered\"]" - -else: - d['CLUSTER'] = "" - d['CLUSTERED'] = "" - d['TAG'] = "" - -# Open template file -charm_dir = os.environ['CHARM_DIR'] -with open( '%s/lib/node_json_template' % charm_dir ) as f: - src = string.Template( f.read() ) - -# Make the substitutions -result = src.substitute(d) - -# Write to ~/chef-solo/node.json -with open('/home/ubuntu/chef-solo/node.json', 'w') as f: - f.write(result) diff --git a/clearwater-homestead/lib/node_json_template b/clearwater-homestead/lib/node_json_template deleted file mode 100755 index a697c70..0000000 --- a/clearwater-homestead/lib/node_json_template +++ /dev/null @@ -1,17 +0,0 @@ -{ - "id": "$LOCAL_IP", - "name": "$LOCAL_IP", - "chef_environment": "_default", - "role": "homestead", - "roles": ["clearwater-infrastructure", "homestead"$CLUSTERED], - "cloud": { - "local_ipv4": "$LOCAL_IP" - }, - "clearwater": { - "index": $MACHINE_ID, - "repo_servers": ["$REPO"] - }, - "run_list": [ - "role[homestead]"$CLUSTER - ]$TAG -} diff --git a/clearwater-homestead/lib/restart b/clearwater-homestead/lib/restart deleted file mode 100755 index ddbba12..0000000 --- a/clearwater-homestead/lib/restart +++ /dev/null @@ -1,7 +0,0 @@ -#!/bin/bash -set -e - -# Restart clearwater-infrastructure, and stop homestead/homestead-prov (to be restarted by monit) -service clearwater-infrastructure restart -service homestead stop -service homestead-prov stop diff --git a/clearwater-homestead/metadata.yaml b/clearwater-homestead/metadata.yaml deleted file mode 100644 index a0f529a..0000000 --- a/clearwater-homestead/metadata.yaml +++ /dev/null @@ -1,23 +0,0 @@ -name: clearwater-homestead -summary: Homestead charm for Project Clearwater -maintainer: Project Clearwater Maintainers -description: Homestead charm for Project Clearwater -categories: - - misc -subordinate: false -requires: - hss: - interface: 3GPP-Cx - optional: true - programmable-multiple: - interface: dns-multi - ssh-abot-volte-homestead: - interface: ssh -provides: - homestead-cscf: - interface: homestead-hss-interface - homestead-prov-user: - interface: homestead-prov-interface -peers: - homestead: - interface: homestead-interface diff --git a/clearwater-homestead/revision b/clearwater-homestead/revision deleted file mode 100644 index d00491f..0000000 --- a/clearwater-homestead/revision +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/clearwater-sprout/README.md b/clearwater-sprout/README.md deleted file mode 100644 index e2b4ca0..0000000 --- a/clearwater-sprout/README.md +++ /dev/null @@ -1,88 +0,0 @@ -# Overview - -This charm supports deployment and scaling of the Sprout component of a Project Clearwater system. See http://www.projectclearwater.org for more information on Project Clearwater. - -# Usage - -The Sprout service should be deployed as part of a Clearwater system. A Clearwater system can be deployed in a Juju environment by creating a config.yaml file then running the following commands. - - juju deploy --config config.yaml clearwater-route53 - juju deploy --config config.yaml --constraints arch=amd64 clearwater-ellis - juju deploy --config config.yaml --constraints arch=amd64 clearwater-bono - juju deploy --config config.yaml --constraints arch=amd64 clearwater-sprout - juju deploy --config config.yaml --constraints arch=amd64 clearwater-homestead - juju deploy --config config.yaml --constraints arch=amd64 clearwater-homer - juju add-relation clearwater-ellis clearwater-route53:register-ellis - juju add-relation clearwater-bono clearwater-route53:register-bono - juju add-relation clearwater-sprout clearwater-route53:register-sprout - juju add-relation clearwater-homestead clearwater-route53:register-homestead - juju add-relation clearwater-homer clearwater-route53:register-homer - juju expose clearwater-bono - juju expose clearwater-ellis - -The config.yaml configuration file takes the following format. - - clearwater-route53: - zone: - access_key: - secret_key: - sas: "0.0.0.0" - - clearwater-ellis: - smtp_server: smtp.cw-ngv.com - smtp_username: username - smtp_password: password - email_sender: blackhole@cw-ngv.com - signup_key: secret - base_number: "6505550000" - number_count: 1000 - repo: http://repo.cw-ngv.com/juju-clearwater-2 - - clearwater-bono: - turn_workaround: password - repo: http://repo.cw-ngv.com/juju-clearwater-2 - - clearwater-sprout: - reg_min_expires: 400 - session_max_expires: 900 - repo: http://repo.cw-ngv.com/juju-clearwater-2 - - clearwater-homestead: - repo: http://repo.cw-ngv.com/juju-clearwater-2 - - clearwater-homer: - repo: http://repo.cw-ngv.com/juju-clearwater-2 - -Note that the clearwater-sprout charm can only be deployed on the `amd64` architecture. - -## Scale out Usage - -## Known Limitations and Issues - -The only currently supported DNS service for Clearwater is clearwater-route53 (when deploying in Amazon EC2), so will currently only work on EC2. Additional DNS services will be added for other environments in future. - -clearwater-sprout currently only supports running as a single unit. Clustering support will be released shortly, which will allow units to be added to and removed from a clearwater-sprout deployment using juju add-unit and juju remove-unit commands. - -# Configuration - -Clearwater has a number of configuration fields which are non-defaultable. These are as follows. - -clearwater-route53 - -- `zone:` This must be set to a DNS zone name which is managed by the AWS Route53 service. -- `access_key:` and `secret_key:` These must be set to the AWS access key and secret key of the AWS account which owns the DNS zone name. - -clearwater-ellis - -- `signup_key:` This is used as a signup key on the Ellis self-provisioning portal, so should be set to a unique string for each installation. -- `base_number:` and `number_count:` These define the telephone number range assigned to the Clearwater system. - -# Contact Information - -## Upstream Project Name - -See http:www.projectclearwater.org and https://github.com/Metaswitch/clearwater-docs/wiki for information about clearwater. - -Clearwater source code and issue list can be found at https://github.com/Metaswitch/. - -The Clearwater mailing list is at lists.projectclearwater.org. diff --git a/clearwater-sprout/config.yaml b/clearwater-sprout/config.yaml deleted file mode 100644 index 5c31de8..0000000 --- a/clearwater-sprout/config.yaml +++ /dev/null @@ -1,28 +0,0 @@ -options: - zone: - description: The DNS root zone for this service - type: string - sas: - default: "" - description: The location of the SAS server - type: string - enum: - default: "" - description: The location of the ENUM server - type: string - reg_max_expires: - default: 300 - description: Maximum registration expiry time (sec) - type: int - session_max_expires: - default: 600 - description: Maximum session expiry time (sec) - type: int - target_latency_us: - default: 100000 - description: Target request latency for overload control (microseconds) - type: int - repo: - default: http://repo.cw-ngv.com/archive/repo110/ - description: The location of the repo server - type: string diff --git a/clearwater-sprout/copyright b/clearwater-sprout/copyright deleted file mode 100644 index 39f1ec8..0000000 --- a/clearwater-sprout/copyright +++ /dev/null @@ -1,31 +0,0 @@ -Project Clearwater - IMS in the Cloud -Copyright (C) 2016 Metaswitch Networks Ltd - -This program is free software: you can redistribute it and/or modify it -under the terms of the GNU General Public License as published by the -Free Software Foundation, either version 3 of the License, or (at your -option) any later version, along with the "Special Exception" for use of -the program along with SSL, set forth below. This program is distributed -in the hope that it will be useful, but WITHOUT ANY WARRANTY; -without even the implied warranty of MERCHANTABILITY or FITNESS FOR -A PARTICULAR PURPOSE. See the GNU General Public License for more -details. You should have received a copy of the GNU General Public -License along with this program. If not, see -. - -The author can be reached by email at clearwater@metaswitch.com or by -post at Metaswitch Networks Ltd, 100 Church St, Enfield EN2 6BQ, UK - -Special Exception -Metaswitch Networks Ltd grants you permission to copy, modify, -propagate, and distribute a work formed by combining OpenSSL with The -Software, or a work derivative of such a combination, even if such -copying, modification, propagation, or distribution would otherwise -violate the terms of the GPL. You must comply with the GPL in all -respects for all of the code used other than OpenSSL. -"OpenSSL" means OpenSSL toolkit software distributed by the OpenSSL -Project and licensed under the OpenSSL Licenses, or a work based on such -software and licensed under the OpenSSL Licenses. -"OpenSSL Licenses" means the OpenSSL License and Original SSLeay License -under which the OpenSSL Project distributes the OpenSSL toolkit software, -as those licenses appear in the file LICENSE-OPENSSL. diff --git a/clearwater-sprout/hooks/as-relation-joined b/clearwater-sprout/hooks/as-relation-joined deleted file mode 100755 index 2fcce72..0000000 --- a/clearwater-sprout/hooks/as-relation-joined +++ /dev/null @@ -1,8 +0,0 @@ -#!/bin/bash -set -e - -# Set the cscf-uri on the relation back to the AS. -scscf=5054 -. /etc/clearwater/config -[ -n "$scscf_uri" ] || scscf_uri=sip:$sprout_hostname:$scscf -relation-set cscf-uri=$scscf_uri diff --git a/clearwater-sprout/hooks/config-changed b/clearwater-sprout/hooks/config-changed deleted file mode 100755 index c658148..0000000 --- a/clearwater-sprout/hooks/config-changed +++ /dev/null @@ -1,9 +0,0 @@ -#!/bin/bash -set -e - -# Update the /etc/clearwater/*_config files, the node.json, and re-run -# chef-solo -$CHARM_DIR/lib/config_script -$CHARM_DIR/lib/node_json_script -chef-solo -c /home/ubuntu/chef-solo/solo.rb -j /home/ubuntu/chef-solo/node.json -$CHARM_DIR/lib/restart diff --git a/clearwater-sprout/hooks/homestead-hss-relation-changed b/clearwater-sprout/hooks/homestead-hss-relation-changed deleted file mode 100755 index cdb12ff..0000000 --- a/clearwater-sprout/hooks/homestead-hss-relation-changed +++ /dev/null @@ -1,6 +0,0 @@ -#!/bin/bash -set -e - -# Update Clearwater configuration and restart -$CHARM_DIR/lib/config_script homestead-hss -$CHARM_DIR/lib/restart diff --git a/clearwater-sprout/hooks/install b/clearwater-sprout/hooks/install deleted file mode 100755 index e749acc..0000000 --- a/clearwater-sprout/hooks/install +++ /dev/null @@ -1,38 +0,0 @@ -#!/bin/bash -set -e - -if ! grep "# added by clearwater-*/hooks/install" /etc/hosts ; then - private_ip=$(unit-get private-address) - echo "$private_ip sprout-$(cut -d/ -f2 <<< $JUJU_UNIT_NAME).$(config-get zone) # added by clearwater-*/hooks/install" >>/etc/hosts -fi - -# Install chef solo -$CHARM_DIR/lib/chef_solo_install - -this_ip=$(unit-get private-address) -this_index=$(echo $JUJU_UNIT_NAME | cut -d'/' -f2) -$CHARM_DIR/lib/node_json_clustered_script --ip "$this_ip" --index "$this_index" -$CHARM_DIR/lib/node_json_script - -# Update the config file -$CHARM_DIR/lib/config_script - -apt-get update - -# After we install dnsmasq, DNS might drop for a few seconds. Install it ahead of time -# and wait for DNS to recover. -apt-get -q -y --force-yes install dnsmasq -iterations=0 -while [ $(dig +short security.ubuntu.com | wc -l) == 0 ] && [ $iterations -lt 60 ] ; do - echo "Waiting for DNS to security.ubuntu.com to recover ($iterations/60)..." - sleep 1 - iterations=$((iterations + 1)) -done - -# Install the node -chef-solo -c /home/ubuntu/chef-solo/solo.rb -j /home/ubuntu/chef-solo/node.json - -# Expose the correct ports -open-port 22/tcp -open-port 123/udp -open-port 161/udp diff --git a/clearwater-sprout/hooks/pcscf-relation-joined b/clearwater-sprout/hooks/pcscf-relation-joined deleted file mode 100755 index d282531..0000000 --- a/clearwater-sprout/hooks/pcscf-relation-joined +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/bash -set -e - -relation-set public-address=sprout.$(config-get zone) diff --git a/clearwater-sprout/hooks/programmable-multiple-relation-changed b/clearwater-sprout/hooks/programmable-multiple-relation-changed deleted file mode 100755 index 3e83972..0000000 --- a/clearwater-sprout/hooks/programmable-multiple-relation-changed +++ /dev/null @@ -1,70 +0,0 @@ -#!/bin/bash -set -e -juju-log "Invoking dns-relation" - -dns_ip=$(relation-get private-address) -if [ -z "$dns_ip" ]; then - juju-log "No data sent yet from DNS" - exit 0 -fi -# Set our DNS requirements -id=$(cut -d/ -f2 <<< $JUJU_UNIT_NAME) -ip=$(unit-get private-address) -relation-set domain=$(config-get zone) -DNS_JSON=$($CHARM_DIR/lib/generate_dns_records $ip sprout sprout-$id icscf.sprout scscf.sprout 5054 _sip._tcp.sprout) -juju-log "DNS_JSON=$DNS_JSON" -relation-set resources="$DNS_JSON" - -sleep 1 - -#DNS_JSON_1=$($CHARM_DIR/lib/generate_dns_records $ip icscf.sprout scscf.sprout) -#juju-log "DNS_JSON_1=$DNS_JSON_1" -#relation-set resources="$DNS_JSON_1" - -#Wait for a couple of seconds -#sleep 1 - -# Update our DNS server -if [ "$dns_ip" != "" ] -then - echo nameserver $dns_ip > /etc/dnsmasq.resolv.conf - grep -v ^RESOLV_CONF= /etc/default/dnsmasq > /tmp/dnsmasq.$$ - mv /tmp/dnsmasq.$$ /etc/default/dnsmasq - echo RESOLV_CONF=/etc/dnsmasq.resolv.conf >> /etc/default/dnsmasq - service dnsmasq restart -fi - -# Change Resolvconf order and make dnsmasq on top -dnsmasq=lo.dnsmasq -resolvconf=/etc/resolvconf -loinet=`grep ^[a-z] /etc/resolvconf/interface-order | head -n1` - if [ $loinet != $dnsmasq ] - then - cp /etc/resolvconf/interface-order /etc/resolvconf/interface-order.orig - sed '/'$dnsmasq'/d' /etc/resolvconf/interface-order.orig > /etc/resolvconf/interface-order - sed -i '/'$loinet'/i lo.dnsmasq' /etc/resolvconf/interface-order - service dnsmasq restart - resolvconf -u - else - exit 0 - fi - -# Verify DNS record to make sure that IMS dns is working properly -if [[ `dig +short $ip` =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]] - then - juju-log "Verifying $ip in dns " - exit - elif [[ "$ip" =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]] - then - juju-log "Verifying $ip in dns " - exit - else - juju-log "Unable to fetch $ip changing dns order to original" - cp /etc/resolvconf/interface-order.orig /etc/resolvconf/interface-order - resolvconf -u -fi - - -# Update Clearwater configuration and restart -$CHARM_DIR/lib/config_script programmable-multiple -$CHARM_DIR/lib/restart diff --git a/clearwater-sprout/hooks/programmable-multiple-relation-departed b/clearwater-sprout/hooks/programmable-multiple-relation-departed deleted file mode 100755 index fd94890..0000000 --- a/clearwater-sprout/hooks/programmable-multiple-relation-departed +++ /dev/null @@ -1,11 +0,0 @@ -#!/bin/bash - -set -e - -INTR_ORDER_DIR="/etc/resolvconf" - -if [ -f "$INTR_ORDER_DIR/interface-order.orig" ]; then - cp $INTR_ORDER_DIR/interface-order.orig $INTR_ORDER_DIR/interface-order - resolvconf -u -fi - diff --git a/clearwater-sprout/hooks/ralf-ctf-relation-changed b/clearwater-sprout/hooks/ralf-ctf-relation-changed deleted file mode 100755 index 7b5f0de..0000000 --- a/clearwater-sprout/hooks/ralf-ctf-relation-changed +++ /dev/null @@ -1,6 +0,0 @@ -#!/bin/bash -set -e - -# Update Clearwater configuration and restart -$CHARM_DIR/lib/config_script ralf-ctf -$CHARM_DIR/lib/restart diff --git a/clearwater-sprout/hooks/sprout-relation-changed b/clearwater-sprout/hooks/sprout-relation-changed deleted file mode 100755 index aaa71a5..0000000 --- a/clearwater-sprout/hooks/sprout-relation-changed +++ /dev/null @@ -1,16 +0,0 @@ -#!/bin/bash -set -e - -# Only does something if we're in the middle of scaling up -if ! grep -q "new_servers" /etc/clearwater/cluster_settings; then - exit 0 -fi - -# If there's already a deferred timer running, stop it -JOB_FILE=$CHARM_DIR/lib/cluster_timer.job -if [[ -f $JOB_FILE ]] && atq | grep -q -P "^$(cat $JOB_FILE)\t"; then - atrm $(cat $JOB_FILE) 2>&1 > /dev/null || true -fi - -# Restart the timer (set for 6 minutes time since 'at' rounds down) -at -f $CHARM_DIR/lib/finish_scale now + 6 minutes 2>&1 | grep -v warning | cut -d' ' -f 2 > $JOB_FILE diff --git a/clearwater-sprout/hooks/sprout-relation-departed b/clearwater-sprout/hooks/sprout-relation-departed deleted file mode 100755 index cbb60e9..0000000 --- a/clearwater-sprout/hooks/sprout-relation-departed +++ /dev/null @@ -1,19 +0,0 @@ -#!/bin/bash -set -e - -# Get the machine number of this unit and the remote unit. This -# takes the form '/ -peer_index=$(echo $JUJU_REMOTE_UNIT | cut -d'/' -f2) -this_index=$(echo $JUJU_UNIT_NAME | cut -d'/' -f2) - -if [ $this_index -gt $peer_index ] - then - # Remove this node. - monit unmonitor -g sprout - service sprout start-quiesce - else - # Remove the node.json in databags that corresponds to the node being removed. - file_name="/home/ubuntu/chef-solo/data_bags/node/"$peer_index".json" - rm $file_name - chef-solo -c /home/ubuntu/chef-solo/solo.rb -j /home/ubuntu/chef-solo/node.json -fi diff --git a/clearwater-sprout/hooks/sprout-relation-joined b/clearwater-sprout/hooks/sprout-relation-joined deleted file mode 100755 index d8bc60f..0000000 --- a/clearwater-sprout/hooks/sprout-relation-joined +++ /dev/null @@ -1,26 +0,0 @@ -#!/bin/bash -set -e - -# Get the IP address of this unit and the remote unit -peer_ip=$(relation-get private-address) -this_ip=$(unit-get private-address) - -# Get the machine number of this unit and the remote unit. This -# takes the form '/ -peer_index=$(echo $JUJU_REMOTE_UNIT | cut -d'/' -f2) -this_index=$(echo $JUJU_UNIT_NAME | cut -d'/' -f2) - -# Re-create the node.json files in the data bags. Mark the new -# node as joining - the new node is the one with the higher index -if [ $this_index -gt $peer_index ] - then - $CHARM_DIR/lib/node_json_clustered_script --ip "$peer_ip" --index "$peer_index" --cluster - $CHARM_DIR/lib/node_json_clustered_script --ip "$this_ip" --index "$this_index" --cluster --joining - else - $CHARM_DIR/lib/node_json_clustered_script --ip "$peer_ip" --index "$peer_index" --cluster --joining - $CHARM_DIR/lib/node_json_clustered_script --ip "$this_ip" --index "$this_index" --cluster -fi - -# Re-create the node.json file and re-run chef solo -$CHARM_DIR/lib/node_json_script --cluster -chef-solo -c /home/ubuntu/chef-solo/solo.rb -j /home/ubuntu/chef-solo/node.json diff --git a/clearwater-sprout/hooks/start b/clearwater-sprout/hooks/start deleted file mode 100755 index 64f8b4d..0000000 --- a/clearwater-sprout/hooks/start +++ /dev/null @@ -1,15 +0,0 @@ -#!/bin/bash -# Here put anything that is needed to start the service. -# Note that currently this is run directly after install -# i.e. 'service apache2 start' -set -e - -# This hook needs to be idempotent, so this could be run when sprout is -# already running, when it's running but not monitored by monit, when -# it's stopped but being monitored (so it's about to start), or stopped -# and unmonitored. To cover all these cases, restart sprout, reload monit, -# then finally have monit monitor sprout. Note that restarting monit can -# fail and restarting sprout can fail because monit will restart it. -service sprout restart || /usr/bin/true -reload clearwater-monit &> /dev/null || true -monit monitor -g sprout diff --git a/clearwater-sprout/hooks/stop b/clearwater-sprout/hooks/stop deleted file mode 100755 index 10d56d9..0000000 --- a/clearwater-sprout/hooks/stop +++ /dev/null @@ -1,12 +0,0 @@ -#!/bin/bash -# This will be run when the service is being torn down, allowing you to disable -# it in various ways.. -# For example, if your web app uses a text file to signal to the load balancer -# that it is live... you could remove it and sleep for a bit to allow the load -# balancer to stop sending traffic. -# rm /srv/webroot/server-live.txt && sleep 30 -set -e - -# Stop sprout via monit -reload clearwater-monit &> /dev/null || true -monit stop -g sprout diff --git a/clearwater-sprout/hooks/upgrade-charm b/clearwater-sprout/hooks/upgrade-charm deleted file mode 100755 index 50bffee..0000000 --- a/clearwater-sprout/hooks/upgrade-charm +++ /dev/null @@ -1,9 +0,0 @@ -#!/bin/bash -# This hook is executed each time a charm is upgraded after the new charm -# contents have been unpacked -# Best practice suggests you execute the hooks/install and -# hooks/config-changed to ensure all updates are processed -set -e - -$CHARM_DIR/hooks/install -$CHARM_DIR/hooks/config-changed diff --git a/clearwater-sprout/hooks/xdms-relation-changed b/clearwater-sprout/hooks/xdms-relation-changed deleted file mode 100755 index d804efb..0000000 --- a/clearwater-sprout/hooks/xdms-relation-changed +++ /dev/null @@ -1,6 +0,0 @@ -#!/bin/bash -set -e - -# Update Clearwater configuration and restart -$CHARM_DIR/lib/config_script xdms -$CHARM_DIR/lib/restart diff --git a/clearwater-sprout/icon.svg b/clearwater-sprout/icon.svg deleted file mode 100644 index f9ac92c..0000000 --- a/clearwater-sprout/icon.svg +++ /dev/null @@ -1,407 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - - - - - - - - diff --git a/clearwater-sprout/lib/chef_solo_install b/clearwater-sprout/lib/chef_solo_install deleted file mode 100755 index 0833f3d..0000000 --- a/clearwater-sprout/lib/chef_solo_install +++ /dev/null @@ -1,38 +0,0 @@ -#!/bin/bash - -# Configure git proxy -if [[ ! -z $http_proxy ]]; then - juju-log "Configuring git proxy..." - git config --global http.proxy $http_proxy -fi - -# This covers installing chef-solo and the Clearwater chef recipes -# Install chef and needed tools/libraries -apt-get -y install chef git libxml2-dev libxslt1-dev wget curl - -# Pull down the Clearwater chef recipes -if [ ! -d /home/ubuntu/chef-solo ] -then - juju-log "Cloning Clearwater/chef recipes..." - mkdir /home/ubuntu/chef-solo - git clone --recursive https://github.com/Metaswitch/chef.git /home/ubuntu/chef-solo -fi - -# Update the chef recipes -juju-log "Updating chef recipes..." -cd /home/ubuntu/chef-solo -git checkout release-110 -git submodule update --init - -# Create the solo.rb file -cat > /home/ubuntu/chef-solo/solo.rb </etc/clearwater/local_config </etc/clearwater/shared_config <. -# -# The author can be reached by email at clearwater@metaswitch.com or by -# post at Metaswitch Networks Ltd, 100 Church St, Enfield EN2 6BQ, UK -# -# Special Exception -# Metaswitch Networks Ltd grants you permission to copy, modify, -# propagate, and distribute a work formed by combining OpenSSL with The -# Software, or a work derivative of such a combination, even if such -# copying, modification, propagation, or distribution would otherwise -# violate the terms of the GPL. You must comply with the GPL in all -# respects for all of the code used other than OpenSSL. -# "OpenSSL" means OpenSSL toolkit software distributed by the OpenSSL -# Project and licensed under the OpenSSL Licenses, or a work based on such -# software and licensed under the OpenSSL Licenses. -# "OpenSSL Licenses" means the OpenSSL License and Original SSLeay License -# under which the OpenSSL Project distributes the OpenSSL toolkit software, -# as those licenses appear in the file LICENSE-OPENSSL. - -# This script generates a JSON-ified set of DNS records based on an IP, A -# records and SRV records. These are suitable for the API to -# https://github.com/chuckbutler/DNS-Charm. - -import sys -import json - -if len(sys.argv) < 4: - print "Usage: generate_dns_records.py IP CLUSTER_A_RECORD SPECIFIC_A_RECORD [SRV_PORT SRV_ADDR...]" - -ip = sys.argv[1] -cluster_a = sys.argv[2] -specific_a = sys.argv[3] -specific_b = sys.argv[4] -specific_c = sys.argv[5] -port = None -srv_addrs = [] - -ttl = "300" - -if len(sys.argv) > 7: - port = sys.argv[6] - srv_addrs = sys.argv[7:] - -ret = [{"rr":"A", "alias": cluster_a, "ttl": ttl,"addr": ip}, - {"rr":"A", "alias": specific_a, "ttl": ttl,"addr": ip}, - {"rr":"A", "alias": specific_b, "ttl": ttl,"addr": ip}, - {"rr":"A", "alias": specific_c, "ttl": ttl,"addr": ip}] - -for a in srv_addrs: - ret.append({"rr": "SRV", "alias": a, "ttl": ttl,"priority": "1", "weight": "1", "port": port, "target": specific_a}) - -print json.dumps(ret) diff --git a/clearwater-sprout/lib/node_json_clustered_script b/clearwater-sprout/lib/node_json_clustered_script deleted file mode 100755 index 259865f..0000000 --- a/clearwater-sprout/lib/node_json_clustered_script +++ /dev/null @@ -1,58 +0,0 @@ -#!/usr/bin/python -import subprocess -import string -import socket -import os -import sys -import argparse - -# Create the ~/chef-solo/data_bags/node/.json files. -# -# The node.json file contains the machines IP address, the machine ID (a number) -# and the repo server. -# -# If the node is being clustered, the role 'clustered' is added, and if the -# node is joining the attribute 'joining' is added -# -# The node.json file is templated in lib/node_json_clustered_template. - -parser = argparse.ArgumentParser() - -parser.add_argument('--cluster', action='store_true') -parser.add_argument('--joining', action='store_true') -parser.add_argument('--index', type=int, required=True) -parser.add_argument('--ip', type=str, required=True) - -args = parser.parse_args() - -# Populate the dictionary -d = {} - -d['LOCAL_IP'] = socket.gethostbyname(args.ip) -d['MACHINE_ID'] = str(args.index + 1) -d['REPO'] = subprocess.check_output(["config-get", "repo"]).rstrip() - -if args.cluster: - d['CLUSTER'] = ",\n \"role[clustered]\"" - d['CLUSTERED'] = ", \"clustered\"" -else: - d['CLUSTER'] = "" - d['CLUSTERED'] = "" - -if args.joining: - d['JOINING'] = "\"joining\": true,\n " -else: - d['JOINING'] = "" - -# Open template file -charm_dir = os.environ['CHARM_DIR'] -with open( '%s/lib/node_json_clustered_template' % charm_dir ) as f: - src = string.Template( f.read() ) - -# Make the substitutions -result = src.substitute(d) - -# Write to ~/chef-solo/data_bags/node/.json -node_file = str(args.index + 1) + ".json" -with open('/home/ubuntu/chef-solo/data_bags/node/%s' % node_file, 'w') as f: - f.write(result) diff --git a/clearwater-sprout/lib/node_json_clustered_template b/clearwater-sprout/lib/node_json_clustered_template deleted file mode 100755 index 216ec77..0000000 --- a/clearwater-sprout/lib/node_json_clustered_template +++ /dev/null @@ -1,20 +0,0 @@ -{ - "id": "$LOCAL_IP", - "name": "$LOCAL_IP", - "chef_environment": "_default", - "json_class": "Chef::Node", - "automatic": { - "role": "sprout", - "roles": ["clearwater-infrastructure", "sprout"$CLUSTERED], - "cloud": { - "local_ipv4": "$LOCAL_IP" - }, - "clearwater": { - $JOINING"index": $MACHINE_ID - } - }, - "chef_type": "node", - "run_list": [ - "role[sprout]"$CLUSTER - ] -} diff --git a/clearwater-sprout/lib/node_json_script b/clearwater-sprout/lib/node_json_script deleted file mode 100755 index 4be4214..0000000 --- a/clearwater-sprout/lib/node_json_script +++ /dev/null @@ -1,57 +0,0 @@ -#!/usr/bin/python - -import subprocess -import string -import socket -import os -import argparse - -# Create the ~/chef-solo/node.json file. -# -# The node.json file contains the machines IP address, the machine ID (a number) -# and the repo server. -# -# If the node is being clustered, the role 'clustered' is added, and after -# clustering the tag 'clustered' is added (for homer/homestead) -# -# The node.json file is templated in lib/node_json_template. - -parser = argparse.ArgumentParser() - -parser.add_argument('--cluster', action='store_true') -parser.add_argument('--tag', action='store_true') - -args = parser.parse_args() - -# Populate the dictionary -d = {} - -d['LOCAL_IP'] = subprocess.check_output(["unit-get", "private-address"]).rstrip() -d['MACHINE_ID'] = str(int(os.environ['JUJU_UNIT_NAME'].split('/')[1]) + 1) -d['REPO'] = subprocess.check_output(["config-get", "repo"]).rstrip() - -node_file = "/home/ubuntu/chef-solo/node.json" -if args.cluster or (os.path.exists(node_file) and "clustered" in open(node_file).read()): - d['CLUSTER'] = ",\n \"role[clustered]\"" - d['CLUSTERED'] = ", \"clustered\"" - d['TAG'] = "" - - if args.tag or (os.path.exists(node_file) and "tags" in open(node_file).read()): - d['TAG'] = ",\n \"tags\": [\"clustered\"]" - -else: - d['CLUSTER'] = "" - d['CLUSTERED'] = "" - d['TAG'] = "" - -# Open template file -charm_dir = os.environ['CHARM_DIR'] -with open( '%s/lib/node_json_template' % charm_dir ) as f: - src = string.Template( f.read() ) - -# Make the substitutions -result = src.substitute(d) - -# Write to ~/chef-solo/node.json -with open('/home/ubuntu/chef-solo/node.json', 'w') as f: - f.write(result) diff --git a/clearwater-sprout/lib/node_json_template b/clearwater-sprout/lib/node_json_template deleted file mode 100755 index 726a5ed..0000000 --- a/clearwater-sprout/lib/node_json_template +++ /dev/null @@ -1,17 +0,0 @@ -{ - "id": "$LOCAL_IP", - "name": "$LOCAL_IP", - "chef_environment": "_default", - "role": "sprout", - "roles": ["clearwater-infrastructure", "sprout"$CLUSTERED], - "cloud": { - "local_ipv4": "$LOCAL_IP" - }, - "clearwater": { - "index": $MACHINE_ID, - "repo_servers": ["$REPO"] - }, - "run_list": [ - "role[sprout]"$CLUSTER - ] -} diff --git a/clearwater-sprout/lib/restart b/clearwater-sprout/lib/restart deleted file mode 100755 index aef504e..0000000 --- a/clearwater-sprout/lib/restart +++ /dev/null @@ -1,6 +0,0 @@ -#!/bin/bash -set -e - -# Restart clearwater-infrastructure, and stop sprout (to be restarted by monit) -service clearwater-infrastructure restart -service sprout stop diff --git a/clearwater-sprout/metadata.yaml b/clearwater-sprout/metadata.yaml deleted file mode 100644 index c85be34..0000000 --- a/clearwater-sprout/metadata.yaml +++ /dev/null @@ -1,27 +0,0 @@ -name: clearwater-sprout -summary: Sprout charm for Project Clearwater -maintainer: Project Clearwater Maintainers -description: Sprout charm for Project Clearwater -categories: - - misc -subordinate: false -requires: - programmable-multiple: - interface: dns-multi - homestead-hss: - interface: homestead-hss-interface - xdms: - interface: 3GPP-Ut - optional: true - ralf-ctf: - interface: ralf-ctf-interface - optional: true - as: - interface: 3GPP-ISC - optional: true -provides: - pcscf: - interface: 3GPP-Mw -peers: - sprout: - interface: sprout-interface diff --git a/clearwater-sprout/revision b/clearwater-sprout/revision deleted file mode 100644 index 3e932fe..0000000 --- a/clearwater-sprout/revision +++ /dev/null @@ -1 +0,0 @@ -34 \ No newline at end of file diff --git a/dns/.coveragerc b/dns/.coveragerc deleted file mode 100644 index 68210be..0000000 --- a/dns/.coveragerc +++ /dev/null @@ -1,2 +0,0 @@ -[run] -omit = contrib/tests/* diff --git a/dns/.drone.yml b/dns/.drone.yml deleted file mode 100644 index d41391d..0000000 --- a/dns/.drone.yml +++ /dev/null @@ -1,5 +0,0 @@ -image: bradrydzewski/python:2.7 -script: - - sudo apt-get install -y dnsutils - - pip install tox - - make test diff --git a/dns/.gitignore b/dns/.gitignore deleted file mode 100644 index 42a4f92..0000000 --- a/dns/.gitignore +++ /dev/null @@ -1,10 +0,0 @@ -.bzr -.bzrignore -.coverage -*.pyc -Gemfile -Gemfile* -Guard* -.tox -.venv -.cache diff --git a/dns/Makefile b/dns/Makefile deleted file mode 100644 index 99f5402..0000000 --- a/dns/Makefile +++ /dev/null @@ -1,14 +0,0 @@ -#!/usr/bin/make - -test: clean - @echo "Starting tests..." - @tox -e contrib - -lint: clean - @find $(sources) -type f \( -iname '*.py' ! -iwholename './lib/*' \) -print0 | xargs -r0 flake8 - -clean: - @find . -name \*.pyc -delete - @find . -name '*.bak' -delete - @rm -f .coverage - diff --git a/dns/README.md b/dns/README.md deleted file mode 100644 index f55bc2e..0000000 --- a/dns/README.md +++ /dev/null @@ -1,102 +0,0 @@ -# DNS As A Service - -[![Build Status](http://drone.systemzoo.org/api/badge/github.com/chuckbutler/DNS-Charm/status.svg?branch=master)](http://drone.systemzoo.org/github.com/chuckbutler/DNS-Charm) - -## Overview - -the DNS charm is unique in that it wraps several other services to provide a common gateway to automatically provisioning your DNS configuration. Regardless if you are setting up a BIND cluster, PowerDNS, or integrating with a third party provider (such as Amazon Rt53, or GoDaddy for example). - -This charm provides [DNS](http://en.wikipedia.org/wiki/Domain_Name_System). By default the charm will deploy a configured Bind9 server, assuming it is the authoritative master of the configured domain(s). - - -### Notes about architecture - -The DNS charm implements an abstracted 'provider' concept, to ease integration of new services. Specific instructions how to extend/ add providers is outlined in docs/HACKING.md - -# Usage - - juju deploy dns - juju set dns domain='superawesome.com' - juju add-relation myservice:programmable dns:programmable - -### Offline Environment Usage - - juju set dns offline=true - -You will need to branch the charm locally, and fill the required dependencies. Each dependency should be listed by the provider(s) specific documentation in [docs/provider.md](docs/provider.md). - -An example Heirarchy: - - files - ├── bind - │ ├── bind9_1%3a9.8.1.dfsg.P1-4ubuntu0.8_amd64.deb - │ ├── bind9utils_1%3a9.8.1.dfsg.P1-4ubuntu0.8_amd64.deb - │ └── pip - │ └── tldextract-1.3.1.tar.gz - └── core - ├── python-pip_1.0-1build1_all.deb - └── python-setuptools_0.6.24-1ubuntu1_all.deb - -**core** is used to install the baseline dependencies for the bind service provider. This is agnostic to any specific provider, and required for the DNS charm itself to operate. - -**bind** Specific to the bind provider, and exposes a sub-directory of *pip* for python modules consumed to make the bind provider function. The order of operation: the bind .deb files will be installed before the *pip* directory. - - -## Charm Integration - -To integrate with the DNS charm, there is a specific format to send data over the wire. There are Three interfaces that are possible, as documented below - - -### autogenerated - -*Not implemented* - -### programmable - -An easy integration. Geared towards A, and CNAME records. But any record that conforms to this format will be acceptable. - -Set your associated record resource configuration variables, and expect to receive a public-address value from the dns relation. - -- **domain** *optional* - If using a domain other than what the configured domain on the DNS server, this will create a new zone file, and append the record. -- **alias** - sub portion of the domain. for example, given redis.example.com - redis is the alias. -- **addr** - Assigned as the public/private address from your host. -- **rr** - Record Resource type. Possible values: A, CNAME, AAAA, NS - - -### programmable-multiple - -A higly configurable interface to pass single/multiple records to the DNS host. This will expect a json list of records to add that conform to the named-checkzone output. Sent over the wire as **resources** - - [{'alias': 'test', 'ttl': 1600, 'rr': 'A', 'addr': '127.0.0.1'}, - {'alias': 'test2', 'ttl': 1600, 'rr': 'CNAME', 'addr': '127.0.0.1'}] - - -> Note: this will be moving to a JSON datastructure in the near future. Parsing -> an array of strings when integrating with providers other than bind is not -> trivial, and the itnerfaces should expect consistent data. -> The Array notation will only be supported by the bind provider until the -> 1.0 release, when backword compatability is broken. - -## Known Limitations and Issues - -The charm in it's current form does not support scale out operations. It's engineered towards a single Bind9 Authoritative master deployment for use in offline environments. - -None of the 3rd party provider support has been added save for the provider architecture outlined in [docs/HACKING.md](docs/HACKING.md) - -# Configuration - -**domain**: Used to configure the base domain provided by the DNS charm. Defaults to 'example.com'. This is used implicitly in the autogenerated relationship. eg: if you deploy redis, and relate it to the dns charm with the autogen relationship - it returns: *redis0.example.com* as the configured DNS. - -**offline**: Predicate configuration option for determining if packages found in *files/* are to be used for installation in an offline environment. - -**provider**: Specify the underlying provider. Defaults to **bind** - -**proivider_keys**: Used to warehouse provider specific configuration. For an example usecase, see the [rt53](contrib/rt53/README.md) docs. - -# Contact Information - -- http://github.com/chuckbutler/dns-charm -- http://github.com/chuckbutler/dns-charm/issues -- chuck@dasroot.net - -*Dont forget to check the docs directory for additional resources!* diff --git a/dns/config.yaml b/dns/config.yaml deleted file mode 100644 index d167236..0000000 --- a/dns/config.yaml +++ /dev/null @@ -1,17 +0,0 @@ -options: - domain: - type: string - default: "example.com" - description: "Base Canonical Domain name for environment" - provider: - type: string - default: "bind" - description: "Used to specify the provider to deploy/consume for DNS management" - offline: - type: boolean - default: false - description: "Specify offline env configuration, and install from local filepath instead of via apt" - provider_keys: - type: string - default: - description: "Space separated key/values denoted with a pipe character. eg: apikey|1234 apisecret|abcd" diff --git a/dns/contrib/__init__.py b/dns/contrib/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/dns/contrib/bind/__init__.py b/dns/contrib/bind/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/dns/contrib/bind/install.py b/dns/contrib/bind/install.py deleted file mode 100644 index 07826c5..0000000 --- a/dns/contrib/bind/install.py +++ /dev/null @@ -1,47 +0,0 @@ -import os -import sys - -from charmhelpers.core.hookenv import open_port, config, log - -from charmhelpers.fetch import ( - apt_install, - apt_update, -) -from common import install_packages, pip_install - - -def install(): - if config()['offline'] is False: - apt_update(fatal=True) - apt_install(packages=[ - 'bind9', - 'dnsutils', - ], fatal=True) - else: - log("Installing offline debian packages") - install_packages('files/bind') - # rerun cuz its buggy - install_packages('files/bind') - log("Installing Python packages") - pip_install('files/bind/pip') - ## use the nameserver in /etc/resolv.conf as a forwarder ... - import DNS - DNS.ParseResolvConf("/etc/resolv.conf") - nameserver = DNS.defaults['server'][0] - log('Setting dns to be forwarder to :'+nameserver) - import jinja2 - templateLoader = jinja2.FileSystemLoader( searchpath= os.environ['CHARM_DIR'] ) - #use Jinja2 template to enable bind forwarding - templateEnv=jinja2.Environment( loader=templateLoader ); - template=templateEnv.get_template('contrib/bind/templates/named.conf.options.jinja2') - output_from_parsed_template = template.render(forwarder=nameserver) - # to save the results - with open("/etc/bind/named.conf.options", "wb") as fh: - fh.write(output_from_parsed_template) - ## use jinja2 templates.. - - - if not os.path.exists('/etc/bind/zone-backup'): - os.makedirs('/etc/bind/zone-backup') - open_port(53, "TCP") - open_port(53, "UDP") diff --git a/dns/contrib/bind/provider.py b/dns/contrib/bind/provider.py deleted file mode 100644 index 4082ae5..0000000 --- a/dns/contrib/bind/provider.py +++ /dev/null @@ -1,63 +0,0 @@ -import os -from random import randint -from charmhelpers.core.hookenv import unit_get -from charmhelpers.core.host import service_reload - -from zoneparser import ZoneParser - -from common import resolve_hostname_to_ip - - -class Provider(object): - - def __init__(self, domain): - self.domain = domain - - def config_changed(self): - zp = ZoneParser(self.domain) - # Install a skeleton bind zone, rehashes existing file - # if it has contents) - if not os.path.exists('/etc/bind/db.%s' % self.domain): - self.first_setup(zp) - zp.save() - self.reload_config() - - def add_record(self, record): - zp = ZoneParser(self.domain) - if type(record) is dict: - zp.dict_to_zone(record) - elif type(record) is list: - #zp.array_to_zone(record) - for r in record: - zp.dict_to_zone(r) - else: - raise TypeError("Unsupported type for resource %d" % type(record)) - zp.save() - self.reload_config() - - def remove_record(self, record): - zp = ZoneParser(self.domain) - zp.zone.remove('alias', record['rr'], record['alias']) - zp.save() - self.reload_config() - - def first_setup(self, parser): - # Insert SOA and NS records - addr = unit_get('public-address') - #hostname = unit_get('public-address') - #addr = resolve_hostname_to_ip(hostname) - parser.dict_to_zone({'rr': 'SOA', - 'addr': 'ns.%s.' % self.domain, - 'owner': 'root.%s.' % self.domain, - 'serial': randint(12345678, 22345678), - 'refresh': '12h', - 'update-retry': '15m', - 'expiry': '3w', - 'minimum': '3h'}) - parser.dict_to_zone({'rr': 'NS', 'alias': '@', 'ttl': 1600, - 'addr': 'ns.%s.' % self.domain}) - parser.dict_to_zone({'rr': 'A', 'alias': 'ns', 'addr': addr, - 'ttl': 300}) - - def reload_config(self): - service_reload('bind9') diff --git a/dns/contrib/bind/provider.py.orig b/dns/contrib/bind/provider.py.orig deleted file mode 100644 index fe83320..0000000 --- a/dns/contrib/bind/provider.py.orig +++ /dev/null @@ -1,82 +0,0 @@ -import os -import sys -from random import randint - -# Add charmhelpers to the system path. -try: - sys.path.insert(0, os.path.abspath(os.path.join(os.environ['CHARM_DIR'], - 'lib'))) -except: - sys.path.insert(0, os.path.abspath(os.path.join('..', '..', 'lib'))) - -from charmhelpers.core.hookenv import unit_get -from charmhelpers.core.host import service_reload - -<<<<<<< HEAD - -from common import resolve_hostname_to_ip -from zoneparser import ZoneParser -======= -from charmhelpers.fetch import ( - apt_install, - apt_update, -) -from common import install_packages, pip_install -try: - from zoneparser import ZoneParser -except: - print("Notice: Failed to import zoneparser, probably installing...") ->>>>>>> dde677d3442af8d85865e04a235d56e3eafd0ca6 - - -class BindProvider(object): - - def config_changed(self, domain='example.com'): - zp = ZoneParser(domain) - # Install a skeleton bind zone, rehashes existing file - # if it has contents) - if not os.path.exists('/etc/bind/db.%s' % domain): - self.first_setup(zp, domain) - zp.save() - self.reload_config() - - def add_record(self, record, domain='example.com'): - zp = ZoneParser(domain) - if type(record) is dict: - zp.dict_to_zone(record) - elif type(record) is list: - zp.array_to_zone(record) - else: - raise TypeError("Unsupported type for resource %d" % type(record)) - zp.save() - self.reload_config() - - def remove_record(self, record, domain='example.com'): - zp = ZoneParser(domain) - zp.zone.remove('alias', record['rr'], record['alias']) - zp.save() - self.reload_config() - - def first_setup(self, parser, domain='example.com'): - # Insert SOA and NS records - hostname = unit_get('public-address') - addr = resolve_hostname_to_ip(hostname) - parser.dict_to_zone({'rr': 'SOA', - 'addr': 'ns.%s.' % domain, - 'owner': 'root.%s.' % domain, - 'serial': randint(12345678, 22345678), - 'refresh': '12h', - 'update-retry': '15m', - 'expiry': '3w', - 'minimum': '3h'}) - parser.dict_to_zone({'rr': 'NS', 'alias': '@', - 'addr': 'ns1.%s.' % domain}) - parser.dict_to_zone({'rr': 'A', 'alias': '@', 'addr': addr, - 'ttl': 300}) - parser.dict_to_zone({'rr': 'A', 'alias': 'ns1', 'addr': addr, - 'ttl': 300}) - parser.dict_to_zone({'rr': 'CNAME', 'alias': 'ns', - 'addr': 'ns1.example.com.', 'ttl': 300}) - - def reload_config(self): - service_reload('bind9') diff --git a/dns/contrib/bind/templates/named.conf.options.jinja2 b/dns/contrib/bind/templates/named.conf.options.jinja2 deleted file mode 100644 index 84b4bd4..0000000 --- a/dns/contrib/bind/templates/named.conf.options.jinja2 +++ /dev/null @@ -1,21 +0,0 @@ -acl "trusted" { - localhost; - localnets; - any; - }; - -options { - directory "/var/cache/bind"; - - forwarders { - {{ forwarder }} ; - }; - dnssec-enable yes; - dnssec-validation yes; - auth-nxdomain no; # conform to RFC1035 - listen-on-v6 { any; }; - allow-query { any; }; - allow-recursion { trusted; }; - allow-query-cache { trusted; }; - -}; \ No newline at end of file diff --git a/dns/contrib/bind/templates/zone.jinja2 b/dns/contrib/bind/templates/zone.jinja2 deleted file mode 100644 index 42abb7f..0000000 --- a/dns/contrib/bind/templates/zone.jinja2 +++ /dev/null @@ -1,24 +0,0 @@ -; -; BIND data file, generated by DNS Charm for {{domain}} -; -$TTL 604800 -;@ IN RR ADDR -{%- for record in data['SOA'] %} -@ IN SOA {{record['addr']}} {{record['owner']}} ( {{record['serial']}} {{record['refresh']}} {{record['update-retry']}} {{record['expiry']}} {{record['update-retry']}} ) -{% endfor %} -{%- for record in data['NS'] %} -{{record['alias']}} {{record['ttl']}} IN NS {{record['addr']}} -{%- endfor %} -{%- for record in data['A'] %} -{{record['alias']}} {{record['ttl']}} IN A {{record['addr']}} -{%- endfor %} -{%- for record in data['CNAME'] %} -{{record['alias']}} {{record['ttl']}} IN CNAME {{record['addr']}} -{% endfor %} -{%- for record in data['NAPTR'] %} -{{record['alias']}} {{record['ttl']}} IN NAPTR {{record['order']}} {{record['pref']}} {{record['flag']}} {{record['params']}} {{record['regexp']}} {{record['replace']}} -{% endfor %} -{%- for record in data['SRV'] %} -{{record['alias']}} {{record['class']}} SRV {{record['priority']}} {{record['weight']}} {{record['port']}} {{record['target']}} -{% endfor %} -{{ newline }} \ No newline at end of file diff --git a/dns/contrib/bind/zone.py b/dns/contrib/bind/zone.py deleted file mode 100644 index dc4f6e2..0000000 --- a/dns/contrib/bind/zone.py +++ /dev/null @@ -1,155 +0,0 @@ -import jinja2 -import os - - -# Supports an incomplete SPEC of DNS Zone entrys -class Zone(object): - # TODO: Add Validation - # TODO: Compare with RFC - - def __init__(self): - self.contents = { - 'A': [], - 'AAAA': [], - 'CAA': [], - 'CERT': [], - 'CNAME': [], - 'NAPTR': [], - 'NS': [], - 'PTR': [], - 'SOA': [], - 'SPF': [], - 'SRV': [], - 'TXT': [] - } - - def a(self, value=None): - if not value: - return self.contents['A'] - else: - idx = self.find(self.contents['A'], 'alias', value['alias']) - if idx != -1: - self.contents['A'].pop(idx) - self.contents['A'].append(value) - return self.contents['A'] - - def aaaa(self, value=None): - if not value: - return self.contents['AAAA'] - else: - self.contents['AAAA'].append(value) - return self.contents['AAAA'] - - def caa(self, value=None): - if not value: - return self.contents['CAA'] - else: - self.contents['CAA'].append(value) - return self.contents['CAA'] - - def cert(self, value=None): - if not value: - return self.contents['CERT'] - else: - self.contents['CERT'].append(value) - return self.contents['CERT'] - - def cname(self, value=None): - if not value: - return self.contents['CNAME'] - else: - idx = self.find(self.contents['CNAME'], 'alias', value['alias']) - if idx != -1: - self.contents['CNAME'].pop(idx) - self.contents['CNAME'].append(value) - return self.contents['CNAME'] - - def ns(self, value=None): - if not value: - return self.contents['NS'] - else: - idx = self.find(self.contents['NS'], 'alias', value['alias']) - if idx != -1: - self.contents['NS'].pop(idx) - self.contents['NS'].append(value) - return self.contents['NS'] - - def naptr(self, value=None): - if not value: - return self.contents['NAPTR'] - else: - # idx = self.find(self.contents['NAPTR'], 'alias', value['alias']) - # if idx != -1: - # self.contents['NAPTR'].pop(idx) - self.contents['NAPTR'].append(value) - return self.contents['NAPTR'] - - def ptr(self, value=None): - if not value: - return self.contents['PTR'] - else: - self.contents['PTR'].append(value) - return self.contents['PTR'] - - def soa(self, value=None): - if not value: - return self.contents['SOA'] - else: - if len(self.contents['SOA']) > 0: - self.contents['SOA'].pop(-1) - self.contents['SOA'].append(value) - return self.contents['SOA'] - - def spf(self, value=None): - if not value: - return self.contents['SPF'] - else: - self.contents['SPF'].append(value) - return self.contents['SPF'] - - def srv(self, value=None): - if not value: - return self.contents['SRV'] - else: - self.contents['SRV'].append(value) - return self.contents['SRV'] - - def txt(self, value=None): - if not value: - return self.contents['TXT'] - else: - self.contents['TXT'].append(value) - return self.contents['TXT'] - - # ############ - # Template Methods - # ############ - - def to_file(self, filepath='/etc/bind/db.example.com'): - contents = self.read_template() - t = jinja2.Template(contents) - - with open('%s' % filepath, 'w') as f: - f.write(t.render(data=self.contents)) - - def read_template(self): - with open('%s/contrib/bind/templates/zone.jinja2' % - os.environ['CHARM_DIR']) as f: - return f.read() - - # ############# - # Utility methods - # ############# - def find(self, lst, key, value): - for i, dic in enumerate(lst): - if dic[key] == value: - return i - return -1 - - def remove(self, needle, haystack, value): - if not haystack in self.contents.keys(): - raise IndexError("Unable to locate %s in storage" % haystack) - idx = self.find(self.contents[haystack], needle, value) - if idx == -1: - raise KeyError("Value not found in %s" % haystack) - self.contents[haystack].pop(idx) diff --git a/dns/contrib/bind/zoneparser.py b/dns/contrib/bind/zoneparser.py deleted file mode 100644 index c203b5f..0000000 --- a/dns/contrib/bind/zoneparser.py +++ /dev/null @@ -1,317 +0,0 @@ -import datetime -import logging -import os -import subprocess -import sys -from .zone import Zone - -from common import ( - return_sub as sub, - trim_empty_array_elements as trim, -) - -logging.basicConfig(level=logging.INFO) - - -class ZoneParser(object): - - def __init__(self, domain, file_handle=None): - self.zone = Zone() - self.domain = domain - self.zonefile = "/etc/bind/db.%s" % self.domain - self.implemented_records = self.zone.contents.keys() - if self.has_zone(): - self.load_and_parse('/etc/bind/db.%s' % self.domain) - - def load_and_parse(self, filepath): - self.contents = self.from_file() - self.array_to_zone() - - def from_file(self): - contents = [] - if self.has_zone(): - self.backup() - with open(self.zonefile, 'r') as f: - for line in f.readlines(): - # ignore comments - if not line.startswith(';') and not line.startswith('$'): - contents.append(line) - return contents - - def save(self): - proposed = "{zone}.proposed".format(zone=self.zonefile) - # write the output to a proposed file - self.zone.to_file(proposed) - # perform sanity check on proposed zone - if self.passes_validation(proposed): - logging.info('Zone additions accepted. writing to ZoneFile') - self.zone.to_file(self.zonefile) - os.remove(proposed) - else: - raise Exception('Check failing dirty zone file %s' % proposed) - - # Call the default zone file addition - self.add_to_local_zones() - - # #################################### - # Utility Methods - # #################################### - - def backup(self, fmt='%Y-%m-%d-%H-%M-%S_{zone}'): - if self.has_zone(): - with open(self.zonefile) as f: - of = f.read() - zf = datetime.datetime.now().strftime(fmt).format(zone=self.domain) - with open('/etc/bind/zone-backup/%s' % zf, 'w') as outf: - outf.write(of) - - def passes_validation(self, zf=None): - # Must be first setup - slime the return value. - if not self.has_zone(): - return True - - if not zf: - zf = self.zonefile - - ret = subprocess.call(['named-checkzone', self.domain, zf]) - logging.info('RET Code: %s' % ret) - if not ret == 0: - return False - return True - - def has_zone(self): - if os.path.exists('/etc/bind/db.%s' % self.domain): - return True - return False - - def is_number(self, s): - try: - float(s) - return True - except ValueError: - return False - - # This may get slow - def find_type(self, line): - for t in self.implemented_records: - if t.upper() in line: - return line.index(t.upper()) - return -1 - - def sanity(self, data, esize=5): - if len(data) < esize: - raise IndexError("Array Notation should conform to " - "specification in docs/relations.md") - - # ####################################### - # Parsing Array to Zone Dictionary - this is going - # to be a bit messy, and specific to loading - # from the named-checkzone utility - this is brittle - # ####################################### - - def update_a(self, data): - self.zone.a(data) - - def a_from_array(self, data): - data = trim(data) - self.sanity(data, 4) - addr = data[-1].strip() - - # If position 1 is numeric, its a TTL - if self.is_number(data[1]): - ttl = data[1] - - if len(data[0].split('.')) > 1: - alias = sub(self.domain, data[0]) - else: - alias = data[0] - - try: - parsed = {'ttl': ttl, 'addr': addr, 'alias': alias} - except: - parsed = {'addr': addr, 'alias': alias} - self.zone.a(parsed) - - def update_cname(self, data): - self.zone.cname(data) - - def cname_from_array(self, data): - logging.info("CNAME data: %s" % data) - self.sanity(data, 4) - alias = sub(self.domain, data[0]) - # CNAME's can have TTLS or use the global - if self.is_number(data[1]): - ttl = data[1] - addr = data[-1].strip() - - try: - parsed = {'ttl': ttl, 'addr': addr, 'alias': alias} - except: - parsed = {'addr': addr, 'alias': alias} - - self.zone.cname(parsed) - - def update_ns(self, data): - self.zone.ns(data) - - def ns_from_array(self, data): - self.sanity(data, 4) - - alias = data[0] - addr = data[-1].strip() - if self.is_number(data[1]): - ttl = data[1] - - try: - parsed = {'ttl': ttl, 'alias': alias, 'addr': addr} - except: - parsed = {'alias': alias, 'addr': addr} - self.zone.ns(parsed) - - def naptr_from_array(self, data): - self.sanity(data) - alias = data[0] - if self.is_number(data[1]): - ttl = data[1] - order = data[-6] - pref = data[-5] - flag = data[-4] - params = data[-3] - regexp = data[-2] - replace = data[-1].strip() - try: - parsed = {'alias': alias, 'order': order, 'pref': pref, - 'flag': flag, 'params': params, 'regexp': regexp, - 'replace': replace, 'ttl': ttl} - except: - parsed = {'alias': alias, 'order': order, 'pref': pref, - 'flag': flag, 'params': params, 'regexp': regexp, - 'replace': replace} - self.zone.naptr(parsed) - - def srv_from_array(self, data): - self.sanity(data) - alias = data[0] - if self.is_number(data[1]): - ttl = data[1] - priority = data[-4] - weight = data[-3] - port = data[-2] - target = data[-1].strip() - try: - parsed = {'alias': alias, 'priority': priority, 'weight': weight, - 'port': port, 'target': target, 'ttl': ttl} - except: - parsed = {'alias': alias, 'priority': priority, 'weight': weight, - 'port': port, 'target': target} - self.zone.srv(parsed) - - def update_soa(self, data): - self.zone.soa(data) - - def update_srv(self, data): - self.zone.srv(data) - - def update_naptr(self, data): - self.zone.naptr(data) - - - - # see tests/fixtures/db.orangebox.com for expected format. - # As this is handled by the Jinja template, it shouldn't change much. - def soa_from_array(self, data): - self.sanity(data) - logging.info("SOA data: %s" % data) - addr = data[3] - owner = data[4] - serial = data[6] - refresh = data[7] - - try: - update_retry = data[8] - except: - update_retry = None - try: - expiry = data[9] - except: - expiry = None - try: - minimum = data[10] - except: - minimum = None - parsed = {'addr': addr, 'owner': owner, 'serial': serial, - 'refresh': refresh, 'update-retry': update_retry, - 'expiry': expiry, 'minimum': minimum} - self.zone.soa(parsed) - - def array_to_zone(self, blob=None): - if not blob: - blob = self.contents - - blob = trim(blob) - - methods = {'A': self.a_from_array, - 'CNAME': self.cname_from_array, - 'NS': self.ns_from_array, - 'NAPTR': self.naptr_from_array, - 'SOA': self.soa_from_array, - 'SRV': self.srv_from_array} - - logging.info('Processing records from: %s' % blob) - for entry in blob: - line = entry.split() - rrtype = self.find_type(line) - dclass = line[rrtype].strip() - try: - methods[dclass](line) - except: - logging.info('Failed to locate method for parsing %s' % dclass) - - # Array_to_zone parses a full array to populate dict to zone - # assumes a single record. - def dict_to_zone(self, record): - methods = {'A': self.update_a, - 'CNAME': self.update_cname, - 'SOA': self.update_soa, - 'SRV': self.update_srv, - 'NS': self.update_ns, - 'NAPTR': self.update_naptr, - } - if 'rr' in record.keys(): - try: - methods[record['rr']](record) - except: - logging.info('Failed to locate method for %s' % record['rr']) - - # ####### - # Default Zone Config File Maintenance - # ####### - - def add_to_local_zones(self): - zones = self.read_local_zones() - if self.exists_in_local_zones(zones) != -1: - logging.info("Zone found, returning") - return - addition = ['zone "%s" {' % self.domain, - ' type master;', - ' file "%s";' % self.zonefile, - "};" - ""] - self.write_local_zones(addition) - # Tell bind to refresh zone configuration - - def exists_in_local_zones(self, zones): - logging.info("Searching for %s" % self.domain) - for idx, val in enumerate(zones): - if self.domain in val: - return idx - return -1 - - def read_local_zones(self): - with open('/etc/bind/named.conf.local') as f: - default_zones = f.readlines() - return default_zones - - def write_local_zones(self, config): - with open('/etc/bind/named.conf.local', 'a') as f: - f.write('\n'.join(config)) diff --git a/dns/contrib/common.py b/dns/contrib/common.py deleted file mode 100644 index 9f0669d..0000000 --- a/dns/contrib/common.py +++ /dev/null @@ -1,118 +0,0 @@ -import importlib -import json -import os -import subprocess -from charmhelpers.core.hookenv import ( - log, - config, -) - - -def sanity_check(): - if not config()['domain']: - log("No Base Domain specified - Aborting until configured") - # It's reasonable to assume we're not doing anything useful at this - # point, as we are unconfigured. Abort doing *anything* - return False - return True - - -def trim_empty_array_elements(data): - val = [line for line in data if line.strip()] - print("TRIM: %s" % val) - return val - - -def install_packages(path): - packages = os.listdir(path) - for pkg in packages: - pkg = "%s/%s" % (path, pkg) - subprocess.call(['dpkg', '-i', pkg]) - - -def pip_install(path): - packages = os.listdir(path) - for pkg in packages: - pkg = "%s/%s" % (path, pkg) - subprocess.call(['pip', 'install', pkg]) - - -def return_sub(domain, address): - address = address.rstrip('.') - if domain in address: - return address[:-len(domain)].rstrip('.') - else: - return address - - -def resolve_hostname_to_ip(hostname): - hostname = hostname.strip() - out = subprocess.check_output(['dig', '+short', hostname]).rstrip('\n') - # required to obtain the IP from MAAS output. See test_common - line 83 - return out.split('\n')[-1] - - -# Parse existing nameservers from resolv.conf -def existing_nameservers(): - dns_servers = [] - with open('/etc/resolv.conf') as f: - contents = f.readlines() - for line in contents: - if line.find('nameserver') != -1: - dns_servers.append(line.replace('nameserver ', '').rstrip()) - return dns_servers - -def load_module(full_class_string): - """ - dynamically load a module from a string - """ - - class_data = full_class_string.split(".") - module_path = ".".join(class_data[:-1]) - - return importlib.import_module(module_path) - -def load_class(full_class_string): - """ - dynamically load a class from a string - """ - - class_data = full_class_string.split(".") - module_path = ".".join(class_data[:-1]) - class_str = class_data[-1] - - module = importlib.import_module(module_path) - return getattr(module, class_str) - -def provider_keys(): - """ - load the space separated key/value pairs and return a dictionary - args of conf for easy stub in unit-tests - """ - if not config('provider_keys'): - raise ValueError("Missing required config value for provider_keys") - - provider_config = {} - # load the keys into an array for iteration - pkeys = config('provider_keys').split(' ') - for k in pkeys: - if not k: continue - provider_config[k.split('|')[0]] = k.split('|')[1] - return provider_config - -def serialize_data(datafile, data): - from path import Path - p = Path(datafile) - p.dirname().mkdir_p() - - with open(p, 'w+') as f: - f.write(json.dumps(data)) - -def unserialize_data(datafile): - from path import Path - p = Path(datafile) - if p.exists(): - with open(p, 'r') as f: - return json.loads(f.read()) - else: - return {} diff --git a/dns/contrib/consul_client.py b/dns/contrib/consul_client.py deleted file mode 100644 index c94aed0..0000000 --- a/dns/contrib/consul_client.py +++ /dev/null @@ -1,61 +0,0 @@ -#!/usr/bin/env python -import subprocess -try: - import consul -except: - subprocess.call(['pip','install','python-consul']) - import consul -from common import serialize_data, unserialize_data - - -class ConsulClient(): - - def __init__(self, host=None, port=8500, filepath=None): - # provide filepath to load from pickled data - if filepath: - data = unserialize_data('data/consul-host.json') - self.host = data['host'] - self.port = data['port'] - if host: - self.host = host - if port: - self.port = port - if not self.host: - raise ValueError("Missing host config, cannot initialize class") - self.client = consul.Consul(host=self.host) - self.cache = 'data/consul-data.json' - - def cache_services(self): - services = self.client.agent.services() - - service_list = unserialize_data(self.cache) - for service in services: - svc_name = services[service]['Service'] - svc = {'name': svc_name, - 'port' : services[service]['Port'], - 'address': services[service]['Address']} - service_list[svc_name] = svc - - serialize_data(self.cache, service_list) - print "Cached service data to: {}".format(self.cache) - print service_list - return service_list - - def cache_host(self): - serialize_data('data/consul-host.json', {'host': self.host, - 'port': self.port}) - print "Saved host data to: data/consul-host.json" - - def read_services(self): - return unserialize_data(self.cache) - - def build_dns(self, webhost): - services = self.read_services() - all_services = [] - for host in webhost: - for service in services: - svc = {'rr': 'A', 'alias': service, 'ttl': 60, - 'addr': webhost[host]['public-address']} - all_services.append(svc) - return all_services - diff --git a/dns/contrib/requirements.txt b/dns/contrib/requirements.txt deleted file mode 100644 index c8b1ec5..0000000 --- a/dns/contrib/requirements.txt +++ /dev/null @@ -1,2 +0,0 @@ -path.py -python-consul diff --git a/dns/contrib/rt53/README.md b/dns/contrib/rt53/README.md deleted file mode 100644 index 25b3811..0000000 --- a/dns/contrib/rt53/README.md +++ /dev/null @@ -1,64 +0,0 @@ -# AWS Route 53 Provider Integration - -### Warnings of caution - -It's highly recommended you setup an AWS IAM account, with the proper sandboxed -ARN rulests to only access the domain in which you are going to be binding to the -DNS charm. This will prevent addtional damage should the credentials ever become -compromised, or a nasty bug creep into the code. Please use your best judgement -when using this integration. - -### Prerequisits - -You must provide the charm's `provider_keys` directive with at least the 2 keys -bound to an account with authorization to access and modify Rt53 resources. - - juju set dns provider="rt53" - juju set dns provider_keys="AWS_ACCESS_KEY_ID|1234 AWS_SECRET_ACCESS_KEY|abc123def" - -By setting the provider to rt53, and providing the requisite Access keys you are -now ready to start the integration of your Juju Environment w/ AWS Rt53 domains. - -The charm will do its best attempt to validate that it has the proper access -before making any modifications. But please note, that this will assume full -control over any requested resources. Meaning, whichever domain you assign to -the charm - the charm will overwrite, update, and remove DNS records as if you -had made the request yourself. This is fully managed DNS provided by Juju. - -Changes you make outside of this charm will **not** persist through an update. - - -### Currently Supported Record Types - -The record types this provider will accept are marked in the -`contrib/rt53/provider.py` initializer. This helps cut down on attempting to -make API calls we're not sure how to handle. - - self.record_types = ['A', 'CNAME'] - -To date, we support the most common DNS record entries youll encounter. Should -you need another, PR's are welcome - but you'll need to include a test that -stubs the AWS Rt53 API with a fixture to illustrate usage. - -### Known Caveats - -The DNS charm does not actively reconfigure from one provider to another. This -feat of configuration requires revisiting the core logic of the charm and -implementing additional calls to determine if the underlying DNS provider has -changed. - -When switching providers. you have two options presently: - -- Attach to the charm over debug-hooks during an event, and manually run the - install/config-changed hooks. `hooks/install && hooks/config-changed` - -- Redeploy the service - - -### Bugs / Feature Requests - -Please file any/all bugs and feature requests against the dns-charm -[issue tracker](https://github.com/chuckbutler/dns-charm/issues) -on github, and ensure you call out the issue is with the `rt53` provider. - - diff --git a/dns/contrib/rt53/__init__.py b/dns/contrib/rt53/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/dns/contrib/rt53/install.py b/dns/contrib/rt53/install.py deleted file mode 100644 index 8dce86e..0000000 --- a/dns/contrib/rt53/install.py +++ /dev/null @@ -1,21 +0,0 @@ -from common import provider_keys -from os import path, getenv -import shlex -import subprocess - -def install(): - requirements= path.join(path.dirname(__file__), 'rt53-requirements.txt') - if path.exists(requirements): - cmd = "pip install -r {}/contrib/rt53/rt53-requirements.txt".format( - getenv('CHARM_DIR')) - subprocess.check_call(shlex.split(cmd)) - else: - raise IOError("Missing rt53-requirements.txt") - - # validate that we have credentials as a preliminary sanity check - try: - provider_keys() - except ValueError: - print "Missing provider API keys: AWS_ACCESS_KEY_ID or" \ - " AWS_SECRET_ACCESS_KEY" - print "config-changed will fail... incoming failure in 3..2..." diff --git a/dns/contrib/rt53/provider.py b/dns/contrib/rt53/provider.py deleted file mode 100644 index ca20cfd..0000000 --- a/dns/contrib/rt53/provider.py +++ /dev/null @@ -1,100 +0,0 @@ -from common import provider_keys -from boto import route53 - -class Provider: - - # Primary Interface Methods - ALLOWED_RECORDS = ['A', 'CNAME', 'NS', 'SRV', 'NAPTR'] - - - def __init__(self, domain, key=None, secret=None): - if not key or not secret: - pkey = provider_keys() - key = pkey['AWS_ACCESS_KEY_ID'] - secret = pkey['AWS_SECRET_ACCESS_KEY'] - - self.client = route53.connection.Route53Connection( - aws_access_key_id=key, - aws_secret_access_key=secret) - self.domain = domain - self.zone = self.client.get_zone("{}.".format(self.domain)) - - def config_changed(self): - if not self.zone.id: - raise ValueError("Missing ZoneID for Domain. Is domain declared" - " in AWS Route53?") - - - def add_record(self, record): - resource = route53.record.ResourceRecordSets(self.client, self.zone.id) - if type(record) is dict: - # single record - self.parse_record(record, resource) - if type(record) is list: - for r in record: - self.parse_record(r, resource) - - - def remove_record(self, record): - """ - Used to remove an entry from a zone - """ - pass - - - def update_record(self, record): - resource = route53.record.ResourceRecordSets(self.client, self.zone.id) - if type(record) is dict: - # single record - self.parse_record(record, resource) - if type(record) is list: - for r in record: - self.parse_record(r, resource) - - # Supporting Methods - def parse_record(self, entry, resource): - methods = {'A': self.create_a_record, - 'CNAME': self.create_cname_record, - 'NS': self.create_ns_record, - 'SRV': self.create_srv_record, - 'NAPTR': self.create_naptr_record,} - - if 'rr' in entry.keys(): - methods[entry['rr']](entry, resource) - else: - raise ValueError("Missing record type - see README for accepted" - " dns record types") - - - - def create_a_record(self, record, resource): - print record - fqdn = "{}.{}".format(record['alias'], self.domain) - if "ttl" in record.keys() and record['ttl']: - status = resource.add_change("UPSERT", fqdn, "A", ttl=record['ttl']) - else: - status = resource.add_change("UPSERT", fqdn, "A") - status.add_value(record['addr']) - resource.commit() - - def create_naptr_record(self, record, resource): - print "Rt53 Does not yet support NAPTR resources" - - def create_cname_record(self, record, resource): - fqdn = "{}.{}".format(record['alias'], self.domain) - if "ttl" in record.keys() and record['ttl']: - status = resource.add_change("UPSERT", fqdn, "CNAME", ttl=record['ttl']) - else: - status = resource.add_change("UPSERT", fqdn, "CNAME") - status.add_value(record['addr']) - resource.commit() - - - def create_ns_record(self, record, resource): - print "NS Records Not Implemented" - - def create_srv_record(self, record, resource): - print "SRV records not implemented" - - - diff --git a/dns/contrib/rt53/rt53-requirements.txt b/dns/contrib/rt53/rt53-requirements.txt deleted file mode 100644 index a0826ee..0000000 --- a/dns/contrib/rt53/rt53-requirements.txt +++ /dev/null @@ -1 +0,0 @@ -boto diff --git a/dns/contrib/tests/fixtures/db.orangebox.com b/dns/contrib/tests/fixtures/db.orangebox.com deleted file mode 100644 index d8c78c1..0000000 --- a/dns/contrib/tests/fixtures/db.orangebox.com +++ /dev/null @@ -1,9 +0,0 @@ -; -; BIND data file, generated by DNS Charm for -; -$TTL 604800 -;@ IN RR ADDR -@ IN SOA ns.orangebox.com. root.orangebox.com. ( 16640992 12h 15m 3w 15m ) - IN NS ns.orangebox.com. -ns IN A 10.0.10.55 -mail IN CNAME gmail.com. \ No newline at end of file diff --git a/dns/contrib/tests/fixtures/sample_bind_parsed b/dns/contrib/tests/fixtures/sample_bind_parsed deleted file mode 100644 index fc88bcb..0000000 --- a/dns/contrib/tests/fixtures/sample_bind_parsed +++ /dev/null @@ -1,5 +0,0 @@ -example.com. 604800 IN SOA ns.example.com. root.example.com. 2003080800 43200 900 1814400 900 -example.com. 604800 IN NS ns1.example.com. -example.com. 604800 IN A 10.0.3.103 -ns.example.com. 604800 IN CNAME ns1.example.com. -ns1.example.com. 604800 IN A 10.0.3.103 diff --git a/dns/contrib/tests/test_bind_provider.py b/dns/contrib/tests/test_bind_provider.py deleted file mode 100644 index 40f57f3..0000000 --- a/dns/contrib/tests/test_bind_provider.py +++ /dev/null @@ -1,34 +0,0 @@ -import unittest -from mock import patch, Mock, MagicMock -import os -import sys - -from bind.provider import Provider - - -class TestBindProvider(unittest.TestCase): - - - @patch('subprocess.check_output') - @patch('bind.provider.unit_get') - def test_first_setup(self, ugm, spcom): - ugm.return_value = '10.0.0.1' - bp = Provider('example.com') - parser = MagicMock() - bp.first_setup(parser) - ugm.assert_called_once_with('public-address') - parser.dict_to_zone.assert_called_with({'rr': 'A', 'alias': 'ns', - 'addr': '10.0.0.1', - 'ttl': 300}) - - @patch('bind.provider.ZoneParser.dict_to_zone') - @patch('bind.provider.ZoneParser.save') - def test_add_record(self, zps, zpm): - bp = Provider('example.com') - bp.reload_config = Mock() - bp.add_record({'rr': 'A', 'alias': 'foo', 'addr': '127.0.0.1'}) - zps.assert_called_once_with() - zpm.assert_called_once_with({'alias': 'foo', 'addr': '127.0.0.1', 'rr': 'A'}) - bp.reload_config.assert_called_once_with() - - diff --git a/dns/contrib/tests/test_common.py b/dns/contrib/tests/test_common.py deleted file mode 100644 index ea77bc9..0000000 --- a/dns/contrib/tests/test_common.py +++ /dev/null @@ -1,98 +0,0 @@ -import sys -import os - -import unittest -from mock import ( - patch, - Mock, -) - -import common - - -class TestCommon(unittest.TestCase): - - @patch('common.config') - @patch('common.log') - def test_sanity_check_configured(self, lmock, cfgmock): - cfgmock.return_value = {'assume_provider': True, - 'domain': 'example.com'} - self.assertTrue(common.sanity_check()) - - @patch('common.config') - @patch('common.log') - def test_sanity_check_unconfigured(self, lmock, cfgmock): - cfgmock.return_value = {'assume_provider': True, - 'domain': ''} - self.assertFalse(common.sanity_check()) - - @patch('builtins.open' if sys.version_info > (3,) else '__builtin__.open') - def test_existing_nameservers(self, omck): - omck.return_value.__enter__ = lambda s: s - omck.return_value.__exit__ = Mock() - omck.return_value.readlines.return_value = [ - 'nameserver 127.0.0.1', - 'search maas', - 'nameserver 10.0.1.1', - 'search foobar'] - nameservers = common.existing_nameservers() - self.assertEqual(nameservers, ['127.0.0.1', '10.0.1.1']) - - @patch('os.listdir') - @patch('subprocess.call') - def test_install_packages(self, spcm, osldm): - osldm.return_value = ['foo.deb', 'bar.deb'] - common.install_packages('/tmp/nope') - spcm.assert_called_with(['dpkg', '-i', '/tmp/nope/bar.deb']) - - @patch('os.listdir') - @patch('subprocess.call') - def test_pip_install(self, spcm, osldm): - osldm.return_value = ['foo.tar.gz'] - common.pip_install('/tmp/nope') - spcm.assert_called_with(['pip', 'install', '/tmp/nope/foo.tar.gz']) - - def test_return_sub(self): - sub = common.return_sub('example.com', 'foo.example.com') - self.assertEqual('foo', sub) - - def test_return_sub_with_sub_domain(self): - sub = common.return_sub('offline.example.com', - 'foo.offline.example.com') - self.assertEqual('foo', sub) - - def test_return_sub_will_return_empty(self): - sub = common.return_sub('example.com', - 'example.com') - self.assertEqual('', sub) - - def test_return_sub_return_with_named_checkzone_output(self): - sub = common.return_sub('example.com', 'example.com.') - self.assertEqual('', sub) - -# This test is highly dependent on the environment itself and fails in -# CI consistently as the host has improper DNS resolution. /etc/hosts -# has localhost defined, but is not working properly. so rely on the mock -# to really determine if the method works as expected. -# def test_hostname_resolution(self): -# ip = common.resolve_hostname_to_ip('localhost') -# self.assertEqual('127.0.0.1', ip) - - @patch('subprocess.check_output') - def test_maas_funky_dig_resolution(self, spm): - spm.return_value = "'10-0-10-55.maas.\n10.0.10.55" - ip = common.resolve_hostname_to_ip('localhost') - self.assertEqual('10.0.10.55', ip) - - def test_trim(self): - a = ['a', '', '', 'b'] - trimmed = common.trim_empty_array_elements(a) - self.assertEqual(['a','b'], trimmed) - - @patch('common.config') - def test_provider_keys(self, cfgmock): - cfgmock.return_value = 'awsKey|12345 awsSecret|abc123def' - keys = common.provider_keys() - self.assertEqual(keys['awsKey'], '12345') - self.assertEqual(keys['awsSecret'], 'abc123def') - diff --git a/dns/contrib/tests/test_rt53_install.py b/dns/contrib/tests/test_rt53_install.py deleted file mode 100644 index dbf4daf..0000000 --- a/dns/contrib/tests/test_rt53_install.py +++ /dev/null @@ -1,24 +0,0 @@ -from mock import patch, Mock -import pytest - - -from rt53 import install - -class TestRt53Installer(): - - def test_install_missing_requirements(self): - with patch('rt53.install.path.exists') as pmock: - pmock.return_value = False - with pytest.raises(IOError): - install.install() - - @patch('common.config') - def test_install_pip_command(self, cfgmock): - cfgmock.return_value = 'AWS_ACCESS_KEY_ID|123 AWS_SECRET_ACCESS_KEY|abc' - with patch('rt53.install.subprocess') as spmock: - spmock.check_call.return_value.returncode = 0 - install.install() - assert( - spmock.called_with(['pip', 'install', '-r', 'rt53-requirements.txt']) - ) - diff --git a/dns/contrib/tests/test_rt53_provider.py b/dns/contrib/tests/test_rt53_provider.py deleted file mode 100644 index 1c52278..0000000 --- a/dns/contrib/tests/test_rt53_provider.py +++ /dev/null @@ -1,61 +0,0 @@ -from mock import patch, Mock -import pytest -import os - -from rt53 import provider - - - -class TestRt53Provider(): - """ - This test suite depends on cloud credentials. You can ideally set them to - anything. But it's fair to assume that you want actual integration tests - and you should probably stuff in real credentials then. - - export AWS_ACCESS_KEY_ID - export AWS_SECRET_ACCESS_KEY - - in your CI environment to ensure these tests dont fail due to cloud creds - missing and life will be good. - """ - - @classmethod - def setup_class(cls): - AWS_ACCESS_KEY = os.getenv('AWS_ACCESS_KEY_ID') - AWS_SECRET_ACCESS_KEY = os.getenv('AWS_SECRET_ACCESS_KEY') - cls.provider = provider.Provider('example.com', AWS_ACCESS_KEY, AWS_SECRET_ACCESS_KEY) - - @pytest.mark.skipif(os.getenv('AWS_ACCESS_KEY_ID') is None, - reason="requires credentials") - @patch('rt53.provider.route53.record.ResourceRecordSets') - def test_add_record_with_dict(self, rrm): - record = {'name': 'test', 'rr': 'A', 'ttl': 1600} - with patch('rt53.provider.Provider.parse_record') as de: - self.provider.add_record(record) - de.assert_called_once(record, rrm) - - @patch('rt53.provider.Provider.create_a_record') - @patch('rt53.provider.Provider.create_cname_record') - @patch('rt53.provider.Provider.create_ns_record') - @patch('rt53.provider.Provider.create_srv_record') - @pytest.mark.skipif(os.getenv('AWS_ACCESS_KEY_ID') is None, - reason="requires credentials") - def test_add_record_with_empty_dict_raises_error(self, amk, cmk, nmk, smk): - record = {'name': 'test', 'ttl': 1600} - with pytest.raises(ValueError): - self.provider.add_record(record) - record = {} - self.provider.add_record(record) - assert amk.not_called() - assert cmk.not_called() - assert nmk.not_called() - assert smk.not_called() - - @pytest.mark.skipif(os.getenv('AWS_ACCESS_KEY_ID') is None, - reason="requires credentials") - def test_create_a_record(self): - record = {'alias': 'test', 'ttl': 1600, 'rr': 'A', 'addr': '127.0.0.1'} - with patch('boto.route53.record.ResourceRecordSets') as crm: - self.provider.create_a_record(record, crm) - crm.add_change.assert_called_with('UPSERT', 'test.example.com', - 'A', ttl=1600) diff --git a/dns/contrib/tests/test_zone.py b/dns/contrib/tests/test_zone.py deleted file mode 100644 index a702def..0000000 --- a/dns/contrib/tests/test_zone.py +++ /dev/null @@ -1,167 +0,0 @@ -import unittest -from mock import patch, Mock -import sys - -from bind.zone import Zone - - -class TestZone(unittest.TestCase): - - def test_dictionary(self): - z = Zone() - self.assertTrue(hasattr(z, 'contents')) - - def test_a_getset(self): - z = Zone() - record = {'ttl': 300, 'addr': '10.0.0.1', 'alias': '@'} - self.assertEqual(z.a(), []) - self.assertEqual(z.a(record), - [{'ttl': 300, 'addr': '10.0.0.1', 'alias': '@'}]) - self.assertEqual(z.a(record), - [{'ttl': 300, 'addr': '10.0.0.1', 'alias': '@'}]) - - def test_aaa_getset(self): - z = Zone() - record = {'ttl': 300, 'addr': '10.0.0.1', 'alias': '@'} - self.assertEqual(z.aaaa(), []) - self.assertEqual(z.aaaa(record), - [{'ttl': 300, 'addr': '10.0.0.1', 'alias': '@'}]) - - def test_caa_getset(self): - z = Zone() - record = {'ttl': 300, 'priority': 0, 'issue': 'thawte.com'} - self.assertEqual(z.caa(), []) - self.assertEqual(z.caa(record), - [{'ttl': 300, 'priority': 0, 'issue': 'thawte.com'}]) - - def test_cert_getset(self): - z = Zone() - record = {'ttl': 300, 'type': 1, 'key-tag': '12179', - 'algorithm': 3, 'cert-crl': 'AQPSKmynfz'} - self.assertEqual(z.cert(), []) - self.assertEqual(z.cert(record), - [{'ttl': 300, 'type': 1, 'key-tag': '12179', - 'algorithm': 3, 'cert-crl': 'AQPSKmynfz'}]) - - def test_cname_getset(self): - z = Zone() - record = {'ttl': 300, 'addr': 'abc.foo.com', 'alias': 'd'} - self.assertEqual(z.cname(), []) - self.assertEqual(z.cname(record), - [{'ttl': 300, 'addr': 'abc.foo.com', 'alias': 'd'}]) - # assert that value pops and gets updated - record = {'ttl': 300, 'addr': 'def.foo.com', 'alias': 'd'} - self.assertEqual(z.cname(record), [{'ttl': 300, 'addr': 'def.foo.com', - 'alias': 'd'}]) - - def test_ns_getset(self): - z = Zone() - record = {'alias': 'example.com.', 'addr': '10.0.0.1'} - self.assertEqual(z.ns(), []) - self.assertEqual(z.ns(record), - [{'addr': '10.0.0.1', 'alias': 'example.com.'}]) - record = {'alias': 'example.com.', 'addr': '10.0.0.2'} - self.assertEqual(z.ns(record), - [{'addr': '10.0.0.2', 'alias': 'example.com.'}]) - - def test_ptr_getset(self): - z = Zone() - record = {'ttl': 2, 'addr': 'joe.example.com'} - self.assertEqual(z.ptr(), []) - self.assertEqual(z.ptr(record), - [{'ttl': 2, 'addr': 'joe.example.com'}]) - - def test_soa_getset(self): - z = Zone() - record = {'ttl': '@', 'addr': 'ns1.example.com.', - 'owner': 'hostmaster.example.com', 'serial': '2003080800', - 'refresh': '12h', 'update-retry': '15m', 'expiry': '3w', - 'minimum': '3h'} - self.assertEqual(z.soa(), []) - self.assertEqual(z.soa(record), - [{'ttl': '@', 'addr': 'ns1.example.com.', - 'owner': 'hostmaster.example.com', - 'serial': '2003080800', - 'refresh': '12h', - 'update-retry': '15m', - 'expiry': '3w', - 'minimum': '3h'}]) - record = {'ttl': '@', 'addr': 'ns1.example.com.', - 'owner': 'hostmaster.example.com', 'serial': '2003080800', - 'refresh': '12h', 'update-retry': '15m', 'expiry': '3w', - 'minimum': '4h'} - self.assertEqual(z.soa(record), - [{'ttl': '@', 'addr': 'ns1.example.com.', - 'owner': 'hostmaster.example.com', - 'serial': '2003080800', - 'refresh': '12h', - 'update-retry': '15m', - 'expiry': '3w', - 'minimum': '4h'}]) - - def test_spf_getset(self): - z = Zone() - record = {'addr': 'example.com.', - 'txt': '"v=spf1 mx include:example.net -all"'} - self.assertEqual(z.spf(), []) - self.assertEqual(z.spf(record), - [{'addr': 'example.com.', - 'txt': '"v=spf1 mx include:example.net -all"'}]) - - def test_srv_getset(self): - z = Zone() - record = {'addr': '_ldap._tcp.example.com.', - 'priority': 0, - 'weight': 0, 'port': 389, - 'target': 'old-slow-box.example.com'} - self.assertEqual(z.srv(), []) - self.assertEqual(z.srv(record), - [{'addr': '_ldap._tcp.example.com.', - 'priority': 0, - 'weight': 0, - 'port': 389, - 'target': 'old-slow-box.example.com'}]) - - def test_txt_getset(self): - z = Zone() - record = {'alias': 'joe', - 'txt': '"Located in a black hole" " somewhere"'} - self.assertEqual(z.txt(), []) - self.assertEqual(z.txt(record), - [{'alias': 'joe', - 'txt': '"Located in a black hole" " somewhere"'}]) - - @patch('builtins.open' if sys.version_info > (3,) else '__builtin__.open') - @patch('bind.zone.jinja2.Template.render') - def test_to_file(self, tm, mopen): - mopen.return_value.__enter__ = lambda s: s - mopen.return_value.__exit__ = Mock() - mopen.return_value.write = Mock() - z = Zone() - z.read_template = Mock() - z.read_template.return_value = "hi {{data}}" - z.to_file() - z.read_template.assert_called_once_with() - mopen.assert_called_with('/etc/bind/db.example.com', 'w') - tm.assert_called_with(data={'SOA': [], 'AAAA': [], 'TXT': [], - 'PTR': [], 'SPF': [], 'A': [], 'CERT': [], 'CNAME': [], 'SRV': [], # noqa - 'CAA': [], 'NS': [], 'NAPTR': []}) - - @patch.dict('os.environ', {'CHARM_DIR': '/tmp/foo'}) - @patch('builtins.open' if sys.version_info > (3,) else '__builtin__.open') - def test_read_template(self, mopen): - mopen.return_value.__enter__ = lambda s: s - mopen.return_value.__exit__ = Mock() - mopen.return_value.read.return_value = "{{foo}}" - z = Zone() - self.assertEqual(z.read_template(), "{{foo}}") - - def test_remove(self): - z = Zone() - z.contents['A'] = [{'addr': '10.0.0.1', 'alias': 'abc', 'ttl': 300}] - z.remove('alias', 'A', 'abc') - self.assertEqual(z.a(), []) - with self.assertRaises(IndexError): - z.remove('alias', 'NOPE', 'abc') - with self.assertRaises(KeyError): - z.remove('alias', 'A', 'abc') diff --git a/dns/contrib/tests/test_zoneparser.py b/dns/contrib/tests/test_zoneparser.py deleted file mode 100644 index fae024a..0000000 --- a/dns/contrib/tests/test_zoneparser.py +++ /dev/null @@ -1,231 +0,0 @@ -import sys -import unittest -from mock import ( - patch, - Mock, -) - -from bind.zoneparser import ZoneParser -# from bind.zone import Zone - - -class TestZoneParser(unittest.TestCase): - - ez = [] - - def setUp(self): - with open('contrib/tests/fixtures/db.orangebox.com') as f: - self.ez = f.readlines() - self.ezp = list(self.ez) - - def test_init_loads_keys_from_zone_class(self): - zp = ZoneParser('example.com') - self.assertIn('CNAME', zp.implemented_records) - self.assertIn('AAAA', zp.implemented_records) - self.assertIn('A', zp.implemented_records) - self.assertIn('SOA', zp.implemented_records) - self.assertIn('NS', zp.implemented_records) - self.assertIn('NAPTR', zp.implemented_records) - self.assertIn('SRV', zp.implemented_records) - - - @patch('builtins.open' if sys.version_info > (3,) else '__builtin__.open') - def test_from_file_exception(self, mopen): - mopen.return_value.__enter__ = lambda s: s - mopen.return_value.__exit__ = Mock() - mopen.return_value.readlines = Mock(side_effect=OSError('Intentional')) - zp = ZoneParser('foo.com') - zp.normalize_contents = Mock() - zp.normalize_contents.return_value = self.ez - self.assertEqual(zp.from_file(), []) - - @patch('bind.zoneparser.ZoneParser.a_from_array') - @patch('bind.zoneparser.ZoneParser.ns_from_array') - @patch('bind.zoneparser.ZoneParser.soa_from_array') - def test_array_to_zone(self, soam, nsm, am): - zp = ZoneParser('orangebox.com') - zp.contents = self.ez - zp.array_to_zone() - soam.assert_called_with(self.ez[5].split()) - nsm.assert_called_with(self.ez[6].split()) - am.assert_called_with(self.ez[7].split()) - - def test_array_to_zone_with_data(self): - data = """sprout 300 IN A 10.0.5.1 -sprout 300 IN NAPTR 1 1 "S" "SIP+D2T" "" _sip._tcp.sprout -_sip._tcp.sprout 300 IN SRV 0 0 5054 sprout-0 -sprout-0 300 IN A 10.0.5.1""".split('\n') - zp = ZoneParser('orangebox.com') - zp.array_to_zone(data) - cont = zp.zone.contents - self.assertEqual(cont['A'], [{'alias': 'sprout', - 'addr': '10.0.5.1', - 'ttl': '300'}, - {'alias': 'sprout-0', - 'addr': '10.0.5.1', - 'ttl': '300'}]) - self.assertEqual(cont['NAPTR'], [{'alias': 'sprout', - 'order': '1', - 'pref': '1', - 'params': '"SIP+D2T"', - 'regexp': '""', - 'flag': '"S"', - 'replace': '_sip._tcp.sprout', - 'ttl': '300'}]) - self.assertEqual(cont['SRV'], [{'alias': '_sip._tcp.sprout', - 'port': '5054', - 'ttl': '300', - 'priority': '0', - 'target': 'sprout-0', - 'weight': '0'}]) - - def test_soa_from_array(self): - zp = ZoneParser('orangebox.com') - zp.soa_from_array(self.ez[5].split()) - self.assertEqual(zp.zone.contents['SOA'], - [{'addr': 'ns.orangebox.com.', - 'owner': 'root.orangebox.com.', - 'expiry': '3w', - 'minimum': '15m', - 'refresh': '12h', - 'serial': '16640992', - 'update-retry': '15m'}]) - - def test_cname_from_array(self): - zp = ZoneParser('orangebox.com') - zp.cname_from_array(self.ez[8].split()) - self.assertEqual(zp.zone.contents['CNAME'], [{'alias': 'mail', - 'addr': 'gmail.com.'}]) - - def test_a_from_array(self): - zp = ZoneParser('orangebox.com') - zp.a_from_array(self.ez[7].split()) - self.assertEqual(zp.zone.contents['A'], [{'addr': '10.0.10.55', - 'alias': 'ns'}]) - - def test_a_from_array_with_ttl(self): - zp = ZoneParser('orangebox.com') - zp.a_from_array(['ns', '300', 'IN', 'A', '10.0.10.55']) - self.assertEqual(zp.zone.contents['A'], [{'addr': '10.0.10.55', - 'alias': 'ns', - 'ttl': '300'}]) - - def test_ns_from_array(self): - zp = ZoneParser('orangebox.com') - zp.ns_from_array(['@', 'IN', 'NS', '10.0.10.55']) - self.assertEqual(zp.zone.contents['NS'], [{'addr': '10.0.10.55', - 'alias': '@'}]) - - def test_ns_from_array_with_ttl(self): - zp = ZoneParser('orangebox.com') - zp.ns_from_array(['@', '300', 'IN', 'NS', '10.0.10.55']) - self.assertEqual(zp.zone.contents['NS'], [{'addr': '10.0.10.55', - 'alias': '@', - 'ttl': '300'}]) - - def test_naptr_from_array(self): - zp = ZoneParser('example.com') - zcontents = '@ 3200 IN NAPTR 1 1 "S" "SIP+D2T" "" _sip._tcp'.split(' ') - zp.naptr_from_array(zcontents) - self.assertEqual(zp.zone.contents['NAPTR'], [{'alias': '@', - 'order': '1', - 'pref': '1', - 'flag': '"S"', - 'params': '"SIP+D2T"', - 'regexp': '""', - 'ttl': '3200', - 'replace': '_sip._tcp'}]) - - def test_srv_from_array(self): - zp = ZoneParser('example.com') - zcontents = '_sip._udp 3200 IN SRV 0 0 5060 bono-0'.split(' ') - zp.srv_from_array(zcontents) - self.assertEqual(zp.zone.contents['SRV'], [{'alias': '_sip._udp', - 'priority': '0', - 'weight': '0', - 'port': '5060', - 'target': 'bono-0', - 'ttl': '3200'}]) - - def test_bono_a_from_array(self): - zp = ZoneParser('offline.cw-ngv.com') - zp.a_from_array(u'@ 300 IN A 54.73.45.41'.split(' ')) - self.assertEqual(zp.zone.contents['A'], [{'ttl': '300', - 'addr': '54.73.45.41', - 'alias': '@'}]) - - def test_ellis_a_from_array(self): - zp = ZoneParser('offline.cw-ngv.com') - zp.a_from_array(u'ellis-0 300 IN A 54.73.45.41'.split(' ')) - self.assertEqual(zp.zone.contents['A'], [{'ttl': '300', - 'addr': '54.73.45.41', - 'alias': 'ellis-0'}]) - - @patch('bind.zone.Zone.to_file') - @patch('os.remove') - def test_save(self, osrm, fwm): - zp = ZoneParser('example.com') - zp.passes_validation = Mock() - zp.passes_validation.return_value = True - zp.add_to_local_zones = Mock() - zp.save() - osrm.assert_called_with('/etc/bind/db.example.com.proposed') - fwm.assert_called_with('/etc/bind/db.example.com') - zp.add_to_local_zones.assert_called_once_with() - - def test_find_type(self): - zp = ZoneParser('example.com') - self.assertEqual(zp.find_type(['foo', 'bar', 'baz', 'CNAME']), 3) - self.assertEqual(zp.find_type(['foo', 'bar', 'baz']), -1) - - def test_dict_to_zone(self): - zp = ZoneParser('example.com') - zp.update_ns = Mock() - zp.update_soa = Mock() - zp.update_cname = Mock() - zp.update_a = Mock() - zp.dict_to_zone({'rr': 'NS'}) - zp.update_ns.assert_called_with({'rr': 'NS'}) - zp.dict_to_zone({'rr': 'SOA'}) - zp.update_soa.assert_called_with({'rr': 'SOA'}) - zp.dict_to_zone({'rr': 'CNAME'}) - zp.update_cname.assert_called_with({'rr': 'CNAME'}) - zp.dict_to_zone({'rr': 'A'}) - zp.update_a.assert_called_with({'rr': 'A'}) - zp.dict_to_zone({'rr': 'NOPE'}) - - @patch('builtins.open' if sys.version_info > (3,) else '__builtin__.open') - def test_read_local_zones(self, mopen): - seed_zone = """zone "255.in-addr.arpa" { - type master; - file "/etc/bind/db.255"; -};""" - mopen.return_value.__enter__ = lambda s: s - mopen.return_value.__exit__ = Mock() - mopen.return_value.readlines.return_value = seed_zone.split('\n') - zp = ZoneParser('example.com') - self.assertEqual(zp.read_local_zones(), seed_zone.split('\n')) - - @patch('builtins.open' if sys.version_info > (3,) else '__builtin__.open') - def test_write_local_zones(self, mopen): - seed_zone = """zone "255.in-addr.arpa" { - type master; - file "/etc/bind/db.255"; -};""" - mopen.return_value.__enter__ = lambda s: s - mopen.return_value.__exit__ = Mock() - mopen.return_value.write = Mock() - made_config = seed_zone.split('\n').append(['hello']) - zp = ZoneParser('example.com') - zp.write_local_zones(made_config) - # mopen.return_value.write.assert_called_with(made_config) - - def test_exists_in_local_zones(self): - seed_zone = """zone "example.com" { - type master; - file "/etc/bind/db.example.com"; -};""" - zp = ZoneParser('example.com') - self.assertEqual(zp.exists_in_local_zones(seed_zone.split('\n')), 0) - zp = ZoneParser('nope.com') - self.assertEqual(zp.exists_in_local_zones(seed_zone.split('\n')), -1) diff --git a/dns/copyright b/dns/copyright deleted file mode 100644 index 10f6110..0000000 --- a/dns/copyright +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2014 - -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. diff --git a/dns/docs/HACKING.md b/dns/docs/HACKING.md deleted file mode 100644 index 8b2b397..0000000 --- a/dns/docs/HACKING.md +++ /dev/null @@ -1,56 +0,0 @@ -# Getting Started Hacking on DNS as a Service - -There are a few convenience methods setup for developers looking to get started. - - make setup - make sync-charm-helpers - -will ensure you've got all the required python packages to execute tests and begin working with the charm. Concidentally you'll notice this is included in the online installer of the charm. Therefore any environment you deploy to, you effectively have a working dev environment to work with. - -### Make Targets - -There are several targets in the makefile to assist in assuring your submission will be of high quality out of the box. - -- make test_contrib -- - Executes all tests against the providers. Providers are located in `contrib/` - -- make lint -- - Executes all linting tests against the code in the project. Such as `charm proof` and `flake8` - -- make test -- - Executes all hook and deployment tests contained in the charms `tests` directory - -- make sync-charm-helpers -- - Fetches the latest copy of charm helpers, a library to assist charm developers in building charms rapidly. To learn more about Charm Helpers, see: [Charm Helpers LP project](https://launchpad.net/charm-helpers) - - -### Adding Providers - -**See:** the spec document for provider specifics - -Providers are to be implemented in the contrib/ directory under their own nested diretory structure. The only mandated file here is the provider.py file. It is a wrapping class for exposing the underlying service. EG: if you were to extend the charm and add bobs DNS service, you would place it in `contrib/bobs` and touch a provider.py that implemented the following methods: - -- add_record -- remove_record -- config_changed - -and add a `contrib/bobs/install.py` file with the installation routine. This should be self-calling so invoking an object will run the installation routine. - - -These are the only mandated method calls to fit within the plugin architecture. Ideally we would like these to be python, but if you have an interesting use case that is non-python, and can be embedded as is, submit a pull request! - - -### Submission Workflow - -As you make changes, every change is to be code reviewed by a peer associated with the project prior to merging into the codebase. So the workflow from a purely GitHub oriented aspect would be the following: - -1. Fork Charm -2. Add Feature or fix bug -3. Add Tests to validate feature/bug -4. Submit pull request against upstream charm -5. Implement any requested changes -6. Enjoy upstream code changes - -Every pull request is quality gated and must meet the [Juju Charm Store Policy](https://juju.ubuntu.com/docs/authors-charm-policy.html) - - diff --git a/dns/docs/provider.md b/dns/docs/provider.md deleted file mode 100644 index cdfcaf9..0000000 --- a/dns/docs/provider.md +++ /dev/null @@ -1,71 +0,0 @@ -# Providers - -### Purpose: - -Providers are service wrappers that expose a common interface to interacting with them. [The DNS Charm spec](spec-document.pdf) has a simple and straightforward purpose: to add, remove, or update a pointer/informational record against a given domain. We can achieve communication with any provider through this simple exposed set of methods and consume new services as they emerge. - -## Requirements for upstream inclusion: - -All providers must meet the following criteria for addition: - -- fully unit tested -- documented in docs/providers/*providername*.md -- peer-reviewed and acknowledged for merging -- Self-contained, and relies only on itself for method inclusion -- Implement the methods outlined in [README.md](README.md) - -## Adding A Provider: - -The charm has a `contrib` directory that houses all provider code. These are ideally in Python and will consume as few resources as possible to communicate with the provider to ease installation in 'offline' or 'limited connectivity' environments. - -Resources that come from locations such as PyPi should be noted in the provider's documentation to ensure anyone deploying to a limited connectivity environment can allocate all required source packages for 'offline installation'. The charm will be forked locally and deployed with files placed in the 'files/provider' directory. All installation logic is then to be handled by the given provider's installation routine. - -### Note: - -In the given examples, we will assume there is a DNS provider named *GoCheap* with a WebAPI, and a wrapping python library named *GoCheapLib* - -### Create the Directory Structure - - mkdir contrib/gocheap - touch contrib/gocheap/install.py - touch contrib/gocheap/provider.py - touch contrib/gocheap/requirements.txt - touch contrib/tests/gocheap_test.py - -### Setup Dependencies - -Add any PIP requirements required for installation into **provider/requirements.txt**. - -In our case, requirements.txt will look like the following: - - gocheaplib - -### Add Provider Code - -In provider.py, you need to create a scaffolded provider. In terms of example, you may reference the `contrib/bind/provider.py` provider code. - -Your Provider class should **always** be Provider(object), as the directory structure will delineate the different providers. Allowing the charm hook wrappers to use inflection to infer the proper class methods to call. - -An example skeleton provider.py file: - - class Provider(object): - - def ConfigChanged(self): - pass - - def AddRecord(self): - pass - - def RemoveRecord(self): - pass - - def UpdateRecord(self): - pass - - -From this skeleton, you are free to add whatever models, classes, libraries, modules, and other requirements are necessary to integrate with your given provider. In terms of setup with API keys and ensuring they are kept together, there is a configuration variable: **provider_keys** that should hold the credentials in key|value form. e.g. If your two API tokens are APIKey and APISecret, they would look similar to the following: - - APIKey|1234567 APISecret|foobar - -i.e. Entries are space-separated, and keys & values are separated from each other with a pipe character. - diff --git a/dns/docs/spec-diagrams.pdf b/dns/docs/spec-diagrams.pdf deleted file mode 100644 index 29b63cba372e36972bf06b69915da8888012ecec..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 333335 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

2_&E-opDVg!{dYd6-%fJ6f4 zGY)As6;q3e#Pqv#euQWVdvqLrJ6C^M?C&El20#NS=HDWw!Jw&wsRH

h?(v>DSZ(r*zpRbI$Fu#Yc(CU2qpvAWHw<#4pV?Zmt-Hvgoze}DAa zeAkDy305_#=Wtp{g1v6=QwGkyTzn6;^E$B1etqw0OzK6n^k(AI&meTa>6zJFN6-k5 zhM2!E#Ew}Kya=~wI!NLCQBx}A_D18Xn+zuQ=}I4=Nl44(Ei<;kiaEzgFoClxS)u;D zUYOHaSvb8}TU_?*5%{8nzymBY0R3zn8EDjcTxWH!gSREJCd|+l(#r{l+0EVKl#GR& zz8+7NwvMmOu-^cgVMne!N}t$+UY8ECyjwh&5OY@d*sxwMe6%gZZM~Rglx0tuG%W=U z3mt0Kz+ZuTdM2fC-}Cq^3d)&QOFlWNnwqA}A!AvQkg=*_C!XkpDe7Qt`jH?-TX)-( zZ27rb3#_D~qGmIL)x=7r%#+S-h}$qbSnE{v2zjf;EaoWo2VgvMH~BKoA`xChDh`;VzcRMjy4pxWz2KtHMY%^vO%; z!Yz9|#R&b#?y5j}c}nk8wB;E~@C-2Wm+DDPK!g+LFV^Jc(A;+nvTV5IRGvQFBNqGz zPi2;Jd5>Nq)m9mO3YS);^AT0Bw*vBw345iRBBf+CPgS+(*!nW+x#=;st!V2(6=UoaL7?cxJGyLA-%@bK=%zl_V$fKQpL>p8 zsu;VfXrr>FV!Nj>v@zf@;xtb8Et!i@+h10Y8ctLUxH(!5s2YmdD%01*BLw~*TWeoUz3zPpXU4SyV&nKXsL=2T+ zVSdF>+-?4$c=CKR!@qiD9;}7_!`@2FEVh~DM9jh<2u$5{O^=Ax2i5%K{){0L^c7M<6lPOM|J!S}_mK2ZYf zHYz9DbBk!R1n1=M%fOd~?Elc{B%lC7gwTb!!5WYF^8j7?-7&uG&`vP40b`+qcGRee z-2pPzY&}7%dywT$22LJPU9WLqnaNspjxuK;gF9Tzq<2`{O1R)XEM^3)Sj^Q)kD1C+w}ql91?P7~xF#$XnNRU!WOS1v zBMg@FDVKzDQ9O|^B;d?FTu^W>U*yf|pMkHpLHU_k!!@#KG!pR-!-0nHB3`>ebVUAL z<_A^V)Jh{UtxfIis_qO4h-}`|(5m#7OKa4X=5dc&Or=GR*1Z?&dBaT)aREH_oU7y( zoH;9A{rS!-UaQh_D#4TnMc<-h?xTVL@vvJPFm?CIim6%M>9)o^3ab{t?~(_>Yt zAqA^7ODc(y-OwLjiwez)f_`LwwT-x)|KzC9svd(X?+V zuSpY=a_~72=%*dKR=Jd+tn#TZMTcY`>e6e^0cRpxM}+pBefbrCL**>s21Pr?EAl5* z^|V3ZL}qLLVNJ^O7vNhXI6TbsArD<8SZXWR?kR9suUQh2T9!$jUcsGrWRla z^+lLxOne*mYY)h=4eAiV{xh&nZVehR*ok6X78uk`61%5e7Ag-=bh)$-Xu;dmo`}uo zPR!w4t2m{-CB#^!RLqHjS~1uNC=Y8WzzK{tNLjdbO2bvTE>*ekY!=d0xzs?3#L^ra zwZU`{yWH4!@-J9Vw6y?Rma6oqZPnVZ%ygi>OcJjdP_vKAuD+r_LPO6*iY8?mF16!X zDFZp|hoqLE?fC}wO^0wXg;{bl53MX@p7p$R?>Q2o!X1gO3zASDQ8m;uN~(xeM5Cj4=lrB z;CE5?4UbT!|COo7R>y_#D zuDg}ErdDrT*Ts5sOMB_+Z?zP@b=BSr_Mfmlab=<~cMLnf58npUC9UhNt0cTYYl=72 z&w5a9!=0yz-3erj8n8eRNOBV(He)*_l~VL1dob0v;)(MUkx&cs3nq~y9GM(92^35y zPzPnBW!dzjjtpz}_^d@PWwahbSuh15)6AOgX{EhE=*0-I)ey*4OvoltXkoRFO?*E7J**cPCQwlwk> z2k(of<}1js3f$kDzGHwuiAp^OAOShBc;7+4RetsZU=9gA-%@F)Ou)Vq3uIz|&dthq z*kR}bu>HX)OIR1}F1t8T%Dp(H`1_%qTvD@{rAN&1+-%sl_--eZGtvF57*Jw;>s)8Nb zWl-;L+~#if&&aKdTzpWZhWdp4hypBqe*>@pCtH*DWJLd{gQI7y z%j*5>@wJ^$)fzrX+t-J%q|33WVU8ik1HlWjw+K1v9YY6;d z9WG;_uO&U(n+dyPCbZdK-v0Z=fbP(zCzY5KcJ4_JQ=1Jrk=UO_B-P;E9!uO5H9VgH zP^^PmUJQsQewg!imrx*J}aN+K6e%xzE zL_(ff$0VknC&A>FNubAociyKK9R`11_Heh>BK#Wpa&+%2JHSHTyT0R4mE+FUy{J-% z?V-(a-|dEvZ({f1x<83pDD9J!;g(zQ(w-P;10>h=y>Q*JOr1>igGRN{Q%{}O*8!wh zGL*#uDuOwIO8`Q`SXOU>@Qy7x>3N5>-Xm0RXOcxK(qis+y4tpm`?aVYiZ?=kKS&Fe z>`B?>W$HJ-#^af|U09JZF!<#u^kU_K^kSuQ<*Vl-S?7X9K6Bmz4;sTIY|Kb5%K0xC zIiP#xQDL1p5o@v)SA@V~e2Zme@N}$Gxs~FmTMxU{;LJvIuCM(V_{h2ROk>rRacm6D z14kcEdPO9yDvCt|{Vd5P-gmSEB5?+X%*GjfSb{z#u?0WQvUgAIihb7Qap&x&$;ILD z>T$+?se^;zzQO*`>xS1xCwLs__4V&;R5@K3o^`6Mh``>G==`#JVqATg;wZ})o5EGK zgs7NXIFAunHo&StP<vutHBlqrXvPof-29qziEM)YFLeUHX5q*y+RIjBRx?g?*2$_xzOA-W@Oq`l($M zuOZ9C*mWfmuABriwmJH3f{8TUF1LQiD-NaL{p@Q&^YI*PYNb^~a}Tbjr@m6bNoHh$ zyT~sj_yB+xyPden&rmcAMu#z z>b5_9Dgc{wFunrBXk zzkIL7uuX|)K49bIOiR|L8#MBOTde7A&_;`KGmj>wB4BTw ze+cw!%RSwH0lv<*5>2%>UQqm_CSkhlTZa9%3^JwgvufHi0s`Z17y$pt_@)M)5`);^ zA^ZpAi}C*p`A+?Xd?mg&9q+UO)>|OTr~Z@j-3N;`TxjXmv`H=A1UJe5zyulZF>fr8 z|F}oxtNtIHui_@1m=e%Gpzp9~qlIhmWnQ8_Jf$RKLo8`?R&Z;0ZeDBD;$V62Z0H|I z7HAv?q>r5SX=mSvgx`(W!Q2pU^APRG%&XF{Ye2bCz<;fhUl_v9YIz#h`d95iT{=~) z9BEvy|Bd17p2a;Z%+-LC-_giZC39;|FwX4%~1Gv)g*DfmoA zU$T@W-nKg&k1OOyM`K-0ChNErfT)P%XXhzV99)sg8iPIx{H+E+y}`yJ0UFKV0~R(- zCf_7E!}!YTFxWZ-&OQ6CFMuDwIzW)+m&-jUoC8Lh@6O=ky_wR`xAe3UY2%&bgFyPx z7l}x)Eyka4Fb4*rT&K69?HcmF5t!wVY|Gq<`!13ISI!4gnp(K4(mNZI8YT6H0N&OF~EF z5;Z2|sD>sq7mv}VR-1_VY%n$n=Si51BOQGFp*DT;Aq>$79xsB!F%4M@ON_PwF9Zs9 z`bSGRLf#$;+2?}RqZxpMNGVmse0MIcNzM)w{PCx%yH>CEM0r6KETkEIEPPF;`EVmL zmNUy<3wvkXFk{tlynSc1yy(D{cpENc>*!QFxr0}9@MgR{7m}^mz?F4Y9M;J^>DC|K zxq~jMN!!!waq3%_8tt8`vquTRUn?(;Ot;cz>Zoiau*eVXz@gETkaPbTG zyG9XL-<-8a&0q_}v`;ALD*uu)le~`-SSLZ2yM$%3i(TDdc#4?G{j!$3 z4+!V7I^htS+8p|f3(;a3cP0-<6Sn@6G0b6@2&gnOfUmCFZrp>5I8>T z$5D1?T{WtRHF_@YLz<`+*4Jf@U`k5z$A?mM(^2%)J}1L&u0R?^uUmX&*+1>uucE)B zJv(TeyB!5-v}?=Tb>o{dk7C_FzD%*MEm+)9u~r;u+F@0ie{U4^G)q+DOIv2O@#j1M z=Lqlniv){9+B``ou36C06mqt8e-UPIy{$_;thX9FcSvVbHb2TRgtHQ5)c_XwCa=LD zA&HEMiCkk_S#tEMEd=yOCuH481tnjg_=bPzr67chHwe2jb45*PU{DQUfFg}x_?kpp zq<}@8)9w$dn@!Q-RGvf6oTSCvkieG;}}6 zv?d;x#i#@5u?b4-ou>Q7G!1}-40pDBl&i+aJ} zPjuqs>^>)HVYO;h2q`Z$Y0WK3MO=CvX06~qS`gQ50~4!GbfaW5mt!gJ8A&i7cq}iE zB8w=6^kF8~XQf&>j`%=|H%};d1@B{8lSNO}MhGIjSpz@SI;5%JTS`mlqH|#at`39f zhP@8A(b7@8)M8>RG&kES@!PU!6;|K`${8X)QPJbM48q<3uSoCMfw?T=98^Yvs+$Sc z42Rc$R|Jq_kl`DNyzN8$1SFXlrKmsvF3{D+ zX}L2Ciger16|tWNej5?n4om##AdOF~&ytWiG-)(25eRI=DsZzbtd|x(dlg`M^?Kj% zPMM{sdL-eCrBL~PPH1UIVadw;ghI`f%~zpt;j$&b4uf=(Kdvp4c!w3wvws)q?!Bb{ ztQ|Xn!j~z|h9h?)ufbA=V}1IvhCpi~qdP~i^%roU(&nv%&oS2t2rZRokI>5E0j;WQ z{ksKj;13V{2?O@3zkSm18+{|{nCimq5_mh>C+{US-95l3FPU9c&sSjEp=><%GPDuZ ze{N5Lg>r~0sZgyyQIo+In!Oq`)k}tgchlOEp;wvd z(u>_**fhOfukX*f-u>#m)ii}Ey*J$;c9lcu+TY!U2>awSB8uPHUsMQEC=G5vlnSih zXMonFxpPC42ZWEKZ&i{W+(=;U)Uk1Yvv$($T2uaDFeL!g%sDi*wN#ipY(3BdUZ~3L zC9}Kw7{m7vEp?BKFgm%1ld;9mfUU$J#-tP^%_KRGmCsJs{=Q8B)<22(cGMThz_mbYSr|F~xjWxQmY+AhKQ>RJtvh!os?>h>a(}8T z;+8VlG3TfWtQwjwe~n#zO`h|$%f{L)DSJ~1zU^x1cA%aDW_enn5&By;RR$2>CuFe8%YXt zk3r;iSS~^}xQ#5Y7Mt>f92fpa;~EaQ$a2PhRJ%TOIfGa6xVb*2N1cmGTRyKcScO2D zXILfQ!&dxm3mH{?m|~7PZwpWmKz;&bVDNC3goMGj$}E`EkdyFH)jB7GaD^O(@zD&W z3H1AsqM7?((-RLg9yWiYmSHMU506F9)+t2+F@KJRyBGrJp=&WWoi7q>uy;kJDH=r$ zgcPKw1_JYuPs(qE%{oU3(+|Yx_zoP4uUi*&07lMgSWumZF|$BS1oywxVPuXz>i=CG zKKh^PF!cU^sKaLep$>=tRfj*V|E&(oDI5B0Ml5(=937I7E|Y-j+C_|BzdAvt3e!4> zNRizZObKK2D~=EYS#dkQ6F#^{c&0r8?yBk9+XPsDPi^oa3rkiqI5r=u#WSAPW3>18 zX>a5^C%bXek2Hy_n+Et5$|OO#^i4V=h@b9*^5Y^CI#4gcR(mX}A`nc9m?MawA^B(v z^<^9*zCH&-gN1h`^kdQ7=jo<#-eDcql8gkykM2^%q%uUOS9WJ7GOH*vgEo=l<;Vq+ znzlaf1=HYVN2%Y@Nz<4B8-Ptx-CxRArx~ZL=rezY)(=A@=(uErcJ89VAZk%?X9|=iu(YQPP0M6v)W6w-jkg!X z&*Inb;<2aI)L+0|jsCQk78IT?ehH!tjoMMyoz>X`c6EZl*iA;&lOBH zVRPsGAM&uZ4T<+YcK1|oL?vB04`ZW`4k=%d@a`%60H|sVsAk&JNQ>IDt%pG#U>GgF z7dox2hKx^uhCrjmy}ygPGbk#qs-E3G2_s_SG}I-?Jczda!hsbs=c%1k!xA##*us?2 z+k51!DHw@}(RN%c4%v{fDLiKMaP#&iM?Z`lU*vr1NjY6m1Ki!#PkXQ5<^rI4P+cM% zP#qz<^Z=%0$W8m$r`9h+W!jAecgl>7 zq&Qaq0w5A~8lRbWE87>L%>Ju|A?@uuYX8WfGEo%PAkQ%7L(xou^Q-`(+fxj)b-)74 zO-vAb*L5dsEjM%{#yg&2`finzRSVJ4Bw437H@YOf>QUe+RQk ziaiO$z%lW6nb*ZvB_-F@`O0T3gS3Qx-TFGW5_K)6e-oRV(-5mSM@A-k%gvD$<&SH@ z#!diM&V0aMUi(cOJm}cAzy@vT|A%R{rYH(sc*~8Fh@h5t-$i$u(2_UCmZux-OnT`< z_&HLdS_ULGnf?E8#D_B3s|3Wu266Tcv@uo6LGQ9B=i9#=@d?%nI>sCI#5{?TkbBDr zj5|i-F{PDL=DRlUpzWmO!5cLPrUs1N9vKEWwdV>7U_)66_cW0;`&-A8_x0bU6}+Lv zC4iD07hnw_%6Pa5%L`}Cbijfz&^3*~TYf;ZV2~+MA?O-QSM)!uyBrz)@RYNhLr&aG zuArX-*woSCO=5X~SKnqv>t>*-ups8TKC}iOU}=EjgZfyfNTpn`8M5fVMTU|YwClkC zlo+8}Cp#x=V8>p2^))6~`YyAVwwC}V6OatPY!Fd(;b@lDG zj9~mQem<`TqdsJzlGbCqV4A8u%;s8u?*$_8rJ6hX`2~qTuUz2Ue@c+~GGv(*Tmt6y zGBw!vF zQ{(>fLU}uHI=mtC%3on0;8hPWtk3n&s_wFY(lGbdu}lYbpv9dZ5zunM8Lp=5+@2go zMsZ4`(l43%Y#AL>8SJb{jEn#k<-5zJRU4Wk6v143~2O}Jg6s|9V9e+bi0O@893~Xh!52q4YC+7?0#vWo; z;5Gftp*QApP1r}w=>Rc^Fz}&kurtAL?kjO4Rbai=pkr*pSW|yMqy1?`cue2`EHZ@& zNX{y|1}HY89#H|ZLJ(_y6f$a66{L{23 zdu-9eanjS>+31bPbcr570w1aiNDAY>3+2q;dj|S^fj&E5Gx%~#cbWFIv3h$uqjI-{ z6(z6>)D0x*U-ytI^e$5CCElAZD`&wue}*1gLhzQa2lHikhE6u;lHfVJi?M*fexb307rx?boO3tT1N30uK6?#io&eDb|skaBIT zfu2rvSEU#Xf>+AA%3qbXO9kS zxmiFJCL*rsp9t6u6&&u@wLJ}qka;!{mb&o)oM=Tu!BZq??_f`w>$!6t41 zxqu;NvG(kYFo9$JR0~UV7rj$zPEd2_#Pbm%Ii$)Z+kB|Zg?3KiupUk(mI%%t3{-#~ z%3a>Bt(t>r!l>zOgD?t<@^~(VKl})t22t&rZW&hYTDO3ldC`9R#xd0hYW95)#dRDS z%__T8)_S1)9@-xid$+A>t-lWQP`zfvm!u=xazLOxFE{0oD`V4x_@TEGZp0;;G)>B8 zxJ{TY)&jCcf9l^aDI3O5wSlJ;~7P zke=Idf|J0*Iq~?mOX600QAV-BQcsS;ChD^Cc1_+{#h+i5hSO>GAKa0` zoVbZo%h{hTD$@#z3I|%{NhsEghu(XzrYeM~w)mqW06sThSr8eQ28OhjkX=F31fSel zYy;c~mDq=k1oh)ouAxn zQ9QTrb0{&erR}%+_6H-U)6wy4{7f!BRyW_Qt^eWrWOVawPv+9A!wE0}Cr4@qE#UEp zw3R_nppm4%Xp0Sj>VI>4O`DSpE|)d0wufB3XyfRh{i{^!>nocrs$*|j_m>DM4Yo}> zC?CRg((drQlDR7hYS4!}%lOx9jsCakf*2gJ$KNeBFjBZVPju{NY=k-2w!F&z_@P$dEe7S;Ua=$GcNb(d}HI$EW z%13IOU7;Mict>ihT_*?o%tLzB-PJsPEeZQqef{w{MhOz;$}@55hGP7pMS}E0BZx>C z2>wY~ClX8+xwc&-I@3}mO2RHpy7IAr<$--TDdjWckgZg3y|1HLcrDC>TJN3?30(*n1o!-dVU4K%v|GqI=&lUx5S!R(aUSj1^57 zk`AcwX1c@T>)Dds_IPe=Wvq6Y4$>ViwphFN7$=N5<0h+<9vQ!8I7_6iBKxXk8mdZq zvg5BVn_HTYsx4G2e2v9mm2SHY&@zc*o^;S6(3P{nc_>;{qz-#JPG)WNiS%ZC7qw{d z{8CJAtI!@qr)s{=fTGw5suqZ5*+5X2l8lC_4JLuOx63k&<`qjT-L2SF14-5GO ze=Y9Y6Y%Z%Q%V|e=(9&Xq+qdkwi^7lobI+1{G_;K0)@h+|5OLx+ZEU0hYso~RAcg%Mp zvIvb#pgy^04(CabJ|*8b!WI~cmY{vqE(XadBaTl(%3ofe2>D)K32XAc6w*z7%J=Sh zGKd=BS_U#EBJFd@L#CBjX-{dWr-9hCKqp9|l!V;ld@Ep~IQEojmFibR$iL)?nA@RN z3VwcOBx*u%FSzb7wQWcoQ!uCoS9ZVc-*jXY6QXl9xbqrwI!9JE4xmoK2fk+g2s1?GU63rp7W(8>)HHzVwgDJ0Q0oe0rnf=ngnn znYdlI034M~v*HoKxm`W%_FHOZqBGI6H=5c`!BBb` zv&l>Du#!!jc8bZ~dgj{cONZkE-rkCMqr%N~9l^86-iUrnCw^Wddu8G?s7zYqRkt{2 zhvH@2qIc`f*^Hp{OgQ6Si2|qF5exk&#$m)JJ4C-!qRapuhtDop!9uMF99}SaNDlWf zwCj6=Q`exPPVvcbh!(8!nBITrNK|^8&SL8x^{hS!y6y*m##t$x+;`$Cnb0xlX_B%b zfop6ES_U7aXA@E4McmQYDp^ME&n|zY=LlsdqD5WJ;Sr@ZflN3QzFhEwk2eZ8+HV8N zDHa=bpdwe$%x=KOdPxCN*X{vGO(pd|o<;9FdEAa@X_}i^AmW-tS<}$eHnT9uHi^8T zrYUP|N}pj2aZW{>*U;oQ^FW_{3qc(3q2?u8UlkD{0e317KN?bd>vPn!P#UNA2a1GP z8xr9w1!mQ%&v%4LF)G6mI>w+JmFJj=jm?8!Hoof6-BQQtj}@y+V(E`Pe2y(_tK!tw zeXYP-K6W%RjXPMVwD;&l0)uqV=zP855}1IYp|G4|T$y0dMuVlj6#~2~u{=94 zLNVr1$q_PC9@Pz)H@XT(QFmxLme6YC&D@(4t4%U=(&iQvfD>ukN7Y(xvjidH| zmD{>>x@k@AQ1I``y)>#+gHr2Ne?eo7-{}nABzZ5*&0gP!6?Nzx=U?+ul*_w#x#lVl z5H0?E^M0iIo|F&-vv>yR0v_wbiIv$>nrQBoIhUE+No~xIGUiEEB35(q{{{YLH$;`g zAiy{%Q>E=!sL>WX(B-I$_p7fC&c0Y%fl4E?92Cr=bd-)1wTQ zKjRAQ!Oa^R-Gkt}2e~y4C&wrCwx$wr)Hit>Zbq`LIMk%lCTil;`s>aSr8h7kDf{~u zUcG(nF0FN51*_w1{Sr@b)mc_*ah&4}Dq)qXkPq>N3JGuw0mtA)UF31A~tHN?It&-y;o3WM0fi{hdwDo_wCZ%qa)Y$h!$j5o-WjQuU3M$&8 z#oDFTPdG7BjMyME&4AdOtf)(wKP-g>c?dQ@B~XGQl{0pwQ7%#=k}YVE{K;PWg8Ynp z(+dnFqA)EGxD`LooJFQ;glaT0!4WW7Mm(RUG+8k(h^@geD34^;vb4o5kduiXeY$mN zmN+cKJD;>(266UQRpc)`f1K}97FHt8(8rtykxfaw$(RReI@Bi2=p*GTfPBs?UK0^U zZmYmN6&w42%={C@KrPpB7WK0cQQlSR5mqD_0#CG{X5UamxuR}yndP;2^@i`FhA!b= z`SutxRhov<4bPGL8~##{b|h)mZ*FdX01;5Y#|I~Z;rUw$7@24j{*YcEd$k^l zaj+$*4J5aODBb`oQMoX?tY{o^Tsj6dDVcyun@S{ghMf=8RL(dgjFDv}r+`89yByWe z`z86y_{Z+0%t;57$(mH#f~{Oy6pcZ#I!7lA;d{j9fQ8iCseX$$JMsalF9;na&gvPm zGlJ-={=NxPppTUDAz7n#m*esQy4Jx^S&d)ON7AiW#9WC?JfUdK$E|qde?=Wj8tJPg zXPVaL0Cf_JFT>#24_WHr;PE;6@$>pt%|hT_;dR5?^$h~+obe-6wN>)9iGao45>0zv{{C2NjatXt21nl8k62^Eh&p&$uhY<9=ASTJ#WwQxiY1o zJ|L_)VEYf7C|G}>Si&{-`XBGAH(g$&_3avT;diw0k54?QOhwOmfd>2yu`ymV5g2co z3`?7o=t{FCbX{(b!?$@tC(8zouNmEQSvaI>+!;(2{u9=_(`OhHun`1+*G^E}{YLe? zVQRG2DZb%U953%5?rgB|XSdMa4~=o`w`qXgKodxJW8fHvTS|SCU3x#GX0e(9Pd%4E zy51Bl-aiTztrXm&+m}DI9(nb?-vJ{wkp@4)2Zd5;#p%VixE;h%tGgV^Xw72V?53L` zBdR{>@HX}OiJZOq0^4Dywq-)UJ2u=~tWb}U$<{96uFiS3lW`HnD$I`_&=Tw1ynOTZ z1qBX_N+x*4T?B|Qp(6rZUT$a5*F*Gwk}s#IqEs!C<}D(N37SMr+k_>@s3MA1am#iQ z#YvjPk}ab0V>AhxR`K_WEuwjC%GNQR5j7F7^4ALfOJ-9vO##w&!7RcuMuh{L>mv_# z)TksVAxUhn`S^vL~j0U+ZR@ti?~6$`w(1z~ToFld&=;W<-xin?w2A ziD}=8(n(u#C;b350f{*0;_yycH0X@bAAh)5Y5(38v+KnHF(T0WztgFTD1{ojfRBm^ z_=n%;-lA221nYraA2R>qo=y!2CcNi$t_4A_kQC09-?&+-5i66|)I=3s>2;H-E(V`>Mq+|lK_IU9;a^=)Mo53*|S z=&MAk^ZTabE^^+%Nm|D-nS}$eNtlf*hElLZW?;++(b{zLrZUqDUF7t6O4Ypmf405D zI2ObI{tienjqVxL896~F5{3{&i0-Gi^k_BL#Y^PX=i!UjP`~{1BY=N^1{Xn{!2^$} z42~#l2+q{XV3l~@OTT!RLx`73E{&4el`O&}9?h7evaGuqKqw8M4r&f-JxUd}4%Cbdr~{QCc7Y3Fa_%oHm^P^92%M-AAhk0u z;drd~BaEp|-71BZBt3((T(>!8SymHD@VM0WU*h z{EO-ePP$v7Pqt&{T8m~w%>%0nTD=3^Ie8y4?iUv($KZm;Cj?%PD z>TB>;lw6p05o#s0N7A)f?nq3tqgR-KB(dzjtgbQ!s_7_w=4p<|ekKQa00t~r^T)9x z!qc}xiMMchubw}`(zusbKe2wA#E2{@T12Rw zH7WY>aXg@qSZeA`!%#UOM`)2RRC0Si$_w^<71!Ebe%z}kiXF7)W}w47`y?rh@I1qy zS%68U*`c7d1$*WCU&vB%E6I`6g4V@8w5&Zq%?RTkKhI>?fFAM#679%_W`Txsjgs6D zW$K46MCpDd611S9_@yBo3)-h`D4f`k0%a329kv`wQ7Q)1VOPueiN`pun8u>^zu9I` z0z6%2Cx66?8a5y!I513z(S_bEA~v`+Hu=}s|BN4mTa%o&&!_-?o=Q#%gLykVgcO_n zGmNUrkUff_UOg8Serk>?8=mS8HCVO67`DPNeu`L)KgM>4?N&c^XPYprJ>+oOMjvaY zluFv#CJ7p3$T*2rAop$-p*|tH8N0!NHLN`3voNUhsJq)?(vDISKRn~hvi>*Ete`R= zWln6j1ZxBSZ@n&lzA3Ib+%AbAjf-D7O?aUu|3R$`w1)wTNQ5h4apHvC46o$x)oxg4 zbDKsSF52gO;HalU1}_`oF~p`)=(SCwj;^slEK!|9eE&kh*CkH$+#)r&8NjFO9>iGZ ziZ`!f8{2B&ixH1wTTwGku?cuQKnB!%A)w)qaL>(_WdBBVfIc9$Ct>fK?u4D?`wmtb zRQHMMz;!L3MdFlC7PEwF0GUi;=aB@m2EejZCR*MxF+lwW7_$;v-KQd2hzt%{dSdLX9^Uzs2OrM!eMyK*eMa&QsVH9w)51Kc0~TG*qO`;_A+Svi zh|Thh@(yT@h}4ZXxXUCE(o^QPE(3{ik?G^UAQPRZs=t;Mw(^RO6`c*d=Uo+S8M0wf zYn>JB*gqb;2UQ%E-(#|d_$*}|RCM*T6>|8t2g_o&rR7+{dUB<(qQk7AvXv_qL2;BY z@PK!oB|l|~;agcKD{=Pcs(8_exJ203k-GPG#HCp(BHBXwC+*a{bI!I$oQ%*%;9 zsLP{Zej}nby3k!HW-d(J&2jTsUaQ;Hj~S89jDt8n-_O?@iy0!CglwnztPK_-|Dcm- z@yPsm_MO2NGO<*Mp=cF?1p#xGI|FOkuSlWwM!nZ`*Gikx4c7{rsK+4z`7JgGVkAUk za)|_YO;LS4Q4J5%VGeB^#q+GLSJ4?$upqHqwv* zJ@2>7M7(dDBucky9f~S}v}U)$x{`%qWIX5S+nEw9b>4wb>xxl8IgV!vGo{VDcvqp- z041V_r1I}Hxss8Vgh3F;`lND+kX5l3v=pv!Y#kOdwb0|!Ll2+o`|!DCMjowFPBrIR zvx zqW3;4g`(nc%#CC{07^W-eN3>1%+qdC`H$3a&5B*GjNg3#pKIQ?IYjz18J->0A(4L! zz#ox5+}E$x-et?z7rvin&#&v*Pp8t9-u=>dg)~8%X}O5GPaWp@+NYlt zkZUK%Zho8V+;5f+{8_qhu`#}_IPPvwvni*BI}h9}mdkXtpU>T9?0fiK*Tp7BkAv3I zlvB5VTKYdWKh-|iy?gjMs+R|Yes5p5e$78`c6V=Ye!1UwxpIq(k8u`HFS~Ck33hAC zKPp99U$e%&?=OBDU&U^2QGA)76Jpub<=edIPIq*7VH}D_DRw z&ZuR%M8DirHRz**iHyb@s^#0e)gFF%52Uxa27j6EZbSEX%+rP2*WjU760xxj4SEpw zlNE>)!F^8Eljih6VL5~{wlOBJ2CTeIIH=&yLI<4E9~E>9EnM7X4-f<7x+8l3q37)A zM&Ix?N@RL}K>#+W5oLiMI|TFy8~87tsepxD#0ugHy+*3IT2d<5&G^mJxzg$O{EnlZ zN%^la`OEk^zstjM(IDMn%pu{V-n5FvD2CG&{CR)f&b4<*LF!DwNB)W89b- z7;2E4Yog55-AR|Ep!>bQs@mYjB-p%{uFSYq7z}VvR-l6$^-K3NlR#x8Hxz;Bb*R`I z7#qK}MyyIM7JCa3O!ie_>H9xU6zwXvH|yECVRiH}u+X9_exypj#l2-zn z9)&uV+a%4fYs$^}8>jw%E=$Ba48!H&E2acp1i2yWc&&<`TE5poO-c~8@ocLd`eoA@ z_9EA?ANa`%Vu@o0r!*YvFwMz|Ewsk$bFPwI`#HJZdxX%GXA(U#n71;U)vW5P~h;Y%`0@iCSMgR z>8_?rx6f06oAg)oV@DdN(2uLlg{b`OtveVe`6WndEfcgIT(Co+Oq3C9k@U1LEGY)} z2v&gdR7(v%CJKNZV~^)9t`0ctYu#%?{4v($Zsskw+78%h-8KO!OJ;*UYE14=Y6e(} zbk}<+^F6uZEvJdgyQoZz+L#4oR%ep9-K1X8O%MJyVh|PxQetkS!GZ|gK*0!;5LFw2 zffblTFy8#8#NsFZQ>APlFS#lFieSO+v0h;wEM9;_R=GHyrJ4;pAiUgRvtawW632@H zc&XoK$6ks{by|-r{XYEFF0Sy=CN0$V*?`>^od+mcE{6&U*r1t>grbT2 zwj3>spo?0)aEJ!kuTXKkHF;A+$4U)^BE{j111^Zw!DLFBkv=S}a z+pXKl>iqS1j@=C`NyQWjDB~(ivS5UgT5%_0RX4%>4sCsCQo24MzsSFX)t5F0lwtWz z@n2OI8x`1~UF)d#8S=^F4$z4ivIE1}+(a*k>z<^m@hpsUbp_*%z7U&bsr{!^^1@%j zaVy#ra;dmQWB%}`&=j}vJ#VBCwb%~V?<&yG}O0aDc7ek7BWe4!~);Oo=u&N273@Yc=;w==0{4 zDGKjIz-Fg)^pv6H*U2k0>&=)W)`aktvm^9*X?_PhEt7dk)i@lGVKoC@NbbaS`9aYl zQ#o{V&Sv_@3*AEktY6l;E3*_mYi+(BJ~DJ4)}CplZHx}P38nnDNF@t-l^f_5GTzAJ zQ!B?NJWZVNK2fwSV>UbvN|DeD>YhkT((FvK-#gKfb`@&=&qjDz+|Bh7R4AFTw3$UG zQnZFFk~znq#Fx(Vm$LZ_@c9eDu@gm}-5GT#@%exS>r@k(ZsClPn1**q;?%m|-NVpXu`#CQr0*uy*08mdy+e)c z=SNB@Rl{5Jy-ukVFo=s*?Qe-%)QM>_a+m|kjs3mLo>ZEHgj@FYN0FG&9MB{{UiQFl zemV!?IOGjBcQuX8GZz=5IeYXg$L!|UY^L?3zIinBbS*=b>qv)&QXp9Ot&OCyf92w! zZ=EbSWKN8Vj!#Vekl8wl(Q`g6z+|^!ck`;Hd~U{a2WZSN5k#F?PzgGR@7pD~p3`zFeXG!$`Pi*jSih}RdWjzhbtSvi7TJ3psp zah&S$!&mIbRUiZzw;&Ep(>!DgBw+YL2_`2%$0P!!>GIJdMHnJi7?O7wS)&LwbxbqN zw_$fV(%Vk%G%sw$7YP3PhS5e$Z%1{!1(?o%Pj_%0pmwjmxW4-Kf~4Vlzo5IE&3xab zhR22rWIN&5Qz099JF!tJHNN(8Q7w>sHX_hD}Y_>ROhzohn!R{Q+nE@m4xz9;gbS&IX$wOl*#n<~XnhPK?4PkhyPHo9eiQMaK%Z z*t_!l)csH{HHdKtbd^tl@2Q!5FF$+z5?>(4ZVdZtP6MQb!k%Y|<_Kg1;eJFB++r?3 z$<2vJCvuxpF_7p`m?>3?NJF-U@2$d8Rp#Sa2fp?d(Zf=xyxZb5Iv&7Xbt?xAc)vMI zQK+maVq-E`DM#vlzM@SbSIilX0lbwm;aWdPS|&$MKE<9jKK(eMpJlkrzj<%I2wlj% z&p_$BM>QnBg7lw8LT~=o5(>yauZbOqw*1g|n^6JM(ojkFYgpzlGIDOQqhlTx&kCU? z3GHQ6Db|Qm&w;FStX3#I2r?$Y-l8D^xu=e<^!u*o^W_>nL3cOnrxju9E&uRq&(&#V z^lBzk$aPH~#jzDZM7iJg=jdOoC`!X?fouq8(!^YD%6yPG?+5_JVC)PI%PK*>3*v@n zVa|rQ0(nvAE3B06+Y(f+POmntC6yAy@PSK?Sq7uXZd7YwS;72S+pHGU8P<^FZR{k4 z(fZGfUM+y#ZxFUfWQf{3!ki(mgtq@EpyP$jMigNGf>GI)K22QVBduTP-pJ^d@rcChf#zNQHcdu-V)p%Q-$9CgII zyLDP9ehVqbr}#-DRQ|2)a#7l#Pr|eOOor-f8Z-77QVF%Wfqk>SM^VcIq3v8LOkcz( zuO_!+)||Jh!PC)3BmUN`pI%mb?#9}rnDHI8pHP_fQ@7##ow3AS_86u353QNT*XvBt z`PiOoP)_xJBiv!FSHz0l5N@h!%oMl_8C8Z!4->V51Yo>)8d&rc7^3*=;#5|e%YQVUk>ODM>tm5tHjM23iLir^t%>ZgV)-!B z>>Tpk49{SQ=o-ZG?hEV{iNf{ct)VrO#$qHz_76=0dIR4^B_d<(NXQPvW!i91pY=gN z2Gp(dhTWZOo@t`=Ue$T0+(=u^!N@xdS#>pqw}@eEY5rlVrvoH%`wKC9_l5!Re{Bw@ zS(3}+H!N(ij53~OJJW5nUBS-BlPu7iw2O$Y?H-FoWSO#J7=B5P+C-k*gb>^pOL}qbJ@d7W685>>=`xfti>z?Fq*KaSRqO2?;qHx7|V5nCbE>wu=x zvJlGN-w{KA#*ZBU(@wdA#EH4F9l4$Hw5h=veQq!3VtQ5*NLq7~c6b4H%b~8^rqqVhLA@Uk(dlq=;h~JsnSEtw z!`X-(7Wy93ejy*;m&R#>9J(^57Skyw^w2v7-b;y}jL5Aw0-5zjAp0+7yig|`WGiJ= zuZtZo;q#g%Y$w6Qpj`rpl~1nkB!dvBvLMnk70+3wuSB3626Pa5l4Y3xkJNn{Pj!^D z4m#{M&jGt>Y)JI;aY7$1c!U?< z_QB?JS+Pf!Y*7ffQV~7cDz1G7HNMfUl13nwvw1twhj0(N%gc0sWQ@G?%&p27_8<$W z+))Xgh75dr1|r}hUdE9eF_)`(&q2Y4vRqQ9>thXo2w;GENoW#0vx}!!`0?2{FMY zPmRi;N)=SxvJNbdh|L_1aYZ5HIFQar|L_tIu`~BHoAI1fuT>xKkYa9>vIWtN&&AeP z&J98v!qbKGbP;4h(?OIDK+hZtbdqi}s+DMU-htZZm7G79nx=hTzO(^M(4h0{V2RTQ zm)BY@emp@BqKS6bF308FGd`Fzp9q<=Xn7;QXH}*5g4kRB#Q(^(K!^LH$xtYyFF;Xy zC6h^7AnK<=f{|zmd;u-OFI})#Q%L|ey}A^3_lWvu;@1ywj#@Zt9qMEV;DmMtM+7It z>9!;Wg$+18cs1B0PdZmEyG)jWjz?P=d;B4-gJr5YEDH~@UWDaAh5Bm3fy}OP$8RDz zc-G>o0maQdop<#=;kDI{f;ZL2nOr~f(rspBRFYB*oRpdI=)bCtep^JaM4x{|p}k&X z^ifY$LSI=HNix3|3;(lAk$mrfKBpcVcx#P+C*&4Qt5NtZzfzX39uCbyYmc0Mi_Ucm zyzY_a#fpA>JDAd2n;_(RmNOkf_yr}jaZEzN@?&{7-3uk&RpIWn(7OSJ^H`h1)st=H zsWC`aYg>MraFkh|4y(*Yubp7V$T+oUi9_FL)Nl@e zC77j*kQywYGU8tp;$>Qlx=SWJ&Bk%lYmKC$!@$)}4mC3GAAfAXN>j3bOMAPqIEy)K zKs3=YIJ8|hu=quZqr`hq5BjeG4~J>0T+7`A^WUj?0kQj(A`1NBi2U}_sg438aQfB2 zgLZQ;>XH6SD040Qtr@?9+zsjGs%gex34Rf}g?oPC->yza$d^xX9K+g9kukpKR{`gt z-?4%NLuw(`VVr1}k435r!7ZWEVnv^lxKX5_4Y+9w70FHA-!t`2LIM(Ekf1Sj$-=HL z*NGZz>Bz|z3IJ)sKIkH{VFcF&dtgg_EWuLM<93@wQ*?DCy%XbzU%Qw+S z%YY=~Kyei(Ky>g1gvf?9Ks6~^tIsv9B3r+wc?^WXL}-bE{qta?E^vz3y1yB&j&Ti) z_oT=Qohj`is~P+^fF#dr0p2c2lN1!CK$KX9Qz1q9|M>5JJe2Ee)@$MOsVnw-doK1R zLY=hHb`&=TUwCeN$dX|k>Z-7~4yF1=Y7#zk32Xt1xgrps3iriDo?-)=3x-N)qi8~Te zE>J2aR=~uXxW^QEPkELHx-OKh8+{Z`Z4tY(^Wtpsa{K(u3M^U_N}xiFl*Shr3&^~$ z=9*N~s@Hm0K2~g44(hCM(*PY4z^NtUMl9ckDPC?}17|FI0e53^jkAm6_1R5>!3M>&89}yz(A+w8U zLpm(~z2ahx{%M{|-+c+kyH**oj0W>;g~^{)W&D~SYQ-Q}foax)7zSpP(p++W_FY@b z+X}4OU_)UpNrwG~HAVy!P0Zr5a)_A$2jpqi1Nv`NTMtj5dFH9vDa+L4fD<>`*61t} zUFH2H*(F9P$Qkwd?-aE_co;JWL( z7C^PYUjU(>wU}=EGr3#mpC*^BR*@rnhTgvFqYBY8U%#vmgwnKjmC&T4sK@-$mHH z?Bvgh1(&~38y$}_GK1DMBDW+_heuoE)CGjkrfDmhDUSs`PsgEV2sIP51{M|rhnPf{ zDxMo5G@E&JhvcIzo@yzvs8&$6VtEi7nbdk&Te;_(Ev=UmwM|OcG(NgEK4Qgeml55O z{r@|%qt`8gbR%&zS(aQ|z;TXG3mX&S@JAaP58Z=uJO1Gy_^|N@cKA77+~iJhb^eI$ zE5Q+f(CcWh<_HPRyN5_N5rP!9q)&+{e<6)A@3|iD30V6id2p z8OJznXt zEiu)vl@}XZ8LH{c&|R3i$lRfnI??r;y+mw{8f*y>1Ro$dGz~0+W6P2ZUlGP5zAZ%- zm(f}seLbb^yDUm|+-ugDW`li>ZcJnRwiXSv>4&Fhqigr&ZFOxrdgt(c9VcHt5LMG` zR#>a}fi}6y`GLNhGS~jns~!{hS)E-yy)yc@KYBTvoICx2ERQd+eIHY{`Ta&ZlD4x0 z-;!>dCJmJ75YJnXsrXwa@;}RkYdzW-#XjILtN;vI&BmsZHo{WLx!7UK{|Hv!+xEUr?K>`JWV_3O;L*+$^=a77bh)pMH<_*B zL&I}({(L-BHH-h*@QTJ4L&<)k^@+v%&VDA_og2ps+pgc+xTL{x08!DCy5_MqXlaJ0 zy7^VXbk-H-vJ=d4YS_%ASDyVQd2NAy`DRJR)#d`dabwuLJ#5)ZtJ=;oIrgQI4{v#i z#ymY;K{{R4b6!31rBmIL&AUURZhM86Eb({0xl_x=t##vX=d!C?<@Jp!ms9J;jcxPx z_Qrp`#b4((*Xm=Nn;)#6#YqXBbMfD-riRT2&EU-dnzR=177*Sn4{xo5yk5(%hHr~6 zr?J{asaU?jj|a`TsL{J)1Kzj$wU=Ij2j}N!k4bdoW5a^3kO;*@zxlt!-C@(X9lDRq z*!Gj`DX813sw5aCri=`c34g0FgjHb*P(CZF#;iqEi2)D=#FO&=@IN$=rH{cR7B!bl zM7Pp;+_lu&G3jJEqa3*i#Uz;(vf&Nm?6Za$r>V2i z`S~l!qqFqO&FdQnY^X%GJA)fxn?ih;^XvDgL$>PGiuVyMhtU4~2b2OzY;F&`wgf5* z!UpQsT)07AJ>WNLls(i29#51`|49)4K~_8XHnUYoBkWU4uY7+K7iQ9 z_Uw=-Ep|S`eZw|Zg)k~Z(4?!gI!fSh4Vpn0kpny_AURoO$%=t!704xAV$c}z-hZJL z_(#gq6oM8Kf}vHPfk5%N=J7Xf;QtnvxMkv{P~+wFYSrX_PdHz~Z(8x#73{6@Y`j#Y zE`ZX)<47^%!pBi{6zU&x8o=1lj9!sOpmIGQXtoU+S{l34kMXInif+XqsQgpt$jD+6 zSQiKuGjLnhZOi)sohB!N-{1|U>Ah$KmV_saLR2cdp0Xu7p+eL|>s&#$?9MGy!;PeA z-fD(v;r`ofOBSB*J_-FVjN+FfEfG42Ahux0Cbr#V{bsYhR9z3>S-I5ysZ{#s?CNTo zW)1hP*Tv1Wo)~D*DYg>`nCR}eyQ|Bj0R4p~` zkhh@8H!yg$uD@%(<6r)G37NPXB9Ix{`Ul;+DG7zXj&xHppj5t>&bvdYdL z(PC!}O&{MY+pNU)6u?@y3r!;L;5|$^KaCXQb6f(UqOffR0h>55e_XXs4&0%F#A440 zpccr=A!tqD0)ehBRDGEOsJpC>9<)<5G2m{@`<0wn2349spSY9~zhihS&W*}_AIInChZv9|!3r@Ycv)qeSS#tHPDtemG{k!E}fLh=M5>aP$ zRS&#WKe*A9_6OZ*+3-qhLN~JQJmZK?TyNU&>a|fUaB$;s)A*~#;wk_=08o?P4}^?2 z$Ldt$ zK97ywPmZ#S0*~HK@4HOg%J*sWn}{A2UqTbK8^g4nH1ZOU#SxBCUq+cHoaGhm+ierb zAO1+_2>44`jhNmKP(}4Qv;sTF$2eW|tQsP&l8o&SxGezuc7$50B}9k%kUlnYxkJp< zfPc~T8kt5gaE(WlcL=HzeJxTNnDA;P)@YTH3%Ps;g|b13+kQCl+RPQQH{xEIz*G+q zO{`O0P>_|$r^jdMU|EkyT=@m;p$jWS1s->z`Ez$KO;F7E)_Z*nyuw~4|8ojC>x-ye z9CMbpYk+b)@1g|bY;VdNEE?KU+-sgd*O-P1J7)YN1?yfXZKgj@`@q$iP6!(+s@km) zQcO&%CW42BoW#agpg;=M(_0x;G8=t(Oenze#;O*btGsxwo~&dx{RGZ&7dcLqgb>t&(Z)!?$ft@N8!eaXzM^=y|=u3tDSB(xPDe~*P{ zz`{N3{J*PyVc-)bo>LzvM{YnlD48{HOP;gJ8=I|X@B~nEBOtUr5MvaQzTMk>Aza0+ zar}4u6X$=Tk^cX}KQS`=R{+$%um3+Fpcq-%nHc^H1j>8d@u4+gtLu0EPl4e9ycMpC zw3|D3^d~hc$$3UXkNnHvk7qHTbm)3p8X?_h`1`VaD+9oJEjU>%J5N+hkozMr@1RbpSCf4eV#em#?a@|Yjiywb1w z2I;~)`Zn=qqV(GVGDYC>paK_;zE(cyEyu90HiV&#!q-lGU+rdjdApuV;(l)^^Ie0%KqhSm91R9 z47)`VtT?-=7-W`FKVkh6tI~*NYq?~7QSXOTIlqdZ!XAWoZc_v?D&rDx0cc>c;e9VA zHINpBxvsaF6u6~gxy1H*3)z;XxIa*~h^V%3>NRFPqYHpcXMIOY4(#mLlX;&?~sT*6K$G7d(d4Fb9N?)oqZ0SN0LP zGUPc2&;mx#&YF0i-3WI#bUmLi&$=IEv@PvA z2&pA@<4!AW6WOz$1}^%z(S+xt!c*jzI~WDI|sk*4)vS%S4e9W!}aX z7d4l?A@dQ~>aRKhV?u=qGl>>J;5|5gTp1(641tHV!W$88WV_hhUDXd-?$gq%Mn8#e zl*WMu15F%vP7yMYhM!|pD48g4j{uxIowfp79#(&J6GWkjPNCam5P3YUD|w==8vAH+ z!;^=fcJAJg|F(O~-T7zP$rgdrUOBxubNaI-RJGRUxc)jMLC5W#%gE!mut83+^tST9 zhn@46)!^0FZGxax9G17R25Pqu6lReS&lmJ|&}BN=LkSj@Iua!qHgQwy2p1)kUY`aR zxHQXOM?nNbw9&h?JJGrz@OOb$VKIGm*VS)Su@Ev}OtfzL=COg*O#!1>D@j53oj^P< zFBGtDw|(pTGtc-!iY?3ZBfgt!G7kzkLa}ASkC2zcM?^s;AnX;fw^9e>fdb{WiA9%W zo(za(a{v;+Y6a&Fk4K`!GZPv-phwzB>6~k|ni3(R%x@&E$uKXH>35)KPi+N37AcT~ zh5Up%zTyhiCsigRI*$W80+X#<8lqrvo}_WZ+1KIYx*g<9Ly$p)&7Q*OU<84DEuyK5 zcxd4n6a3(KUe$U#fTi+^uOb&T%shAGxXajzKPQ4&4x53FZm*_pI4Fdctl1;YJ!GlM zb&AB`Lqz>3I@ST?k%zN`M?-cq6$DU=utd+bb1?ut{AMs8FkH_`_sU-2>g%fq^5M>6 zkAFV@14dWl*MCPC$F4+_kRwVRx9Jbp^9FWadSU7RYLOrDUAF`ik=H^KQ6W_sx0dUE z2E7a^Z^gjDaOXq^P2>kv2;l&_%`uniMS7XQx?h-r{wNPp1XGuFptHXiM#Wug#e3iU z7e0?MfRfG}uqYXMvczZr$-?rm5P1HTr5Zs#1>!)7 z;hvs`naiK0&2w{mUULnyy@PfRKG6XKO8+HPX=oaTOksP>3=-xE2_}+HTtBsQ;C3qS zGt57LduLrZ&qgU369t9?1M)i}PfS^FIHG)gViBW~YyD(o2D6vV zd{t9Z#R@%j2`*P^mLJFzTbj`%MPXr!pcdwF#84{@xeA06;wj{5%z}&;ObfCpoC=`G zj=>U{0eo`pKO*l6o5V*%*qo|_#MobP7Xc5oB#$bXasf#uutHt_T#^|i zA1?yfQIA5%shDGi)9^@Z?{tlAI=Pd1mrE?0y^SVRqa#rd7be;)T9uXk^hS6p(KI0? zl}7%u6=o}$G#@VY&L6JRD&9u-M)n3T0zS2B_7wMF>M1;bkRT|S7J(xS9?qF~xLk#i zx@|WP4U~>5t=uR+i>8J?Z%2c6^v3F}TB>CeK1x9Woxp~`WYoDo*!k{qfh2Tauev`^ zzYM;PKl#6Yd41M=eZDjB0KV*2K7R9`iOUtwq;e|=X!DsqL}@A;l?w4y<~IW*Cz zP_$v<@^dp$*CuK?k!8P$MUC7}({GKJD-it5RO5(B5mGl9{hQE*i1GrOn%)akK zNA_neqv#+kyE70BEXdA1A^H&^(HXe#4DL(Yw2X^jSIVQxc&0_2szCMD%VwE+*>1@G zAP(FTI=>il3dJ<)3z)}^$SEq%Ay6yo&AbQ-M9;SH1JG6t;o8-20e@;(8UEIzb(ErY z=%@Bfe4!oLlPet@U$&x$f^5=zqN`|~@7++(#vKkCk$sncI zW9^Lg*%GggMUYIb%VUdo;tc2YRN`0?a<*8}bAKtS zT}%{>s+1d1g4LJ1H-zqQlB+qXe1rji{t0|inJQ)01RoqB?cZ`sW`?9^_TE|)V7BJl znuf}kF7(W@rccm3;H{B$1{DkoJjPwop*M=IKby{b*)JK&UEvJ;lbHL|VwiGq38jVd zC2;W<#D)ekhoq7sdEyJPA;#TJ57Ak2KofC;P!r??G4QNwYgIJ2E?i9ZEI8)G&*;N; zPlR@2b8pa0*i*6JZzvtPo`T_s2FSL&88qNENPe-EsHP{T;3+z5?J!}V=~mYttH}BTQlc`f^Yz=tj9f9YRPS@%pe#- zFp9}|hZMuj)}k2_tFE0w)!1J0&FZb5ackpIiCC2>VbcsVhcrUd3#c-I_2R`Inu$6& z(2~R&g<8b1XhmQegI)(cEP)uW3<;47yg6^GWwk#M%)-?K6dBy1g`LC5lG_0$6ds@m zR0tru6rn&9L!fDM{4jx>snVnkPhFIg(#|7gt2|%dA z)Ybf=)CPNQ20p1_tHnu5)e>E+CR12OLX z3 zBSKmDCoZkWtv!pfeI6q{o7L9JJigOU_Y# zlMo>O7o%gtWWfy!_512N1&=M@KVPdA?GE-AwzmU&OsHIY^TK5Zw^T0v72L=&Y-qn+ z(0-jEorGuUjimEYN`Rt1;@ye~v2TI}u#KBk-yiQjU9@?@AiGC*kFS>ci{!BcPjL+@ zX9Rp% zza_f9>fDXH%V)FwISt9QZT&~M>n%9W!||_vWP~Mh5Z!{4cSxWTk8rYOYx|RN-p-Mb z=DhnXUAOzL@@s!#-kskh>^pIAvMI6$1Q*l;fECJgTbhpD6KYW6D&AtGIJh8G&N;F` z^F?=e=w;EO!4!oAsHEkq;lfPg5idOmHM0yX_|#nL#HwTUrhjR8b5q?8e*C=f7Hu+K zPC>@a9`xF7bG0NWh87_f4PU1^b^C?C@u0dlFg*4B!R+y?#1h zGjTA-tn5}7?zp_RQ7<=&uJW0QRHsat-T$WL&zft7fCk1#GJT-i z*#U)%+}0K1?{dQbsLSi#$5B^cecYXiTMZRPe6tr)_#wx^#kcImagaU_VwTMzf7n#G zJDRjy@_h%6jm2hjoafPOVw#AH&r?v3nx3?F`aG_SMDjgCQ*kosI9^j5g=!)4?zKSN zrZkLuCebYTPo*m)B1OrNMD`?z>Ov5CvCkZLz@%~(@!7^vJNct@muSdT zWkVM9z6mx8aeltdLiFqF+XknbYqVAQn=0dUE7;KRGCw9uC1H71U$!wX>IXpldRvdA z5Kh+Hernrvc+S3?v30}!zN#f_TDalAFW-#FqU)G`n^_;P+1D=7$V6zURR~xF=RF{Y z#+*l&JNLPKkq(=qh5TUU459{*2QZGhJcb!K7{MUE0;RQ|3zQjPLY&zKJM`LMx;P;q zB)1XlVQ7-KIVS`7N=svF1#-|u%;za3f) zadT8$8isZ`N~_q7n;w~*dpUUY42p#4^2b|ru>q7*PtV?F4;p7FDD}$thSk6IaOHg$Xvi;etAzdE;KRiImG%HMmM9bP}itR_59>I32!@eFTImrCi&i-ZwTw zQillKlz<~9T5tx3m{g|1s1OaHe0Csj=xM4Ee9jo*UqRD4#x%AulnsueX`hT!ov=eC z8NP5vVnGUBeaBc02v5wp2IPU3f?;PxA2wvvpJ4)w6H1i)uUK+3{Gk{1X#}LEkAP=W zOWn0w0P$`uDLv|?3Ed}RVz=YA#`CsOFKooEeB;LDlIS?2K%PbvD87R=FH|LqhE`5% zHL+M*I==T19l}S7XO4Fq0v2Jr?us2er|=p}#);XSLS@~*AG`SZ_==Z}@qg$!vW@cp z__f_ViGNW<^hB;0-{0e}Rl^pyEbrSW>TFlNX?MDI35WUB?i8mhc=WgGPKJj!Q};`a z%SDs81tF<(aW&G2)5-CxSu&KMq{vNqL_f!V#S#X)rkj0)h!Y%ngMx)Ax1w@Ky}hsd zn&XuT08m8~2=+Dr=O(D9YP@jv=%WRC!k^mxkU59IzcNakjNB?QR&wQA$E?)3Y&>Y()Xm+@|w zPo|Vf$-m5v?HkO>dKm-7)5f0a32%gM@WC?0#`dxd4GF!;xluPrudF{H6IvzlA!+TG z_RfCl^BWTu^fyl-Gmo>r;FuzA9U{RtUNo=!0?yWU0E=Q}#oL{W`<;z`k8m)NRc7CZoYn zFWNZ^k5J4}5q50u0SSA%#NOQt?-74%hSbU^N$MX>0Z8+5|ksc-2iyOkb`hg z2{(5R2ozD!_OTIQ_bhG08BV&xK`bagTpf^~JC3hy($>|7ds=Nq<1cvehTWgRN;v)N zm|H_PY$0yPfLKF4CFzk=Euk)J7RQjh@p$Hkw@_j;C2suWN{FTd!9Xp{c2v$p(<%Ky zi-U=|Se@vs-4LifDO7smXmzM-PYq3EfNeZ8PQx0J8JlU7=#+Ilv#!WZ%gc20NII>f zX=GO0FU5Z()4_Perr-g9Gk5MRo;jMQ3)X`MW5me(f<+dKnzop|?XY8q%#fy23_jWO z&13qsL_;+VtbTE95%T1N~O>Zhn@+rzqz;9yzkPcG6G0IzZr_|8Ws z5YN2Jxq4zesKG*B7pR&112Id4V=b8m@ETsGJLVM=?{8<7%#Wnt0+8wLjd$MRHb=esi zAmK5x$9jmP`eJ>xvIuMOW9-f4|eI?H6BjG)CT#5z@yi6^R z`0oQ}Io65bU$jL>dxTx?1x4u=V!{UFu@N<$RA#M#v1&uSk8rDm%2eoXJ~!>TogaOQ zf50Z^I<^IK1C0TSgPS8cU1K!`039X;<|e|tEHV6uR-PY1AaVph3?*_x_E-O&j$*y; z%u$R6uGyQ0w+j`VKyK)$FmqAFA_zQ!db`m##srSU-{jBH5+seC>{S0~zHUpnwIzaZ z+%9E@95zu=+pUF7UAJ)`CNoVN(w{8Cxq-5j_Z=MIQH$=c4A;~y--fbegm4}m@apW7 z2?gOAc#>-mi>66_H%7tNW>zG+A+@!>F0|T&f(e%13FiW6(jgU1u{XM{(KorBHP#g+ z-pac{fH-yS&cV9!?*DMX{F1x5F8pSM%4*z8X!MNwV!h0|8giWib>J$sS!oKwtR@}E z{VsdmueDQY^IX=E!orh&_xf=6ssX(oZ?ocw7t^rj;yK)}cs%TpmCg~{kaCp&J=He- z#w)KuC<sB_>y#;id5nNPsFXT+U3M6S+-~Tobv{IqTxu(<3{bVLdW| zhLEP7y&6nq0vj>wTSG)m`Z=f{xHIc}#RD5jv!A>DkYTsFFr|FuP9f5o>n$tB&ay0L z{BC0BwehUVZq?dd0 zF;$e!rR@K+%T-Hd&T#~9$;p~TqW)~Rzf(?7$JLAH9fW20?;z;?C5ZZYj)3D02fsgUOGCByEr(@6nYQf!cZF zmZ%OsvGB9?3fmQc7A@s>g@o7Z0YpoU#d#_z#d1PXFd+`oT=xtci@fRLY-HY`M53H( z{5G@);(S_zG)0i?uMLUkm5|DimKkA2seesA573(?@iqYxc^^bV9PWZ=<>_jGRubA+ zW1fEIiYd>CgfwU@a?|0+BBrCQIZw!A?uz+m-$9)rgbmw!a@76vzOci0MkSR-pj#p; z-z(z2{~t?Ch_rHnlOwsVc#~sm#g?a6)wXpk;2--6PF>PI*?6ODmkRnH`-)x1q*pbC z>jhJtrwx8c8Bgqxp9|i{16uQW2Hd!avL4;{kgM4qJ(Pl0RM?Cy2od!mM>*sZOft{c zhE`toLC4x8JQ#Yar({613QOnL*yfIA5oW%!VQC8}j@6bvt`*y_rUN|;GrkOOGi87I zgY0jk!%fT2D5farDqBVg43%%m>_K3c^oc=@g~PyQmc53yQ}`xA7}-ZIy?g4e;@|(O z6$)bQb+E{K`J5WRuO}Aco9_BPJ`zt)JlrkA{b_RWulhB6dRN^8&1aB8O$RCH9yBny zWG`Y5N#=mtIV^Nv^6qImhE&L3QFxGjd z&!l>K4!U;cDcaJ!W(~^)FD~nC+2>DeGh#B3QiQ@yvQ=xSDhZ9bs2rCT3={65SB{%|z3Uo%mk zz2h<c-!h}^DB7Lx}Z;*Zijz+rd@HODgM)(23v#?kP>iPs^`fv z*m8mlZyBKh2<&S7ejI`$&V<*dLWhzc_;D~+9rPGW9;cy;E8~Y;`m@&4dgv^i10wMj zmKQO_C^G_W()o|#LK8km+;I+41TmBvreu6}+i!Dy?#q$rmTm9B1~2DhFN0^r<<1~C z2O($h7%^N8Zc@>s3lr9x6GC|Uf)hx8MX`} zW#{0xcZ;oqThY6~aK@n;cuV#lu2!&=d)Yq!Bxd>&qZm_yD$-DEWz>9gymoIen)PZ3 zE%6GIQePvNm===h?DLnQ zvMtd^b>lOb%}~X{ajqZWAA^bFg(ZbC%Sq6`w2xZFydt{(fLAseV4SCVeR}4n=U>OO$J#3* z*Quto($0dFED&)Oies1`VE*oZ%$ZIQ^Pn#dqX1ps0f`(~9d_In$INuA2HYQMHsADX z@tx$SrxJ`9!aiwDxGl(Lw8J$|iyeIY-CZiX7PUttlW!S&73?kOda=`N8Pm16cHtcD zt`C#duUB;;8>+VXbmPm}k>fvDlDC6-8U9AO&kGzYXkr;7(A9+$1o{VA@K-HUnb9|{ zs?UwH{UT=jr88sjk_vX}_rR^v*ENIi^G8^lHey7+8n>!HH#do6jj0 zrl0cx_>wSi&jK$FR+&P)5knDPAf+Rqc!UwN>Y$M@web{%`Jh~?S@)ENfB)BjmP)1Q z;I~K@gtt}|K$?lMzU3|{+R&J{u%bX~IvVYzO?Smc6W~)diCQ**c8C7;-U)l+G(WG+ ztEZI&mp3OX8wzwID^LQB1lEy(1boP*zCHxTuD%YbYhz6gXK4XWQLKiNQI_1xT)c!l z1Y$;8_4eHEJ3$G5^hn98%K0Wk)vijJuIHbChpi)7SavskhQ7S*)^sE`{2BA|*rqG+)Q)t z=Q%`Ys0D^CP#3w9lBd^$^(U+|E#Eg>cYx&&t3aLF%1R}gD_bTIm2Q0`?!oExi&CgA z7<-QwNJ&9a60!zfs}Cx0YEU#7icKGvQAO2Hf?+il4ZDI&0kiDugTI~xf@^)-nlGYn z7e7crETvK*Gxrt6FR7h#xsBrc( zk!4dJSE8-^#jTJmBkZ7Al9?0dlEX>KR6`y@r{KybdKZs}1KZ8etR47E3bOYad3NF#`2 z2ajIf#xZ8aHVGO^_HtQ%j`829VXT0Jb?eQ0y-NJ-l56gQ+91*JA|C{D6>C3eQ7B>3 z6g=rNz#X_l?Z6tTqIN44G%Va+BWVgJZeZFbKhF%V_bwxMpMOU0kZg`3-dUqng9mgk!~J16S0s z^za)y6mvOJ*mxrrb0VU*5_5tWBGHh65`QkNw-8e%qL;^zff}M=X@VS6XMQSGt@d8_ zZ`!=mle3&|%yKv5$uLdq=X0ek7FLX29&8HabIGGY(SDnEfnLM9ax8=7wJ_1c z$NqSAcg}Wx26FH_!}z0gu}imha>kT-DZ1X~L-fb2_)4ffmY4Wb>e%ZneC>5noJ;yU7Nx3q z4SQekGS#{s%{Pgq3E>N5s`ET_h#qv6oRik1XSDOz0`~AvG6Ll{rUsI^!pNwSe0I2K znqkQHDtfZ>wqO&d<;bcnvh4G2`3_F zO{v~`@z*(cTy<{rp9r-}m<0;MKb~en*y9HEwWZCiBWDe($)!ezwQkLUvs=ZCp+qW| z!0h`8$C-%6`dWn#M>1PHHMuMqSamv__nNA&`Iz)=ZKXQsY>aoid$^hXeF11O!mxf7 zh%0PzCeHTD!kL#L^>RsvIrGAqH!Rdn0NvIOyL6*+p(8ljXsnuAS+6q zhK8Em&%30xpLqrpBY?hhXX280X74$$JBM!-eXFa7Mlu@GYi|%sTh6@9*cl3!E1a96 zBRRE?tUyO9UZB&UD?N1`mBM5sTTpe|^L;KI9#m8#{+WZZXkGsGSwE&Ps7Kk}avqOIpl$Qhq7ci?)Ng5(kt!U#qtNE%;YbZN4eC|E1dem3;L< z@sI43`unMHN05j@4~(n@Z0N^F3endCg6QwUr4SLLlxMobt3nfKHxdPj_?!y^$VKv7 zV367!>NRZogK<2p#}|aGRi1Rs8UtR|T=pkM*#-w<8|=ZWkdfM*bg?-}J5$lu+kMg% zL+YB)z<;guyfxez{&eRC={Q+1&>^(AUS?Te3H080C7aWi4(qi1d@A&oOB? zJjEPS?XQe2R?bn_j4RmDc}&lSS@RJ0@^7-)ymj4x7Nr?!1SAtoL+y-wOB2G%`EA7?+nA-IheiR($@BnGS`Xe@7YdOxFVy%%N?J%7cJel1NJ_X^ zM&U8;_Q_u4g@R2fx$+<<84g4PI3Q3=aRG9sMH!006o9#=!VL=Bq$?AyGKOEA84S!0 z?xCx^8JW|25^`_kMr1{AP17xdEV{$eJ)t_9Yq!w7l4}rhY2C}&xRTQV?j|_H z9jmOgijK5K);9Sv`Y7E188HC@?!*EK@}uiZUal2p`=xdNCjMncMHYaG{5J z*8JNPcZrJf3*R@XxQb{kqjH7@Iz%D-LhGbBgh|H6T)B_CnX;f{UvgftQG)iP7xg0zw9cKCn}T*t}`QsJ#M9BI4(_tndGE`O?3`Iv!YJ_O?NKZy+7p?l@d6 z#yvV==tJ?EDUf4mh1Meu+*c}4BoikZZZ0^MIeeXvB4spL`0rf;IU8_}Z8bg_-84U$ z>7eza%}ZJi(8YM7zbOXnW_{x7snv~cM>?g|{x`Y&MT%T+k1Ytcl6RoJm57;w1f;RH@SUk?TfCB>V9q@z3ae1MArh>JB5tW)54<<{ zoRX+C&#gs3xkmP6j4}-;GY_7PY*tr3emGbj*!Su7-$>l3~-a0IW=S@ z5Y{s70WUk@*4gXz%KfZeEsWE$F0v>K6%=(KE_euCt5pW--e19(HerK>YK3U|&I5Gy z0f*oDKIz$6WX*ChKlT%0acgyF*Kw%18Ku|;KXskoNuJ`N4~{1i<{>0h98(cSW2!n6 zp`nZW2F{0TS+6Ti94(sKu6i8<50KDkB*+!Nko}ILde_|OOagL2hiVKDK8p<-ld!C? zzwM~XB_0N!D%J__B4?mwU?!e^zA%?5npjsh~7eHnqsq73Zm`I;Jg`F(51=*vl z)nefj>B}cr_Wr0fI@h}h&~5rACLE8Myfv+kkoh>r>{nFNRJ5c7U>LQ;fanuIY%;{heLq;yf8>D-e0vlg>A70$zX zouHtvJ|6)`WgI}8kVY-!6c;QtN^n`hm`wvL5p6C3;Nb0Fl_FF13CzVm41uUt56p5) zbR7o~o8rO%=%&mPnHz_hzU!~sF_mbY!1fjQnyZ_QL51Y%l^c(G$~4IiH<%)sG#SgA zr3;&+3zGD2Ap3Kmw^!j2F3-pdnYbA~YTNS}F4LR-iK%5HfUYbe$Msml)e2&(%ROyKe65 zl8-SETRLTyp;5_$vFMe4P$%Z4>(QlQ(;PT)pI;(05oh8HY8qz_j~{$9e^Qx(Z#sJyD0Pja(S^cwIOOFO$1GC(3-{1PLMR z)D;=-c*vZ?Phu|?*F%iV#_`c(N58WNdXmrcku$)oilb<>EZQY=eGD zd6xtJvVsY;s(&oPCc)PIQXB^m=$}Z{?>t}YxR0~c&7_)DXYgnLPE9bZ3oFv1EC1H z(>J~Nnc%+p1CGYI*GXX4u(C?Gwt($zQVsBgpv)6S-##FpAGa zgb1Xb1ax44{o_1#-w1Sg*c0CV;8|nuEiBfhQfR*U;C-afyx-V@wO1=|U;Cj=Y!?v( zw}h}qBOy4}cqNJRMorj{mwMrWd=JUykHBLw_M=D_7jYbIYK@K_!*R{MxtvkfA0-L0 z!6e+}(eapbuy|GZ*z>S}1iCD;Gi3$jufEfbjRWUdRo9o@$NKPn@lqPzQXR7B<;5TT zp<2XaT54{lo0&(Xr&00iu#ot)F*5d0@PNH^+9S5B1Z8K#NYiFY4cytlhY+j~cv&#a zgzS6Ak^06{Gjg&iuwlF($VdC>B5jidd*5u-l4NM48?(=Ds0=p(B>g)wjOBt9OdJkT zRnjo1JC*_o%jrHByC1C_sQxG{ z#96zxt%ElUP?O-JWWfd?+nj1g5C^1<$a2BH`;M3o%kLxbGd#}w)4z+HXAG0 zL!I(x=Qi?dwcAr^dS1*F)onLK<})6MRb|D5t_zbP78tct4j*+WnclN~BFJ95326cv zZN9-U4c9c62A`N$XYJHIb;s+CqNo5}j0D$R(8n=Tcjk&?ZD(KPoI<}yMFf8dmk&0B~iAHq{&SuHn$2r zP518%nIFt=-!q?qW(_kLa+Sj(_;={Lg^2DYovUDM{ka>N_h-3c($yZ>Fhe`c!j`ZY zJ0H&-NQtQlah3&|QI>Xqr3rDK2byv6yVvsi;v_7SsL48zbZjJIVXpWmPxLZGa>4fh z*|YSX*Ag8?Z^Dr)a9w7-SXTwS&EuuV_vc5ws6Z`wQ6X6}Mlf)^it8YKK@FlcBb(uo z)btnBINALC-M?>DO0*Ln7!(8^(b@?578){A8|0Np)f1=XWS)f!HDw>8L@gvQ`aq_{ zc|<}$3hfLqrB}rSDgrgYlgFC*$_ZqKyvxftEa1(uH>X1v-{XxF@I9Thq05nO~U}cFdPH#hsIlEg=@yJs!UIi0JK9 zc1DTYak5*IYUg|@Evvw`gX}FbcN0ro%t*wS6tp!YIFv_5YM$}j7!)7X;BjdS?hf(R z6~+cA-2T5M+kZ)vqkunx7e0{&(Eb{ znzIUbr(E*Vc3!&Dd-TP;^mJhXJ>nn?@F~)>Xndtub)+aqSW6Egg27;9)MyDd(F~Z# zsrmAGrZ<+Dd~K|dXmlkHlCsPl9Z}f-oY$bf&9OD~ zgxx&BCGD9%^H`4$;TfS%qn~>Jhj%=x9BG=>#B`y00io= ztpe%%zyZXZ5a`qcIl`Rk{C!W+2wv&cm57-DhCr$1z_H9C;FI=D%RgO@=hb#_yv!~b zFk%5|KBlXh$Z%Mro8{8AuXnPjp?*-$SN7fIG@TSo zxdX+Kxe#{*)@DAa`2<=Dh9l;_i3cWTSVSc-aYSg)pQwV>C;?aa;T1rm=#GBk;ls$D zrWcqdGtBs7S-3!E8RVlY#YsBJXrA3J-%ibS#($g#f($E~cUWrR6GKwyiFsZJY=(+L zJ=DWnPQ4{@G5S@+o?~YP#rJ{6>jw;d7c@QSg34LclO4VDFo~<;WPDr?$$-6&&8OE+ z8)RNWOfm~{Ig!6jvJAVBiza>c5T$$m8vZH{W~KBw)D5!(%^P@NW z#_rjGn#A38V|VgIL3oGi(>3EA-329wN~|WM(Nwkptj1)*o zQBtJ@i(%?mc}P4l8dWf@AZ%z!Z1lQb!XvA^z6O_yWNpNMINQ04N}lN+OPm ziKF%`LLmjSN~V<-jjV}{A1F){(Rp8tuoB`rxPjph2@hZvHQs(MjsSH~;y!NJ~W9BS6TmTV5bFCd~in&EAwzCSR zpDeluITl*Ipi6pK*zRm8@@INNf73QA?}f+hR-l}t=g14KTcG58F~(gsbHNQuC*vfE zd8n|uXFy-GY*On7OfQGhTNPX_ZIj5c{2&12M81t^lmk+KQiP@Xa?T!H{znM?C z=o(HXvpwe^c3*=L5&bhmt|ddJH(u^(vpeWecQO^u48JC)$2F?~e649Jrm7tb#o|2$u~ zUetr8YVW1_7l<}TVq-uoS+lc(z-6nmes=U^7}bc7HQ?qeoYayDz6M=ZPq>qish(RW zGwIxkn@eAF=t(cLexqsWTM28_(@qiLU-m${{hUgQG(6^|6i zD+16N0eQO>P+bu_6)aw8z3P&4XN+A-YyyAK05hR)#l~go@#49=_Pd}XQ!n?$a0<5A z?R@)toiuCMbPoi=7N)WORyV7^G*eVF&+*YifA%XqccE4QEkOz-7A8SQ#q`$7ird0c z{tt)oSqP)bg5N3cIT?K{%=_ssu1CN=*j~B92Js#$KvR33Tg;fTj)dXFhS*CFHnlPF zwYf=(^icZl11ZyYbN-U>{rU^^d3mYhzvqZE{=emjJHgOPTNpbLXfywl-A%yyul%Q) zh7!R)IpsF(vUZ_G%<39p;vY>boQ_}A)uGE zF)%ZM`Mr?l&bfWN zZ@)h8_UOO=_r~9EjXidStd%dx%*+gvnz@mq88I8{XE-K+vAL<4BQYxn+aENo zZ0(io^bOx*iWxhb8yZX6>$|}*iI_V&d@;5cwY9dhwK0Cr_C1}1xs{`_Ju#DnmA<2~ zn6aU)(L3G0xnK0{tsRK@_?W)f8ylG$I@;P3vv3gqjk*Id3-Mpj`!m$Z+Q#7zrrwLh zBx`JA`pzN?7Z>Y4Y*Sx4Sy0!+)dZK=WF7|x@A4(y@-JubL*@UPE@x$^OdMja+=I0g4p) z$^*t6G^&K?S^3fy^%AAipc|fYjpArV8s?H2+)ZH!6*gQsld$?(3zkC;+TvIYiHNZU zgX$$KXHW{*xL_c-(@zMomh!5+};A)!mDRCmz7a`r<0u!Kdr{tVZ=~3n@uO zyBX{e#Ky{2TRrB_Vg@F@D0${L2H!}Cw$o7EQwGwdOwBV(gX zBjd7SP|u8E@)Sv1KP{_Ud7&lxOm#clT3aLxs$Ka|bTxI7VQ!krAE`(JQoe#l5p5OD z#}^A0f_6-3>*Wj%9eL_6yxk~K&+7zWX>_76iz=D$z3N*smsZCK@neCENxb(pt<{`w zx#L$mA?O6<6_Kw4Gl~WdTpo_@&Y4uzA_6|n&lDKCt|)ezjvYj4p?TFeQDeT7i@Qk6 zPm-nqTPh)htu3E<0aSZr{EW4?jah;T&76xX+CO~8Dm_;QGk2I|xOO$&kN0S^Wcv35 zTQev}&xlJMP7V>zZkdg*^FN8JgnV+cH7)%<-J%}6eX<{ z-A0WwRSvs+o`ZjQ(W)%!fMFy1ytWOb+d*=8q4CgY+Hx<0zQl5fFg%GKadIF|0qqH~ z&X4*URir@-YH>lwHrpWP%jV4SkKI5yXmjPe5)!NleWo3`A#~7Y%CBP6h+AF8i+lxX z({^jkBNczaD?2^S3F-AeU;OiaCKs;$tQYP#l4h@`SIxG^dbN(M0vHtiZ;n;e8AIS|0kG{s31y!Obs0$p2GfjtAi{| z`})aEq|u%RhXB;*2%!@A+sK^5`jc+HXd!S|^1uB-{)_vsPd@xTLAWqEu3f%YCkLnZ)-ms@wL{oaxd6Sml_Gpfn0rD z93GqmA508>FPEr+t9NXl-1H9a-}F`sM2SNitwwI8c_l}VM_e~))WH#h1fq>Mg-l|H z<2n(8z9JYwFx~Dh3iDJmFWs$_ynU=KXJY8YIkjJp$07Z|MvRA@dfx@? z7$@<37%&!k6%2$LXbIfI!Xl3z#la;`V`mrQG&lQhH8laYn4j~ZMoJXIDDFKDB?3aU z9B%3vi4wTc!)a!_6P248A+a?4z+a2j7ElzZMKC(+@gi0 zLvuP}sZ>$o5j9|F2d+R-JgS&OqwTvnXBxalf@6P4LAF#BO+1EdhH9Sb+#aCAT`~aO z9v`nUemLL`9hNj#;t@~tHlz73h6=3jegD7McKMq(sOhCyW*eB@`i2cVFzwX$2{U?oPMN8~JDG}CiiE_m`INM*kO9dt79M=65!rac&tfd89 zYljmL@mjXw}!Y0REq~ZBg)0T|BcuR%`W;xVkt(yw+zuB@%r9p^ zI<`_4xz=KTthmU~RiI&LRZvR3u-YSk7NRm28+=osz- zQeSVxdpB>gt6HZq4U$XTv>_q_04}_-7^(Ox)u-d>U8)W0{W=|+5xFK4Az3s0P&;}$)uNl6&?Y6r7HizeJzPeU0t1*-M%1zBHE$G2u4EK#xNj;_Ri~Y`P~#1`7-~~3hSg6MY|FKZ zIpDj=NpK|cRB0^F>IB2CAyeN_rJM( zCMG2%eN=ct_?1#E`xj8f{$GTuzryQ3XMleNTK`k<`j5%$|KISMo%`R-NoVCsbM3GL zFW4FC59B(A%2Yme=E@N>_?e+hE_+^Av!!v%Lz`Ua>dZ^tU!1Qr zlc<9_OTql^xq=q`R>$xJ?*#t_JFrhQ9H)?p6!lo)3K6xen zuxgv6gc^j_VVwB7+n~{QXyNc#_iP*s_Q0(p6De)BZobBQYprl4YMu#o0u*C!`v*$- z{rUS7u>K#t|DDixqJK~RclRG2{k8X>NBrlZ|3LAdNBlGN=SE=K#@~SZ-TlLR+R@BK zbNBM_w+?NO%BFT#q3iRzaN-Uc_Q1O}J@#|@J;Rl*O}As~Ht`bCcoH4#p}oLqp5BU+ z99cI*u|)SFJQhgXo-acRP-Ix6$rMOXr2liu#YVi6qpi~l=j3+K*FXN)WzwGWJe5Ox z(B2ZG^(1O<*a_XXfjnCdzdY$GX1#$)L()1pMtvo$zc5-PVD_pW*Ds8|5goujI;kw7+b&d^bcce4i3(Lv~#(1 zq8VhrJl7B*CY`&`yY^{;PRI}P19AZQLK^unMat9L+r^wfycIVr$>JC_mpoo3CIZC%-QCKM(+a^6i< z}L0INjGI=O@pT;YVm|BKvjMXBU)Ug7cPR;TA`&R)`oQ&aCmm+z)s4CnhGH@(p9D zEZ0+RPB##6p07(uyU)7KdP;Y*R1B69DDZdb(TmVKec~b zHE14NWI5bL;9Pmea(=Quxw2n<{?Xub==eZ-Xiz`-6LT7+19bEp$C%Oh3anVd2!d$; z3V^h&WV9x4YH7ZC1n+EZ)%$(s)LM}dDF{L>u382aO>+s-Z4B2pajX75d-@9fQ26kc zv7vUVm-}U3{Hs~Ql>XVMy0{;%TX#wcF({DIX}Fm8PMwblKN>8OE)h^LU2Mc>zkk>C ziVDkAMS~pLmzx77^e7cBKi{`qZRW2^eb8Fp_)>`p2fcAm)BOuf!ll?=4cGE0dvuYe zKj4Zpn%Zf1(11lHKsuF~$3rQJ3kHHQk#^RiKx?GHfF$clfe5$N<|W36GD%7TyiqMO zEb9_kt&wUxc{pE*<`G`_YxM2vQj>uILF?5T zDQ6YXrKWJ&uzhu?lMhV9l^wrFr)c}e@K~?KuI2f{Kr*vp&Y@gQ>Z9$r^eG%UotI(# zdL?{yBCN-lIldlJ&nG8+c23!8c1LX`@^>R?2SK1h>JvRy-PV^$tAZae?~4 z3%qkQq%EuH70Hl2C)&V`JK*R2i=a5WTnO`pGj>ZAY9&rs%WjM&cq~O~!V?BgAt?Z@Pimp zRDWt=r2D8MrMx0C3URSlC(Ln7bUKcGrTSPATD9E-w2EtFaIumYB(shn0f2IgxaBu7 zs!YB^K_D^Sba64_y=op}qw+vutdv&#;P0?))elp&;|_Lmn*eh3(4N{|RR#1136ypJ z)evwd{sqf^0zeFDbDvO_dS6w_Cyy~#gU@$%6%^x~tp$^n2Vffq43RGY4az!UhJ2aC4!p9YOzja%%nU{=IYQ~1;>l!qw9dR)oz zBPrPeP+t?q^J9x08-A-CWwaZNsST%4zHd9I{Tf934Zq6_A*@)15C6JWb5nXHl+I|n z*s*;I=VuG$9WFj`Z^i<*yIpd#C!RQ5A$5(MlSeq;rl)2#=W=iIzCnAfSSXIM9DCyO ziTyi{LFUY#eadiejvn5yD+tEa-R>86C>GM+_Gi}vGf5@wS+PqP-+~kvf-8xpUfQWgm2B1H0 zQmhbY?(C|wvDq-BRlG!KR@>`I`YD37KLJ#Z$TY!yZo0e^YDG5YBoSDQaLf{l&hy(0 z!bLgUz0%qh9e@6})=9%~2&wb(38%lAa+5&}gf=bKRJGmbL|SFdXCW4>)*z&)pnBtQ z3|RJtAq+VDvET^-DO^PN=mxEddUsKE90Y&oyvbvjExUZPJx_ba9Lb@MHnWTq+ z(;U`i26RYmqehnn5{-iRgQ0v~X#tEEzvJa;;CSD+FXnlt^=w`);W5+& zrWPZlJq&PfE>=YHtXt6+C0=H)q9~lAO9?J1^~;&8*DP-Gib{5aRXCh`u4{#i9h>y(z{-zEBB{?x;#>aDf6R zpX6arh|Q^3o6l5?%DpEFOH*%VEa-$8TncQvX2Yy7bq3_TR4x%wmcN7wbvheKfa{FMlv-)*1<~rjMc;UiDpUAh(QrfU}g^C+4BRF|BpH zWarjnXEDkhKke9sOOls}y56<194yyb@FFA=L31|;e)^R}8xn4=CQ~5&gNQb?XZ}-) zY))U)bx8ffF_ECRJ5L1zn#fpLG3?Fc$f1=qxG>v!0)iY{;qWNtEj3aQv39V@BXG8G z_{6)>Z)XS?BmfM<@NG~BJ4xZlN*NN`2=Fx+*&Cj9uU)M`G2(b}oK;p*@oHEiKzv$t zZe6zCEA1e3S}m#XBQffK7Ac2u%x*oGNVrWvF56614r{6?R~N=Kv(8BtZrirG)qU z&aOjuRJI0S;*6WyyIrI}M06H)hZGeRR6rSz>OH@x7g2|aEvJfi&)S|SX=l=Ng;$jO zJdKsZzZ?|wB5PrEddzI!;;C<>R1^%Jy&-tsE-E#N;v!e9M1xLiy+A4pX1{ut6gXqe z`H*8HJVEdfUp!?QFO#jXWYw%uQT46u``iA50KH?blxLpC8KQ5GXQ);`B%%;XqzUgl zl(rbtBDv)ci9;n5^J^MHqy{^5n&Wvo0>e7*1l90Z@f8R z`sAW2rTE1pMH=Tr6bSFa2xGt~gox=ZD-*F+pP!(v_n>bWAebzrfFEnwuwf2Jq`1#r zDaQ<(Jst3$vvqmu*0(HflPH)vF0JL@gCy5RjegLoUL(`~Aohhx2Jh1PUeDSfvKO;S zKCeiY9xliq#SK5Zm0faG1Bc#Bb}o<|#zHiyJKwiEG5ll^bXYsuy)XEB4=xT4K+2cA z#a>6)*t0+A0)-FCvX4j)t{VsIV z&;};gYmqUfu*Id&h_opOen0PqC+o5O-S>?{IHkh5fBT|J8@C%XNov3J7prZ(*}#<=4sY&{T>fDF&m1EzwH-dsb{*Qx;mF9}W9q%0!NW6? zMg8ol2ZXJy5X5-3lghS>);stJ5}|&61X{2kyl6B9+}!6q%$Y?pWJO^z!!;crCD{02 z)2QSC$yHZS`h$s|R-qJ+@9=^rOXheWyxI$cFX6~CnRz^WsXe-adrje<$#T?7U7!rR zeyY>jISIQ(Oyb_@${V-G%9sulz_3r*7B}f0V9fK0$Bqz5$6SU;slVjUB$) z$o{8-7GkBYZ!2iTL95`QOBQ#qNIaKAbM8I9cTfDr*m*Vu(fhdkPOMa4aef@m(akpe zOxb3Rd|Oxe=_2&(t=Lo|RWw)Ro5JIxh>8AglFSFcXGiQD_HG?o-bGI@&96(C?393%RA!-Z^25p2;M zJd!&{Ag3Di^wb?G#AA$9CHO@sI*#8Ly54vEH1-Fj^>?4lk}nN#E36-V8VVt0i&u_m z`uy437EcE$o4VApMm-Y+tT2>9lygXGJWlNHx^3E$nYjABM2RU@Y=^TizID0t*6Ii846 zWAFjW2^+$+##xT~GC~jW7S~rvvX8&gw$QuSyns0DC}JKWe41Rc+P-L{IuwyqU{W!{ z-QGf9JLh@@c!jOUQ_)nU&rE5Cm?tTGrH?unpsMUU5 zU#z;Ny(QxzC9_ctRsMax>W%fx?^0ZoT^@<_)XXq$!p^IG3Xd*9;34*uy)Jeuv~qrU z;mww>YB#63BYiI9`ZX+<1r;gIl}I#jDuSho#6%nx$39uZH-LwkoSb?g;yY3*0%4_D zpMR{95}xpkD|sSKBlLGqD10FpRsYG%7=Wb+^t}IMcrtf-jQCom`kt;;2^q#X?}oZ?p2SaR|l@^~KfCwdOBpxR)l z-zOuK@g^I!8efQp3={M#mjh-=REmDhW-GetBLA)dK9HgWl^*(9OpEM+ZzSXZ3Q%dC zBWJ&M^cLi80&0N!G0&(U`ni1lJ|KY)=YEG$1q))&fq@o;9_4USLH$|m-mZVogXLR+ z8OxNgd&bK@BUl{T2d6bBaAqn-qILprq@rR({b80nK4-#WCQaK@Cl1n|dz7-Jgc=N) zFbE!wRr*D~O|iJ%ql#4(7F=Z2q;1X0rM28b00&l5Nvl>+B(<{EuisUyAkL;z5F0)S zqI$k!Z!1TSI+1~cM}+9v$?6NYuej$1ct*a0i9B^gym12Dr(@m?2%}u@w7qAVGw9kZ ziytN=-ut|>yB(FjN}NPYuuBH51xvx&FTuyGF_jG-uC^Mn?C1}w^)Opn=5P?SmXom* z)%~L41CET27eZuM-GQ5g`SLrbk`@m6 zi_n%BUtycN6tr~;zM9eYE*~$Rdtb>f>U}M%HGQ8K+cTnEwpgV$MhnCi@sO8R$Mn5u zZ^x=^Exns)DbiCaEs(e>PSDQo;#VyOh|&mm2x3PnaS)Y?d39?+(LsM0K1NRJulPQW z1=9;8RibflJA(OG>?^GFfp9)z_yozT4O(sB-S`-gJ`+%AMo~<#)$G)42BgM}LThqc zY3s`D$N2PMMVT?U7l!Zh$kxAx93|DS!?M@WKOosgNbp?WYf+EY;1x5PKKoorz`6Qv zTq)Ryb~KPQm!t#h?6eOnk}?(u%Q=eN7aAQrb0d{W8W6@4?h^AkWVA(!ura8`l3m+= z*HNeSfbe3P-ymj!;AYitM#a#J!>Xk{HwHtH0C8F-n$#V2~DdQe6=)W|LQf6<AnuX@r=y`Pi@Zvc4y`hNuT+Cp0fvUd*xS!prYpeK z$6dF|to!F8(DIKH+*cUjz+nuv=D63=aBX?7a=k5mXRc9eOz@fHONGujzGvE-!!I5A z{W~__@i_P~+dPCyNF8qtT`n)Y1jo;>*T1I6rX72SmDbWj^dJBjxcDh} zvV!URnll%uf)aX~W-k^?Ix+1L^;+J7t@iq@uPviCm?xlztJV{u+q_ziGWMDHkzV(l zFpf0;SM*nj=~)ww7!6z6sSdNtg>P~WR%Li55g#vmIm(eK8669zi_k`ifb8JRqYL(eY#&0TFdc8Yo!$&{B3Y^XoHzD|#6;yw9btKk4s?#;y?n2E3Z%t#$fotta6Xj}T++ zmKD1?#)tx9#q+buHCQkjGFDB|I$i;AG{{K`?bUbd{wR2{5^#eUwIR{t#+A6ievR$C zLru}1Ej}nph73pwV2h@Hg}=3J<;>UC#RCuKNb<)n9h| z7xMbg{~P4>f2!2}mDKuw%HryOIAQ8Pk=L`ZGBf|HHVilWUlslTdyU^enED5aJv%om z*S}j{)wZ<68${(WfF*_0_d_A^*GnWGsY#~L!X#sqxc|Yd58IinH$vf$EP`ypA__yQ zpY+nTPYj%6f5#C$?3%4s1>?5I02lwm;fxb*_7?s`=3SU9VZJGsZ8w(AgC>20^VmV^@;A{~Uwciedk>^i2 zqQiHr7Mp&2Jf2teub|$u&T~RJ?+#!Ju54IWWJMO==a+Z0eG(EmCRD&BGo#H>-cpy^ zZ(1^IY5IR-#eDWd#=Qlrrq*g1mkVUt&FHM3obISYX((Tlx*FZ%!t2&8W8`wLfSZ|@ z*GM?vf$M9c1_RUE33 zV^!1QYfCYs-zI;iw(^Za=|fLF_3^>uU|uPQwKpD4Kx&4bd0tEa*V#|!E5a1U(v8sx zR|+qr%Jul8?=}nW%z+i0AX|w7&7sS^-jm;-+x6=Dh%7~cIVh6sRwqn#W3FBto0%H6 zeDvz1=2Ay(esBq^9+aywRb9oTmNzuQk#ceH46F3XpdF=@0^bd8R%=HR`kWqp7*K_V zyrjCtY-aw1jMjQL2+N=2*N=j5RRu~P>kJcqBQ6P629$o{nq5(kTY*N0q@u3y;FphLgf!3x}lHyYt77 z`UphGw0WybE(8@FpHrC<-G>V0qn)}fRvFTI#qW~8Hcw4HZo65quv%Ev1ZKSSmaSFV zniKe7IbOzQyeOJqd(iY5U@*eAdMG*7kp1!|9&eh`>fbNZd6HK~F2a&r?29vSfV}!u zX5tSRmctX!PZ&tf%=q>3_$ta$=VzVOYDIzkdPHsf#J}-U&KLzkh|%qfdpV4WWKHM( z%w@$pi!YaUvV3AVv|qC=bNG|TCuQk00=K$M-`FRONRX4I^bV9ZCsSH-GpTPI*vvHQ zY2FjUidky6ROK;qPDZeK1L0OkBgfSVNUe64VvzUpuc_~O?IO`#w)d6a7>+gXruw6% zC}gOTUBota-FiPIaN3|TI8~Q|I7`n?^7#}FJzQV>ssaP2Dv4erkRr!Yi^q~7zIAK8 zORX%l-M32tl3&RZ*Huq6eO_7^uLe^`Eoa?_8;`kpN4(4_mD&JQ+|k+{w-fwsDI zhr!J9Y;m`TZ#_XUV-FL1J65@canOG8CJ z7>vPD-I(xVZL_AWst<;&Sx6)(Xi3k`+HifaJv9SzOg+q3**|d3WAl8{F#W;#Cqw98 zW#rU-w4VH6g!RvUUhqwp z%I3H~X%XA@j>T-7J;90tXPktOWXrq84^mkFz%izcX>PK|+FSv+Ua|g0;GZXof~l5( zs+E$&NSR|>(G>1&$jA!=+Ju#12sn!C;d4IkpTBb|KsrxAQ+0Yc4aBR)#i0MK)Thdz z`|6C*=Rf&)4Hnn0s)g&!SiAsJuB+U19Zx@O>0U#;%?)_fvx_C!D6&(RIrExbg9VwP zqBx&d3A8_9zvqjbXg}?xQkp~8slc70CfwDh4J5kY1vbOYyYrcb!qz&OG+i`m)P zpkEzvE-!YbkseAmGMRk4*DoJigeHy_bwFHa9qft#$X;@Hw z=1;~Qf$LB3!oak&+Wm{CF^j3B?fi>POAn`a<`_D>%MmVHliy?IFGM}Mmd50LN>ZhY5QTw5 za(BuFn(j{aHgYuOU$?!DG4<7L{i+BNO;TT7G27yD6KFl&KfO3iEGc{j{v5w$JIbBi)6 z4jZF4Wc0g*9wk!@+GAs7G!*4YqwE()FN#7yBZpL55|wDj=4U2Wk-_X~f|&LYWg(!H1;%>)#kj4~CNQzUmr{d@ zz}me%q&5^W5{i{Jh|x!}oIsyJXUxlVLd;)sJe`I@4GhRcS#lS8n4_EB7`AKUqRf*9 z#jAtA2ng?^-ir9>(cLu_Yt^#}HZ8#lm$bT@Cwse4>N!L#*+C;}X*zKj8O*-6zocx) zTJa;B6J(Gv&eZ+mtf9tK(mTIkB{8kHw060xl-oC)>{J0s6!@!vk~Cdl*B@iHkxSV4 z29&g@)A1XOF|iffqQ<;x#iUm|s|C&CtV%;8HsUk9et3Dn@adM=J|pH}9+_lE8_aOl zY#af~pxd^Cbvw%iKpVT*WcO&8ipJvy!bYJ^{%$LScv6-qPO~6Cy#7Vv(Snn};=aF1 z;hETt$iad}$L}xU;!3vc7U%Y0_2gx;iO8lhxhQcK1F;?(w!Ktt4tGiLvzJcCr~VY! zjxA)64G7Jg>&fY*2Pmm2WT_Bvoe1Qbi#=g=o{a_6ZQ)g81@-&un#VFg0~yd8)F0XO z)QTJc>3Nyi4M$^XHk`$Qe^5`B!C-6+L(6E>A(CRMCF^qceBP9V{&Bm&DH|7KD?8?d zJRTB!7c&?dhh?4;a6ITu)Mc>W4n;~ot0;O#>CBIfpa2v(fa!8)?yEU8q&qTC@yF#Q zdk3>YPQLYzpAuKmZepyy2y*DVS>X*c&jnT8OWD;Lq)3-=<;EmN!1TY@T43IwS3`q; zf$i2$R&ANX0$Aj^j<_0SKkume5wVUu4*JDlDO7AiaH!!^me%5z?yzk@?j}LGzFS(< zt|h+9A$jtPsEGHxeAc3T5kui0R3)GI=c;ZOVt0l7>m@UU=M~)HLVtKjX(UwUp!qH3k8mxeWE(>b^rMLD!ut3yxnNk+MtvR6An3x!BZGvl%tp> z^w7HK@q zaS=64E$uvfM}pPoXuqlRcuPyYE_IzU37#Es`kW1q`w>(48@sIEW-L!-RSj?_nu?+% zIzbUh{^YQ33vhuP9U_7O6#?r*_aBN_a`;$N{ee7ru2@}L5+hS$ml4U7w6f_CSRD<- zHn!q5HI6b&+om+lR9(^(?pyPa8mNg8bvmCLjlsI?uR?rAi;(nF66FVD$}Y7NyJSF= z;$Tz5&yd3^SG`&T^rBG&HDvobALNE6)9$FoiJZvkO?v3%$k&6-=T_02$)ExZz&niA zS{q=WpZV>4l-O{&~JnSj!J7w?= ze3Xmu>37`hx<9|Ra4KcPm*l74b_USIriBHkEq5TcqGGZcm>8QfJ@aF~5d6INr~VjV z5V~Vzve`fr`y$)=o*89QTsmyx{+XpDkY~!G25-1%y44G3fL@)G2m{BzDqdiIzQ~qt zkr_^r+%$OL$mKiwf)Ep*+so#Y1go1MJ8Jt!i3eH-TxL;VJBsO)nEPZgFiS6MOOkr1 zef5i;|E+^5?|q5%Bgr=1Q1h+fni`INF~IW0x-A5L--b06FgvcZ6jp_+xu~4gUS_O7 z^M(@2j?z57h;SZ*l3f&pk>Uk3W4||T?-3i=QKFh8o_P{(V7hgisM2O=k4)eaae*Sg zC_W@WQ5A{a9;U^FPC&t2EJ%P@jbuG?p!fE^*YOu;9UGp(eJj)KoR19lhQ1HSn{YuG z9ur_+{mJIaZ;0?pqH2=(l>abrxk3ZYL%fm+{k1&3{%d znzhS5i!>avg}}JZ<;N)$8GUOG{zd#2(b%Y$LQQjZ158E!k1E2I-kL)RBEih)sEI3C z*fs%`N-naGUAYpnst>&dw(iHoFXkOh-Eps79?J@|X&$n^GFTv(DI{Zm3iSM;19AX~ zzB+F3J(0F?J}g-v2|WnDkB{Q1*nQkRPxsiFbry;H1=G7k=Tp{Kf{EE)o|l@nCda(~ z{vB453mkyXR2olrRuX)#8LLz8&{_>DOc-u8uNecAml9)GdCX-tbA!5g0ZLB@0y>?Z zm&(d2I#p!>uiv#Ojy~+iLp)1uNE&kbQZ(o4>>WmZMTyPYyBC;Yo*yxZvG@ry?oJ{F z(ZT7mGD%Lw0p1Dc?~~h%JJ?F=(4FPkAS6!hZq;U+yXYMX4%0k?!7n>9$G3#7V!)o@ zRh5Q;P6$+nzL7NOfG(4SQFSv-yihbe>Sv5h1Cn`^e4-&38EU{j;V)_>W45hM zjCX34e7c6}*p1szQMgme{4@{MV6&76CMJ7p=)St_8Mbidp-bWYzKW%c%`syA7~;Ph zzCigYn87QKsCK<=MmVQRog?MYm6Jr?DjQ%siTbk@&Rc5r8W*g~dZc^CQ^g#edhM}5mNcxkaH1K-uD%*cIksg|q-kRBFP$@H~ zqUMoMqJy|BgKbWB7q2{H<7}NSW6vM&*RTw(Odu=wgJatF%Yd{X@Wv*6xvOZ}{i|Zn z2BDY7yEh%duWzwh#}~(|(t<|odgEVwOL? zL>tU8c)KHHjYO0e9G?98wMa}u`jrVCO`~!`uKPeuMJtyl+Y zjK0fk(Nm!X)ET^rPKOw+UHY7n<+*X)7yMKrWuSP&3Zt30V}4Y@NGe7=7*n?(yPMZ7 zDlS*MN^cSdyemDtZiGC7C2eyUZCtGB)H{%4YLaSzbb6{@20!% z5gaxPy8n-YQ`SFDjQ_7}$oMx6-*?}?>>~Wr3c+7V5C4YaLU5U`S}mq2@a0-#`@N9u1hCg&}yL1^QZZDkcwu$q6ay5gcj5}&@eR7 zBJSVZ!P8l~a~m;W0k44A(N z$@98x`IU?Goo2uj({UE(%$x4imk#Z4&e^xhN$AZ_86@xeXsjt(j7ft=iq$Y@$tL-I zAKcZp0>lhxz{m8E#yyJv)>YvZ75ZZN zLH-6WY!6<~JgHC59W?CZ)IC0(^_ z!3igJxI|uO_Sw`=bbEPTh!=@iT|68A ziK%7*rWD&W#>1in3`nNhVK8Hq=h&JV;Y$a7nzzjtB!?6$1qEWz;L(x-l3hc9vI907 z-W1W$T!TxM1jQB`uDlmYT*?~Kz}CryU#zcC6OL(*7nr|ixtyl)VlQ|M1>15SuQk3@ zR^_~1uWBU7jxf9zMan!N96I$@ElYSfTJ86X85)c1L_FXohXbd@$)eB%e-#Ld-s4}C zCb>5)Zx4ByT$?caD5orbPw!vDcr`P0yVkk6x!KunZRzyQIdym?K(W4)kM)C81cVYA znaK|gT5=h6Q!KScXXo>wU~j_XS<9UrwG3)@0eQYIx@2I*+?`iivgTsV2~l(x15k35 zvfQuA#}`n+fe;xP6@{2u$BLhGZ{NNrWQU0m4>LcJL$o21{;^>Rw-vyW{nR zXecksZF!>%$zFUz!DW7(!6Ss+lcllEmL`$54#|GrXisurp;!r08SFxAuhkR{H}>}< zi3t+il%MsJoN6$!Srw*c>Fcj%w%HwZ_3tk^065EA@3!Z8>Ds0fwY>X_r;*4$r4^u$ zovFRPD!9}0dir==B}{_3U0@Y-dqub34N-O+T(`W)GGX15OM~i?<6XKKDk;Ik)v+ad z5M@?G={lX5#53zq2;A9f(q=!T3po+x7_5B_#xz7%wU=B}%ewh_KK7#JD>j^9fW9>P z3Cz)$YtkwJO5thIc4E6bjh=J z_*SO&qodkQfl~!Qt$N@dSWd_Ld@D(WS(o%WtEl2XE)PkuWJ_n7X$CiJ#CoVqihKFQ zhrh=dZ8lQdFbr)LfN#f5(8>mZTO3cl>xTGCV*A@KR)c#jd|QU_o5PKFJ61!7zv@O) zIZ$nwa6D6F6PwGoOQChCQvu``Z@HCBr>zdlQ?5Scx$VUfad`WqgMCJosXB3AN;t+( zc}`C&qf{m2mQIDWobR@8I=Ac^We0F6`>JQR@#d!?NTQ<^XQu;86e=`tmtj8=%p)%U z>a|K)cKSXk(FxI6QC5ut25asvvDX;Ml&&z|Y4@&2&_kAk)p&L3YB^?*D=_~%Xyzu0 zX>sgyg(-)cTAW>Zz~yW~w;mBk8CBwKwe^&s)!_$^2HH%4G3|J1i)wKJKe1k@$HT@a ziEQjaBh=6fJMpBX2gX9S5wgJfeV+(QMUH9621gG>cD#Hx&OE%;3eBgbCMlXO5Pq`x z>a!LcECp?Mw3E~Nw{csaI{kNbr}xDx$Q^FxT8FU$+ODES=_US#lkmM#+XVY!;D9L` z4Kw3b4Ds;F$Ek{#o8f&RZH8=*Awn>u7S(V!Pf9v_NiBs<*NyE5F1@$JLh6+O+$=o_D!Y#=){AL`L49FspeafR92RQvLWWIXMb|g z2hr!v`VkBdj1L>Z%{EfC&wa+IF&N`9C#8HE+J_t#msW-o-Kyw2$iViS{}!xbvGR(K zW!h;`jHUKc%O&57EMU2OZkmvITa;B>ki-VvA{k22$l(D$F_LY^xIJu8mCpcdmTl29 z2TT?%6w-R|tfh(p7%m+cCF^8JiQ&zo{tC6MOgxLg)M(?47_iZAZ}jb~3j=KZo4`{k z7hN6r-67nID4^+sO~;+z`~V7%30;c5{TYqQ%j;Jf@NMa$4K>JUgyofwtS>#MqGewg zcUcE1MU$yjXt0Egi_vW`zI~3YGFtFx<9aLCWJKLk>AiZm?>_a{q1>P>T?@IXEwZCc zgN95E4-n0nRE_XFeb;?VD#0f;BMYin(K*H+s}31z5-!4iw9Sc}K*W3rg3 z9}|7Mk8(}tu+yh$SAYBN`&(`7bEg<&fY^XqnJTxF@lRZ)_BIT(wdn#90){PLHDoz) zqFWP5Dsw9gXx#V`+d6l*1vsjlMS`Fwjt|gke3!WJACSlppy9E@O+ni|o|tvtUzeZP z>X}jpH;GcI7r$(1`u0NH-OgSRy1#uW7y_QVTc%5#IGKZnuCCd*n>ZwvAi!hGMy8jA zR>-rJiF@gy!6z^iA=>(*lP(x3H7|l=`t3@fzGqEBeql)akV?R$c^wXmIU8{{%V*|l ziFBXPtLizr{uBANTC08?daJ6}?WA(8%+@v_8D!VcaVq|y0avFLzNh=nQzG&lry-T+ zT+fyWgC5iKRzwh|X{{Q)kJSZevz}4$D`5(yBgxjCTxmwyIQK&qmP@m~*M%czmJC$J zlY>dQGI>w{>#Rjzo1pQ=5g22 z^`%XR=}tD9IOioT>2u;J`S0uP*FDsOv)iD!MFSeFyUg;)jZWkoSuS>;4fw*#UQZs( zYkqD^goF`HIN*#|yMS;L2e@Lfu3%}(J{wQ{d_g(g`1H7c9Zy8eEf`BvJ;ZdY+IUlw zJ~d->*Tc@I@2>^}!=6dst%J1;j7arf_erSiekXP{nQZpw1qd@_>|EGf`|3R1TruF; z(Ic5tfX~TBS3cmOcb(Y2tPu3V1z5$t_o$ehT=G$CDUz3R@mnVr4K=)U5${j5|LU!}Ic22V_t1JuV~Z-F%q zTbw{RWuAd*{bumQnNDY{yP2gz`y88eBNu;&7tC+OxXG>n!9Jwkxt?4ZT8&QuTka_a0DDF59|j z1EP`@BngNTg(l~WL=nk3HaSSnu>k=k2PFpq5djHJ6C`KJIp-W2geK<(8eY5B-e>Ri z)_Uijea?B~o;$`J!@(FuNFBo60CR)i_=<=`7uJs0-+`S%C(V?*?j*20`9N zM;B*a#dXMY5!+COcTK7GBdBWm|GD(@u-~E=Ry33pTlJ_!tu0{q)6r zl}K^gYv@4tWw&pyH?`^go4HH!F;iuFGq*523onnensx0?8~Y1@hMLM7yPMD~YS`B+ z>k=`X&!{*KXgT3}l3AHX)#9BIntE`jJ>v&q6MkbK-eDuYX64@cJs8zIc@m|WOU4L? zKh>`!&0nx|iZ`}_NjiSs~|?oxP-HR=&pijMGbH2FX5PkaMus}3k%eu30hl~F6P0tryS`4=7(E@fuW-FupSnd1}A3Q@1iMa`!&sUPy zThtt2`*56iX^sW@Th0#5uf<$Z2X;AJFQV{ zGJG+WZXUn&@Ml$*NIT8;%-v%QAulD_5|u4nWX{)GQlc7Cc4FS7HGv;b*O@W5;pB#b zrxwhbpP0uBH|*%uQW!ku#^ps0mEOeiBlGIV_hT&?0Yr>hkxlKY|{9^4# z?-l4S(z;=B&!@aZG`e-==`>Rrsxrz(^;b?^EadVXdbQBYIvdV83KHT9DCV$u6)lqnN<`JAX#*VrMK zt5k_U7*hUAH{BUsb-0pRQcV0OQ@+Mb1etIQ*iejo{dfRc6<_nj6Zg>k4h78|Dk2aDQ)_GvE2VO4Z1EB zV^N{kufyE)sI+trv8=qog~lv0Q-?@}lDL}&d`TnzN?1U-(E8Hul)XZ;QFUtr)U)~! zJvC@*P;WwZ=e@4S9UMp4^umT*b?0~j;Ob~6o)ofhHY-+i_DJr}_q^VN#DS;DxvkU7iHp}-GD^I=f5dqU)dlnM%|kTOQ;uIq_=$37$>)`7ESHNBw+B}!AF?Ec zb&lzLF{_H8mnwl%c-{%{wI?~?ttx~#pk>yAs7)Li?)7>Q=q681kucn?o0z>3tS2ba zQp=>RfoZnPm*w%YPd9G4Fa=(rltf+zT#|3h96NzwZ)6T!j=*I@BY-C0@P^?arS12- z$NR_SjFJuLF7WJ5lT}8zZ370mW+I}>Hp`YP?d^}p$kZujGJU?!x;< zgbBgyFYHbkX9^wmqKwtY$R#VB3xB2x`VUrhXwhii{wSaZi{yCfoB5m#N$Q?=cPici zBbL9a(dOBg{IK%340cWtBq)Z`JyM6ZPDx|$P0@7-W!QZ`Bc6^kL}Mn4pSm zo~NV&d9BP9=(bEv%YXfemP4<}=KAr0zm9J=7cRjWvzrt!8T#Ia)1`uQbizqar-V9~ zO_JO<53fjidC~dsLGL;|unJmEyb|$9-e7e#pX^k$G0&m0ShvruMXNoOT|W>h{_P>R z=}wEFm|j}iOq2J3Ww=27XVKR90#>b2ZpzaomgE0k2}yRby_%;h0vBJ)pi^r1x5kKR@%?Uf_pR`^}Q2i+qdP~a3Hs^D#VT&@oj3X^2A#NWnqG& zA?-HDakME#K_lfe%S8{uo!mIyL`X04mb;SzHOr$N?v&w~8Xt;$z+ii9uhWsmPGFZ` zVqNmY`ie&XV~Jcr3lm%a1A1*{V)MxqXa(^_7`iDoEYSNC>q(Jx6qzp@xI&QAT35tR zX|r3vchio3xBX@v`v*3dvGZL}OyUE8UHdkV#@sB@m&Tx}I+bKi zdiD=3LK&e|pAOaNcLl$vJ^CRVh(}b_Mdu`HQ006r7yP1lhn^%?Z72rvzGJ~QsP@}2 zJ#RrdG?w49$(qK?@M;#$OX2526TKJ7` zSn#W7wiHh3cDWxsO#`sYbw%@SkBeT$mKnt8ImrA{_=uDyM9T@@S){6!O{MJzav2B( zxrJ%2&0s*?F!W7bu*3`eA8ObBzQy&20?)|?+Ti>jB?WgEvx=#%hw)ffDw=CHR}X%U zCbJB01b#Ps95`2T9I8U2C+vrzoLK^T&%tu-I7}8x+R31`ZQ}pp zgOM5V&H2TiS)tm%WlX_JHv<3+d2N|8@0s8CzI))ZG=?@3Lj>wi&CVH`IM-yCF&7BO ze&sTzc_mDyGpCVKB6rrIG^ct%D%@83;()2jG(20w^IX|Z0K^3&T(>^DrXn^A?Q4`( zdeZ~hP5G2J|wXVAgOF)COsAv6YFvjxyk~u5fDH# z*IqRTU+nzYr%RDAS7+A_4NYz$i-!m(FLG#hL_2#}JGsC^QZSdw;9MCLlXkg46!fAY}@?~7O zQyW7@^Het5ys?A!8NRDBV5~2lBd27-Zu#prKiTtf$j|Q78dhz$-)s+H7aQALoe}3G z=|9v*>Qw{e&BuL_{ELuyhtO%kgvE31K|nw(Zn8Z>5{(jka2a5>2f3 z#B_ukCTAxJnO4Zk>@^~i(J{{|xhlp1dA`5rp1WGyK4}&gTHREZ(dgDVS0l8>2D4sS}*^Y+Zy= zE=?!*led^YkL_2*nL^mpc4eN66v8P!!$fA7X_>}gg_7Nb+ewB)@y_BMbu81q#0Oat z4VI&UMH#SYb%IF9%}u(Lc3$B-&a-;W7`(%U%z?S(M87tsO3{NYkX$cVt0PbY_oiyL#-eNjMX)qm?klXTx55WT93mh( zf@GA{nJQe`ES9r9t;0SKT;vRd?cVID=LaB}FFE%#+F zUG(TH_KtwBmEA-pawJ_7vRAn$s?rDH!ZarduY}<^ z)TtFI8f9Qv3LQX%EPy#`BZ|l9Cv};!Y0EoSYM!-0+zv9gUZq^PzBb2Js@zpy>ic;) z{lN+4Ok7OHG^v9d+2>I*jmg4e7<$Y=W|Wx_$uuTaU$B1UE)y&OAPK1Rh>9_bx`bh6?i4R1UKsNw)VMi z?x&iiC%bVsH>ppJ$qM2&FjJzDW6^e}kxNkXQsPR6ZeyU&pi&A*fY_ITkc?kHFfWu= zaGLpJFG(Q$8Z_7-mg%5#&uv{1-@G@=g$X7Gdc4I?9_`oWdPTrl!=Cie>NeRbNVfK! z(?Jhx(!rhpHF=sCbh7u2t!))QbZBwp*bU>1H7KXn6IW{JHgosJ3}GhbG0jOe935K; zdW7avEsr{%%~j6?=UdY_)e05w2Qg~LktvW^Jo#i&p*d$8ba6=J(ns2~)ob@9_BLh) z+eo5Izwu4P{E+NSc8FRUDTkl{&z1L*JlnHHwLB=+=nS?pyFuqr9Na>2IwCH~is7d> zey1xcSJl$-$Cp>zUPilWJY1cb0Y&DZ61%<%ts-|z=d1a6mgZK)o9_Zw%@h`)T5p9f zDq)lUeoH*4*J)V7&T)L<=-5mQoIhCiX{di%GCb02JGbEv#}AneLT+1pyFmIr`>Dfc z+x{t1VhqDChhnN#`eKo9X30#q9@}CxFfwr^Gcc7_E1_`#sa1nt$!c_wSH#3|6D!ud z6fvK1|6~m)V>fy|kAL@w&Se2dCzVWJ;KprUo~?6e*MxqjRw?Zy^=T&P0uqNBqvI=7 zKEMzzqXgv7lUb^sM!$eI^yKt-zsKa2%Q$8`;l1bQ;Bi+!pF5G62BPmJMr_hhgQV@r z@j&YZ+u3p-BnP=cj`~nVK@J?SIQTwS*CuCd1(rFk*>z}9?c>vTXr2vZHLQz^x$O8w zxKUlk8OL*txra4=;`x9c|`V zvugbqyyhBoSP1WnR|_X}A4wjg<0n~aD}l2NiRhFTy+##Azsk%Up8QC}wjHwy7MF{2 zs|{MAex};Cder|T56Kx9P-My;qZtVTMasu9n!M%KY5js`#ERydcY@q}nfcr6gc1(O zXO?y)>yygLZp5N?Tb@ zVXJZQY1v9?@q=0D2ByJ2#6C+rz@s^la#6|5mHsvEXz{-B(=nXnnRKz48)Md;w~jJ- z(-E1Y9>#Wv3nozUy5v;4*#;cdU#k0Wi=*6;@v!5OVJi>(YzI49`F2^=$c|Dekfg!9T`=oEwD4$X(XeZVW>xishXYqAi3w?6WL zJP|6Zwbz%g*aMTS`T~4id`SEK#hQr&ut`#uZJcaO-Z(Tg>yII$`^jAE1!t#Kq_LwV z1*MeRrK2X&(V0$!!0Xlx*o(hNBxy5 z+tY!Ql)#o6H++<*Ro+0hW7>5>UJP-m*8FZ}4M&m*+rT`JaF$SLiTedPr7s6 zDx0K5d@m;{S7Ao0m+*L`p`kgSSV>4>E+de5>lND@BcEOzR0h4qK-U&Q9LGJ8YuEE=H=bf!fC>5y)PuDL0RaR-;obS46rw>YNpa+~%3|S9~R@Zp;~O9^*2Od5PUkB=kR0e;+f`8{aFN zQHjNrXayuCXm}S_dAQ`9XLd8*%Hev@va=Z7b?#IyVmF)?rqE@;dYIX47X1C zV95j+qffUCQx1%rFriAeUAznmoV4pG#(_!$#3n1c7Tsz&Bb(*Ej0fm{_f@mt(g&Me zeo3;!6!G&~WJnq?C1**-YBkU{P7`)te&t$9d>c)^PS1t7L%ajsoat>9#$HmgEc zZ(XVEQz_|pj(k_OROd@1M~6O@o^uz^IZ`@ttqy|=;-6>mjygSa7RGX7qIryteK48X z>w8*ZhY6N{8bYm*L8#mnxrEEiwEGw0Jt66Uk!HuroOwzZbSNT{X#4 zk=F+ewsm2UO!GL&#zyO=g%frCzMDl9i6!@`E=i0*xMW}wf1Y9$VU<@LdXHE!P}&8kc(rg6iw=AySL1_ z9-i(c9pFBwMN<>uhSyY|h}}SnA7YYAX*KTU`14Bf|g@JaOm_w1rOf)#C7}mJ+`Ky$ncKqcbcPPx~fLFEit4Ztm)wdcACY z5M?LCpqW1Et?fpxtXdoK??#mDHI*e2c@ioVWg2sT9FyJI&ACsR{ARdDtxS$Z^XKKW zYRe@td6XcSmjyxZhbjZ7P^lz|1HCu;pIu-ygxWQ{l!7x?nwakrcH+aG&pc+Z7X`=nIBPaXsPon z?;958sq_KgMi3vj8^S12k|%pD%9Krk0u) z#Rs&|3z8b%5&Qa;I`+p5mxxoPE(kcwxDXEyd)-i&&XTbQG+1Cv33E#^Wa#BiriSEI z2htj$1)OHFwq`WWE6NO3R#=#_ef!uWA!Yd9g}&t#4#t58A2y-pKF>O*-eL+$4&Ptn zTmdDFO8l>wigbHmNtEA%&aqdZv{jcGb{)+fsQ^>=ge=-BYWw!%h8D;5ux5`PS4S4AM;zJVToni%?r^DIl!nuCW+g8C27 z!xvv3Q}U+^Bi|OQ-SkCs27mFW5HapjGm}`g^gc3cOYMm}LFbytLLD(>#VS`2ON_Q- zo4XD&@e)@5un3uHl z`tW#as2bO_XlYREgckqoIK69Aj@gI}L(8c+HeJIs({LN_>XT+6{&e9X5gRCFbwZ61t%FJp@}f z3MbK9PY!Rfs)E#eW$J}9*@;o7D7U{Kd1myrf|(}8c%4t-{h7zS`X!?j5zkYY60C~{ z1&h01s2pC2-yRcJ6@~{AzebpMyZUOup=Mnfj0xSSW8n)E>b=gV9*4U^w1{neCMvMN z(wq*+U~yVF6vu0SKUaFdVngvDcBpcU9?WLVnaTnrm9S*VsAXrCx`NnOp?{t3zlm73 zmGA&~3Ym>!_BY;MZtfG{mQCzqh)6f=D+NPcg(RO)cW^hXzvkc$y#o7vdCB_FCDH5$ z%xz~7_a~363Ui|JO8$>K1E5rI3?fb~LuvZQ;VAx>I_-2ph;Tv0uDTFC=hxQeA7nJ* zNE#${afX@&KPoEI6XE1OhPAW17kpcOB`C|nf=!Mo-EdT?k=R>-=~xzEhVvsSC)_oc zBdR#FE=MQ(gW}{#^Z5mBkDVArK+_`qaqj(A1E-r7as|>YJXDBqO0=t3bNB0o61jGn z0QUi$Z`aqGQ@$a2V0CheiErU($LkX?OeAp_9VeLszCbKV=-~jF@vK0N(|Uw?pr6GT z_LB6-<4B5R@sPyD!VaO@BVJAW5<_(LdF0HB<0O?8kCB{6*?u)5vfq?yXftQfel<$i z@tFVmshvYWxX(Q&eqh=BW1!zA2l_kqvcxBO4D4?mvNQC%Nh03c(Hs~XQSGa)2`dTo zaz4PSQA!G}=S4{_En<~%4|4!S&aPFoQx_(0oS;y+moY66+E%FpcyGF*@|;rfE7uH| zjmSC6QQ@R8S8~wi7$I==o7lpN=Gtb^FJ7>>zL&8?dk6(XG&>fekfCxVM>ksLzohpH z8m5_7rg`P}GzR9Pt9DEFGueQR>QJC6f1usvvGGe}HKNhhC~8qfT7u@l4EV^uX|Xeht{gS#eG(L|AT&twj`d{)R-Lr{W8L|B zC(MJpPfvy{$}}E*Yv!9Vdc8;Y0C*zBt)EZe?<2K>DR?=w;vz@}y$gCVG(I@+TEsA3 zYg|tjpI4xxd*evZAf_)^6aE?W>f*<8qHJFfo;3-EOqm-qmLTB6i+*KBZ86U7GdyX~ zLJS0v3d6`3r*w)N>kmns5Vd-`(_En&P7!mly?&8hi-f3CwJa15V^YpJdC?Zg(!RLB z)_=vbp*sf)o@@KGC$gBi57i6BWezWWBe-!QS2P}^7GrzwV_C@rQ1nycG6A|PXEe)B z=0dFzC?{N+3++K{2x#8Zsvc*YZ2jBhR)cx-r;GOt#5{QuYenhD8M+lopV{@$o~O#gDu|N9<7A;&ULZBRIgJb4=FCXit#lH8>YS{#Y5~3*+=GxYBW9SCaSwRq@VL{ z%AaZ}6`ZkXdhF2mX~BJ7eH2^2uRHf{GFcBDQ`hW?Q*6vEFLT24)8KcOt#Xur zBnF-$PZme@kGsBTbqFW>PN8aV7gtaB7fRfztk2<(K_WGE^<~f%v_l4Wm+RsF8*8eT zvEmlYos4;bT6a-iGPEDsElSyn4Bj&f2IZsnQS}B=Zw>P9*GUs^v#Mt(Y%ZJColoNY9#rzGFx4#LY?EPspG2EEnKP5}`#R0u$ z_CAM5MO}rmcF4<^JU7hFfT2=%i!Og}?Xlav(WVL!%<+6jebS!s&TJ-2sn#H1G?Krf zL8Oi3)uO1i;ml&uE1~TeSi&~N@4r;{XwYk(5KFUIPQ(vOIZ9k6@gz@MW;Y_Mlyy_EKXf5^tgTJo8 zB&#%NQagF9$q|!txesurG4B|u6JOvj__dkE#}nZN-Jg4tA%yoTu`Vz5lb6d1Qc@T3{%acwPq)(#saIw-L^=yTBlO=J__q^5-EPRwU_iB7_Kcz@r&(+V@ z;;@UVFp%PV_zYB--mf#&10*llv+jsOO3|*xtDC14`_l$Ym{(qQ5~5dWI5k+;)qQ79 z)fv`Vjx5CP;7Y8TGq!#tJ*)&DW=8L*ycDau@$-qXs(l=hq#UIsmv3uFk@VfZyIcJo z{oPnfdz(pV+%J9-;&-f!VudI(cwi`JQfie6K( zJ*=2bDKaoi5^6|D_&c<3Y^1MY%+p(UxfGM+*@%zNM0Hua`Y68A+&q2AeQO9jkkQ1H z^il|Z{h>-P)Z~~jHLgUQRgtWMvOfDUP8@S}LKPl4B7&pcn%T*AAT3Nno+VMPoiG!B zvnrH9v=bpEeAcy8O!EXKBpQ>I9?=??!OG`dh<9%{wYb3b#wN9OdHw#fu96zcqiyzIF>xFzgTdi>o;Q4GFz|yUFr@ zazZbze>I^OKL;vP>EE)J3j9yWT8aYoPd9P6xOx7VbYM!@wh$GPd}Nn1Q)P`&C+s<2 z(C75G(KL5aiM-N!C5Me^<*+4K=-TOy$GZA87-n`4@QP1~9N0jMn^Wwlt6(>tG^DEc zAw!b4WR5%}v5ZgKR}Xbgy57-_c+sTFTXE^$FHm5}kY`vO=~hN8esx8dGTrZeATOc9 zyy3c)zK<5LkqeBsh=_}C)e3L(^KhYLa-|EGcAu9QNATR!KxkMAV8CTYQ zdj2dC%2OX4ii)tTCoXXxqTSbVZ8B`HbyRSX>_66FQ^cRi&Of)n{chBuUU#soQL8fL zX4H{Jy|D5szQYm~*7{ijD$21X=T^0Ct^$pVUz0_UkCgx_c}dq8&b!gAnkBaUHYL$+ z18S!cX+CSWmnhs$KBCPM_cM%?LlT3HD1&RQU+he}o-X z9*L^)Tls&W@bve}sApj!zZjwZ4Wpib-2PiPzxVO~?&kMC{@$|4=x>dGwftM(C;xuo z>+IjV`Qxkny_-Lx{(Cq7D!@Mp=NGy^75+hD|3LkBIsB^tQDg8&;rv4Vr$Sq%f0Ee0 z$@)Jqr2h&)l#={$^nL;PQ{mq==Z~oWFna$AK$I^2arFLD5n12=rl9{_I``KAQCj@R z(fc(c{#5wCMdxz=a;E%eb?&cc?XTJVr>Xp>!v8HgSNZ6#CdGeR=l*(9eE$2Y^mjS@ zQieYj{-@Wu|BB9`VoLqR>inNw=OX?8-trF;{9i7_CR?6t!CsF>YJ*LkgpK-N?}hbIwg8H>{DsbPknd6l0NHl z)R83>IHl(qd}uD@`%WcIk#Vinut;l=-b@<_!mn}5R&#XsMXvmOBKa#{VjaXM_d}D| zl9>1d{+T&>$c&9=UGuYqx(tsZ-J*F*$C74(WdEGxw>U8QN-f71auc=_^0iJq{Fk{) z0q9DbM;&Sp5}qXleb(sT92H-`Qf>)1XU^y`e}=lv>&BdV!L;-peY#S22tcav7x&A+a=-@FdMD7D9}`wQdvzae|#Z?@(C(Z=z=nmrM9C0y{|tYcoj z|0xkZQK4~pc0QB>362S(>&57E*^gOz_4t&8uhI`TIz}SjzTp_#wYEFIs zc4@PAkgICx+>y?dUXK{}r5TpYE9OJ%?)_&vfZB3l(Hj*Ww(`$(rXx-&JaA9LW@(6l zgX4?q#JH__scao);^Qsp>3xr3;IjIDV%shO3=YJ3lmgm$l|(8cUM^7#|GleX<2hA0)O0!hmYr0ISVbzC0kXAdyg zzz8}L6tYLH&!3>8f!gs=oj&ex8{ap5c1P^geHa#-7a6^1%{E&nV>+M-O% z?diH|8atWdhJ2+*0FPlY-`s*jJt#^j(T=4>@_s4)UDFwPtM58}e0GzOGvAglpLxfmu`VUZhnP^Y zoiKVA7*59+rfSiQkEaH&;Z(bkRehRhmHbXHpybN9;8Z~0{=EEQNOsZH=}wtp4`U80 zeT5VrFJHaLg||2$;VY^G4Y;KOT18nnGg^bhAU z7$2pDw4ZCjA&YBnwZt7l87FN%vrtmCj|5!P-DW&xNmVb4g1d9G_q4{k98eDYR_gL1 zB>0!z#BXg&!KmxIC11-4a`lSDK`9+v-xpTPbQS!<6#FTUMqy`Oej*t|T#2!}dQ8b$ zrXN)}mXGTU*Rfl8b=az>NbiTxIMW}iyg8FD(^v!Z;TNga(=inLW|_=TvIR&hXvatU z)O|Wdx{MbPbIG$%9{9p=qMNTMq5)!Q#xwow)it7^01iTBSYPywxjP42az)*f>d1o9 z{U&WJJC9`cPs~+amL=bJ?S`LQu2cEO&TTn?HH>P}4mj!huICd{Dz3A2xQyNqkovA2 zxs6QbSV~}dfAZW-93xVDl9p51J@INmp>6`q^|+y_O|B4d-TR4NOA2uVsv9QjKkpnn zmN68abUmzJWKFi(Jv2Z%dy8Z!n|^^OR6t7#N0|Y)a0iAZftcqAy#R)wQ#Ub|%S45f zOp5I3yR=*K$|tHDHsfr$W*efKHkix&kNQaE*9%1-7WZ{bD++g*cl%aH4{sdYNtE<0 zpBONx>}QMQ}I=JmK-M!>Y`@Q~}vkb3feC*X+_nM#gTBHHm#cpey zqr^EZ#r|8z^gb?k&kma9#@UOynYq}2c2Lq9v1t?B(JJs}?p4v}JNp_B zT^nqo_-0>)l%=i6NC5UL2sb*?Jb0qkj>?jv7p+?2)-(&})2VT#STfK3A7L%u+LlVn zXY($83z;69?GVP(aqKM84v_DbDgiY3mNu+t)Cw=bJDpE;`lR;`!-hAU?#1WAsmm!f zxIKj#Fb-~obvA5acyvfcg@$Hw030DlypfjG@|Co;#L@LOFoxz$I-x{77>mK^b>9!>waP% zwXe0J#|E`ZTmYaE{h$f1Ctyfb98-sQ$FWm+3FzCy??&7E+((22_YVo(Urx$=)FJt; zAl;eP3W=7~z@qn2G{IA*L#TTJ2-p`j7DSeTZOb($7QQ1NoYxoch7YVnn{T&87g8q& z%QLTg94O;2hkQ1N=TsK%ZZ5=`o~{X}kC6_fZknxjk$R(^p9ku}D1PbvwsJ>1?pZag zk#KF!E&omZ8~C%0%`KF4tUT|5`7t65Ysr zo?Mvi*cDwoac|WlSzPsJzsI&SZ?VOXhHc#Cyf1eW;(ksbzGqb(X^B65BrdA@s8MKd zV9kS|YZu_KJ0V~HDfW0!YDcVb_KC-Fy}?V&1FSRofVyvy4z1fbcvB$7>Z{#ieYKy& z0HDw5^;cHkj&~iT!$zkzw7!}eHG_cVGZ5r-)5>pxUDr=)+a2nW{w5Tz4Pe>Lwx%*%))9xpVtM0H?=3Dup)EC@u#}?2qZ1^piF&cz+QR53c>3A}GyoM-d-5&+! zPG{JkpJ2ZwZj9K_A8T}0OBRLu!tj~tXNp)G(VbkOl#6mzoQvC?&d$jo`o2cQUTFqO zVffE`i3Z0*Y&C`Fv$gS+pKFq{oOe;78eH9XQ|y&k=mTLcXfePvakXD+o)msK7y|;T zp>~mtKQ)^_eXr40a^#sJbJVcswpI#}-UWpYw!dUu*{af={wV1`%&D%!yj9rX-vl}okp9i>6m z0yd#C%`7>lGY{yNI+@%oecNgl6P=E|9<(U-3#%bv653NaK78AicF(>y*7@aTo-BMn zeXw@;^sRpWQ_rWWJA-F+9)|Z>(}Q-V6kUt5)(D-XrBGOMz_S9(`Ox<7bJ9IR5pIcq(I27KtGkBYy( z!xq58*aSbF-j6}Wh!I{UR$BXsVe z>2Mf_t3F?%bmZ|q{H(4J(lAI7j50E@nBFHLxX&vb!pA%ST6j99B-^RVN0T`lQZzqn zS#`LyTfLgt2oCYwPj!Ym1?=w`FTAB!E>&`&ErzWIrU%ix5{GTtV3e6=e&79Ss1X#U z4JdwGD%qpvS;artX|f2lG^@VT*RRQV;SXd$(uM%ZKZ%0t7<_Te#LVwAS=b4AvM#P2Ia#$9`*p zk;EQXazfg5TtQxJ!}sK*Zqe^KVO!$dFEfWrTUpu4l_E;dNdg_Ps35cV!ocC(EUW`n z)qrN*wRtYgqGG1Q3FQZJ%=;0D+(OV~;cZOcMbn`uo+8aVVI(j1Za(`Eb*DYdq8eE> z8KyIn!3Guo)cuGe7@^cGl2Lj3O;<(oNtFaZ>rCG6-HJwm=uCuhc%qY-H3@gPQw!YV zCab5sM@x{Bt(yNqOg?pC#)u0vi=EM3)PYppVD3k{dQ?o|BQq z*6EB&RmV*5E7{Y5_dMGav|pF63^PItQ}Ux1m;SdZ>U?;khfvmcr0FzsUXDLLYR=K> zpgk-;^K$1I+>Ec&SGo(a;Y^p8Zp(gaG^p#y?r~I=$W9i{RA84q`ZH$3rK&E>&`Fqr z2ivrcMjNpA0(26U_ZrgGp+wxlHIDS*+u<$h05jya(?~4MX_xo+Wt-ylCl?Ovt+`cB zv9p7kz+y%4SJg?{KWMZK?Cv>*qV?^$UC|auHi{ckNB515$=JEWQGCfk<2AyK=ID${ z%{5$M130kgR5kl1IoasuP}#`u<}0KtLmtKLoINm?L9K)cEFi+hszK;erufxr_{VqZ_ly!TUz8`RL7`oql(?1D`LQLx8;;B8V8&AdW18db9xygpKgeN{sK|u{Z8H`;>AmF@k z;#5eggvLmRFB9Z{Hq*CR;Xln{l_QZ4B1Cxmg5tERejI9)sFd*52OokPleHCxE}cbp zr)rn*mqE3cGwvf5D3(~E+B{aAh*PU!pzvDhiv2paAy9=UQ^ATFwW$`svqbrQtY_FG zvdfaQ<0=@iw|oj0XzNfC&KL@*G8zamLU6_>Q||fCbMJn36XdGF*<~!=Ct87ACIV*K_urEa1drRTt4u_^+#G3Zt9Br_ljKcP*Ke@jy;GlR0 zfLBQzPkG}_rFBnii3Vg$rJD+hLj6q&{l#q4osXZj%HD5bcJw#gmf5hmS!`xIRV*!O z{@|aV6}9G~(kB~mv-k|qu7xZuB4ZtWaViH*G;T z9`S<3TEO_cI0J&O2bM~>f{BMF?nVZie1ogflA8TifmlAN?znAhi)E_X;CfPdH`Pif z9BlSo3(s&T8TJT+1Yp|d&Le+$!Uqr91(t``%#XY_{*Fib5 zf##!^wPICTc1dWZT|4|M%B}$2!w8quw{u`D!<%%~T973r$IXXgOghZ6(V?@v12{Z+ zCz7p^>&WI%p`=>20B85Mt8+SXLa^zghr8^-(YpsNkVcn#@vz0!-%K0oW8HI`@`0^bn0`E5 zQip{87o>=YHX@~|C)zJ}YaVNincyg=+TlB1)II2Be3!~`PY2*Y8Pu?Gm{=I@Ui$4$ zF=bHy2&7q{cLai>V>!R**ilceeNo}uD-Ce>tgOX+gYU>JN$XH`Nf1YxBMrt zvDUDu-V8-CpMKshea$(=IRfYANEGM-Z8Vg{xIf-Dy1E8D1_0v<2&P`T^TQvliafpJ zqz^5Ly?4ItZ-V3&@g}Ym(%FmsqAlZ4gHcnaedKXv?ERo|JMmGZuY2?g*mD}SuZ6M+ zGvOH+yH9;BoYhMBWv%9r9`dLU&pgnPZjQ{+ea$Ntlp1RGMVkU;)^30V{YJpHb^J=( zM{JGqkA%ktVnAr6U$o0Y&#t}1Z2QDeWSR3^9@MDet%J%K{ zC6-IL1R@c-Mks@n+8dgn7GU4tW%4)TRr*aE)>^3@%=K^N?B64Je@U+X!32M2a=$Uf z-?_l=O!7|z?=Q*KKbYX}wDUKn_&b>23F5!XM*ji!&tU!u{vo~C{;M`29q4q2`IKT-|efGCa zm;Ox@Tf32#IFT1EK@qR%wbDa&{N*YT>%{RJaeG(}$$9U_Q5VkcmXbREnk?J4J8Dv+ z;#qrg=#n&Yo$S_pjP`l~k8PDU*6FIEW~X&n%kkTZW7K`s4EmZ2s6|W5$jZxA#DnK#;cH^`;%>pRZ{$!F}P1V3TeP7W$TGRlt5T^!?aGph%OR=p>g(-+3iG@XD(wYy8xV zcL0wCz`st%;QD1dw8IdT;_aT$h*0mW+v z1R2~XZH#+PgF*84oC4bLQDN6PcYE7%yjynPiP9nY-sM2J&i%^MQnuUscUb`YydX@% zBX{zJ57p51tL~)p?NhAOU(7~H70V?EbGgxyniJGB<{^~~U-m1>N>|+JKMN5whMAgf z(&+#ey-%F)CBnVvjXHZ|_l|_h!ICKuf~naafqq|@#&nwY|p^KCSA2{etfUoy$)PvVD^24-OBy+IlpA) zLv*h$1MlDVTemmiUr6f4=4qFnHQ?(ckC}`VeWewPDFrm>mjoSxr5u7c>tnPxgatW| z@4#I+J;s!nh|g+MP`^u+pdT98mkMOEb^1@T(o1;?YsE3@$d`#OkS9l0kym5C;?{YD z)8aU&Sv8xrk9Vw5kQ9Bnf6gje-$(jN`?5TGm4Jc|rzdP_*PQXrf&Sem$^6eIe_ zsixu`cApXBT#A@>mb-qyTuwHFx%pzONaszH^;?t-c1@Iij9t3I&!2vQ{ODG|{gG($ z)xzN20T-q4yGT%V-y6P2Yc#KT=5)jMUf9FRh~KE6DYS3w>%=e0=o#>`bHnK>XUoM3 zbwzv7%CGscC%nUs3w7Gz_oPA>C7kruUf9_MdbTwUQsoU^9z57z^tBB!>@=vJny!cY zTqkdyq?L<}RBB-`bis!y>s4otWrcwV`<;V($L|(khlt=6Ynnt>4Si&fOa5a_~lo36i^{+_5z1o$nOi)gb#la$ZQS#|YfWXH= z(xPb8sTgtPC~ljIXP@~v`%Hk*+EQ&nk?3(%Gxy=O`EERL184&_)-Azql4 zw+H)X_2g;{_!cCi*0EvCkXM9CpuSXMnFFf$^jbEU`i4sE*y6J>%0pmc7CB;9yXa62 z@2KelV=gZpYdVS;LHOY?yX$HDgyRC8pPt?{R`Ik+;*Zq1-pl7KP{%OFAF8a!qv)99 zAT+J9Ys8CZ$1{Dxl#QIIXnFr6@DAc`PJ_|2{h&nKXsx1&ZzL5c;&pI|Fs{6-{-x_`(dY%o&mcyt=K_o%f8;y!>vNGYQ{xZ*mn#vw)EtLA#zvVL^~l zZEVMCnRUw5E&z0|&noE|N%T8(G3XWT#lbjS`*9=cUi*H0)|;o=fRcTJx<;`cxO4iF zBjWju-UcJ>C#B>I@unfbvUmu2{2DDz@GkESE!u@Q*+ueihsLNs%d|)0bFU4V{=)x& z@4p@=pX=B0{r}Pa2fs3;{wv7>c=!eW&HaFr2bD13e?pvm6sUjlKHwDO<@#fo{Kim^ zwVMA2xj;t0vbH_W*~&TRK#Gc#C`zJomUDMc*x!HuxAE6opkz69&z>{(oStK@HM80_ z34$n~Pz6*y_~n<+>+|}&KCjQ~^ZL9#ug~lA`n*1`&+GI0ygsl0Yu654f9087x}HD# zowD9~+&+P5*AMmxm|M;*d z>*>#?xbTSi3_Y>8`@B;;7WnE7n;Ypq+(Cc;-uL|Ap6`C!i>G(~N9Fy)(nZ}54-XIa zMdgKnAffL)LysJvoSvSYot~Z?A06_ivjnLq^T+LW+2a=HM{v<2PaA8iz%#SV*IScr zJUlu%IXODqf9u?#lg|Z+4(sYMn9q~(Nf2UBx8EZ<0F?Bcw%dKhyv|) z2QHNP^oH#>Q!#ks(Tl$d4X`g>b#ZxtTKw1Yc=-l=#mU)UE$<(f?$Oz$jhfVYesV~I zOi26e)yCa^#^P}M0>Mxy81Q@DPTM7Qk3(wLbBo>Sa@s9t$J9Ue%$A~tIiUu!IX(19 zFc=8tvS2S>-p^s3Wy z3w@EpdO=_GUyJwg4QC`@{ATa)9dHXRSTEGIMoPiU06X*^zF{Oz(i^vuu^&m{9oxl*Z=OGQP_rD8#k z!)mcQe34X6%4cGJ$HmDJjRS5ezsKotc|(b;TrA7QZnkm+_MpaG*92cjj7Y>#Mjc$K1nwBedjjl$6o+!)dh{t-$%1#uRDyVwLFgooP zm8xsCQZ7zqpbq;7>q;C_OQRpiQabFRwmyW(gUMY?45U&bu~;+`4oBmOL@Y!d=)}C| z_*e*r&B!Av50|h3*%sS+gVhsBOG+V^2)by(qI*dvc|);yEbMb&4#LAH8+-D(k!lw7 z`pZ6Az&rcLm!6ocwv2Y8n1V>oE}i~JEFSSYEoUdkFexlni!dwpc6J`0$=6!le!pu# z8ZBK@OEla<;aIk$b%(=lLrM7Uq>w!%i-Md@#-fR=LT~9B9g-bAfF2NZUL3J_xj;#6 z4@RS5zt=Uo-CnoTtV(ge9XjB#5>z{fKOvPWB`Fc0wmv(*uvl%d)aXwa^tNP9lJnU# zO#!6fqXCi+-(wNxA3DL&3Cu32aEs*vCgIxZ6K^=9Hw2P$MODjEGDI5b@|;XQ7tKV} zOKHxbdr%qc+Me9qQo}T{9h+5s{bff7AM9G~?4MZu>2iBCo%A&&;S(ZBq|>B39hQp= zXgi+|mJk_Bho-lKXHvyxZ!{kESX!M)hLTsoC2YW>;O)x6)3V_t{d6(CtuD@s0- z&K2t&5?|kF(*y0cUN2=M?#p9nXHj5DA53P`QNP>i7`?%8*lShuF|YmdTvWo3N?5IS zPdHOl8)`8V@j7fa2kGHJz)ydoe|sYtrPgSu6-A~?%F;JDZC2c4e!@99Ua2Q$MK+{(?84Y+`E|;5H#P54%9b)K`a8Xs7 zrC)y;OtX(Qy>|{yZNaSCn_pkg`}KUpW4Al~v2;G4i3e%Ibb11jcrrQ$HoawZn^d1xAr(kBS>55o5Np}Og#@H;=rfh;k%KO9RW z6ZDx>W-uYE?f#(KQcF@M5e)_Wfp9#Xm#Du)L!nTF?o6c~twYoJBg5|W((VaxL9f0{ z*a9DFdhb%phVqT!>i*$o+9{_)9=9i$$jLIP+JN65ily>}VzF4rr`gir%JDO~T4y*O z7#fMRP^vXM{XyT*>$Q4ouzdLakKgX5tzs%12(m1i^|GQ=wBBUCnDo*1u%If1bl4od zqCnLc&!&S;qgpQ4G-F6zq1RASVL#2 zkH*u5dUvu|OwonvHP{33NFR$e^E#~5J_fbg-tv~&L+Zs`eOQBP8Ro2p|MO+ z>y4+Afzc#IAZ3#ASRzejC{i|+Os471QmM?wo^g|9o8y>T)s~OBHH*yVabRQqN+Fwwgrmt^u}UhX zp_WNz$+T2y4Q9*5Y(y$aqu=GTIrO4(k*X#a@pMa6CmSRCU6>Cr{#`UMl!{8G{|9pyg6mPqS*)bmU`7lOx znu$k)UT-+N`TEEI`_HeplXgW)W5#Xa;k9_DeZ5vJU}l78WIH?B+laT`9Zd#pRmrB) z`4SEF#bRRAi}`F`A?45;4EkM~-;2-{y3y@u6*-raNz$aoC*z?(y{1@h^kz2?54X!P zP2g=Z_sT`3R8eUjg>gXEoI--4XGqlysXSKWWhqNNpsICx{hmP;ElRn(qP9nitE=T~ zOom=ZuUBZaR@J&%Etg2Kw5cwGevj^Mlygz9mAgDyhZoLZQYzP*tv6p*lM_DM>A~)Y ziWhpZJG;5RyPgl4rCchJ$|^iRm`e%(WmR{%B{uQ>UPN34!UeMr!Ow4o#A9U z9uCLz>xbX|`2E}MyswoDl0pr~twzeZJD!u`Y}YF!Unxh9)T6c+;vG+h9hD5UOupRg zO-a06twIm72&YpLVy{i(Le<(`*wsb3P_DQ8llkguMct-Vuc>-}ehcwVsGpIIP9wOk z>1{H^$rLAB9D*v>n%&WCKAVo|8qw@hrEXDg^~dxADxYdf9jI=MS-g`mO!0H zdJ#&li21Bksy12%w-Z^igRWl5ipmM`R%ukKa?C@n$AvvWhIF6o-pOP-zq983ZqpiRndB@o7=nF70I{J(EGFNJL;w*8eAAJ28?k2f{p<%uH9^CE$U?} zQX}Khn1=npAlYf1!E`~&c#PSW%!G1-#JgI~C&S^0BXado6rkiEN)*m4`_m z(~s7KLNe-R1@8brslp`&BuNIKx*Gk12dyiYxPNi z_enzJyA&~(TOUVnL~8VBOOj|)t=1ZCwD)Y-Y1C?3XE<9@6V2(G(E#JlMrIsoUQ#a9 zd{?(LvD_|4MzcXbF0ZZ@q?@}va>8hc(KYC`VXV`)kbb9!N3-SCk{qyJe>jGGTdhui zLW6ZRCu2&dniVSz17nV+UZaP5&|64>b;+e`qoL?8BtvrEdU_=jw4WX9?viWgODc8J zD|lJ+)t8YgP)hl&C;J~P-W1h~DvQaJ+?yKNwRO505Nf|m?N}oH*cz&3 zO$+EK)Ti2Q16q7Ux2X+!yx&o!sO#czZx1F+wxV}?9ccT?t1lZ3+SD6tryna`TOdiM z1(_d4kGq`Sq)Wq)?GA?hHmn<|L>5A?B`Wt+ypFTO{eu%*016)Bok9!HG)cM(qsLTA z#SA$YnL?$3*I+JWyCmkdo~}*th^&ps>b+h}M#J%Jb$xR^AGXy>h1B%o`j#e+DV4mK zPe-H{NxVI>#w#>=^q`#Yzkd62GwbQ~x=vCZ^*c={(aC(dq<%K(>lHbh$(I@iNt5a` zTP&a#suh|Tha-r$L!D%~q6vINlRM2E)kcT>0HX9qnG(X|#0U%Yr?ig!qZP%UK1Cunqf-DWZAvm77nA6th z&c@7$#%>qNnJUPyAa8pL?fm6|#aknd+3V8$!m~FiTxfekmExgbG*fCqv69A~O$M#1 zoPqmABZ_Pbm_S08vwYP$Ibt+0$%{zkN?75bXGEtJMrN~|OT<$$1SV$FgM;HsZ$iOW5Oh-ak?d!Y{Z>Lq0qHfFC z>ABsXERuCbJ)Bi?mkhKLIW5D*4W`Abal4XBM8c6+B9%&K^M#5=U12shYCQN&@nY^J zvxp|w;beJxe@9PM3;9A7qxWVtB_+yA9o>$!$^do_^b7Rg_1(k$&8)9Oyhhh()vIK% zlb3yU!{V*wW5GZqEm|Cs8k0K7CgK^T-XXgWwp<+@8$*qz-&RwHNl8OA9gnA#I_ip9 z{87AGDJw=OiMN~<;FDHF-Yl{3LQv2kMc1hd=OzqRz z^5*{jW&R2qg@{Kn{oH4Kvsqc;%_#j<5`%*5m|Bqtyhi>73nEXfwCk?jb@HJ&ZS zQWdO_Ub`kIqqv96tZngbCM=9zqxs6{jf7u)Sxx`v#9J(6lQHUISogVqxLZvJusBEw zwn#F`v`lkvSASw!iI2-f{9=_~?~&&0v47bkbBy#_zT6sLfBWMDwe8PuYfv- zLq}c^*={vS+(RWzAC+^TKC@F-a+y37nxPewUd!?R{?UayR;YKd(nCWv{o2bm#>z*F zH=ZjLq;xbCie^Y8R}bW7%m!p}knY2#fh=Kdt!HdIMj>95gaV62V=0MT0x~(eYluDDd zEcq5xHpy142}MfcCHt97w;4<>GM?{fw0GGY9Pn(4!N^8DDMvD9b2+&}cJg@4zAzby zG@!!?7>wOclSVJw9W-0tki#yFb25SRxF^p>608dBWs(nty@r-rQm{7K>(u2KEayG4 zoFn;KyG!4$y#BI$rP*p$vL9|*S$(mrEa#FTpEnT8tG&hTgYY8B>7!oUZq&#FX;U{R zCxCh?q;YBwWvkdTYm)xVNkwvK$>hccOI2;QT3RI^5Bft%MIYaM`~A23MW1Y7GO6nI zT3M0uQc(=gp{~SD<;**b%?=IwbTk->0B-o3%Hyve?rm8fW28_MX{pN7xc-7fvtg0!5Wp22Kl*VoqQ1b7-|}L_-Mt7 zA{R=vhNc$NVXxa0N>_~8E!)7Pa><+2N%Cryd|6(JopkH@2(`_wC@@u2HLTJ5TrOXf z1s9WVv9T6H8rMaYy)kTfknZ(wtDVbO} zgzf8slnQ%n7ne3K3~H$G3ORmU81F6e`WTefc7cxpa_6TPc3(VSs$oMuo8>KC>R;Go ztCi$jhCHhhLNu`^j=edo2a7SQ)yad!Zk$$ERhp*DMWt9#$sE9jAaBx@*aT3>(C~Xa z{%Be*mCL0fEUP9Q1>R$5Lcx@a^5&kh8NXR6SL&_79Ge#K-?|;WR!9cyXNO|Ph_qm? zRR7M)@^U^E7GL~q(G7?dNCudy8np#!FHaE2`??yKpwPH5vwc%4lU}GRD@lqES zr+K@d`l!$C_Jv{`rHU}ga~nyCZmJdY$!LTOurf`|ZE;c8<^oUG1wMwio*bP}kBeob zf}A5mJCTt}BqCkx&u0=b@}9G~d@h%lOQfc$C>>)Je=e6wG>IZ`lLT83A$2*rOMC)7 zp3fu`$#f=@jQZUUhdU6Zl5$yW)>rCHR_Ejemt+pLPKkREe<8^nyCq5FvG=A|>^PE& zXjBX7u*(b=qv31!$8yE$TQBoR&F}lraGYJlXp6Y!lcU4qGn+S(%H36v9v_W zwpvACOVqcHOEzPpa|IE#jJRd&@3|4_=JtoeK?D_1VF(ze#^JyeuP+qOa^M8I1O{d* z;@v9nF*qF|trreoIG#+zBEdi~lE}!YM5R>7B{}jb6p6;7QAEhl?=h;V21SL$nITCP z6r~{LvIwq^go2?cbr1do1nk5@fj}@6L5RA|VzIe=p=b;xCF!0jx+$cRPSCHSk*#~; z32~*eirN`3*-z7PD`}~9EHAYqb;@Y^J1<)Y-+$nCK4)00vl0+cd~PEFMa)Prr#BSi zp^`kfn@R2o{F|f` z^z+jmdOA%{=V%lKy)K7?3Us?1mW#8q3yT9KdA&aR6a-{Zzo2?iSERx{kN0?ee!hZX zvZM9EbUqz+8Wlu-1>IINQRNq8Hy|APJ1=`Zb>>4d$T+8nvj7kj>U7#I964-tdT2C8 zkZ0hqY)1uhj*-`ojG-X1j5tvC=#YxKFte{Vg}Iz|D@Q0(ZwW@Y`DtAF-1f^&7TB}2 zt=+wY<1^%wAv^8TLeEhriE=lwSuWOb#?$M&r>C-9>Lj81sdBk42=t*&Wo`Q%c zoQ7oPNg-Fo>U{fUZzaCHd;aGpBXCCun^XKqR`{pI<{)W1Y&5z_WIN9?1jUCVpYKeh z2p&AF zORAj9$q9wj4-jB}iKuq;ii>kRe1xa1^fcXNy~M*u{5UcOIOFu>lrzq#AXW(^TM~gN ze{+wSm3n?@C&Ppc%C<&UbqGEBd^>J=2iYp$dD+`(tM4q|t$bdQ?0bZ4U(TU8LT)Gt zh&teQggy5OPS!;-?j{>|=V_kvi^5K*jS#Spq=)UamKO=qo19~_VNTC!&~l$cPKuf3 zeYmL{dI8crI6n~eLDTZX{Pfv59!5gKzL`KSl9Kk;>Eh^1JkT!AMB2i^lY0*NIy${{ z29t#rH9#w+ZPzPo;gCOaF{g@UpO@$K0ML z`8LlVJ0?ev&ozJ&|eD(GlC>FC)# zA`9%0vyWtjLwIRsdF>m1^s@gA_=as^KhiEBxu<82RY%L{mu+4jqZy77G^+Xtwf_ipqLeAO@S zK^)sL2^s>xc4DsRe3#j_yT*5%yv}U<7a0v7YoELaacpOA&1`cB0Gn8W`yR867dO|d z*KZ=({tYJo_qb#4@V397IJSjY1G5c04(sI^4an~>+putcX?KY&n1DdC9UXkszVG)P z_SbBIziu5o+1fSt7kB=QN__h_-$96BrOE2_cs)+*`QzTs&VQqdUl>o1h_zfUn@;jB zll^5jW-Dk_2-&=A#D+2(fG9WH+)Wd;!;xtT*5j^9&Ht^sPe4o@ut)s@{|Ea%U|3i+|vFI+Fs z^ZNb~FXbN`tQYk+|N6es@Fc7X&^P9Ki9Mz5)$NUp*xY=&p|IJ+S#Jq#Rq!c@82Zvi z!zRkZ<%AJdua#w*voAJHw#wdl!fX?WH4ZfsjFwp7ZDv~tp(=@-8P~*A4kfdWY$+oEUd&8N%LtvK_eIq&ye9!OR8Y3Yt~MMx}rTGdtAj6F@mq&!UoM zS#&qb;(O0dUqI%}-Mz=M*P8taVy)Xjtd~|}W?SerW}7T=)LKfaXdFMuaf#sigj^Tz z7F5UZR3@=mIUvF=P+hG?IiD~$su|;V%E2`rvHgzS@l&yd?sj9(nnM_{du{%c_pvX& zYZ32oNV8I7?igt_tIfgjDmFxi2(LkGlpdN5e+CRNUTALS9}CoRmuurJ91TDXGS6FvsuZ7^vT5kA|RabMQc2UL5f~}~%$v>S@_GJ&!%6d!l*=SS9^3ib&eg7<6g1nddNG@bMiC@O z&u3Ghiu#xh$Q;0Pay@*3067v=w=@@>24c)5V&lLCPA6hWDos)Wg@QyCL?o6KqcxGr zWPlY$NL?gKKV?PkS_0{(Twm(KJ{NjUIL?2C0Zby_xD99^n>!d!n`MVQb~EhHWVRV2 zYO6MoNR>M0>U5gwE6lcX1Nc2zF4lpjn3@njny}Ysf_L%;VcVnf3daLyGC8H*0S#u# z`ROGo4Sb^8A4%qmRfO7C$m$9Q0-<IDu;b<`7Rs2}n2ED!|UgQRI+)O8SJ%M>7!yJv=}GGY!$Ma;2Dw7DX$&~w_yw~~t~OgbkbGs0ad~|tK2s&C)J$RbJ1OTKlgBI-5q_@% zo?Iwao4x7k=6Z=d>6TWbp%o5>qG^d!M~qGjX$y%&S}Gw!x@#bzO$8v^-IaQkC?ezN{wUCOSi{v@WjONwuV2jz7Pa`Z^=V}A$1q@pc%(em| zfwKb1_b%eyJGyWNscXSdZvwwqrEhN!ID2M39)kY{reB<-S?2Vx(P+@6nn_XtBq?Af zdYoO0v{&R$bE4&tVQuYtA?aVE*UZBW5cymqRubugfD;X2He}OCX=t(#yPO2ns3KdX4oWF* zBx@I$qhL3vCB^I;=$Pc>G|D+MDTZ`2xo?qdsnPCIZrTpl>({zT#{q>-^DTFq|dlq6FEYR+peh^lA|zsM}XS& z8AX+qimGmKBHj$Ma;02rg8Mlq8=*g(08O%-Pp5N0ELQUgW2AbmYEC2)ZjLuG*In5% zS+`Dq1P01z!fX{F$tEMFjJ2Dpl8r~A$$VLBchQMP6A)P@6V9JT00e9nGnll<02&iQ~oSxJ})Uc1PfjOD9( zcPPs4*5#P{@@OMofhLIiY!~O3c7H-?pwo|$NWNjV)yu^qk|LXGQOXx8IzvclhOU4` zpG}56@8tKwF?WWuNJix>wkj^BgRQQho4W$042yso3-G5YcpDR7-H6qoFw_98uD{I~(zWP{{q_=H~ipwZtV_zTZXub2^;|jFAL1 znJreUS zKtLcjN~I3oT}LdWxSDk9G$`b<5HIuxh-Yn9_XALo z>N;|WVM(A;Bjkv#=$?@QxLI$^G=(uMUY$F|U^M8|OzPCm?%|o;pDd_CyjS#b-C9-v z$6heofHz2&;Rl21wPChlw$Xr&ZB*oRB1!TEvyG8$IvLJVGGr#7{r!73NY5=cjh>-Z zqzt)66I%i3Apn*y%7i3o4MDH+K(TzkU01M+%-AMTl2Wlj^lvqlH-ydMFz%HQ!*Gm#jxGf~!>kUq79t_9yg`gyp zUW2Bd^z~{%E~=g3l%%Vc%Qbk+BQkA}|Jr>7=!lJqR8E!|Y|UFXrg*!P<<0$Na z>zCUVG_M$%17XFrTdXZfl)x(7-!Zg|WR0eynF2E}NYC+PuEZ)FBDlS~Ayq>2>SPR$ zC6_NYx?|{bYHO$gbfMLJ2tr-H2;wU#4~Cj)r9A0QwGC4^zZUP&g)5v9g`@17$5lDQbH#T#tf|p>Ob4Czq}u zInEXEL4)j5Zp1qU3G&z9fB*JyJ?H#-@D-?QgY*gGmVuw>S*zRoHMEQY7nnmAQ*hd& zSc}mNz>iqGw`AR+M~r*Taz2%oYn>6Sz!5%xQI}*tbnAswJejYwh9Ihu=`b-==*~Rj zAWiY^?Cc*~JW;Z&3{$+ft5H*qIA4g@d46<2E)+l`n0o~YM*!7mAWQ^kxWLv#eneIs6&n7{t^+t)k5mlR_rle|T44uFtht>q$ zu~p8-!jY7s4FF5JyID<)nv{$s$j~HZR?DwPC`gQGg>~P)8tY2Tb$<9LUN)`Vm#4^h zj~5ysPlCudhEZCE{-^n!wEuL_s>yJVL(zCD&57cCkO0}@fQ`O`aRucZ%ushVFnR+R zyQZ(E_amp~=t!)6U0kyY5ukHPK^nUw> z!8_`5zP{e5RmhDsVSzLfX?kcGgW1)M2`%HKenwT&_$+0jArcciB%*@%B&<0`>9ntx z^Xas#8WWn)=Tk;IidnI*DXEY@n2;O2nGo-^s}{`ByHU=jYXLeB_ifS)*VFbBNR!EI z69>mc$Z#I8^A6sYV76fu64;SRIh)N94XH0=_^@s#E{Exdj8IkGVIesGY&U*oYSpGP!OHO<(r* z{$^%WQa;P^hS}C$>pr(HLPNg?8yre#(CaX>Er-|NP@NV7Xt-n&neNGW9QFcz;&M6Z zhyr)s!7;LU$rJBT4av|*W1U?t)yR?ti7gR{BqWG;#JF14ZWGLa7{1UxgO0kUtabD? z-HneX$)TCH#QV18T!U$IP3?<5L%yIU4wuNJGBjgOt{=XU&K|aE;s6G+%7G%}t}q+iE8K zXHxQG6256Z?Q;V)`%Eii@!l@_YBsnbE{a9TdLd1k9tHsE5O_i@aD;fwL=*XHXL|j> zKut3Avb^q{OD7YlNAafp)^`yvbFP66r8MAG~Ge6_MoBvYd&>;+bM|boKD- zx4U_-L5?k1ZFDE!gXJ|Cv@ zOKQ-r=Au52FG>#%XA53T!TN*RX!R%aX}=*wgxWc_z}Fc}=QC5hcdMZ$M_d9>w%$@` zt3_;zRGC0G5UXGY%}r7~7ziZ_+HiIM^$Vb0MVWUqDuA*)p7K&kzRT$Sg`9RrFkQlM zHMnU3Ga;u8yUZp)t!eZb>W9IA)?>@`8V)uIQB^e*QBk99}D(LUTz|3Ob>u3VSTy>fdam{&()PM$I%lqUSue4q*OiqTta zg6vNHVoqh>t;n{*Q3LyXe6|QS1gRwe?5npJ5#DZ8aZF09rIoW$ADC_B&h+Nt;d%mY zG1J^My1fKq9ggejYbG!CJ=QfW-nhhP0P;B!h!|=#TTf_haKHN__?oPsme-`p(2{UF zvX7Lt4R8%-2qwsVR@dmY-B;I~MYKk-(Z;?FTtLQyV>_sn!;2WtJT?O-XS5r| zbjahRrUz;cOQAJ@T7*%Vf5IbvM$Bpidv{ZExoTf7fBC8o7m1 zPQqzOh_i9q!ECGd=P0#R#o-^!XwsW}E(HdAi*?-LI|NID!GuK_vXz17<+GT;-D{|o zEm~WXS0}mGn?@G%Is0TWrZ$wdN8lQCYZSY|+vv5IJ4Pme4m7wrX<80-0R0`*GdO8p zGDD5=d^Xfe>5$hOh|$c(hZunp)~FU0gn$8>^j;Sk|EFCB{b}y~~ynVmZ@_iF-q{ z6=WRaCU4W)utTZ~=pZ=8)Y-ACi@nd-hO+huT$3{KaEKf%=^1+M*?zBxluXMwh77y4 zHQvmn!CTHJ0xp}?fg_P{Bta#I;{#p(7)WotM=MT#$|3|oN=JN7hZ_Nltb%Y(QJ;66 z#XcnsVX_v!e7V1x^mK$8sDe@_r-4?qd#D1uvyUYPUeLnavm7*d;gS7b^g$qBduG`N zO{px8y{Dmcfb8j7t(Z%M1A%ZtAo<+Zv(pQQP*UVD<`OtbtpsM92+iuWsG}tUj(6G- z7vdpnCC9ZMk@0Ls{Dbu8DZh*bmTa zBF;IQ%pm?HZsNikbwKSx9&E2+7rk7om2xq1DlaS!e>795s626zf=Gf%M~_DR)D=if zsY12U#Fn)gHKGAD$;;DN(gwrZJoHR9Lj`;^uq+OysZocnYS>p=EV0jXGw-Q6G8!0y zcw`ev7(EBRyup-P@$?2)W@>BN#w75mpAoH3c@&tUJa zUR5~a_`L$zIh4o~q_L(Hl|nv^z!WA#ZAfg0Z;K$xNrdZ=#YdKX0jGzhVgdIfWo-jo z;}f(YiP}IlXiSV(DC#PhaG~e=m&*swR$DOh$u# zM8hGQ!g_IjVe^C%Ik^ZTokW*hP;jzVcQjo~Y&q`K3fWXrK;`0yiL;!aott51>3mTo zhiFFr0k%vg23!@)D&~QJ94~?s(Ac+&pR3dQJuyxl#@UF9tY~gHO~(_+A3% zyCQ(o1ic|r_xObV#s{+|Q7`I^qzz>3B=QL?=8}<;2B(lOpV#dWM@|E|76>6`0O#0< z4J#Vx#e~?X7O_@Ney>?Jk`3%Q>YbZTB%x%pj5Z7hyd3@q2-xQH!O`I_2_i#<>1ik~ zh0|zp)T_&Oe)4`vMw>`ju)17!95MZr)@HGSKSk~DX51S|AI<6k?ad|*7&p`WHo&!u zGa77X7eC7VTW1K;ZD;5i>*JqomwZk=RqNbpcRK9Mz}pifFb>$|^Od(iZ-)o?n>be4 zi?C~|Dh@Bjn(l;8*dcoervdm}4*Qy}MpZjBqiivt(n`I9Xa!i?qmG*23Nu0TINj^A zO>i5h=WJ5Qf(W+txKty7=dh@QFLCg5%w9ur{(yJ{F}LrTLdJ3ICxX>=&H%OJ zX8^S|_zjY~z!Bw~4R9&WBY&ihOknZ)7Mu>K?Y;aDF$(!y|8D{T$wFb&=_fcpKb2 z9vV1Ye!YjY3G=szvNuR2e zd-k_pvCgCB$B)ppFjEVsy${ZFN2}z?K)X0G>wjowxQJY88e8xl5Ue#2!GoE|qs1@k zAy8&Vj}Na2m?n~sv$r>hvKM4M6Ranar^IK7?CtHOrFRstNPt`CUcIEXt#8=d`sdy= z%G%R^{uz$N)B8D(?FGSL_u0?;8>ZOyE7oanTrDW}3x_Y3RcgTcv%5kr=!Jk?njf(S zA%IE>6F#f(834MO)wLC%{iN)tpY@oh_B8YC6|&yu&mv!Ky$J9@@P!|$S4TOI`V9+)O+0L9#ZzPflS3Uup2@){uIf)|{l5n_ z^rO!HOpzm9`vk_cKgd{wEd=#z7643J2QuOSVD@sB*xsjf|AbNgcc6xz&AD&BX**+_ zLElK~1l7cPxs9NHxqQY@6>`GJ^@DSGdG>!fvaL?;84L>g=hJyo5<@6K=NBJw=#;K zSha6X5F$I9Gl(5boBu)9`F~-I{+!Z%Iwap7qJIb2h=(Q4Uf|U#GqL)`*kVMD%j@@h zIb(chTLQapz*wHul9yh2Elxy}{F#2Yx4$OW|C^-yWJta}L^lkjEwIsFdfTPl&Fjgj zWFjh-Xb<;ZyvgDY#*$?8x-F;wm1cztW#NsDa4-WP*+0b0V_vg|qkEV|;M~FWS3shLoffiy#Xn!d}NS{yFM!;S477g#t&} z!l&7J)5F~U8MQn(zHs`ZsZ0i^9SOtv^7L?Tcjr5`-7!}x1$bpoxZ3{;NA@{uZ0l#s z>EU`cl2>@l?XrTDj$i}rU~6?}y~wz^*<=q1{?g-d3^*9g6GDKr+GrrLx-4g?vs{AZ z_qb5De|Ty`+($!zhRCbge@f?kT<9`Q@HJa$4<{_y{jqGJsK|(3jK>5=>~Meo>AKRB z+M4?v4x9oc-nI!v+54&4`cGM7)XBGgcJPueucq=Y0UA!6_LvH}tT;60^l_baYw1-i z){1p)v247yb9Y5R2U4!uZJh3?Yc(Ys#r`j!ZelKTAD&*i5HZt5U`rf-Jzl?J9pQDd zUZ#FrJ!Z0%Se(ulYk*4R%`G}7z;egy=bqPAeB0@z!?WJ4JUTEf`wv<}eox)}$QpBV z!~zQdKhqGsI6K3Zj*WLu&dx2aP`apUb({v{b^vx`9&`lr_kg#l);EjYM03w*P4(pL z%Ro}8BMn<19a>tokP3US^>ey8Ug+%7;ZGDAz3~JoDKWp3H!4j!=NjpRohRP0yx{Y7 z&Uw=kKpbB*TWTP9unhi2DiIBOoZu)~_}r?ewH3gg^E2$}u|`L)W+WGCzTN@a`)Q}+ ztpivSemiO0qcgidA@hN(LAM>JczOK+h6mYf4xHtrwe)%suoZv6q-44s79Psy*2kNz zI9ST+KqR!eE1C3kQGEA3^8b z*xWd^?EL(acZ~6)on&Wsx_zN|uA~|LJ|iXg%=c)}&*#ng*8Bcc)K+bM9;d_Z@B|aN zVz~@m;M$Aqgv~YdR57h z{BUyMCP2g!jAe>ie|~d!vp}#ZLUp`OD;R99HPR`{AwmxPoM02&j{^g7Y@}S#x*V!p zFM->{VHdGjoN7W>a#LG+i>Pf9V5UGgDOCY?D^X|jdb|N)+Jl+Ib>ID2EvwDp=SnK* zvRbSzPHyhB)IvHIq1MVPI0#h9rxVdgEW?Vm(*)!Pj7)%qa71X(>*hm6`DT!Y;sDFU z(-Qs6!HRH(JbR}Wvm+{_2ShVd(0 zI8qWvQ5Fy*mcfys^bYWixOybQWF}uIt9oxd=X{k)u~1-67syCOKGPI+iv~PSr-#gU zfIg`LIBb^Ta($6R7mr5bWZD;C+A}lhyJz40FBh*!o9gx2s+zYQgjMcBu|%h^nh zS_=p4HZ>J9Sw<>r9pqBsT-_E281wnR`BW?%iKhi?2)`-G*>svCfLk=XawOu(fe>$| zY5){OEBbcJuYOUmvcJht{a_D?&IBw!}!MTD4Rl%NP+@ z4ZTh8AVr*$5Gk#-x`G{n4pd?=Qn{*%qjwQ`iOAMS5b=#lwb8`%z`?>w1?U*gQOjkr zQjtSA5yQzWg^xYD^=}q$zTO>;2lQd-cr2c#fisvux0Q=U@Kq2VG3X2SUa6*cMfO3T zld*eDuF^P4St`|AMvtTCJ9@oREFu$j1Z)w`;RW!xw2PExZE9e3t~Q3S=uB$By&a`5n4iAt}~1PAvdYVm0eC;luv{pC!BvJV!=5FZ^R%H;GjmxArd&7LBG#|`Cv@dk=!#g^RorVXgFIg=VTpa$tpkD zByhEc4C!OGOFj#wA5DrR05dV7sFWO1-+%pbJsWWlJA<#vsyJa?=K%R;Lj@Rx z-Z2{*&3e6|cZ{xKATj*vdNo7lm)2Z^k>&!=gCyoAb(Ne{VtNWEK8#6003EZuqA}l8 zt0dh12pRau$nVtUxEH*bpH;jONgFJ#uI50?P#ZB9=W0Iew%gs&Y=vCP)zuY!`G9nC z%fK=8%(Yo9CW9{8jtN68+>EHx<#M$`DqpYHAJ4CEuNOnTlnOd8F!u_}xsD_)>HtW) zQlxB>Ce5^5sW((Jt*O}PP{Yr!@4q3#f7}C_3S_fplQS!j>aU4^fq_IdbUPF17~ z{j}nhwb4otHahK2Z#2KYySrIVhW!C3C=d6yH#fI;cegYiAWe=wL@+rMa~c}5=#Z>5 znl5kXuA6Hl_ugC~0dH}AcYibQ)upJHS(Heja0F9D%`l-;8W51VR5Fz-s!e^Jl!zol za4+t^e!E{$d!tVwhZ>1tqhSw>HE8cXlW};YPRS0}fXf(Ar>Qq+AkOELfzjq9u#uT5 zHtBa-NUa(T46RnJHd;M_OrqK@siSrg_09>VO#^riP`R$IZ>|@EcT4#DDDgH&D-wi2 zVF;PtUd<-s>GJl=w{Kq`?r!gpZI8eAfTZHw9rWu4qlWrpk<4@d<>b8uP$b=&u-P;; z4vo9JySqc1L9M2Mo}cSQaU861;9L96Qb+f1K3_Fs1kMS9+Zsr`bS-rNN~ zEp1QNs~2n^>#VZd_UwR9C{2j~XT;cV`uU*;*$?2$vr8|lI=^tA7Aq@p6E>3K+9tAb zo#L(W9KB1vkVH%teQSX)a%LtJIfM7oRO(<1^DZVz!x?ECZvH8tmNXA4M6|n;oahu2 zWG(ypJH8FxM^I9YgcuPl9BeSuR0`IU`416x8gf23mACozWPF@N$`@Y9$Sky9i>L6I z9-uj(kj22G(wu8vGm{S!my5~Cg+TB7^;W_6o8ejm%iE33P4K$@oiCGt+?Z-GWyFRk zmf3z!_b(vkWT-^nx(8>~ioH6UXHvd~%b~$;c%yT@GAS#jC4IwaGtaz<4m&q?ObKjy zmX^)N`oyn05f0LM+EhMaT}BC_t$1*K>%@g&{FrBKZY@~F&^LL#Q8JaSHbh8%V*Mma z&NKE%EhM7&56MIa)>t&@ohdZNQA~!sMYf^JFi=kaeQ(kny~KqA{e}jeMf{#r`^Lxf zu)p7jyTT2%-uQ7D)M_vws$Gr~SDSFov$US}SN|RQpm)rnMxgwM6d20O55Hih19Bmq z1j%0Qruj40PN--|GL303<<49jcIuR|6dlr*&vlW-nDYrlpCp<7C?!$1yGrZRRP=k5 zk~COv*Cl2(DS`r4VOpSpdReoT9^X1QySE#ex8vZ|BHt%U7I2j4DZLgV zDSN0JwlrD|W}<2Xf=QE7UP<~$7bxFq`igSRv5F)SO@P67S(?@!LddHe+Tx6TxMe1flDfN?s? z>SV-rTZ}B)Wb-eOgS+V8Ddc4LNbiJ^YAQ_~A|ZpU7VOYmj%|g{dbhi}%~H`(8z5A` z8&}I}(^UMDmIBW7#gxfdfwGZ%StE%A=9nqR@uv5S*s5&#JHeg~m_g=RepcGWEMNM} zKjm1@c;PkZbyY|yAD3GJ49UO^$!Y~tHDZvbhxS%kwV`&pz)Qiic@%?Jg&O0(0rJ*Q zSc=yCyf5dUy9#W@FPc=jK{ae(O`x3PRj4pK_!4Xvunv4wdJWDrZ>%{Wq)o%;a9&xw z-u{|0Ifw>v)Ir+;Q;4k2d(?PvvSLj`OQ9NwtIr+-m*-%MfImD4N9aL;VB|QsF=H~r z(C}=cZL3=606Z(&-7L36pv_O(fq}VqXdc zXu|*ajQ=^MIXwm{KsldL)fy+F+mj+PN!S%LV z(@lHQ8&u@MLi)T!r$(pp=hK!eU>nB7TX2Z)@j%8({Q?N|qq88W_tV{U`lC zf@^8iF1UPCqzubT*d2_d!BNcN{=>zeWLBZ(<6EFUc0jf?Krq`Fk4915SNbyq0)gZO zH`X`S;DPaB7+DIJIUY_8<6BrX%%RTSWo1Eewr?{}jrP>G@}a1*q@f=~-i0NJG!)Yz zP-@diKF-)TZLlTn*Zt&;W=_(dH`l<%VIw;u&IW9|6>HRG+RH4?*fI~Xl5y#A&=EGk zk3L7~($f@StkmVcfS(%4W}Ht$k>z&~xmx(h6ojxN-?rf(wknM)lN>p{{M6dAd3sDG z*424t^*vc+WlGewVHw1K$9G9FG)$exI6$;|E?wcrkv|`j zkoR%Le5M>P<=(EN&Q8XHz7F+9VAHULjOj3y*N}0hKn6?Jr2%vM-OH?Uj5Q@KU8Ecl z?jf2RW@36H@rhAmDUFH?3nL6ILD5P(;K*?f0en#iRn5TuFlxSb=UZ@(_?!Gm@d9?Q zwT0fmLYYPswBT0KXl_74`fs&>wUf_kgJ94&dC(AQiuCBF0(a7#X5X&??+Q4!cM{ny zT+xrL{?NB1v7FPF+>X$B(&pGP?So+CT#At&d+zOASWuj_T-fs$P95KWD0*h41RGYm z5XG>dAiC0g?i4$VUB=3eOiglfM~88D8Po);KMx@6f*%?s-mn;uZphBd)yhWF`!Po4 z9Pjekmy#}@51iCU=}VZb)Il}uyQnILvAZLcK1+hsjxTGrbh4bpt3Nm8u78L-h$Yu| z*0frck4cOppC5t80rwH=izHxYG;x9ykO^jM5;}J^iM%%PBTDz&e5bzweK+=}6QZq2 zOT6SvkYg}=VM4i=Ndl6kuF?pY2H!PZGNKe^-l|b6F9hxJ>EqGSZT~q;WT4V#H-$LK zU`YWe((N=br7*Hy&8xNujmRO8=7}kwM?y%#*f|**{B@ra{Q{R;0@`+ny8w?qQkQ{u z%8DI4f|Z;_5Gqt%B)%|gAU-ax^|R5_j3(ZujaO=AQ)6pSAO06EXmnt!?AL|>!e|X) z43Nrn+wm7M0ioZvH#YRi)}v{kR)6{Y3)iMTo^Rxhu%0h`)q=SXj6}oApi>>R+ub&m zk}lVhdQmp_u{-aj!3kb#9v9O68GIi?*GROiATRPG>0tVBv2|liz7o=B#-+|84ST-? zt2O}~><8fF@&V=7o7v7Z8@&1X<(o4m_t<2A-!F5x6D*Hl$-QBadEF4C5e9{>v^}!A zTxr@{c~esaA7gqJ7Zy`AKD0e)I=kQWVLo<_R5g37tHW;-WH6>_zfn2VC33Wp-V7cT z-VMB%Zw!x);z$jemRMwv;!a%NXq@snCyOEP=spf;^h)jUptUi|8zNiLNq+v~)+B(P z{V_%zB0`uigo zulS@s+_8n&9+J}@BMaT49p6GkH*E{>e@}Z7aeCS4Aq_cI7gnCPF=+1k)XPnp-7;h*FC0SP&9csJjsoVU~? z-|Y|nadh*nrCUCIvS}{=p?BK%Pw`%VTls#r^zn5YZtI`vow@!+cp3&JINX&^hYnfrFgOYzYjif2!?k{W&{9dgqNhpLO*vWhu&^ zX(xZzJFoUx@?{8!P=UOWvs*2H*E{b&#gA!ZuJ?hucFmexqG0uLfbTui5eMy)>Z?r8 zv2pVP?j**^3XdG`_;%l|pAoh*M~J24XYY^R-a$w_T?7xS`>xA&5I~2w zR9w!7xXQzU@#DYgoo}5z_4zk8*~a{j9*);8R)-CcLDy zlMr`P>!$TU{8&aL>EQwU#`HBekodOYAL85HC-06`LZamRjx%q8^S|rgXa6p~Ed&~O z0R=><>^MQl;opB1-%etVFM!0i*6r!j1b-3Vwg%05vU~@LZ{>#y`|SQEzQugYCH!~s zZO1l$@Z4q1=1I$+6p`oa?=O5rH4XpaOleWeFdgR7GwaeD;tUcwauOB&X|^ubX}kOVj{77Syo<54#%ePD}|7rz=vzfr*i)N7q@9l@^;V zAfZ;12~N~B&Jld$<~EXDomQcA+L(0fy86@kbJ^;V+4u9+5DvnwK?)H5;5wOe3n10&0T^3){U!G2ogCG z4Mifwv)B_c>RAKZWc4NHX&no@R&Ko26gk=}tMqu1)qS>ZzjV6J1Oy|#1m+YtYH*=h z+O*&_C&m_ulrLh_Pl34h8VW_6(#G}O?~lDge2eqI1KXKO^f|mq4h^3p@sG-agjtA) zm>30FdAV4GVV}?TmBNL|&isFHPw9W0zZcXW3vNyqOW6wK=u4F*stnXCR!%;06>V99 zKs`L9wL|90GPHRt8Ez=!=8YK(fIUsV(|SQztqknK;?Wm64mS3VY~bUs#tup2Ctp7g z9}{+V?wNhBTQT(-$)tt-lRt!BY5H%SjsBjlx@7gr^`5HBQ$?ka{nhFNwZjswP3>>% zSA&Fvi!Ili(xr!Hzb;<)EIB)vu)ns`KO6YHhr8H0z}$D@C@@&G!VQ=1k5}6Ky14F4 zpDv+%=G8;^Wgr~sa%OoJ-kF>ImcGhOmt&x-x4AZ_$GNl*yXR?82dq*P5LoI8QYlY~ z>2jbvO!J8LS1DdSai;DO5X69Ed;5HCR1;QuC}?*O7rcZsOh_E%b5bt$`-1IlbJj2Y zg!2MpvE@Ea;lryMxeDxh)1@o@Nt;Wc14JCEHyIBrQk6au)yO`Cq6P)}o ztuX^F`~P97ns5sWYWJflIy$n26A#;%s%2Sn6ODW*96*gkR45hvZd7v;hSp7JN8dNH zr8Aq*UW2P#ZR6M?NSL0k)8%4Xa}uapaTik~WNK;7-0I`%eG#sK9}gMDi}gL}^+cvX zb?Azka&8A!LUOa#7C%>4)dRAc=VHX%|3XSRfUm1<=nAIN&b{dYe#&=^j|-W&zTzTC zx14!}Z(G9wlM|MNO!e;FoK!dQd}e$!WXF)seE#=$evtj`6aUxO|H^lZ0{-9m zo&V=;JdPC_gUJB99*tESX`j{410N~n+R)7*yr`M%j(EhAAUg3tBbttF(MTF#>~t^G z1~cu71^I@VU95lrP`4~zTLQXUHH5nIJS-q2x+ZVQkZMv?|W1z%qYjvim)bHY+U5!ENOZ?qeId!Z|sa`W?OFwsrhR_17 zZrsc~Mk%6wY#bspod3*{=`U&yF;bbO<1O_(vnI^*+Ev(S!z*c#rm=K8ld|PWWMx?q zFA>JblBf&hp~_7uUlA|9_BvL4C7di2RryxHywbHEbdl=J3neLzLYpE@Nl96}8LpIBw1icI??k4jvq03eeFrGM1R5Tpgl>9@yK#W27;z;S%Cu z%f&A*KMwxBotU^C92r0KgAsLkus@OINaMshkG+-nMz{Fjy!Fu6w{&HhHcf5>`ZvPc+F6-9k(md(6)@+dae?#-T^XB!-?^TvrxY+n;` zsy7VTGRrc7YnJugc!(X%efcRYY!8XB#nRP_GvBqVA%Q5Q>Ogt zFJCBAi_FA3K1@?BSBCk?G-g=7#fI`0_Co05K;7_9VjPx#v3vg~G0xvYg#Xbo4(q?T z6aK>1=j8bB5##)otD#_?CSe$JscF85rEBsWwf_hGinl$L6ft-|u#)CXJlA+>9g zYMmrv$Au^3&TurH8#n@*BGo4~PQMqsBo3?p+Uh)uM(|?zC`r{b@bvxW7 z+5K$i|1r_p`DXI;U6S-Yx%R!F)7{ieN~W;M?ip^TR?z^Z#m6Y zJkaOmWkqmld@TVZO(Xqs?B=W1_@&tR%^4#g=mknrQWEN=!{JTs7pe>o*Pg|hpQN5a z8d1;F`D-`H8mG2thTmhQztk@%$|7+ygg=Hxwc^!r% z~BCWFV;cT3UVP>s#GvyDY( zLIbmGc<}_1mrqP-xD*reHgUYM@GZvy8G6O> zV{7a36Y0~cDH+0Ji2Kvos?OHl+|F$X$*d+F4h+;ALFt00lee#{ue+I(cc*^Z9jsh^ zM`?-`n{IX0l?2t6n>KuDm`?=?jcA%6UJND573eAq0qUt2m_V^vJhlSl z<>3VC*{7|$P6^gfa$rH80RVt~^vp?n*23WRGmSs?Z#kR#Z_2cNyDeFavE15MSpzCT zz!FfB=uOnn+$Oq&7pPFsAbpu6K`lyGFFQ9o8>Ct=f^W!QIO*wnpE zD;2~`aDQS(4^rS||0PmxTs> zZeNQlhe#uoYXfkfR0nOk_)-~_utVqfB|7Ghc2a7q>3;HkuZfdcP;Lt2Tag)}Q)$V}qZMvtz(E;nyNbh%4Qe z(+WOA+>+>7SWR^7RQ;1@niAD`; zIMEUVezmK|?mp}8UFq;#HD%zIOS-zwuz~{IoIHV}y2NSlJhO{4rkH7vE98&!*wS!- zB#;g#kopts#$mAUUIv`mdp#;1-t6g5*q%17!f$0d-$6|&c?-T6cHdb5+gX$}C$ImN z*@QxbQM_^&+iwD7lC-Za-_BWo6FpPeWH>ccIQe=sL#Z+4Skkaa(hTwzAwmQuc#;rY zam%KkDpIQP-{l~2_>s=XEwds$*O=GiRM!}?7%*Tag1zj|evN!MnnRvi^eMcmqugAy zZ`A>f<`TdRH#WbY-n(zqmb`N8rT}3;R;UayHk4`g6(ob6`F^-+^}@s`FW_v;9lT3S zpD73ESUhtt<-|htnKbTq=->%+&38US#34*e(@0LwCDCO?%@i zg;v`VVQ5#lL!VlsBF4RS$x0nA!?k5CW5fUTcKeq`q?auQ8<(jPwRz&GY&m=S6oqG@ zcJ%`89Sf)(aG_jDhXU>`hk7`zn z_(1LxtVCTd)6@p+>x{J=ox zv|qu-@eW_bQGm{koJXyZ%nu7!UxDhY@u9r>BGb68)<{-78WpWCL4Ku5)M}YQfcZTC zw<|6?^8tcV8Iasyk2OQ2nlr;d;n~(lTQQ_5lLBps_J^6x08h+B=R%Fx- z|Ip&ima=dGvcYr34HJ$gE+V6usI7uQUKpz>KVy<_>B+EUv(>4?w-@b!KdJz`=DLE8vtx=QLM{En+O6`BD$8b<(zlM-c+2K-K0eWusGT_9d2rpJFGa z#MePyELWQ+F)pm&N!1H@i2?n}*;Dy*fwz;7x0CtF{6yHN*KOBwuoR_@9*2OB!Ga;>3SCS$2R_#Ppc6P_F|1RJqjZ%CkYlkr-Euyf57MuxK;KUO{4h&n`a%1unnTqBjG_$*?@&Kj94Za=a0+a_~ITaI7 zI3G>+EFmoL#jS8Ei$fJdDetG|%nLffGV!+ci#Zz`o0Cv?zBB|1W-<$qzumi%e*pKL zL1t~=c6G_nJDeDmp5Zfup*gCCjX$vmoSZkp>`zI9tACu_;oDB#V`m}FcR+(#xKrmK zE%k-}+dGpLdLr^vo7}{>%$590j55b$MglGAKtxlFI`G_*#WIDpUWzPXK(VJg*Odlz zVVm&7`3M2>jp5*d&P}{<-fjuDN`MT3T>$$luVtZa_e$2fAU{9dP2ewh0SFWm=fRM zHd&4yE?wCPAAk&Ie8w#NtnDJBzQr4_#zw`eGxkW6?F{-|;xiPZt_6#EoBp1i_^svT zQ48iCz3w5YvJ!y>nviZ6cNyL}65Mb}6saN!)$XN&nd=*yjG@iR4~OBcKs${_qcor) z6A-)!g<}57kt=tT|LtliFOTrxOrJCknO_h!1uHAEg6b?9BIe+d*?}Kd0LRa!L z(}oGg5E|Ao81PQ%J9Pl+GT(BLrtYBe7%SpTU97c??ZpHw;PtBbNGN4WbC4H%KG}h@ z0_z>Gc3-)8R+ztZch)f+Se6>~OAfCat8*B#>2p<<{Gm+9T^2|^M^>pUFIl5mPpT4O zouEoW5Jm2?Ku~eH?LJqI0at8Y$(cEYsp$PC!JH#y>~|JN`ATEHGz2nbGq)(}I|PLK z`*z|lsOZm6BRL=!He_Gx)ZcOi#pG2#7(K{S%s-v&4o~Ohp4UodOGL4~G5?+`heu<` z#d60*j}CMt>9+%KrX|Owpyi*y@A4+jku@8y-aZA*LM%#u$p|}KT5@SYX|nIyI5j*{ zEZmQ@?aBg)u)=UhTD9$NEy{M+eS`Y|S973IkVJcrwn&?W%4Q00Aj0nv&%sa}SE3GW zAEvHQ76O-+kc{|RkxtI2CsQsl-dLzEcevf4=lsR3;fX6^tfT?qjzq*2O9J~dyzC4s z$5tiv6S$C=Ukk3i-hOQZeSWS+Un@A~waJpAB@3_MW#>DF!vJM!)EMJ*XU_-cAnJ4| zvpm8w?zB0cxU?C&VG=v9^8C4@_f3G($Nb=`C8sn@?bGSy*5$;hjPYfC-qZo7hvoT8 z)>9iJy4XugWi~czu{mL6$yXSovqZVzwbb#2qdSVqH{fTK>a?h%X4?y3ogV$HX*&Ik zm`f2hq-NMSMY@xsejUrKZ|he=0pr0~o;#ZN_O`CBQ2X=yI_Lo;QJh@ zZCHB8JPYe6_{2D4+cAr8irke=&tO?2jKbrbZNHws9~{Q$r`JjY4aJ4z=FO8P%x`Rv zAz#u}kyyVbd-CBO49=!#6(X1vWXm|J{4w;mI6t?q z$+%&MMAL@VaxZ^T{2-7R9+)pxQ1B=dG=*+NeS_6<4cn#W;ki8$O2$HS{MvB-RRR@$ z0^8dU|7OLL?#+xzYwc(FsA5BiC-c0lsUzDZr!3ql)safIdYHCv@bFkR#><)WoQ1&Z zCQuk*tzM*X)kb9%bA!&7&9Z^s$sp>>yd{!f+PPGimtheZ#Oo_PX(zNQb= z>`W4+Akiaaa{#w7OCUN340p+>zFLP_6DFwDIh6FKN4Jx$2^HnZ?8`hdBznXUOCU#W zR#*N=VLNlYeb}qIBfR zTL52}UZj&>bN7oT0YA;**E|9L&b&LE6xcu0-^%65dHRY%{6eiN<0HjU+pVGI1|KGB zYRfU1nio1OahvRLb4;CrV0?o|@x4*xLfnK(<%5v^5H_-?5>r^)w;3)+d&d}4zx$O=YEsV$7lfi$Ep22uUSjH0EzZOo{*LbirX*F`NUqm8_x+{X zJZfQpY3dMIZxtI3He0c$T&{~A)&G=#e*>rl$q6Lk;KW4@p^aY+sa%*<$0fcKFeo>Q zFJDe_s9~O=#A&&cr+A(eW(|M+8ow)hJG)*5H9!MJ>hAR6*t&HK=nTugYlEyzkzr~u zWMVic5cK=$dGcFdb?q)PQ{Exp$tX{)UhF<3QJh(3S*DI}@R>FO_%keaF4SFuQFwEE zFX~6YF~Na-t&;RWI0HMguhyj)>IKrImi%xXMOll_x;KYu57*i%KUXW{+ydeO@%>&a zT+j{!V12A+Jv9mLq7mcR7#(u88qRK&#)QnJ@bgYryT9>6&R1R*;!B5bSX%VwS62f> z4mCuKGJ8eSKkJ80*6lB zveN6heMGmFS3Sl?16KE=4*sZC%`Ah)2DN0x+h6;``{z-#tl(h%_)h7ViTxRS}^0bax~9e zn_cEx-u}+s+pZmz?S!sPb8a41py`Lmy^`koniZtBM@SJ8WikMFbN*VV+G zGgn@@2{Fw-LY)sJ8_N9`&fcUI03eA;S&}9b(xmE2-HYY8aQ+)|7*2^nI(1z%QJE_l zQn)G2t2)Q7bf={}y9n`yuUN!GKgL9)XOeL1IPUktI_Ji9&}cA#zs7~MRdq=RBE`0~ zD7XYG7O!)L=J!;(@31Xc)c|dV#!>iyUPdl;moScTU(Fq?24=e(t?aSvN)@;;uI@)VZ3tT~XQ z0E2L(NWBI)Yqz+Zjl#?1nV#)7?4c>W_NzEkm^Vrt+22Z{Vz%*YQjsz6_-JYjwL_4l zc~fl=rXqG>7A4|zEtV>OS7<`1G0}ddU`uQJz2l0NPiJL2CymoY%u~ymEQU|Dq06DM z=l5lS8`D+uOP@O}`2y>R)i33#@2aTaR0MK{=V$KL!1%KrM~{cCi5&%vm9^tO#h6aD zH=$#|}+g_G6cI6}d-DWVsGhwju(c6If>e6=w$ z6h&6S=_cp#5;FAk&BL#h^<-Grh1P3if7ql?4f{;nTOS{nK;AHIM{rs|^`$EbjnA*L ziAm$NiRuWAaVAu_@lDEkV)Gu?62-7oz{+PY(U$#0i@!**r+9OlzAK z{7O9)`AO+C8GX*JHuObSt|s^8v8L!@9Zx>bj9-*DvXA?K2ynacI!7(VUwL>1y2 zw!&QCuyF_!9l*H@ScQ%PmNtMt#y0ej_wl+r;6}*71RpM4oIFj|-byjxvOEI8wogYH zea8{{eelrvWmgeW)V|4Crhzrn$p%WsU#c~J+Jt^K!A^rN|G-5vc9%L;9!aJL0WKK| za0aRVG_z%C08}k{rtilok-xZW+fRvuT00lQ6L;oUrb265EsOAgyt-e?J9fUX4nFQ) zx7{WNN>-&-Iy;uyu^O*O;q0A&&Mn)hn>2I=)fz5Jn3xhfxg_%7HuNN88N6TWC?8=4 z%1@hR&91$?J5##2Of_a22Bf1ZH|kfsE7{ux9qA=u1>K3-nmwZ3?7M%kL(>G6Y{TYN zYmOAnwJSHPPf74`L@nS`yPAJB1{d8oW(NlBjf+>AnNhmWRg-mEX4wJj+(H}oaUPD& z%p2FCp4?3hxC=1?cQ|{i@pHpdt>x&@dDq%AlsXGXUk}dRnXwIB-2fFsO!jVuq9%Uk zQFNS%-r2p!(g7iva6_&wbGBDW`6!lr3YZ=bOy`iE)$6(fDt5w@W5^jH~6g@v! zteiSfe{oODyPRw5Eq^X|ZoJv=XDODZE+km$jALQJxW&krYD-c+)Z(m4Cw5Ju&}aBg z2>2xylDV+Z+KBGGvmK~U%*~? z5Nqe{YR|Z9PspCqz_H`WEt~hIn>I=<8+zB|; z3)K%mwMeLD)%vC1dG}hFISqj~l6-M{pA-2D5IZvM4b|KN<^#8Y9J+L(fWsu>xPfgL zayax{G?7D9?zML0etyPfhh}M`Cs{RL>dS4l36MG=C)yGFo8p!LtzK4(JEFXty_Iw7 zy}MDn2m|s=YM|m2qP;e~4#b%GZwEEzr{CKAnHZ|_+1~YwgDVf_!dbN;7E$F3txJ28 zV0lBZS2zclAE zC`wWmaL22gp}WSVfkhnfdq}4F6d?j1(2A!(M6kgT=$(M@MSVPBQ~}5$H=u2^tR=LbrYPFNYfejOYiKR(C4w2fIfD;~2;qzaUcMjP{X* z2q|-STL3Pq+2}yM{UW(>hDZSPvo}FCDQ}h{#J=pZ*{iUm=HxUtmI5uvM0G4(e(T!E zHJJ9i^Jr**7dp$elWyGD0nY4{ZJ4&z3-6N_JGFW;b?j=z&$`0@LviN94a6BFqMP{q zJ#YRh-cz6*WJ8~p)IfjDErd0QpA^bglA_5OElRq=$~oz>Pg@N;?`w^6UB2qb0ZsDA zV7d(eLi%hyUS21tgDoOYOOmC@!VR7}ZKfdtX+lggoP9du-L!+^L}ZS;J6C-kEu+h8 z(MotXZ*C2&qXnv3p`#_d-8+uF(IQLdj$LU|rVgT1MVKg+`dm@&qJlpYguUzO=V|!H zD4Ld&{h@*CvTA=+;&i8%?vvqU{1#1Jr=K&AeK%JIa5z1iYtLhbBPgA8HEToh7!acg zg7_BfyAxi*i&ztu+Kb4~!>W0@Lt@FIgO@ABl`2JMY{|^S5P4Cc=DR1N6m1fo*a3d) z5V~eo`f1LId(Vz`BX;YIOCn+1oC7DJ0ASn6#mmjz!NfwDH5pqmLk#!!c{8LYe4@J2 z0+fDCmImd{_SLmyz`a>$nlWu(8BX;nW){IU>|NUEgqQ^0mYZ{BQ5%GemYq*=#)CIz z#)A${Py5YSwJ;q+!K`l$JVt!+vu17i+8EFH%*`yp0kvzQ8_n`l>=?q&)3*e`{7nk1 zi*2%`QszpLBi)vvmQ8RdW!>4WJKEhF`BQUOqlag|CyPXBj=>hcD@vX#>Hr|)+``w2 zNvnXSljIvOgndC$#Ylf_UW^9hGg;x%rCsgt7j!Q#cN!&{olx0_+Z!2*pxK{8mc55V zDpV=<6BaA1m^33NW0no=0~yj50mZh|_-1h&(pZTYlmN{XnfWw0AA+r7!S^b0te}1{ z5e1E7cl&6Ck>tC^20w~j^ykRj^dn-{`pPaWns6i7fgoAeNEWQDOH7mJv(`#w8+1+(raSH&Xm z=`8^9;L!EOyq1f1e;mpem%cZ&o$qiQ>Y9fXangPs}2DLj6v~u6-i*IC$Hc zg6#e<4{eajsS_o0%y6xF>$e3n$WJxr?r#9tmvTHB1Niy#2k(ks)_bEeTC;PfwpE}m zD0?Tja-B6kT!ND^i0*(t@!M*9X>FLRV&w89Uo7i=!Mixx-Z_`D2J(_{tg5Rd#)X0h~2X!e)2zBwH{MTsZL{N?@Gs(X;tt z1sNgx1j8IX9eBLdU#^ezNxl9#^XTELdOiR2=Q{oFvY*9hk-{O1mSDm%LIW*}UsNd- z!3=*LHJ3Hm(JK;yOnD>|OO9YR*!WdY_+6e7A$Dgnw@Ngm44vs(8x;ea==UUb7iN7j zp+BUxhm{vr)%q-JyY(&t1W5-!_5W;c4{ORSdk0C#`oB@rEyyU_5>j zrH?gnkIa{Yi*f$Ti}x(o9@w(itlhUAI;Kf}_S3#CzN;H-BOmFW{vwx^S;N!HS(~Xa$dSV|bt{FveU^DR88izKCw} zyc3iF7yYZY)pa5_V_SB=%@zaeMflEYMF(5lCKlrySUXolJOSBnUZ;Z1qdv~|TT0F9 z8~2N=&*@?KKGy!KKEXa(X%v-{*1!0^KM1XPyZ2Tv+ilure6NN=>A9VRmVI6{(B?1D zB*9SDEFWNJj0)2G?%^H-RhiVXu_Cvz@|<{(gdp@IEaW%j*162yDb4WR+tM9^m7V1M zzQFzXcLU&}V3LZY2T>IV-YCzi{;HV4nZ|Y5^`j~xCz~nk7qcdAJ^8H8);IGfogV&8 z$_+mWgQQR&pj55(bu)&pg9S|?_}tSJ`pUTu*} z(T&K0ePywyW&O=IPEQo%vQgE%aww;D^VFoQF)dd>82T$Gd-50)#z`h$iv7Jo5JPt#52OR>1djw_?kvVPGRe%&B6SKzydtgfEqR z@o`A2z1nZO81y*0W$)V7&(5eK&^K>-sQ9vE-Lg2v_rX(e5%A@C?-F;fa*ea_$-8y4 z`mm5;{wbnO1x9Sbh%cy#8F6X>nCgc$JV0|6EZb|{!8gOFR_Zg|o~~0t9_N`0rDugA zxP>whFd0hj?5=^(Vi9t=1;4lv(dk*06ML1d%u6{xb#VWC6gYaN-3JeeTzs1J_*+^H z@r5oF?#^rc+SAyB2CWTwxk<%zue z+V-2lJdS(C&MaFnIOG&*kv4A7kV0I)FSxl>{d|cTp()wXBz|QmG{x z#}Ad~ou|yU!7x780(VOy)sl-T~>;@IwW0}FzVickf=akfywq`sgUO!tg2igstKr5_b_#XQd0&H*00)LFS+_b1A{5w34~&d4 zuIe&HO(Jx3gL3kYd>Alaz`?w8hfwJh@pP|HtAuJrUoValFk`m4^QLgr8|B1a-$0p7 z?fP_BhRPGgsKJ4=^^*rA)X}l&^+*L)+p6^&wyF(ArAm0&*`>0ykMf0|x z>6pTU9k!ze>K?(SkhW#W&U6Hl3}+XhV;_|u&U$2bU{w+q%pdHCQe8R{15+mL9LvMt zChDAvXo<$jeK_kgJUFPgA4owF9XZ+zJBe|=n_FgOQbjYl8~)F)$3~0PV(aQr>uFRR zE7jV>=uu**86p|>Ltg#8YYYlPYa#&2#`uWslNmYwKF`ee9P#K+Jp-;a@ly@TMEo47 zXo0$*isAilsBK!ZVYd&I3=86KTL_e(Noji0;ryi#k)OA$j2I18JR}x3UqT^OXNz z5;_awHI2j6220KwWyk|zU`aB@wp3_tZL6=r$6n>1$Dg^IDI1@R6PR{wX$W8>gS!_O zaTyp@f9cXxEJRFIdrN4G??vGiNm4XeT1>hvp1J;dLq4ds&XS?TW>~{l7+#m2v*7kA zbyv5|6D#KIv0!@bG$_z_dPW79m(G)^$190_hOW@KF4yFe{kZw{c-pEHBQ1bU5%41* zMndL|0@)Qu0lnBRO}~E!S}&EW*SA~I3gMvj!2_-!{Mt%t^bAg>E~uBA$Qez<6`RzD zMjS@ha3hkAysc2agL*pT2w$IgDn|cKI4#2)9A|fqV2mBRi`|53dUt+UGVl|GOiI-J zlR)4#mZ@mG5_KN>^NKG|8C<`V_ecg1U*u*dDhgdf(9H*BZ|wwjyJEEhofRfg2w^GQ6B8pW}|AYt_Ox zIz%y4O_eZ0(fcGsRKq=U&hudt?NrB2iJ_Sh)KK`)XnUpBe)Jmr0l4zv$Q;m2GMGp} zw^EaXyQ7% z0Vmf%Lv~}5_FXoGW*@)B68Aj)3V&{_7MsqTAQVf}Z{K~p#dy(HfRH89trR52`}7&Z zDse;zCnl07Hx{becumGL(()S|-G0SjvSN2M(uo3Ae8lYd!^;^>CNB;pztu=Qv&1zg znS7Sh^ke(jmR%?evQ#6ID*lxnX;bai9-Ar>8v=$7;yFdQXCYwfR)XZJCdIQBz1mi4 z=o5^!t@Tbe8DArS$`F~+#LFFRh9;fH`SA{t<_KhLdwKCmR&ecAHJ-zX=9EZ*Q9MeB zCr3h%7;>7kpkgTyi9G8I#ho@V9dw{%3{^~zxXsOYEzuTP@E**JMpF$Hzm>RI?Epx{ zY&jcA9?rXB~+i~@ebE{r#U z{{F|C$I?_(4AYjCZxMUSnI+JjIt{y3wbb(>KMQId@dregId_(9k+YbD+(8t`4uA5k zXEIt*!`>5gtG9EbSSicQVmVtD5fd1KkqU2aCVRcpZ_1mC&HNKW=K3|ZgXlVndw zsc+~rIN8rgO>{KRaiT7JjR0%1J`rS2sPd5aM%d!ZF&*%8IQxjClb6AwXEqOv3cg}L-b+whT&!aqN? zo93VtE^Cu3=4&fmmPMDI$6mcRZ6<2^L>x>Nm6=d)WW7ixLWTux?>(Qto6l8a zG?2;028)IoGZ9(PV0jmXiyzt0L^m{3=(}o7bQsGjtG@1ym7~VQiPxIAloRSxvi%*15!?L32GbY;{NH zV)}4p;R#Zb-aF&bBPC)BgaUjUoBFhTYhv$gIn$Jp2jl@c1X+IFuOkoG>W7O$CCu9S znVg(er*~yqvI+5XO7L;*62W8#A)mk&Eq{nulNTJymC;a&5yJ#U<9}aK0bA>kW>w{7 z5gmq(`eq&;T8Jznst3vEz@#f8(#+YOqKw0p+TnwAYk5QdHg5YQ< zMprZazCXrs7%l(DjuETqccb9-2}pV)MR^DxNH_6CU5;kk+=PIrgC))f!d!vgL8Sc6 zyXe7Im>-$bEEODRS}sm558Jz?rD3Z!>>@L`SS!3)hy^y+EcBxM58@iEE3b=VoKYQq*R`u}3@Er8;Px^>Y3f@|>LPH=Y(!QI`1 zI}>CeXmGdS!7VVr;4Z;}yL)g8?*1nKf8M$GymzY3{p(e|x~J+=HBi&Ld-v{5_v-bn zwb%EBS9X&LzGXeIPW$k=l%{KZqY5^HrzpcHus<19iclK+arH$0a)gfiU<#$Yt-aRY z>ZXVWqg}0_lsyMMQ@4y}+Z=GfP)9&%yeUL>DhuxI3|%F%=E-W>jUa%BaPGw)B@It} z`>wyACQ+ZskWZ$mh!4)yl#92@*r;o2C-9QX#2OO7wi)T{SF z=zlWyXAV23kZcg3F;?!+ACxE%OK1_UDq&?8Q|Cforp;%-2-YVY(S%(>!xN-k!HK>Fbw>^7mxu79!` z>dn;i)^rI}-{i@Sj~sO(VP&jxLRN=@mt*}wVQuFIdP{8hSVQx9wXAD#n38f`iANf3 zy-PPoi{bOfF&6elZ%@&DtFq(5zE+pV-{k3$`2vTPV zNKC|B@WfSbWtzjuC@Ut^BH^b~=GHkDw{qPK}f zl@Y~n=7uz&g=AC{UjQ=?-lHNb-@UG2uH2MWF9eARBMC;^6v;1jR(|KXNygf|DCM6U zl4y!VxsRQfm>7{_V=kK<-c7gJOol~fuVtH!mU7ibeN;rj}UcB>_j!< zGX4*^$axe5sY=fJRX14azG+{mS3YcOQ%mQX_b~j_jSKjq+#0Mk>O@AdCMM?J)Y<2Q zxJu%-ND@6342^1pWjJ9=WA=2+;+-cBN;V11+pX55xkXMhiC4smyCLI_1g~P0NT;R> z;DGToLpLq^{|u7~(}b|Prai1gg}he^c~jzqpJP0^#m`|hyh0pmOI4mzt(d)kZmws9 z(=Wqp&`cvso4;;s(Q7Y0@lI5D5J2gdXi#K%d`?STQ4nL3rTrM&Oc3Q6MEyPNri5WF zfl`PT;=}xgfJZ$-h6(I!mvG2k=wUy!CZlToWANeYk(QI8X4JdB;sr|8*_^UETY-`{ za&HxOW@6X`3&DX6VUt}&v`b$aKzDcjCeW3F!E3zao=l|yJ$AQ!7xmKU#hx@N45h9l z8>_ks=i2a2ZVHFh-J~bX$VL`K*zaXyONUR+==muY?W=!Mx%pH5mRIzGJ0V1uJ)!L} z|FR1F#oq4|C((R6Wrr&l*AqO69Y8d%NDb2tBv#~!QyiM-`?~ZFN>b8O?0Dh1r?zP%uPr5 z6nAIG$$s4#!@XTc`1(gZo+U0!wYxI6s7OQ6rk49~78a7<(_)}VqRt2pGw=nTbFr=4 zzw$$iw>dSd(=5WKNblp`DS2Mj9<{`WW9>}73^3=kP1?s!W2PY$DeM{|8WiN#$RGP5 zp+YMmAoR9`2%4^JPCgzfET%JW_yydRye)*F%|sMOqG`{@jEiAfMIo>|o4Z$I9-h9v z=B4^)%BsPJfvhE;w~|&Xj3W4jvp_QN=&c8?l=Y@*nA>Lo zbrXG_tQc+kdOSsUfW3kT-pky&oqhYg7p)9M_}AuNM99z1;!vtK#H)#t0kZrT=Z-hQ zuEWH0M~WYJ8;P{a#Ynr+H*1F9rnsZ?nGGwpZRbCeO!c*sW6&$UiE72nc@03mcNwN` zlNhn*GFUZB>;gu(VBsq@t80txfR!AXqQA6KhwH3Ye(ap86gx^%XBIYF=!GenJqh#* zKg>ID4!2FcL^z>iF0L``k~XNLCSCN7fCBr@xD~h67 zJF`Cvbg*_R(X@3}Z43Llc*4jPNxTK0c7hLP3O`vsgc`&kOX)w%&gJpTT;Z+`cv}`R zr$c!f;xd^$WqozVKFScUghyje&yO93Tvu*|dKJoOj8b_6z&9s2AJQ1U$%DPOwV&#| z3&uoDD2r`rDmBx}6+bmg@$s~k<@ppM~FYMnsu5VC?omZFi%S*EB6 z#94&zP-{%h&0J7IC{Sv9$cKr`L@a(#spej@u(C8JqM{D@q=L~>ua`$5J0hxuI6~Ms zYf0YZivdp*06_k0k!=V!;-~^ z$DQ?7`yX?+GT0F|&`bdN#s)GQZgNTx8t0%c4R%BU8SKX9PB{ZvbtPQ41Q5}cf9w&L zo6dytUE(lj-KB97)3?N7{3YPE4ksD?(Ll+EmT*z|NHI+n)`-Kw^Y9g_knAH)6Jx^+ z5@33HCK}7@VM(}ZV4`hySz^elwg1`RNNFW`VnUgzhtzxe)=5)#R7ut(tyA%{HspvT zOxIAQwUI)~>zR;l=mj@&u|&<=TJA3vd3&}s`O|1-gFOXir7q(GNH)y;s+VTQ2Mab! zu_J(m8u^b7ostKR6afAA(0;XuOt zcp(B84Ufb~{VBF;z%IG?Q}g>Z0{ouEL59_;6(AnBp7?N%u&95)ZShoU;aq&H&6Z!Q zIlR*`*}#rjbK-^D@2rVpsN%Z98ZH@HNP%!D#E!yNJH{IThu4zyn6Yw)9p;Aah=1ml z($`wY=6OccWM07bW$RP+0p6R=T_ExZGWs4S*sfqBai0w1!QN{H!ilY%F4LO$b$&PE zHPm!Pcziis^y9f3Yi1;9ZO1{%e^Mc7)~VJ|?gu;@mxv$%ENRv(uJOpOW`{OIw5w~7 zch{eqv~wmV`(YvcTHtHLhMU~;CVG_eT2(j+yUi*_Vpe&|&SM+h?8YxRTeZtlnts%* zC|KjLMHg>LdaY+F&lU$(|HLDsW93p#j_CB>Lpzz{L$PR=2L`FHqN$@GSD=qinT@4# zqmB_qIwer`DLY@ybt5tYLZc&%Mn5@erQk2mVEhPVs-dqj!4QzF4uMjkXNIkC%(@(! z4_YWb^>Z&Qf}?RuM&v^ZS=(n;2eRvHy+e(iG_!u)Hg!8O#1ft&i_RFy-eRi2^I7IW z_WJJMJztZMMOOu!^xl14lnP%+Ur`$p!jovL)AXu4NYDj}l@WZ5nJ>sdlfX6T?iuSF zoGoNkMTdWeUBOY#lqf#7_he2oqIsDc&Ixw2% z886!C4N*PGGSlrr!EWI1d{ndVagcWbC#`H#-lvN~2>*^Q71^%u5 z&Ubt9&kNeD18iwVVMJ1j4-Yy{d<}a0XH08rGWPM6pPHi|nQlym5B?&HnnWAM^vX#}BLLf(x5O{GW8c zJpFz*5{+Jj_|Q&0CJRq2*2-)QkM{$FC1NN}TzCYkcDG8Mb1JbGoH>nY9TWi6g9lB1 z_Ry$r$)5IFmoO%Xlx(Us)Cbt~HG1D9d24l+KIwG{3P<n%;7G0ATas=oeRYB>^%wxfduClbc?vu9?{{;e3|uhc85~+*rDv4C&!R;vK zr)F|b=G$xodo~4wLq&+^FD|B)T7t>TKI`oMm@oUp zo&GU(6lM1;9{XYmfmd)YSM% zobeqdP3xgglRCnWLVBSuwG=?8BYe@QACmgO#sN4bwsvmIcPF9L@xj@* zv2UhX_tknlrKW#|3#*`#w%HE5rSh*+5`%*~o2|_GWv06Qd1~*RP3p$PN#B;^8lz;| ztTxTq6tKpp(DA~LzD4O1=!`(hGCzq(&xUd*3LS<@3rZ<;xr6uMXLJtg*N#aqyu-mA zPQbctSW|HE&sDY)FMC`Z-tS95(H(@s562FdvG`)1KULz^sMi}ol5C8*$H+@=6O(*I z-a<^&`f-M@f3Hos>1mgnSh1N zcR9yT=UjeIQTQzqqOMO*f9&A@*!@6GX_QSsxI;LC7hc49khbm`Ld57X?riUgB)lA* zz(TI_J|SWGH>WU?rpyxptVH*@1?Nx{dTcHp>zmKJbp|XRo)p;|QbEFmR^QvlJ3QDM znB|rurpy#(Ze@;y^L^{Hz^hZaS@BR2VIlA2@h$Glh^*P=Ww24@Do?@l;xW==KY@0A z;EoL9CQXl@S2GR$px3Wk-KW<^IXJWg!_A8~TQr|`Hboz2o6T?cA1fh>MpB(fMUk`K zMrKH$T2rb&?h6je{>%ntT2W|(g8im+tuR9id*|%IopXm}tqYm@O$~mW4fRx7gViB_ z)+0wl)3>hd2C2U)^K1GGOvMZ9`~bY8BgB$FPSdi)u^_i(m5J; zI;}*k=YA(*WzC+>hX47%g44hWvxG}pf{%FAs+M(XsuK7Ob*2)xRNfE)WC2NE(9xl5 z6=M~q>K+D_yn8io%!tvE)QWG8~7{A}F~l*Ws~|ObX$UX~tCc>%(y( zQ=dkxebYBpX7LFoP6LH!$k%s5i1l2xdU>|A?tC*d7xaJUXEnR|{Er?`wlB^;qjcuMu-8w(;$u=@fFs9Ds^ZBxrmTZbxhKy&4d#R zW%}-B!j)Mz^#ET`ApwE7OE$i9bq7aNLtOCiqNSzEu>3tuEz>pAn!~=O4U_v=I84C3 zn=m$R33Z5t`G+fdR5=uz7mCgRpGLQzSk9)-a|ig|pP5{5uY^%rj`7#F38Rt4hFl-@olK2ts;bLZ6%;+e zb`E^`W|1P>v*thAnbSD&jTh#1O#S@M>}4mkF_gs8Vwlz_XzC-D*lLPg&;rwJnm-Zy zg^DQ*N%Fz&M-Ms9x(5My%_{+G#7PWQX&9|GWfF|t_E|w3g81`L{@-_%RHk$Zu zGga2dVOryZ$TjTf;(dKRXZ8iiNZ40OEiUXz^#~A^!9g@Z<0dxKeeN{2G==Dh)IIJ) zp!S)YA%N}mX`{9M4cc3?X*(RRk0tAaP-z=(!hw>LU8kt(<+9Yeg#s-f+PC&Gzatob%1LBn*Ihl-=D>1|aCi1&pik{$qit=d~CU1|v?d0;tGqvfG zZL$KFz=3~pJ9Cuo61GneqXfcJ*J>%3?IVkH)GXR+O?@aYFeQJ)-f3|03*A~=4O@S` z+nVkn`8r}+{`Ovvjo{+@7iaw14^eAp$i{^g_Fp(=sqW|Q9*8tPS1?n^A@IxB*+I0D z#0y1(uHl7wzKO3y5ZYF(Np85-AqAQne_v_U=f|U#1>T#-oZB$IU_y986lO>{Jr_dv zRqlpf{wNDCg`v^qZEm>GAJcRGiky`_r2vdp6{)K8 z#<53EOKJB@K6)&~@Eqvu@r5gFM`}A3OH1`i;-?l%=r0WKJDe?Kcz#;(Zyy?0HwsYS zxnaX;LpaJp#AXRABsh)he-Sc%P`rLerdWvA0*@?)b_llBZ51df9t`*vaxR-sn~C<5 zIJRWIPHvk?6V$#Ad>Nk~GY}Fqwc(`jFPqFna*y6;>I>UPr$d`;Y1fPA<2};RU~Et# za0aX4qV&JyRVjPp{BlvTMpbWjDmn;;lLnCnE!A3vh}-em(eThw8)vGIVL>L7eqij@ z{h>&)at2#$8Q)9s?AAPV0=MXpJAZ6|w9z*Y`p~MvmWszH%0&%v|S-4K&z!H}aW}sld?nUV! zqPrRAaO_z3?=c& z$R9;_LM((Y+e}i`2>82^Sk%^3#$PFky!LY>JJte0TkIeFV5arp&|Ety-l^;&#g$uq z%2O5ADd_^QG27Y`)1CVGfnF zH+y%CDfKDBB10Zy^U$_74Vz8Txh#QuF1J1}{1(pY*+9SVlN$W?&D(f>V-6TdKaJ~6 zGY^VTW2vhOauCz6XI6SMbT~BmJeX@c&JWvmMsA3N@YpoWk}O#eo)acmU2dIIOU9AnlA z5Tb^6BaRD0LFUQ}#Zt}(40pUYPG#%BJ+)XURTY1`h8M7B-%U9eZ`)gf{kTwJCE-*e z^sN%<+nK+3o0EKL&vC9>_9jb_gy37b2?wmPtqOgVp5pLkWV$7NNBf!7Wg>@xh@Wa- zGN9F7+AXzVVqheCRSW)XOMqR9-m?YTn9_i0R-d9SuJg)opJO&-njx|N@aIEw9CJmz4XE#O)#OpaBf^A&Uxh_Q7(}kaVA<_%vgV#O3g^?I)Crx!#q#8>ihDKDy=KZQ%T{saWWrQs5OP# z7IRZ>aHtz=)o%`}#IQzr=o{non?>}7=M0D#4Agc_XA--{_4FuX%ruzJ4yWqA5~-dE zpvB;&^=**dr~RzZ8`NKJy+`vyDh?7{NRVZ>~%<= z;!*X62!^gbTGbS>dTAEueU-#>p|t^UQZYoiQH>unq|yy~-$cZkwGU9(H6S;jdQo1f z-##2b{9munttQ@EaUF(JU{mRbZf4!sgC~z-6P&bh=Rhq>Inz{I(g_m;Ll)D>pOpXw zpQLd8R5KN+-))!=xn38tL^C$K3DTFFomgSXfpsbtY4dZi(ps?UCzPBB=D8|gkag-* z@yy&10AQM)gHcJeQ2R+AN3ZH4G%o#oKbW;<|`8& zKHrUtX~a8%#r2!C+Aeb^eK6WK=}2@E9dc6y{rGauu>rKP=3gYU(Nn~`wegGX852hE z!LvZR&FT!6N~mb)m%F=jzoiO%;61j&kK~I#u!PYCWWNHFP)*=f=wBFd&X*in`(ibB zcfD<3=`HOxSW7>It6%J-t735hKa+nAp0+ znTYEXBIthVhu0{q6rHH-`>Yr2CcRsDsFf_iEA``>LpYMC{Dx=0M(igi)vFyWaZ-D; z!q05#y!{L0AVkDR4X?p8dAzc=%a3lK!Qhi+ijK0`ax*)Zj2J5!iiFhV51?F%GbSlb zEvaz$3shb0$Qvamtz@inDs@p(BUlKMgj`ZZarh4ja&>;LV*p#kY)r-?NN~QAz$|@= zmMuk*zK0CF@n^(z>*GV7KXadfWkzr9?A@y!<=d*^;@Y#x@J{*QcHV-Gp+nmKic%9X zIBEtG1*^*tDwrDdW*=!~Ai~#Re1^IRdYlej;p`Dc1hrO{#xa_$>R!Q5k<67ou>^?m zRWio!$dc@RrX~b*QiFp^rv}*V&-BB?`VnEOFQ{_M&6%E2PWWxKYl+<{;hB`Ym3tvD zV5BxECF-g%t@~a@&XB8?kRJgao#QHv4L81v-l&CWlnf_C_Zyfo4{;ym_vHi68x6f8 z2|BU~D*N=D-N8TXM&Gkc7Td5S)Qrx)MTYp0IfWx3p$g_|mTLEA$#e;zwgJKUm3m*% zf<#Ryx`k@(hL9=W1(quF4-DFX@N? z$x+%C@q z6sEJu+tIpTX^GMCNeWmtS96#A$0+k5n>^U5TEf&>WMT=ILDl?=MY2ageTW~j4$}^5 zlm{*0Ak`gb7hVMnWdB&fF0m;p1I_yA{4*jw0rt%YOknEA`$00KUx$jQ^+`z7-cv*M z)e@wiYA1=4oyyVV_2Eb>`j$A2PaX7FA?6|ilRk-Ty&v(FF!dl!ZKM_2sx5C|M8W|e6XS_tvDa}>CNr*528Z2lV?T_ zOE+=?g454mtCUsd0<^Fxa^s~p|M)(TDE5N8$Jrp7;km%mwIoo0R_>pal-3~~;r_`6 z8=kd;M@F${5d@h(C*+l>Uw&q`;}OY69I8_&JlRRi8L6ggI{%(i4*9FaI$T7Pz@LeN^N#edH0=Fz<~BQ|(ot+r=AhF=!P{q^)BKM+(xN?9vI|9B zm^NYA`doxAH-}9h1U~iIyDFvv0u1Wt4%K&Vgjl~`BzsD#`{k`vKuUB}+97Kucf_Cm zME71-N_>3Pkrl|SizG3@{Igd0N_p=W^H?R<`Es`U%v(4@_iT6Z%u6vt_iTRgyq=fp zIAw5i{fhO)<^LO#=6@vOeCaxPh1;>^u>DV1UAg~7Leu|@)%7na>VIoiSDt@$5;z0| z{$2Nnhn@X@4_a3a=&1fD?s{GUcAo#hUB7CbL^8V=U|sY0W|B_#gy z`pUE>S_@kcV6t%hwy6nnTCSOYSh*+Q+j7h14+E7xAdmX^I|t^ce@yTe=2;`iWlcDq z89+YP8<%`1!bSOl&)V3(RS#OxuD&CO``xZ8%)hEl>qCW8xo%(A+&6TD`lcsAmWJf& zi{Ts8pN~Z@UQco9Nbt|1ljU*Qkp`T;fiB$HLl(8^npD_l*vx>_y&>XonyGx80}Ekn zXtv^pm(FenJvjd_*Iy|gQ9V!;4;}RV3gzc=wTn0Gq-KYqJ@;WfkRw%D@*~Wt4D;Nl zitcl_N=9wL|R9&cWoe7x!^}~C0QS@;2++hX32%U-50z7&$9F1Hz7ESz=COV6@sJodi)G+q3u=k1xD)= z5c**X_%E>&nI?xp>68b9@}oU;9TomX^E>F}A6A?|omTlWSHbipSFQt1 z&gw7l!-s_?%=t;zYJ7d(K3yOtz?PWHn#uUY;K*}b8d6Hv3x z=1wI&Eadpmz0?&A_r=r`ix*L1SZ}p^q}cPqtrMH|}pH%T>A4 zS$H0}n5gHEq}Im;cQhA{YTC)>cnQPzhznxOjmLhuia6;22W3^;T4c)sPyU10XyO(iU&$7l#;~ z4Yzt{ymKFQ-YqN2x0{zffb2TFmbMQ}x&`&-*-BnKKZ<*9Fm%n^80(6^Jo`*}OnW34 z!hp1^P)5(Sc~li-dWWnut97BdR;-7$$BC^yJ8?V9 zwLz@XjoPBSdp*!g(Ze&L`9fTEwUuumztD&!+5|Tz*p!H_v4~bN7;}#Dv$pmEG*n0& z2i4%5V^Vo<)LIdwjTGhT2YCY^Mkrs_5S~@_Op@j`pC{aQ@uqGp35tud>g@-?|86#V z`&re!DaHMm-G1+r2dSwZX*Ax_XF0x^(H~Y@D+s^G+prL@H+zky3#Cd3Z1xm{nrrjAHc;|e7p#!`UfbP^o2uSJWueNGV| z<^%cjTngC7Yljtw?BfBv;RB2(nE+QP&`khuDF^^+z;bo|tsM|x43nz^ZEJ`9FLs<@ z`X3#&=l?zaZ%0>=e~$-<|Lyf}I}Eg=e>>~nGycDJ{6C}mZ$DSb@1vM25R!4*Vb8)& zlWDxQltP4|uW9mD9VR(}%CLIwe>BXhX*Fzl)BSN@y4KK0?vhVDzyq{?p|wABS{jjR zQx0Pho5hjWf^N+Gf+w3&VSKP;Ha8~Y+i=ZjL|hpcS@$Ou+GfDN5*WOnoJUTuYCdvW zQ>3Hp%&$T2YysA32U0=rxrIrlR59a27R#P^opyC9Qw0c#@?Y!p2Y2=``TFB3#y=k; z-Mm_UqR<+$1@{@Z$GqeF4_cUW6Znm`HgIRwr^!4hI|j8uPG!ppMg&A;y@`tk@)_P~ z^{5=)@ISMSy{H#|S(Ln6a+(?lb@UQ{AG-|hi75v^+HzUXQ;G(NwJsO5O-ggr^^_;a zJvgL=l&7;$#X>Y!=M;#U_jKZ~)9>%SKl*Jm{pUBE+l(QRj@b#^)T0?p$R>J;t%iFbf|IMc=y<$ha1m ztqe$K3t$NJ9%y}t)}i0J0~vhVVgm581hcxnGb(p(+$y#>)1%joMTGhsrRejKUQ;(X z2Kb7AJ6C_4#v%m!>P|g4ZPr6i5nl?Z?}1)2v>IaZUCH&6DBeXC25glpWi-LkW@M}G zZ^lw<68;Fo5bVAix00-}2fro8-ly^)fCEWY+eSb7$&EN99!b=l2D=%B4*Kqq7Rgfb znuP$DFn)`|%?)bu7D%(=gY>JMnj@ia;(7<;G3eZna2dl?`TmsGL1bFQio1H4b-0r2 zdf?Fcm;jEV9BtjP2tP*&c!txqubEb+e`Y_`;-u2;yk^MjIk*YI)~?j9@oLZBp3O~( zxHF8_0XUFVNt6OrglB|oAHAS?fB5#wbIyLqmp$6vvAjcxW>D)gaE~G5n$H8`G90reF6!-r3bQdSG7^Ki~w9S%o5{;(G7P@zZ8sg>EgB zdV9=T?!l?=H)6vd|~DN<)>^VV1+}sD~-o2 z!nFPAYC3gJyU9QqAs$6@spewx1?2H2F^!xe1p^JKOb#Mgfyx%=yd%3wNCvChmtNr$JuXXKGwy=*T zJ>BQ+sTngJr6Trtk$L)Z9_QTY?T)X4;B2k4jq)=qh<5GAJI6Yej=LKugYfGrpNZei zpx!bY!nNTaYtPw&bYQFVsCH&hc~)hPZtZO2>)nQln9uDsc-jn_p{H800Jxl*3Wx;S z6fc=Duyx)&V!poIcD~#n>u6Vf8@)S*j6BVN+so7EQz4>_zrd&dcQ12by^`;-cLENG zE~oeMB+c{X#_gcz<69`*9_DKky6)*UBh^jUi;bX(Bdru4n|5`_!|tj7>+Y})fftVh zZlW2vCj_uE9W=caS14a`uKnfye%}A}q}92@`}wd>Zk6r9`822tRIF)V*BUptM6UIb z=IHup{Kb3r_R?<^B_+ocrfJKKwxJ73P-sXxn(F`Md50jU^I_ZkJ(RBLY|N{b4VU2d z-4OJjK203!ymzv6@Nkios-wntenZJb86c#r!|Dpsa*EMTWMwt=z1S=y_dL3{m?$M! z?-)M=nQ$k_TRS&a5p8zwn=_UEf|6p*_SnsMw@ryx0ah&AT8s!P+~|m1)r{J-sT~QR zWQ|%7+}hI>0{51_%%HA`apQr~`p2Xl<+;(MQgXf|WvR9kMd*?AI)TfUlUe3Ua`Z+k zj;D?GI}*HEKBvCl`A&s8ue(22uI$IAemOVE9CDhgC~Teu{1$EY_F47n(5c88Q@t5> zZ9Kng`TTzW)@#jg{eE=Mg6#XLzp52A@!qi9>ZqX4E6PtJ;Pv#L#nt7G0}0=>aluv2 z_+Z&}QSsg7qi0^0}5wLnYRVUv4-ZO_E(X}0hD8L8mG(|#Bi%(zY` z^Ex&1&$Ey{|6xexoyzsgC2ITKa}eN!+t}-4sl2lnH$>;MiDU+N-dM@@beQw`(f#Vl z&l=!K3NgQ*bAy;Tb?(+>N5P9!mXHXGAb=-GoM)yNmyjN3-O^xC11h$X@7%BKciaTP znQGMJ{uhnWe>`>np%`kz@>J>^EA8`AOql9=aY zhqOZv&2g7ajGrCu>}h3abCG5PM}ef6S8LzHj(IfRu#;cbV!>9~l3s;!#hfeiT=lFF zL9$70n? z&lr%oe>A>679eeuZjjZEc4%qIi#5S!|=7kn)binj>8j-DljX{G~=-x-x=dW)HTpc0Er|0{AJvy&|c)Q61#jX zKO98=trC4+2CU=j)C33hCZOYGW-TD@x14V%C|;&~F{K?0v+R_BI#Zinq_UBJ9kDg2 zBX(mb0&^e-efB4;g#PEo=NR;5<$7Hx{u74elHs#=WOu-EU(-Y?{ z-XqzT9z*nQ=5)K&;Z(jox-J>nTUd~{I@!vLO(n-#S#dqa4O2r$szW{dw@Qq;i>-j< zsE1XOW;hVA(y+s&Wr{#3^JbSfC?-r1^F+LDBAe80nxFMC*==Eu3OdXTCg=Bhha)#j zgPOh8xZMqFhdynL9EME0a?9g_MHT}JRRCcgoi52dhK8;r)?Z-I&oyh17`iTf^;=%l z2#PM)vl+v_%a{b7*YAHrUQ5EpA{e>{ho-a!{sA>)JQvW=G#8M^aCjFQE~H~w?g#Vz z12l{gXc%Kwm{w!M-W?ajEfdJ=HrIsa9L)-|dhcis+;}Edz(D=hTYQ;IV7+vcSwTG7 zqlj`O{BIrWUZP6j7nho|G%$fR6n0tn`NFln(zu#X8IU`DyVQDP4~+-r!$6i9@dshg z?9H!#10Vkp57dXMQ@K^6)1A2HXspO6t?+gaqcZ6pxwd{+3WgJn8)nFmsslWkmz8S^ zWI4sL5z6hBWexrsnjHqarg;vPR^5`F4)YtXxsA72k&n-68$LX1afs3@8AoCrSQ z2bhqQe}@L_{|OnI7jsU(CGi-&o66T2lsx(TXtUV7y89AHRt&8|6=r0vy~%4OdF zST2XIxR&JJ-m|3)z}y`=!j#%7RqW7oyrvt@GXD-qO=`)xEnC$6?Ziec^gUagS))2- zyud13(I|m8YOrbTqb@)+4V+t#fX{TT?a6O+V)5+OoQ>`!bgbXpve$?sS(Tp- zw69xXYI&dHz2b~NZB}Be*nejO7l0{AO{hR%fBa(8vhc@pRM>=-UP(V@c_*63sQKiM zqaBe+H$gLREwhqV3wIc6P=6d@^sf-n`8zRyGhfe6RAFSyiv={ok2lH>CA=_N8%0ES zr9K?f;U`sY6#G7)wbXO3t&V`jD0_c}kg)?TQ)+Q3Q`pWAuN(stz{~tR^yU>X)X&VVJj!5H zXx%AXXSUp=%z1q21GTA=kEal=PWrTq4MhT1SVEyTh3%@0!|)G{UP{rk2M_UWMzSF% z_8q^lfgjwOe=VY$X3QKa8JkEXNw{d1Yub{wSFOV>bFJe;0cnZppm zYM(9}Dvfep!RHMd?)@3m?~QPv@#D63Rf0Ui8=+Bsft(J9h2bEK$Qd)h?=S?oVo|9QF)u&FwF$G%2!0&`|%=C&vQ7 zOC!ESDxYZ#FBQ%ph?@k9?bP=PQ*Cw4>Ay{j$bG$i;{SRl z89aP^4r#!Sy#~_}@C`$@ec#!hZ|3Ztnk> z@myNeA+iR9o>!d?FusxOlcnIu$I9n%O-&;@f}N}*ddoAJ8-V=v18%*)cUV`sBlYmr zcVppkbe+$aLhC-KpjginthWhiigZb+$YP^ny8hpKaiN;3vZe9E8S3RC??vV6XY69M zOXiQ<_3PCqn+kvaP`3|8MAsa*;da+8=LPo_&i^1vF6XdwldkQvYZQ_lSv-Jp^cu%4 zn{n8Di)mlwY*w$}(t`^BL0U^vd!Kxtp-~vZGf#K@tHaRP!rwnQ-1tRAp7R2`&afiT zq45=Z74sgrXCsUx?L|PLV%`*y<4N{oTz-K{KYfX;gSV9jt0>R7?r zo@u_byMvLZ;q??9SqGj3Svqa}t?^JeGVqJGteMr!14aoO){CW;nY-_n5@WrR`GK2Y zeRBgpDXO<)qeSFDk1$s{lW(IGsaWGZtSs7YuxEXb zf?pwB%JF-d*QZ2n<9wZ}RZx#(ujyL8HF>xZD1WAE$_V(zG^ z_Pjd3QZ#LADTWGMGVu(g%bJv^v2?muo??n+n)qJw8r*3%H<>*p*%_H=%P$_!9+-7r z(#MWKGA7hcyW{l%1>Sx55+ zx7{O1vGe@AsQ0Uer&glh#+t|s0-wT~e^q2Kz_KtK>Iu%vi_3h@Bv+MP@ug zm_NwdUC5veu!vDO^n0H)Lw`|}5f+&yUi_cGhpf~0*_?a3em}rrQO&0|ZYpB2AEr?c45-H>Xw;*iQeNX9|4S{Eus(K=FuZd%J+m2SsKnFV;QQwh5lhHWz2P4uJatxYyCG^qBlcSLHyf21K? zS%rtlq!Zy6m&v!n@I%*;$JW@ctaidMN9=&bIl$ao^3kW^#LnB2d9THEfJ zTdDa=qK(^KaE^e1{^pX5ood(Z)cMASy(jC_iP+vfu&OtPb-@7EcfKs2y4FUKJ9Q1T znuUeM1lb$3nXNNXmj;e@L=8nId_26I%zV#T)`qLYo13enI;q;1`@A-f7Iw&r@kK0e zE^NYl&w)5gTT@q8z%$j>NvDtHajdg&{lND1v!~|~h2LhE5YsI&`lVMEf!~M4H@}oQ zoJq`#_4C|}KaTb4e%GoM>&aXccSJl*6Y&0k_uEAxVcEDzi{3m?eZI7Sm>L-Bmcj6m zu3DH=NY*d;OpE7UsRNsS%N{JkrzI2INiF#tn(+As-M5yQH2*``55$!$s^RZ=GF<=Q zP5kd=Z~buu{;53~?tgR;SpH;eNtsz$Sh^B(v;9AkyahDsZ<4pzxR{y$Q}UMHg$+I@ z8b@&uSkU)nD-r}7*v8#gmFq_SSI8!N) z0*ei(7->q|G_CJ~uNCXxdU^7726SNZ-HCk;Xhvc837g}TPdu&{LdpSFk+>_$39vi_ zt&;8OnG**f4GCrW8-;79rXn&?+1ZnyGx-U`&CG{Q^m{yvBa52Kd$yK1vdc1bZ`7qr z8ab+D@6*9a1}ruKpBn@PvY#xf#dlt$Mw3~rMI|>@l#=Fp z#1YO)rH>3hiRM>4of+fQwsxUQuv53RW+ebK=Dd2;uHJ3&le9Yd4xUZ3myBn0C=ZG1 z|I&F>sOB=+l_GcCCNg&ncj8ViICc+q0j=`E_Tx)Xad)MAZdo8?Qw)B1gA#=JaxuwJBk z%4{RtP{SaFwGS;$Q(lGaJ}qjI!@QDk0%n8LZ0~d5Co_bY8s9D_wnnXwT{gc4PmF1g z@CAIcr?i=X+MuI?35Akx28%6}1*rWjReo7n4|P8g#MU20B#_o&#_udGg;|ZL17fYI z=wVcsqJxRj3kzDl$T4adbf6}4xOSkbD~m$N%Y-Ws!SmGjB0nC_Tkl2%3$}CzYgMQt^#3SL$)-QeC!B_7J+1y`yTUs!RT9I;+XV^>S8&;#RI<@w_(;j_m51g;lfQry z+4XakJ;)6A+22h}P0CW3%qR(lg{Qj=mOLcdgyqFukgW`2rwUC%Q=CPHLYxC|%GvGk|VduFJdT4?!+!d_q`(`7ATC!rsmLZ_F;{GpcpxkWBS>d zk3C=XEShm>6}>lW<#Ews$+9B6L{LPR+_5LI8cdwq@o_IJR=H8?&QXM9gdt!+oIHM} z3nNM)4%TzfQC*!XDeF*7xv1e7&*Lp-~H(99udI^N4 z5_ihV_LaiCjW6;vB7dv|6@qv%Jh6PMvKv)pPc7V_fAtVL;Pcercb=Cz45O;*X#+}u>{}a~;m!AJQwF;4$1m6jv(6mWl;Y*! zqJ?{+#SM`wtwZ8})ZWboy?qWK<})M@Y9jT_Bd{Awr#yoh$7Sy~yb#l@=y}&B$5nQR zF;iDg5JURBssfhd_!U##_%5VDzpppm{Ob2(3|@~zeQIqomf+%S@C5tlW;$0kkc#(@ z*3(8T%9H|dqx6zyt!;1Pg)zh+@k6O*Qra+EM2dh*#ejbMUt#)|k{R_OBr9kO*T;kM z#)=mTn&T&en4NjWvQ!P{`O*;|AZuRmvf16Ph@)sGueX#$7xa{sPe%NM;b~;AD^zmq zCzav`$amJHbW!=TYHD}FWx@?`M*Ms}FM}WaI?DD0C_f6_>P^{oJh?metCQ<=F#U8H zc~KSyXRM4BAA~fE^Tfyi=AYTZ8KlR&{Jvfv3KCZu_1UMHysQM1j{R8jyEN>HYS)tY z;ZsP|d~C`ad!C#Tl+_dA4oTv?S(k)Pn@Y#7|G{(|Ra+@p%Elhm^Fx88rN!8jyW6xn zxk<-uXH~UkQ>~soZtTKXP#y&9kZoRkMr>jqzQSHX^#P3> z6Y-~*ImT{od~U~U5)-iCS8mhxJ~m0#18c30{p-hk zG9!YezTK<(b?9+6w&cyhGUx`HbhXCYIAw?slpoGs9>;+bDl$>mW)v-Hv8_>`^}VR6uOXVbJ-hxyp`DK9)T;jx(^V2h^ zq2Q_Bc9$xNxl-XZaxXgQi)Hv+OkqP7bVG4EKn$xs&_DMjH$d~9EuH^MLg7~$19RtL z_B9x|nzFI(LDvASDB$(fBgKDJ0pnJVn9hgQJUaVsGrm+yTF_Dl0KJHboIKV^7cH6D zkHx4G%ESdM^@_$5bGgSvUWtlxGJljhxc)*+w`&!`0T<+HN?bWzJcX^$yep z!a9hq6P}$Uok^qgx!PM8I3gzWp=&KSFq>d>MLQGC!Jxs!R9ae%id!2q3V$MLC3eI9 z_;?QtLYY(2Fu3vGIOuN=$So{iJIwqj`;G5i1%t$KkM$OP^toFmXMc-~8h#4Qr*nxB zPTU^NQMP$Ws$~lqOiC>gk%E8tL8*2lR*#d0W{bZQOJVRn)0 z1y5U7@v zm^`@$X^(g3d)6s+M^3KSjD`rcINvn)dQ>gC@Y-2IKJ-_pw&@r<2LU~=eyc?60tmQOE2^#a%F6yNVoq)4$>o1)|J z+WZT!zAt7;p{^pj2_i&^@Q_S~53zs{>r|YxIeE2WWKx)8J4*FRv4u;Mmx>*}xaq<- z@lE?(%a;P~Ik9c8Ew^Q^a+rKS(eUmg4-MLDX!3rVwIc=6_jr`Kq}u)mkfyxt`b@C| z7TFxnLjcpnMF<54URCAsL>MAkGi9Y__+aH{)IEaJQPOPk%8vs}g_nV8pxm2+ExNpR z1Rb=(pxS^6438jQG7ch5aN#h#oX8)m3E0rH!P?D_LR}q|7EXgMJ?+`6cay zmj+vox5Q3;2bm{nrFS;V=UCEyXTHl*t;QoC_kP&>+i5I*l;kD|`Gei1ezOj0Q+ek| zCg>C1zTc1g#%-^T*#`pAJ)TF>!BLR*0zE$OrL;yK2jc9(lQ42tkO7~U!o zd;JsrH3j?^9cb#cCDSz;Z^%GJCwygMjGEmNS_#@w z*p45ybvt7FWibW%t6Rm^k%lg~c3)(4DVvMX)0Utn-PxoR^1NZhN7oU6veyuo2FTIv z#<+CqvWU%sFiM;#f!t?7`ww-UAFb!1wUzXmoNC4kofJ(n)(G&ywYOgRVSz)8Lo!nh z?qqZ&ym#Tc$3YbLApAo#o2IEe!Po2ty2r%Pp15T0r9Ck=-P*}lqgZ$~PuB99GveHA zKO4x5;&vCikk#ju>J5MNwc4kH!~Z0|U64p5;Z6@cPfh+BSKi^#c$;q=RXc*!M>WB? z47DdM5E+*~uGZ!^XHZy9**dLPi2HV`gQET#bXm^UDoT`S|!nFbJbj(%Q0s zqG^d63ZD4)c@=^S#M|SxsB>DK8R=8`b6bbzI{hX2Vu~!DV=L+n`fwQ@`h{iE1z>uQ z#jd?vUobnTqulomxZc6_v(Fleco4aoX*pGQc{iBR)0wOcVudr#{O;l#viee;c!d%`C&5jv9A$(~3YKVigx zAoq|Zs!)qQm1}wTNv`*I>0>g>R8I7saauGLDP11vi=GDN4 zuZAZntibX1qX@|UX1NxJc5w$`V51j_=7-MOjAUhfk*^OSO$ic-&HfS zibQa!FB^E;Uo8y$wKCYLd3>PzpB1oa$2hIYHUs(_It$Y}RiEgjuhZc3e=HNk)x{Zn zttm^T(~KWl`w_>TU31Wo=aY01im7o@iZOUz1&})H7!OE9Rp(ibyfNB`;$52MM^QJH zsJmrNrw~UH*4#uhx$-W!o~Rn1Y3q~yLP?kZgIpskq*HS8A${znNu_LZP`GXPDd3w5 zYP^G#W(0A?V3gamDYFr|2e%h#rJ>@3nC9qw>8m*fITa2w3-pPXF!+|KgotcT~I`AYwvsCvVpHKUvP4^=~Z@akgDej4jlx5+!Ict$7LQlN>j;Cx5XRN zG|6Nt<2%qa*|1dPB%=?aYbfcl@)+WRXb^3gWp4RZs$ZG$aA(73ijA*3b%$={%{%IE z3ky|$S)Y6`>OcQ1@Uv&5_9SH|tB~$xq8{JFs}gDEau(`AU)#+3A8_fHH0Y$7>HmWptwiTP4Mv&^d^ z{O%eLrFi*p#+l_z12g)S5U-$&i>ch`XJ0-$vdTeLBi(DrJ?@SEaa3YO(G)^ULAjNy zn51|$W@YoRdPep#?TuIduUfPj7!?v8k7KUikln=wc;_Ro`E#n&F7yQ0mOoO)OCP+R zf!|4+tue11RUQ;lL6@Wh#!xL$+dXYCp@VxN<&A!AN@gE+)bX{4mduKoR`0)vNNr;K5zy^y@Q@odL$sVc3tCY>*GB?R+ zVHR0Z`BMAH7;9H4I56QxL3XK=sU%O7qCXWe$^*`Z88ewii%n|Yd#8SR=jK$N0M7{xDq zK&@yXiz_Z65tj^{|Gc@z%!c8Wvo>w2sNe!fnm$yhH&8io88GTITX=V}jc~E6bb8Tg zm)M~5S>orK$}-T!{GB&`GdnT0GGSQSM^b&C;BtnsqW{L;P~YbM)mgo@y{)Y%w3_iI zv~2$9eSs%KzY3VLl_Ao#>rxT^Rxle`k+Is*;o!QtZb#yms_U^nd)oAV6z-jRV(8@u z1T9}&I4F4psNO}5H|=Y#)6VuS-G7n^#3@eSZJh*vrSZw^>Fn+byh1T0$ywG;{|(#v z@#$$mm(l(d-Z}0=99VTaTiXVT6-@GHf26~d#f8((}t*>(Aksl z@pUAR?J=aCSSu6`uA}3mMP3kQ>8%5oJ=1ip?1X1VUYLnX;nNz}RgG>VAxu?X2nZQ} zUcQhjS~0A!Im^(OCyu|^Z(5Wv?cpxrSNE14z#n*TK``Qh1r2O3~Z!Ue^ zzdF3AO0N5pDSIq#LPeR2x6=tR)W0LB|BA={ef$;t&tZRq<^J{g-!WZapTDr&|E6y^ z`hUTF|C?j~Cwl%j`tE(Kqe!LUlaGMMe&&scEipi?t3&LAF=dZ-=S-nIit&q)z|E3~ z0v#Dc+PizYdLLTdcI#O$g{+SN(;6W8XP?s>65(vfgO3@IJXQRBZ)WSOgK_;Q*%_6bkyh8XZLhu4yH9U z*xk;>EmbucIqd&(jC)gD$l)RfN_efd8delH&P#B{<5BtC)N9M0z`d1L>mM+SDrc-%wP9`5?}MmJ|=&D zqMrTVe!fDdA(J?qch5YW+shK9LrGbv4lnhaKkw~4A0+$!Zg;$A@A};Tb4?UCOT^UG zcUP6eT{-uqx{zVI+gG|eiHU6x9k*bB2sd3zwzz?kJAUl&#=^L5H!$nb8%C5&xE!JR zd0}I8N#a`d`CB1N+CYIVhgYgXmjRWTic1bQsF1U3clBAa z&G?u{eys+sq8hhnPdcZPp-H4^(iZ)Yu9!cHqG(tKmRlWn>})}2Kk6x)6PK@2ucDcb z^X)2>NlI1mH@Qn5bR;G%FZToC2&bX^JMIn3KX_;VI~eU>-oZb$d-I1P#KG7amXD87 z&BM`*m{C*F*xJm*6_`)=#n|<)J<@hY7G|)F8dj#Rmc*>M5J&Fo!?xnLP3tZZG)oQWAFY>iya#LP?_ zOo4L$eDtAjH!GduCWNOvJ-CjL9}{%ZYVXYcaIsK0?|x!M0S zLOb74zGH|UvYnM$8jgwtQy7a}2Ll}r&4Hqf{6l1Jj5ODn3>;3FPKD3Mvt0cr9Lj9Jl^Fc*aSyDUd?TvCAQDQzDk3!(N!eYVa$tAa|A_?s=CG5 z6=I@+dVyuHH80bA0+Cg_NEDm;+*OPAwOa3&8n@3{n(d5pr<*%hw%j407)ZHs+RQ&p zW6rAboU(_|f#NFJAl|53B`sEA)|eXAE!O}ApZOAt=IVV}QTyE{L& z0@qTmk!8-MZ@crhEoIDex;tr%&yiJLEpt>fC%bZt`&c5b2Aph3`f%CuH{GYS}<^Nmve|!I%UH^4c<$r9=-$?xL_5J_0enfaB zIT~Cbq0YReJJ$GaahwSIwSPw{8NZVtZP!-_{mOj?_2ni?hf}YIGraj__ejo|hu~q= z^4E=?s-lZ*x&D{*?XPv=520k?N3}7Wkxoml@tr%6hH#`Gcjmty4sH&aoVhu*a z6-n&-!MqMjQMtV7^!fYN@}%1I1%tM82)NC0e%n)~k;%JF9RpXUJe17xl)FYPXWZ({ z8PZOaI%P?=i*P(fHG;&2^t|c2B`Ct2(r}`{h>J0E*el=&zSIUYmf0@7ULp0V5X3pq}*fRaJJWP0h`XhGH;$ z?5twY%`NV+JiMc(uuLp%4GD8IGtnUAmg*%-YoN%W0j4roO&JmvOj?!MgqV?|xwGwM zda+b6m$u}F>iN)!F0|jI&hvl$mQ!cU7{|JW6DO?$mM6N0d(>5MK#G(uQSKPwP6cmL z>0Vk;I4*RZP2968l^<Z-qYY&}ZsMU{?#~9yX z`JE84$AAwIH-?@t+i-JSw2y&Mo3u}U_qsNb=qy5!1RwtL^ob%$=15En^%a&G)WG!C z`Q^ue3CsTNT7SGmh0f**^XVC$|JenOTQkS4O$DtioXf1t@)FaxQS+HHbQLc=e-^J& zk|72jI3;`>4Y_GXD_6W#$bzF0Z|l0{X@cIVGmi1HrxDj$hoz05R>=nziS;uiBsl~5 zU*dS76H9qXvAsO{jmnJGyYg4p*a@>VZ4a?Drtn%G77n2fI_}yXG8a>#T~?PzmN#h6L0c6b)j$^RheNYjlI6KH5{ zZt`jQFNNr8NeiBX)M zo@Na57v$sT7Z4~^EfeFzMTGRWKMX$GGj#V6FG@tbufv&}x?eCjPPC*CohNx>0az@1 z2B}ozg5dL($z^}FRq1MWv2<$f^fv@R5|ct3=MIE zvph&L0SnN_h2YtDpg$0TT%VUWVe+~L8d~cav_4>(SSgXNvogBQA3*YyX@0VGx-;ZX z6iq&h^og{eNp%c4vzMFb*Qgkq_l1kW^2AF9ZjuP;4og{9PZU6=t{_3Ah*`4>-#14o zlU_8@W8gTBq^|Ztk3HbYgg0#pQ-)&xxhugXg%Ml65V%q-jN9HJSX~IMA3Pf7m1V1~ zIpcvN*}_aTfDpIs4QnuyPn;)m@&pY5F(!IENwtBd5MFB%PbH?*yd?2Dz&hx=`OnMH zPto{suI6Urrd{_NlBia{BRwJWHjRMutj#GA|&zO@R*AKZ0W(QDYHK#ndU%(+J2 zq|qW79}YF8!@tzkmdl`JRbfsMK6=Ce%uIpaibo5vu~Fott1{Z$W$=1Ig)Xz~k=iVK z?)_|SNvM(l1TqDlhdhvngL0QI+$%74JO&VAZwio5%*j&g@ceO?LMdXUD700CeI0oGjcvfhG~l;`Eo|LNohUo57z%zab}+C@uqk*KN+= zRTB2=i&BtX?MwxkN^%unyet->B~RVFD>knc(!Ou3FUg(2U4t-Wy{g_w7Xpe|mV5$L zimc6iu=0N2Z3Y@6j^-M{zFj{vhfgJwwOQ{?xr4D-?j|cVbmp(KW$Qil$C?~8;5Sei z81!9*0FZ8PSeQFH8qO#o@f$@&FEHT0Qy;S9c}Zf1&Vo^q4z3Ne?D-6($mbfdqsBlQ zLHc;MIxu0!*bz?w2(t}(zA;}UVoMj;E6|+gxxEUdi1D(~Wm~G{{jxpJu+y+761f7A z&mEs50bT-9g}t({+$!dMJ$8`}cP3V`;CVM<3&-1QvXY^(_i_&7mk}pxz1=)arfqxf z!9`~92jvjT43ybHl|wGx^keXJ5#mA|=>Ryb0_%F}@XqbhRq}$-Ce5M+tltKpnTFzz zR;ZK5a)qPt5rcanGOXc;>7SzF5j7%PJD%%l1!+=%H_x z_|$I3iiy0rm~b6PQ-~0DIDN5Bm3>iWfT@vO%EJ-oVT;BSyc`M@v3Ql!9Ldc)Jfi*x z+`yG!A?R?)wk<gu z>N?7D92&&E&suSO-~CpZD|$oz{h~f!uGk-+Ier?+>32L_FL{TQ9Yl@Pynf6(x8z2y zw|zz(9j@D0R+@lTHn;I>+S3rj6GKex6ZZ4zmA^9;6Hvy+OjHfqV_ z2wVrszpPOhuvj4axOFAJt07-SFh`i@s$lVIhp(LC?JGrY9Op=t=_NkaL&)9Z^a=}! zTP(y&gFIeN|DMjA&gWRlLDw@J!l5q^0-Wy;4}xK;p?lljhQ;fhz0xCc2q7v6fa+sb)%Qa=y1`k zv5R^lI~#L6UcKe-JbU+LHw<`I8yoL*GN!MvY6sboXo2cUS=F`^qmU$CmatrVcHmrl zIs1?5T)s&W5DqoYvq@1NNTJ-OIj$gUvCDIZXBXO2)U)A08cF`2j3apLXwZ?k_uo_+ z93<^WCN?3{Tx1+O>yE}GCcy)gL=R;T7Gi2Z>jW=iJxV4NG)P(f@wLIsK{Hl`O5+&M zwAb_YHr+=8cC)go=gJEE^=XbOK%la1v90B>x-D^$5TDff^k6JNEhJww^$dVkTAvl( zNPf;Bo&yjXIQCstSyKG;F#jnRb=iaOSesy1>nT*~F$gyjkS6poKYAZ;9Q!hFQ(u%d zMwmHAxb@ov}q6##||fND}%Yt5Ila>@n?&C#-P4igzwmoc~UHlZ?x~ zmg7Epa9^$OD4x#nS{I6y>HkAYfI@V`CBYtZJ@U~Tb9oXcdxmKDJYpij+c#{pgRWyd z0_6Ts4>g>fDbf`pj`kWMlsIm{$D=b3_r%0^h1`gTA-3F|&YmsOdw^WJ70YOKRu0j* zUGOBH$?{=58J>gj-B`YFTY(I%=jPXRhp7u54)|Hp+l>x@z|U)dOd8vvpYY|K##Q>) zp)M-q8OZ*zKN#d0yn^sS$WOtG_{oum#qZ~rEXc#uQnrkwIQJ_Lee_Rdy$BW;9hyGB zp5$2A`QrIu9BS!v5gI?*PEB(MI@hR6o<$%T)&q5+yR) z;_LN33}|>ePh5!W5*%5OceZngx;SIJk)hr26wC(;Sngs0bmw62C4IJT6T}Df%opP&a*NC4#u~BI-bN95{)gP!)T*6=PGy6jIPwEm_;LSsA zMF5nl`@?Kahi4TTxgf)X;+ zqE-&oXP#o{_(|65=XGZ*URd^-V?Syrp01IhoOC$(C(8EY>3%{jO^kl#wa;~c z&*^3X(q&bODQXl)=hzISz55I|#LH5TDAATz0fN-%yb4H{aif(z`4_Bn6G1fKBJP#1 zb|K_ieuK5^bH8ThX9e%^9_o6+0V=CY56S!eGUB9sOYrX_x5wFo7{px3(Huz1o80jq zYQGEF$!szrin3;H1pDgnM^ZOI%FkrN0bxseBP$0pgLaRLXC{7>BY~fbT1)t(sE~W{ z(iU*fvJH3qw=^;L#LZ&EguZEZL$~bJ+hl+bKLBh#!H`86x~lvA7HZzphl$Ps3ukbr zPRKGa5O^01Itw4-A*|7Nqf}wjLOC%QjVy z;Q~0-3g$?X@=Dha>Ww38#~PH`)~|*@Ark%BY=Ub>*bNd@2iIzQ37oYXcwV)fe~$aQ z&QVEZ%JS7k`<)-NXEo$*D1G=x*KDT%Z$JH;dElfSW2jks;I5?sD>Dy1Kz>_ zN^zV(Xq~q}^a-%JS_-?dVOf#2&h9ZOmp~-IEiDcs6L=JcO+a-5LQ>&G1II=Sf=v%8 z(M(-83D<(8EuQaw#Pe^uieB>Ar#`u2y?&>)+ z$iNLP?vs{kskC@(Dt~Wr@~RhQz8gK_Q);VwmOUdeUY6NmNf=*!veIr3*PJ|(%lP;_ za*}*h-jKCsd?bzS5NeYfLR3zu<`!&Fy0UNZV66K(QGn^_5E49l#aILjyrrPC)8au? zA#^OcFlj%r1@Ey;9>pq&!IFSWXc(dgcdVrcmv(iaIu}u`KV#OFr%-?`{>*C{&Vl;c zftdMB@t07I(pMaidcerIs2+Rl2n%AM8#@LKb~CV|pnF(c(zQFF z(xw@+5F-$QHBH5O zPsrO8!Q6G|lnMS3_0yuaeZ_Xp5WurNSbtP)zNP_MzIuVKKQ5?~2!3IrZ2qi_`g8q2a1bZ(}VZ681nqmG@he5hg&F|9sb=5QchtxGaR%FyO5ED*o{F%2`t@79FZ#+2hKYk3S{Np51|@&xjpwKkZ}W z5Ld{yr#uZ63em;MmnApi@eU0nux9L{;@=QU&E;Id)khS1rPXA!czX2bDL*AzD;KOT zabU+NY(u^Ogktdy=qyD8+!Jg}G2liHK+%~+9JK}Se}22~TQ0)-JUwhc@x&cVHRgsI zEMrmk^tMA1t|q|U0^UO&S#FC)d?K<8yA}@&8hCN>`8ztwwdD%ddi6_#ns(**NNUB8 ze4+>(JR(wOILmAg`1<IEsDhplP;Um@h?GZL>l`{XHQWG?lzBZ?iAX}eR z5Aw1#;kAsfUQS|eW}Y`!mz~G^D}vkk zJnPE?87;?>VnL3OD4sm|-uD+~?dh84_N(fQ&T z>qrhI5Hde$b4Si>~%4Kvb6;X|VyF%tOjbFciW*=h>8yr3ZN!ycwQe-?i#Tx33dfJpw{z4pno zmT2dJGkJZVMqBN!1JK|g7wk>^K0Ur_2*rzY&m+g5sLLNQd<@|-1h^t>K@iw)KlS@#^~9E8b?j;0>@R8Np!7ay(n)mW#DTsV>FE@q|ovLV)`bC%~@6L*n zqB<@kE@16}>GHHy6<0&jThGY3=TIuK0HoBl~HgeS3jbwiN%M|PgmMf%os6{Z|Aa+ zR(u(5rse{#jz|#vW=&~25VEZR!=Y+?sO>#HNFk)m z=oIB4N~V`RsPDA-+g_WKMR%4OCE56BH>q<|&B=Zdb(%?Lv!-p=YvR36)i`D(LxAUu z5Yi{Oe1Cl5*EM~@Hq zH|2DwMcNiz&AbjM{uw1HDdnJ?!S26ASundhzI0?V04bI1bkDNwC@IK59*WN9Yz69; z@~{Iw7_g(o3hGEBCHnl#wieDhYqRiAk5dlOQBh*%*Pk3}hGWX<>X(z`cd2O{1GA^E zK{?ZQKEW_S0x6B}Zf_^tj=F6$u>hNpH@tSkFROTXz~~?a>o*Ss-J69&MwZR0#j{SH zmQ?u=`Lmr$W7lt+vL&(KczvOUGil&7*vc~(?K9}GIJ;kfln@xL9yveJH_m>~XMYa{ z-LQEHAFb_bGGSX4rbx0C%Sxo)v*cgW{akTyFi&fP_vzp&e9&Cbor}Twz%g^x&Vn*@ zC=1~%-dctN1#Suc*uBjpk4Cn}(9zpdxy8$}&2fDrIREMz_a>zBG$RKa6ZY{{VCLVVw)8cY>Tq%m^_{IuoK>+P z5#ETA3-d5c9DbBTnV%p@9a)(>TAS_onz^diTR z<=_)xK0HK{M?mn)T+1FOJ>APE7ud#b4&=__&~YI3;u9u~?V-V%`BMY&lrmGPwP}ln z-)gXBZ!gT|pYf#hZuPr0NptjnR$dSa`QxROsC@(9X2$Ql+4R)Cv=&^NsVSu>5`9|( z0zCBC@zW_Q_m>Giu7PUXveU$e0<&^;4Xj2=%)p zHuGB~;W7D+!7yt~EqXCbw*i8jXn-WKX zK?pZdYB8q%lL-$>n~OmvcI-Vu7AP1~drwR$iEI35A}Lt{FRwJg*$49={@A_3mbz`X zqY_JXs(P7*0HhQ$Ap&oP=UId{dlhyHd_G7#xF5)+pm1IwkCl3ZkE#1}K%J?wh_>Vh znDlj^K*^h>4l6B&%ctSwgUovH!sV6wK5ZQx;vXNW#)RbIaz*~UT?Tyc^-(b(s6oTz zP1fel#@^^Zun6(MrC3mjGirIl$?&tZSD3Q(TER%Z)vJ^I%m;jcn3tSKvhEK3nk$VB zY;=eyA}5|Uwt2>Zcli*Lql1QByOf2H0`Ns6B1Jquj?61CK*w5U&8>3gr!Jo3FIV#+ z@AH!UEJv>cY`U27uFOo)N<4dh_`DqCw3#Vv9hiLk(Qm>SsqENp6Sb z7eT2%9?k4YJO4^&4Kh&6mj(raMEZT>`Panbr<^;2vkbq(FH z5N^y|BH``3m<#H=wc~4DoN;!_;VG0oldFl zw~5fjU`tuN zeut5y!m~6iv3AwsLWf73Q9da7gIf4%gOe)+#ve3!ky8n(QH%_QKIt4x;9`=!FwCtl z7LfA{wRSa*K}ofdX504(9aK+$l?@-Cj3<|q2rpc!%yqZZb!jRZ2|mMSycRA5@U$1* zvlDF~9uDgtE&m5hTx>xY61_Qkkswv4$x26x3OW07_UrZbhh{fii2HYsL?vi7VqDw= z#VH0ltAzzY4%9joD@RhHMx{X&SQExn z37YCXRIfHCYt@}Jzg*W}iI462uVyk)E@R>TkDR(cd1e1E{g~nHc6Rs?n`qbM^#L*CY~XmDwPxwD&heSB!cJrHK4W!t z74PM49tr%{L#kaz{pJR*u6@d^lSNPS8y9k)EoGXIpN$_BV4};#6D-e>YRhGP^A+oB z{2AN3by(_ho>DJd1LHH~!A1vIoGC?yv;^2W@5}rPWG%LU!w>4I4>GbUiyG#4W8X%- zO&*_ZF`fhki9o_(@z_r#yx7x8ZJt$$;m=tuDHH<%CpH~#kOTIiIDXb(kvAD1-`6TF z9F|+3WS>x~w-HKUF{;%^DF^h2gFlgSfrX1?UEFn4IrHpw^TwdCZk=yz7j8SQt#7>I z$eNrI)6T2MgF@nRGW`FIPL9>uv}EDG!U8;=?VME}mAP zS!S`mi;)TEoai~f@SN*)D*-Wnz|NH>K>MvnX*{vApe02&?&ckk+DxGIX6hq?<5Ax zwhk=VMWDRdQGkXgk-XSxv)OQ|UZ3gY51N@S0a(!1wm@g64*Z<1XPsZ9gW61=QA}N$ zvTeGwd49LI@r2HFF)ANfR}2{0AZE0M9l{eSKi`0(wK;dqZ!cSN+6=nR#dKQr2<&q^ zKjVc`CQfLSCyC$^uP^ryf8FtVxwC3c>ONg8!4L6s*zyO`X=*zFt8rPkrOHcG?jO}n zBXw(qza8U(Z{0(PZs+C%Q|OHO3KVocR}Nq?_VX?zyNgY0A@Vqbt7NjR6UNwN<#`OX zZ9ASD=J#IlTRGzL=P6GKF(7-ef>lem@G#LE*jy1jBiHy0*C(9)cz8e@?qYvShZy09 z><@yB_g^KFPC3r1Ucy+J1X4}Q2YE9;FzK|JM;^8jT_(-LJ7du8$MbVP8JlK_P@8K7qhY0;V&oRHI&D|5b?&WHpfm4K6 zqk5huSnbD!8&4(!k`Kb<5LOPBQ|(9&)zz4HhXI}s1BPR!5ssJ{)hhm^Tj-hV&yk?1 z_8AN+2Gx_XAzO|ts@)k)<)pf~G7ObEN2V<>R7cpo!cProR?hiSd>TMLSYbvvFaU!C zvNsY(biaKI-V@443>s&ssPGS10l7okstwKAFY<)iV3KK2 zE#iWW&x=)3yhXFx|7h*{iHkda_iJtl%!;XS{Qz&@n?l@tfLE8$fC^R7EHabn*J0$c zDWJIx$UX~a+_4TU?f}uvm=gIUM+!U3#I8Y=BonMlktI{q@MPS!uth$Y%6OnEPk0;5 zp)qCA0GtGaArVLa?ru)t9nRVCedDC~+szXs{?{mIGt^IxMv(MhY z-}sIq>7R~vK6=iVp@?Ee_op%WJ_^KZ+VucO>gkW9_=13HmEx?>ha^wF5= z>X@Ds2xGEL$?|(;jE*!ItR^>#8E5phC!0;WR?xY#wyxsVJShV`Lf7mnM2WSQzi{hal#J`~&e-pxsq`aD?LPbr)biO)& ze)d>kkmnI6Uel=jW;ep5pe~s(*ti@>aa8+Y3yjcH&yc@9@NW0>FhNyR`$LgSA5FdM zEtHX_$}np)FB@WloF8JygzfB1h6?xDyhTVzSlCwH{P)qj$#B*z{1(2$+$Kb?)8oCM zXhJiAWj&WqptOloH(ClADbFnp^aZACGf+TTyo!HKh}<>Su2k9?VevhyR201s*>5u z;M4V+v@e#Yh?o_Q{A%s5@0~PjSUSFKJ(-$OSI;>E7#aW?ufhdVI87V0m{Nb((a^A= z2h?(Di%;WEH0Y*GPkf4&&=r)_jaqVG+P{$Q;AKf6&?-QA($B{H$nuA;opi7@MXBhj z9z|k8D{Zt9RSE3En=`iOtg0#bogBHfOo_uzOj@B1q5JUu9&DJF4(g^)iCk3U9-TeC zcsMX_G#K9qytK;SzZ$cbGU{=Fr|6hP7deg`JH7WG;Jzv`W;)2-vfxq<`2DqZ)pqXa z=BqJXJMgxg?ll2yu}mbV^=^+*)yXh9F8f<0FGu`qVG!`KTpWDZqNW~KgoK-yNbaw4 z^mC?=B`#UH(O}dp;)ZEEv9$Qn)|m&LNP%iW{eEhOr4SzH{_T@8Y);zPE(GfL;K8`@ zHx?c$Lu%5f*_hr5@sP`74&iCF6t(Dd4_V2%$P4E z6jO0E_UVPJ0WlOcc3k*bfNywr_yx8NGoMK9!D)Py)cN&jO=3A!;3C|SJ~bj2&kpXX z(Y~+9!oPNjHB?y;7tE_!>PZ*GS?9yH96Lh@MvE}80Fv2A&8!KVw%>C^m5yWm(Y_x& z>?tv$@_Vd!*PDx$75AHXdGnldmCfQGI2O8W0xY@L+?>&WqlOhw&DF5V4Yw!nsrf#d zhG6@9do1k#f#tQQt^86tgBcbfY5M+0f5LYKBAUeTDVr20-l-UjjiLi6V{1B}Y^QO| z567ZJM32xwQnu*}1T^sJ7EQMU!V@Bn$D^`4Z#rTyCg z3S3``n}X^OQEpO3*H_cNoY2t~8c1<((yS@lq42YmAuEI_ML~Nq*dbaiLO)J&vTvOW zMo4+#WRN=nBqCAkUgX-9RStk0;`wySSf;GL2h&BTAVjWh4ObJ~jkN#{n8|&k0VgI+ z`x~rPlAgl2xb&ILQa#I`%w2@?2v#o^yoSYYp^r`@B)}DBj&KK8py?_{l0liy=6&uv z`2e(w@cNmRnGfd+iiTXHw3KXSOwQGk#jno?)6>4z_!_vwK>@bqC?dXa)WKzy7KdKA z56b1~VW~~g=T4*DX7AJK%KH@SWg$e*XaC9Ea{dQsOloJ{Wvd7 zSUuZfmDYp_&838|EKy&oaci`gv4*3w8${Fu1p$oz$@f6ojHsWoaGribzb}K?_0|{KpD#zQbWXN=a9BWF zRU#b8c|Cdf^ZtqyC~4DAq&7;S-W6)47tm0*eJla=+YzqHS*8OAeR~mK#tNoFB-gv) zXWBBab!VL_*uS{i_0A(cptEFk_DN6YMt;cBsn)VXt)sO=(({nC7wfuZ$Bk}>midU3 zH!#oxv;ZHMNK3!^4)g`(eq9%My@W6UU6~7qeXvt=8e-umtn(4Nj@ibC{;=hRtDJm{ zOkJafkSu-HD9RzXz{-Bn!EVaHWm8}H{RgSoTNW{zWD0Q!ndYFG+-U6W_fBY|o_v!K z6HJ$0-z2J$#%jrQ*<_t_74aHAO4{&2GT6E$LV%&KuTbTlsx8Jeh+z&uys2a4Wp!oT zm>v5tKKrw%hu!G<$l>kj%uk@QD9%uim8c-Gof+4YY_Wx7wf*_==t{C(E#i}T>-!4^ zS!DPDv5qg+E(665v`?d?cJg%4I^809Gtg0mwawx8WoX2#A~6F!ZmaAjobwYeSPC+WRz3f=Is7&Y&-(Me9?7PE;Pq;1*Je<02z z1(5AG1qCJ!Y+ZooD=o;fk;1^#!jCtN>#uv$J;Pv@h;|dRTGc|aT|+_N$AK$JjEKpr z%;=djgy{#IuU{%v=+rmagVEH;o~?L`GFJ$`j0v%S`nYl2OnS%n(-e=-Qx4k6oS~r! zNYEJwSOBj_eJQg<(sLJJt&3lGO2h z2dvBVj|cTDL|}qYK0tJ+3dCR4Z#p0qOe~k#y#Krt)Pm{Vk9b^G4E(9Yo^4O}7* zEeLf8kU}77*RB#UMk_%8wGcG1Tz%&Y>*O zl2?6?B9w#1wR&-;n^^mi%IoFIaw9a^UIMAHp9KvUnRe>Cj_(grm0~jv7Wjt4J9w3= zS@9KI608m*|ULB2*or7cW6FsUut*GLF1UxOwi5uY;Q$wxOh)JjGX{-U)RncqMb z^~qNH`ib)a%??LqoOZ|iQ5k>=%+g`n<(FHe_Tk7RGB5ye^~deWE~-J2e}52^p}Xf|8_$2^MXB%KzCJ9C`J+R0L)+`}2YTP(8wN?jda&SEyK!Z$vFRwQI9esT8lFdHEdFn_$D`vC6O`nkNPR zj)%J^yoDo#C7T&Mk)|J`c^dj$tv)2)w#SUam?fBgMoj!As%|9BgJS=#ZLO-URC>l{ zdd&C9UIB?`?GtP3YZQS9!;8`-c0qFNJ#0 zQiQ59qe!dSgWM4%(lnWa&fRej@X4+lq?mjvhR1G86`w=41S-!aW5n3?N zsBO{Xn0CZoxz%~FYVXAIeta}&@(`M}xmtDPPUYWG`vKL``SgOtr_To4vD1{W<7>#F zYj?Ntj7V@Peb}(iQzT~2I8r~_nL&7noGWZHZRW<-Zy}nCDoGAfiFa_6H|K0pp~60Y z5OYaeBc2@Ak_1eah?Vul6w%c$9U&Nmlm#IooU&FZ@Jqas-8`MEp2z-vmw)_S0WiI+ zUZdQ3)s44&@Dtc*c;v%`ajU~E;Aho$Ker{D0;~V7RD#bWXBD8lYDjO9a3S9EYdr38 z!Q;1Q!H@Im%a&$a`DL)cR5W&E2bpq%LO>5Dmmblj zO`9n>;z19))~R_uaYIiihdHSfNuh-=CFHndxYjkLa0!ee!`^jHEp_BFO z%Dbd(oLG(ij7Y7Mw^Hrl&%@KogdI6lup4^-cO;CDhkJ{W`TSSo1*~(gcVRu75LSr} zkx!j@yFBUU`bv)%WuKmq^;@SDjs#nQ1-iC2(bHtd8VQf?<&Jj`p^3;v87-$|b9yZq zb@sF+(=ezlf?70zj)zLEGaA>j9A_&;?EuZGcL}R*)qW9ahXp4$HB>N9^{%zY`GqjQ zKHrVaxw5&UHLsQrC0vkrr6@S79D;NB+RJ0$=H|bpD@CqkrPnELjVL^fSvzti&-FJo zp0*HOle8`uDhUG0CN3VkvxGT_iPVlDf8MgG#W=={knzn!g{TaK^8>XKm= zA9wj$t*Y~K$>Qcr>@DAsGryqvO zK0@*AZo9Ac44#XU4Uw804oaz=8#Pg%LL;1Xu-m3eB!qpW^N3r4u+zb%0jT3^IhCQE z`k1>u+Nf`jFL;yrYQMo!t^U60Q!BE`v3R(pkL@9CAQOiYg=|*55Ic3yT!HUkCxN6|X!1k+ENWV8wlGdj z%eN`@Y$o(R;49oExl@cu8FN|93=<_Y8Y_Qsj2RtcKzb%q!>8v}6J+mTvi4=K_0#Es zFS#mZAb7u`i8qJ7#hHyX`uQeVQrG5*_4h-O5$suoojA>J<0*%4ow)ks{zh)0oEZkM zUT7YxHx)splyv!P573#N>fkQhQj*VI4y+k`_sZMgsY9v9r}`s;qqF&rp<~s07w4-B z_c)v~F!A}BwclfDzV};rj&JXUsm=?lQ2)OKTD_yH`rqF5)iw_PorwK^0Yo>)KRZM> z$3F_u&B4O{f0Td!trYyfL&zSosDBr-XX9jtY_SlXU7XB~?GQbdPL{p+)$|F&E}~e+ zKZ3dnO7cI^4*diK7&^^@MDXvTPR!W0Z(zKjyV}+EhHz{FS;Az3yo*VCLPejz9Wl+-YAq|i~F|NTW1BN}th+&mwnZ;xUT zmYTlosH&fj&qVX6j9v-b45M#-Rxn9bOGkYg^XTy8?Cd0hb5<=GdpB6?;6-oMxw{Bd zKgR6^m)-7hVf*vW!rAUxjVCO5ZDO{a#;LHQ;OceyzO}mA{i5iOU-xiAJVNWhN5Sb< zxaIA6thcBjC-GQ9`;p^f_u+-$xtlK{-hEuK>AV; z5qPa00he$rB^oOv=V-b21@n5p4AUJ28F;N7!OuS+E(#fZaSzeQp#@;T3O-WyndbeJ z^2_(ogJ>pV#)bz1UK0MCLHgG7@0THZQnY|4#VEi_JQ)x(=|693|2MLK8~49o2XqBg zO9E{HXOckEK(%C04{#kV7;g=3w$8Enk6@YW{v4U@RWQ z4&;!Dh}oZHpykVzn$F+%2aF|xPJkRL5pGA5YP34#QqBcd6##`4PyvubH$vlZ(uG#1 zSn8_4Dhps22QmlNeU9Kdn8c@D%9F~-w+aWGB!IAhbqW#K`;$Vn_*GH_1y)UflO)h2 zuueVV!_lMxZF;FxS%Fm{z%CUO0IV~N=s%c*rNu9l;?B290_ewsM1d|+5&ip~RA%kCzW$nwEP9%M|tL9-Ze+}-?nW(>rQ;h zRO()9b3XAQj6uDrM&*A!x~ptoWoS@r10t`ji!UxOA`)Gg|6r7?GQT{sAlJP> z%u#=JSIs7;2T(ms_s|obT(S)j11|r-=Ud9WKC@6rg$O48F(ky_{TVE!@|@9gLr8Ne z9^rR~fe3fj7XQcarM&N*x>CW9e&$Xp>3CuQOu=^m?7udwfA{;p_5IK9|2=f}TL6>= z`wakzf_*R`q+q`kP*AX62~}mV7DG{QVdJasr4P z$RQuWv_Hv1yHq6wU0~G;h)x2n0y)$oxQ-^ZXqU>Qf(oq40MRL+EFgzo1oq+Nd)lQ! zDf)b?Q~+ZRkIl&V>u1@~QJJ)|Y%Kd#WaJgQw6b*U zMRiG9T1hpJirS>9?!;EAgQV1YoA=6eD5k_UZtK;?(AAz2U?rSXBAF&t$KLhs#&!K` zvp70l@eG%KY+oh!Y&Uu`&1OxhGtSW=Q+630QlhwcEG%XGaB1nVw9LD|rgbwgR(5n$ zHa6f=Q45e|NlH#kFaPo>Wq*aPD{2O&SthA8A66w%5)I;*{qu`N@PuAlPLTU z&JmtOh=$C@5t+nhW)-K+SLFNHco#Vp{w3>5zh<7f_(^91IJdOcej7_n^W~>Vz@IY- zoT$?VOJJt%uGXQt^fPupW%>;^^Tkg`#9=;V=S~s8i2( zETp{#A4$XI@qSM2f248-GX@Qr{kEP7@O4VFxa%|vSMJ4t`b?L_I=G8+5f(xg-^BQt z0v+YblMp*mu^>(c=2_Sbf#r4+o&qtMb#N0Q_)k1*nqF?EibA7#abXf-v#lB(`bWnP zcu-W;y_Y#gy@`({@>~vD0&Gm0iW8-rE#@x~o0htv#uA<7)1wpe3d53pJ)doV9me~kz?UoB?ZS^LtcMGv<&^AAX$(+Dh zKX5Cj)HwQg?aK9Iu_MWSNHlj|uCDdlzx7b^?wIEKm~+;9!D=c2Yj?phnvb-8{x>B zqfqq8vE47oOcSS9IskWvJss<+(0SeGPba*-=O^K@hifaY~-8e#bm39hM8ZpSu`!BuB3bJ2_METg7K#s}%P%OZSo{J1K zJ|q%s(ynz9z%ti{A?i1=AB*yAjzq}WL}yE*a?Xh=uOv70iRgp)N}w7Ta>V+z8e|rO zeh1Y>fe#m0T}2I%Uo&w>y_vGw!b8Sa_z5H{zUsjzpsNj6RwZ!e=8R(vS-2Ed2%--5 z!}Q*NLB1%-%EO1?X1t>b>i8TcAuLQwWTNuj@H6^13Y8z9RYHK~t2;tHEZ7n1xH%o4 z)oO!js>pHH+AGv6zRLdsC47T~KfwZG+OZ|rns!gr0Sk6`TkfFy2y?uhRrUMhL1XRg zWCUK0sUlt3g_}4l!dzRo#Xl}j%vFha@-yqGrdV?RHClrO_IdBlPD4c*ahwmfm2^<* z@Yu5GQ_7GDS`Ff24pzAPwW2R`;Sd7@U)6I3$<5tncX%)L!%mayqP& zl;8X8sc@sc#job8%X;RJhdXY|BT(Y))&}ttQ1UqrjbI=LgK096%4p1qBKUbpohxq& zn2y8sPFvdMYi=7`bLSbbOlUS6?dfE!vD8!y{T_LBBm>ON+`OEDq2SBN*aew=qe4{z7@{sO z-5_2NrkE&-@DM?bP*7B#QtO=G1 zNT-@8%%1&DdciM(KPMvROt=0rly-4eE+09%Y)ngx%=k?d{VD@IkD^ioIY#Y|hm#oI zVRSE^dL#*~dihXDQJjDr)mj$D@{1@-*ELI0S#0h$gP}Zuo^I%=og%X)j2ed<1$w?% zYz~uKUgm9Wsr&v@w3go=rW>JYVIpd9m^h(L!M4vWmrGlHIVN&~BP84z3RP}lYqoZ*_yCWhHbNL1bdj4L?b-?f78% zMc>y?t&J3s&8>ssJuJw(y}%-CaQL>8RRtW*@2{T*_-M7I4YN$$K3BN^LP};#pIR2! zgsDkyp)iKSbaG&%4VHJZvrA1v&KT5sc>WkrX3DLcGtHyw-r}j@21Wa{n$!ljwARQF zb#-V(pxS^=+giOUV&}j_`_sXF_Mo42f+2f}Ug_ zcTbii&~1Dr_vBk&d4AL?i*>-E+xQ=14OIfl#RGTuJ|qwJBY$a@p4+Do#gW00Kjd27 z*@Z(ZQ@Ye-b;jTUe>F3b^@gtDAg)|!kLOn-%b-m-RanjUf4y~SB$%H_7{{%C_(a=k z9N!k{aYC;OYTV*FPk%SQ--efc(2XAM(Y`KIXi~()Vqd$~gnW%}Fl19xEY8J{@NH#{ zeg5lEH3kyzdHSczbc+bHhHb7kZu{w3q`h?Wj6HNu1!sGV_x9v_oezxFt+ByrZl0B* zb@(k~1p^J`FtuX@aobtVv<9fzrXY zl|XUnYIpUj?Bn~zOuHabNBdv}Ph|9l_?2qdV;1x@Hw=kOA!ncJ&O*S}>*QNf3`5;f zm?%V2{dMJS4UQ{Kmo*6)BFm@k+ASEaimlNo|aDT3V6K0#+NaSjv++mB7S%9 z%*zNUN4~@iur;o;hDVS8Dvcf>PO^+fLH2NfAFlUwRT=#_Sam=?X?8`Kh@4*EX*DNV zBy8yV7&K0<3!{A|fSwiC_@U+$f%kE&kh1DQKe%&I3w!O^BFI<5cCzk_U30J1;#gJn zy7jh5q;Si!a(1yXCogw$SMCGB^~nU>(xA=@&m(??M3tvK`MS4wNJp&gSlyBDTv*3~ z9kSUAs=_4qc14<7yHd_%MJP5;#~rIsi{&(Eex?-=XzyFU&*{p{puR4_03v(ns5x72 z7Sn7Y;*VX8kx1nuNR~cU+0z@7%ld5xep>3FX{lNTi)*;C&Md`hERlfQT%#h)f`V)N+`09D?i51nrx$btRPt(cZBwkz&6HB~F!)py4d4m$Ornxn(GnF3dO5~VL%`t= zW2kMn>JCLXI<-@QHPyRC<0`66_hH-}MvKg`%kXCU=2EQY&W9(GL1o3inoh2BZ{p7= z&8pi78%`TKro>eH&>Q#(9^Bf4y%&jP${K~ybq_-{j=Adanr@xLzl*$fRSyYnPbhH`nksSr#QE@*!b~;eVk9;U zs`X-EGJQ22VR2g*IeE{vL2%OvE?8Q_+4k`!ZVFls1D|EJcXGtKo}qQ|&Q^AMtgh=6 znfiASmjg!40Zs}SMVe$@$5BvM9z3UO&-h~+;{3(sxTYu_^WjvbmA>HwScWV-_=hpH3LwKvs z!ep&rq?^-^#r2t|rE`wxQA~M;U+y09O@@FFMSa+qhHJXtkDq`UGSbKX?*5D#Om<0q ztBVxG^vrnc!EG_K500bOE$PtO=ywd5ceT#KZXN-nRAfc1A`9JJzTQgCgk`;aAJ0|w8PCIsyzZa&y~)&kPPl9q*qv^KIBh=a z?6ek*_8iQiS7PXVDEp&+u3{=eLa76Kmwet3w~t9af|o$7)xl=a!_rIjrHyL^4EqNCo7-tI#w zemE8fJ)`Xe8bjP}q(0W9K8K!~OC=-Rv#UilGJueC>l`xDFxoqYSi_!b4tEUNkJ%n3 zl9n|mz~{|-J2nX2u$EAdtyE_0>?ct+v8cw2^JdEli>WdB@17qKX<`lg(8$B7rxya1 z3v@ag+V?KPK=a{hRB%_9n>hK}A+@XTUvF@G#e^~x*Px=-UhE|7&&UW{6)C0{_1>9Lh1TkCZRWz0?y?uIHR z89T@gDAsK`IrzJxGsTpWme!>~gfCZx8LxtC@KV47FWPXO;xu7MEklwG0oS09Y}5a( zxBYB|qsppqN9A3H;=`khw~VYnc(>6Ny3qtVvnt~@RK8i%G`XOu~4g1n1_TWUz(^$?#j)cx~9u?XxpMGmF z=Q&<(P$*k$0!@#WPz@A_0U{M2mT~E69AVf(K4>8pm^$rTm+R!|&Q;<1HkjjTT#xXL zJ&(@h+o}nSnzEO?yuCNYGj|})hS!FCUAZ1+@>H5MMQYP_c%`%jVoU_d5ip-KML$3g z88=>$`x8!*v~N*=;|AHDe)7|zK@VSM6DQkb$r&(aJy1hT6aRI0^U5Bj&fo|`_IKh8 zZ1MgB#+YxE{fKh#wWwj#pTa57n-zk@$x`sdzQ1MuVV9S#Ey6jsUEdG+_$KvzmV+cE%fwH9XtC+;n7+D2`H@Dd3pberTw1= zCcPj$g_-ziOJ6*`bS8K1Q>!gt=!$+IS7CLD)xkW96#Ui z;L0E({-|s;BLGF@9gMK*7b`W8$_*iT8pzA3AO5rCweWuWSRD1tvdj}|RO$yyRun&W z#p>eN>gwv4?VV)>TVR6)0Q{>!Cz@LI@qEAbY4Ixn>I#jjJmu=X{p|)uPMdKNU%E8v zXGIzu{kc&SciMO={o{FIqF~O&IJ=5AGt`RkZWh%_Wh6v>2gZhlFLPI4SSA_Gg_gMV zmpPZ^hdBAr;rdrAx&kx!rafOL%Hd-=?p2@}}tj){AsE1wh=ic_aZPk}Zgp_f|Xrl)@FNz{BYgqg%Ss$I< z+s#FC#p;&vK_dZX60MC3*fJh3^ZvfsxJ0kBp(6airR-TRgx}YaebP6qtt^$ihwq;J&f{BG z%~|`KS_V!|1Fv~dU$u@e;`hl`TH&0v?$;;ihlaB54xq+G1bSb(OOHtm_eJ4){LJSZp2t22ZV z0!pgJ5iNDdn%-SYfcJ}*+U_ek2^FJLM|!`8q?i=>x2>&C3l1j>Xq3XO& zSuEjf`26E`G0ViK6Zj@zw#}`hXdqR3YjK)+|79Rdh~?(^YKiyk(+bohxmNxNntXW; zBZGzo%`jvgqT(2`o}7Nd>_T2XLflQ2VOte8IaJrz8^VU_bktsT2#<@Dw}9ID-9Kly z*h+c~=$)2{&jyNj>kMMPCl8l;##xIY0u==XMtwW3&EBu4op1DS&ZqpVd_MwL+4$=v z`4eSNns_37q7J)W+L`2QxX6pDtav?%N|st3_j<$l^zVPoeE9&w?QejP;(o}7L657y z=#g`iIzhoe!`-A9y5%X>@7!z4x2LFr(^}mu+|yV3xlYM6{`*9QsBfh38%Zb4M55&Q zpHg3GaKdFvIxEPzH9e*{taxNfY5Z z*8sb**`FQikxc89{4{;!e51Feei+rZOKL*jY)bfTAN9P^DvI@MXJvE}!o@GrWkl_# zj}=Q2!amd3)7%+Bk*IW>a#4yNaMDfuRu z`ka-aO_{=8+;Zn}0P5$ICZ_Yz1A|3~VMo%mcGVtMUripN>GlUc2;FP0>r(QGzXoM7 zk)c%TI#kP8jr&?tCTQV~Cw*aL?+6}(fxT+x9IArg0ue~6TxW&B4J@fo$D>Au z630c+^s$*=_0{)LN7%)HAB~7HHb+qiL~nMUD-TDLr)?6hRx(5?i$8>ySbIe}t1zD% z2V^6p*kJ7@)i%!1JTAaJH)*c7FYg>YxYP)zrEBWq>fN1aQgc8kA4o~PVjdpqX>%v~ z?|5b#;bqcLesaTS?99WkHcvl}2W?8DhPrad{uuh6py)h|Gk*&&Grar#8~G6Sm}T!Q z9vOH~HInzxSP;GS)G~P;<4Q5HS z(GDBP-h6feYhIUQ?b~n**j7{N7)>_N{E$I){4yVa??NP=%1}Xpap6DvRU{TRk_(BR zvdhrsrmN%eb`>Q^qp~(vXbxE`y zU+OC<*dpw2y4p@4mtq{`95K6if2T~1fR>C1pr0eBsKHTOI{KcGwO~1RDOcw&BSPMV zUbOqgu(3mkXq+6yd2VNV(a`UuC8ni5fq_#!Mvr%u5mhn8MIBM5p%w@Fw_MlMUa%71 z&96_^7;8RDpJi=n4mFh?gS!RAremKZ4j64NGt!;P2bzYqEw>`5=8>`*H~dW^@=3oJ)YN$F7zEK=C&cSQ= zz6d$_7#!TNntaB?nLCVKAMqW(b(Hu1(;v_dAVBxaVS;t_(hT zwuXog9vRqugB;?_BO3?Esy|{9fAa+$KJUUbeC|qM7~Xb?J5(t#NTMzwuKY8^fyKae zl1Xp%LsnV?Y7|9K1T69fOWypu~Ih%SgDQyzl+J8$GXfFe9)^b>j~6ft1=w z;o#7V(90_Q&OkY?;!PA_HO343{4NkJ(i@eB!SLjrSjj=Yw>qEIsAP$A4H z;N%knKJV$DlwhuO-Cpm{#Mq8{5>)*kgvxIp>`1ku$yWPh{Dw$^ob_Q_rx=S8SwZ>V zxX3Kw&W`!Ko1La`(YcdZFy^4T-Y=7Cxky0SRM%N|U(y)Yi3GURzYQ^QvvT25+CrFZ zVIvhtCQ#TGx9Ka?nZI+A9~l?yP$;+f(iy9;!DBCwbvdmY{&}rD zp_m;omO#Sn?Op#^lKhd9=p7QQ{TcG*x(4o0K~F&i?RyD(Ji5faZHyeEF@7Q8HM8}2I=o(AIe_5d z&u(*@LW;m1n%SmSZGsWLQM-5QXJzf^Yt710*eC({PBM2HF$70C&c*6UOGs9yIsLwzm2lJni?=Ry-g zup351f8fnY&nreY4GfZPs!_aM2dH4p>dyFjx5s3>|Dps%xmE`L<5>Be2+F=I5?v}{ zG2d^R&a}{#5+n7>xec2+%#@f!Y`+(EH3^;imEE%Nke#SQzOGW#xAz;lF!W2(oI2hS z@Cu72_ODD@`4sv$;Y^B@FrmBHrHEAD4e&~xN}wK~oLkiQ8YRIs$hDC-V2`o%?j<@F zMTVB~C^M7#b!ko}%lra^qf0l7>2qeNQ*E%5?(EYEVt03%#er;Vp;aA8wxQ#Ut;DcK zj!8i7cB(DymqXbST?k~8QI@^Jr7pSF*EB^(+6`n)!sL;ohV1WT;|sFBSXte3y0d4O zo6_vE_s5IURdLVyXFSHUob3y~c9lwZ)#7X(YF(G4DOWqg6AXC(^mW+k$e?BQS>Bj?XF9ktckuc?4^nWMRu~}c_s485QSJZ z7ewogqNl0BC#=357kZKuuBFLIwKGxUmcoXdv@G7N)huX#IP`KWC&{*0V#nR36HN_B zrY$qIt!mhZ*4^L}+h9XUD(oZl`Wi6)4mZ7)%V`}rOTAU!*-A4166iHWO@nQ?kJ-YM z=?9dk>(o);tzbz(eVnGQojAOCkSa5#hU1*|%V zl)w8p>a+I}8er@$N)^pNh2mr9qxI)C%+a?t5KCS_Id#cCAD>QN!rPB1Y6vr8zaq3~Y9G<$QyjV}S)8w*_PJHY z-F{D(E!8LCvk*^C<@mBw(l_c=N8T|94X~K%sR(!~B62uGm%rVOXPf~=^*k#Bsb$&Q zmDjWX8k#`Kw(CV>`PihBmH`Gy06lMO#6vWw29+~I(y1N;Tj{bhfLuNlF?eq%!uK>kjH4$n|V;e$9q!T6*O- z%on7tXE19XPfk*6sw$?i>I*CBb&VLp<0K;eri6*@KHuOm_-(|87a1ht<|+GD_M2s8 z;J$HklS9zXUbL+)=$!p#q=!dGH;P_br9}AG>F2AA=PAIYRCjWqvvyOK)Xz9{3pGS(G#H&H{#l zdckH8WHR4N{q^fT)U^)SWji*l^$*n~6qJZCEWNzUb)pp%)X(`q3F^PP24)mBtP?+G zh(kj`5vlTP7bvhz2}nXg?MEyP(b*+*m81S6yWjs@_qXaRD())o#ox(k`Y+%?;{0dl zLE`-XArBHS&wq@{%(g}x{zY3SBR?a4ktBkMfR^EJBo0;rYvb?#eA)4ha%GDZCbKP z9a}035=+VCst8qexHM_-LS;Z7^V9~->t|}WS6LIeD{4;Eq%%!Z8QF6@QLa=_NA*jq207w4Ehi~^d>RI-JNKeg3EI`@#!;JOK5lqx7jv);*i=bZ|D(rSt@g2vIe&!Fxb zx*zw9je5TUBg6oLgPDbJTjW8 z;12~D5@?U%VpVZxhqd+`c<0__DTV3t5Z|>$#CryE&_}CC#u76}~ z{QG~K_5a=L{I9I>Ka9P^`OHK%cy%Lr)|yQ|*oPV?*>@DZw|V-F&v4&oZ`v4Vd_b_u zL_qW~RP`(-@W#!LC!x54V%}lYxb=kpc)Q@ltS5YBt2h9L_8zpPD3(?c zlFP^_(nQGS6wn}O19$Ex*rxK8T2y_|(9k9wj+r^{hkF0cGyT5+J??Lh#DD5o`(MR$ z{}2@^8zeVX+T7aG%7p|{^!48{PnDI6=RfADuD&U$=o7+y6ODs%K$NDCqiEEEwxyS~ zvgKVM{G6p7ZA70H?_7>%AvVOlqjdIY+wgELFn#*6;w8M7dH00l>vz_MvNAHhC$B&X zA4u|j$+20_UhomFg+)EjYiVnCE6puW7DKS5s9ng91oz{;k3qXEhY47!z2BdUI7Ynt zN=Kv$T>)P&hTWGDGn$zaUGBn4`f9>R8UJu{3u%~HPs+o>NK$N-P~k>l+tCvp?$?< z@rj1q19fZ)XyfIs{ES)HS7&9RBgL;*XEQO8gZ{(eoR2 zQ8#p;t%Ne==1?3)gshmUKL+J0}7#4!^$QDHB{>s`w{M_pFlzPWqe0{H%o2y>54 zZTHlvk?3s-;EsEubFPs`6Sih3hxc=dL+XhecA+>7iQv!GR7U?Ee=bmd!bpX@!6T+g!vIm01Oc zgU;OWC6#3bjRTSv{m?T0-0($1RJ}3XT+g!OT*A4l)3Kdqj?nhA`w-k&WX2mrH68BC7yq{7x=h zeX62kz8hL|w0YCtG3WABy7B~*RcOU8kcZz&d)rKV+iba0OrA*#EPfGO+|bV0*CufA zD_(ou@Yz1mUcYyTUW;2#zd`o%4`|f>pUmxFY%zkp5&vr0oa&sGJwo5Tn zBA@ST)O593*t(-Z##cgUD)=wR@LlpqHSwH3Y66>u^|VaUu^xW%j!Hs1FqN|kqhly6 zR`*BgpIZ#zI}dhc5ZJ8DLaiNZZ3z)Tx^z(1GAplp7+r?1cHK{C?GM#{a8H-zI%ZUZ z+7{QU1Vaecv9`xj>$Tc=Yq7s`JI{5W?@s7^Tq6Tx`!|x5FJ57XaXPG#Ra=bevdr0f zMh>J?JD_Q^Z!^-^xV80}k3}{Pmg8MuXL1s({i)sU()}>^(L=6#$#Lxx?3U6fUrw~m zh-Wk4F*2}(x5ifE=HHlKZnwwCY~$rQGP)$WmQdRe-S}L7vRU!ohQNcqd&z0-5ZsW` zC|l0BU4d&e;!!rRgug~t)8O9-Dj)JJ8%a-T94<%jq#sX@;?|mmm>T-On3^@j)DZqM zHRL%Ch#dz1i|{Wy>?SDxO9-(;sz1ncY7pUfZY|YwAz?Th#I?K&512Wawx=Lb;_|U8 zp@UoCDh8Nqq;>JDJKu>0M)}yq+P^&siNIVv{D!5cbNJCq_Vr0{HW+Qw%_hw?&Dyj$ z&emYAasCwPie1D$6Vk?X+1_7EJZB*uY{}ym(TwTjJ zzju4$r9BLg?td+fg03H*1B_TU=52bNI41KA3I@ss^5(w-#?%j-jvR9(VMIScUKS6q zrN68m|BgrUUr-&-`OjV*&-ss5$Nzh_-T$tYGpy`?ai#ye!+!)rYkh)#VoFmsb2_{X zar_wRzIY2Zfj*f~bjtW7`I79%{WG16ZM6l(ao(!(CxZL8%A<}`uVt_QLETqJ#nEN^ zCWN2?g1a|_;K3z8AXuXzxVuZ^?(WjKy9I6BCAeF#;O_4J`uk?)y_tFUzI)%g_m4ZQ zUaP38b51Q*7gc-jpX@TPXTbm@Y+8k)=k~ByKMJRipM%?nDhulq#Saf2DKOmJ+@4XT z5n|@VDCCP22qKo}MRMheu~>?u5Okm4h8iIK;deeq#OPER8#c66sVXHZfYJd=@Aiy0 z#3DlL-H(qI94H3(UR)Y0NgE&jE)3R>Xy#SA1o_nyxh71w2LLI(bNxI>)L_siUX8UZ zeQ$G8#{Ab0n|uocapr!b>nUbUjOSOPEM=Jk*zbUGm}W9WGl(l6G->N6qfBv7EK>>>KUA>l7CoEmlU-?P`1l z49HUs;60frTz65*bn1*HW5o-NEsku#BxmxlrLi)#U91chlitKeM`wnd0?OMG%66XJQ>4eTogcnZWbq_GeY9SfQNpR& z_>S(BwUpy;o5$+Xbv~flbt4_rN6OzUu4!z0x30POytW3JI#^q4Q=rAMt>;u8xY&&W zjkUGqOofNu(eoU}9c_vmTKiAhlr6twhLh0)X7vc6e{pwzJZXQ);A!muy<$6&bn1CL zlHOlGytN)PVjq@2Opu?{msdNmT*)`7;y!R2K4G?S^M+k(W&1>;7Aupt)l3CO6XzniBy1|$ z$!Ua(mmK|-TfHA(RlJ}&fMuFnU!G za}Y_a(X~_W0ya0q{_O^C?k%bzxm)^Y6qm!2d(*Ac)mK;5P|lZ^hPcaE$yH;8G=4XBY@4#L{x}7n-&xkW?y%j|7j-6|9P|k6TRKd&d~Ea ztxjjZ>6<=>M1ho+fu$v7w~dR$GTgW?ahwBFtIv_DoK$aOzTO);DRUFdE4|AIi&P{a`%rtu7%=p`T1BwXjM=zQu8=6~J|spX0RzwbfB=^g#{SGP}iO)!vu{>-Y0KtcrE#!DaLJy;=cu4$$wvdwS`M<*Adf; z^D}KvB2p2IMirjdm6J+@fB2!EXvu$>pFomk)}Em+MO3u`D2jTxeFxE%8JdvZ-OFXU zW2x00**f_iIW3CFo{Zq;q+}GAlkg_!3xE}l>c`uXYBz2r5kzsVzNl-?h^cd>H}G&M z#?I@hJvMkpdeCb*HQLkfYR$?5o!_y&U)Yy7bGJpqPNXBa z)rAw_lS;*M3F`Hdq_dxpWE506X^uiAG_W%FpEtGCld$a0Qc}kyjZX7A=9Xg(3Y@jA zqQ{m{FyK&4VuPBu=jDbMZ()J@XtKMd9domH-)HSgEn`1u=~#UfxB3kB_uU+-rE^>< zfn;W)4~Q{K!`P`iV0Kv$kN{x>ZJ|oNh^GzV0_&GxE~NzkTm4vDPW;80I0l04m#kO7 zjVPaP(OyrgJ7?{FK;#YQWvm`5Dp1c?0iT2K!@vRs%k|hc9C9S-W`q#)xB}Q;3khq} zNsR0o%!Z&pz1Xv`8Dcz@^^<%+O)w|#E*X%(3Gv?GjRWLE0G|YDv&lMl{lMEE%d7F5 zZh{p40$V6}K3RZxX+X$GG+9*)*+CLv1ptx{HykY<9YKZY+q^~(+@-p@*i^$zBaa~? z6vr3_qs$V*#QemHIGDNlDIR@Ag=h?nRT@30czIt`7&RJSlxWdb&6WfmeE?)>sV^tQ z{dB;e=r2e4ZB$hwJ4^~4u__WEd3TvSR+pDxy?50jP4->&E9Sj42oNtKDXj^_VZ|k{ zH70MfH+IL_y&l)6Z@}X_}dUVarv^=mpY>kSvj^Kx&j!fgI(ntM`KDt49bb zm>&Z9(*}kZZ08+S@IBvG-!BKU-i?A?{l6Yi{<4fA|3zWr%u8aQVr<}N|9vdzK{sC{ zXRR=#{-jE0rEWCJlkF9{E99f?6k+RNCr(H(y9HQT=he@YO}A)aTJ_I{i)Yz5^NS1C zug;I~p#Q%&u>g&l{3&WNGEYA>g9~aGOf)VjQmYU$bLUXi29uSXk5w+Q{^~h~N}UH@ zsT}b(?v^i`7()czCFlcv->=UNyp9%jACwt&W`YM*W5pPtqlXixL@yaFSHCXlV)v)8 z#^8jRKf3%!Zq$3;e8}h?|7~d%#Qd_2N44!>;L5RGq{Th&^_q;c@fn-}9~6D7wtCgI%z;&6`2 zY+%k84uzewuy+KMXDHz0uqbNVL({yOL*WZ&I?TqYQjZ5=3c*#ZPy?MC}aRw#94aYYeLen;lafEiW$%HD$fVFZyu=XWU3a zGNS8$Q5*=6i@nU(z+t#WxNF`V3lLs1>4jx$4r={$a*TI}Lwu@un(yhgOpb4V(Izb8 zDD!sr*58D`;Bvhw?7>k@_GCNJ&JhCT*5dp|(K`5gB38%t^vh!cs}-`KXEN(?h2Q!$ zGF3k}@oYjFg~3SrY3%E`69iB*bLi zB_bTR!+3cpt?qduL>?7OD$&yX?iEB;Req?dof8y3w2R z_C|H8jep{A4Nd`mLgL-XuhH{Em|Vi4gv-hS<#;BH{i)95Axo|kyH;fOdm?s%1r!2x z;qsJ3u3c^_<6{`lEE#zl z!YFIrjgDymi@B+D@t{B9;CWSV;Y{~!+%&n1T-?6XfKpJe{?3v?CW7rKdUtRNj%esc zQjfFn)0qY&f^n^^>ujr6K0e7$EP7XM_Z}D1`MXsA5kCO=%Y-7E|hjaj8AA|)%N`F3YW zqMjP2lL?_e|l z$GmBFv9bu3?gAa++OY;89i7*4rZwy-fh&lZa)VHjO`;b)Z6%35*-g!T)weTx5CkND zRLFTKR_D&R*Hmhi_^JObnD6CriUMdWfLj9$+hBH`j|X^dHyC{zf@R?7A5@XaV|K=f zU*12@6ar&A2{>SiCb35AceJ(@#UQKJv@IuaCoJSam9M3WK*t!UNW+{Ng%z@*n}CN4x9_#H8}^@J(TFj(Z-);cz_xZ-?}nu3OKwX^==mO zV>q$P5f0Sc{SnYd3rv8&EF-V_Ct$@ozG?l@YgjMcKyqYtvl{zdhN;N7JnjUJxl zs|^Ph$3r_%vb|Z)`$WN`1IstPEICt2R z5X3?-U)n~@($b@m=AYsmZRo{NYwEw3sn5?{!IBM1)pgXQQASl{b+ zR5wF%})Hc*Lvv-U_ z%(G&s&T4Y32uqKjus>#Q$|}+ufJGJ2i5`p{tF{C-oH{ z!h4X^drVd3P07%9!=U=XZOwT`Zpk2iL$$u`I=8t>^PI10Ydu)L`J-4uWB&_+jr8`- z^P7|Q(R7rdDo+_cXf9?4N7kFM;(LZo1x*g2d1T7`+l^2|OS`_d1M4Q9hO|h{h3mlS zmb}a8 z3}>PNV)6L(Y=w1y@7eX2C`+ChwYN4cWH*~`_|C!7y;Fr}kF+NXZ5al*jw!Z>iyhfQ z6z-zLC{~#1q>7gi4HWSeM2!_J1V03OA?R~=l1oC{cb<^`HL3RdGpT+>5%Im?wvSm! zRVc{-DPAIXg15b>KBGo4F$ktI3<`P4@3S(a9l5&l6i6dky!ge$)nte$nog>5-aQ21$xFXNSoET zT;tD*;}YvRD~(WP&qT)@nU9?P$H@&7GQ3c{Ky+v;Us}1~DUvsxmFN*Y8)quN5a8dK zOzzpf!=G@bFkNPe4RQX3pex67U_<}elR>v&n-w|vE-tx&gM{R3tc;3SPAqHn-j>C& zAY*e{MtX9%XvYE~)N{lIWaE%qS=*{Or=rL;5?I5J~eGJpHHr*lH(%&Y%&?NR$&HhpZ*b2L-$ zdiA7!>ezd)etq^=}?bzsF;131~`X7j&B}Tr8}Bd1V9)qA}X2iEh4IDoQTV#s%Y$p%M?&x3JM1N z0Zg&{r%SZF$R{>cPMQ_RRg6V_2E=XDRpliW$rs>o% zvzDZVPq&O8RH0Hby1+YQc5xk7+tku(R7MLrQ7su*fHH>9uET20TAuXFa6sCsC0z@k znIEUunY9Ehs+9?uoVYVp7u#KGR?DR-Lqkw-LDl6#pWy>2p>%6wB)-<7<*q6rjMIGP z{B(O%4XO`U9qLp^nt5~?`kBHDa#6Y1X23RYYcbRxiLZArbvaJ}vkNR&Rc-gGYt6Jc zOOB+Vf#y~1)))lLNm}A{js!r8Di0&-#52$5N};u0EsFX_xS%VQhc0#C%<_d&eC=$@ zwBZp0C|}jBPu*cg>r5%Lmb0Z+=ZGJqsnR^6PC9dZJ`-B&*5aUlgadj~Y3@>&oVjdq zFq9rq*Pf|qsWp=BQ+N6k1o(H5Dn1sZ8C2(&iOsCOkdLWX1S;C0Hz^}-X6sSZma{P- zrgm0TFR-OdJr6)n!_l;i;Ku?t!22d12Rkdnr{Y>=xP$g{#uHW9S>A3--N+~rmCvB%>wxnH z4fki7RE`gQLFHh|+`7Ys3B9(=6l!e`EvhGcM#!M74Cr)*5&P!kJSK!(So^cP5p8>x0(1em& z|Nlaxps?wHil0-aa|KFW(6^)KpMg|WPr-9SVzpm!QhAE6E%Z}A&11c`a4$?T_mO+2 z5Ua#YQg_Zw`7(V`>FO|-G-kH@13Zc%F zZmo@^*V4C8=`g$neN?&FVL&!NZlTf}NvT&Ty*p2cU{_wos4^N*mza5U9vVy`1cj(p zuQRZl^S2lpj|9}il&YR8`PRO)DC!<@f&x??2Gu{#JfA5=)M~e28yq2ns8rp?)D>s6 zE|ijL3tQrij)+0is%|~%Z)P%1XMAfPS{!tbI6x69&4cQ6GskB$5w+?q>;^|jAO_W` ziEPuBR0YKf)e`W4@}CR z=H=C@VASTn5p(=ENK*fNW&caZ9RI(P)W5fE{vQ)_{G(;_Cl+q@e*mRsEJ2eNM1f~= zYBZWLK|jCPd?9NxeUlniRmn``eV0K2{vgfCy{(41Is61r?9Fz@=!slxd5s*B_z`7c`lQme=Ja`mms8d z7dNmk;UW--23xR(>vZf)D~PPp%m98IqlxGbg#bq6^tf?Q;9E#~_TIxM?xToq>?yLw zYEmv#;oDIWN5MHLCitg^sG}_d`BcU%u|s(c)FIJcX_`Rc#}o=ACg9m^?#Bz@*AH=l6mq^?UH_DsKO zKaS_jMs7lA%0m9TPuM@~Avevz{@uyH4g3%0{INusQxM~>@85kW{$bCT@$b+5?f1WJ z|2p)ar*QcE-Fkl;`?u}CGv|-PdKmxjYyb0g{uuqw$Nm$S`46W2xxzpH3ICZn|G}mI z>GYqw_Yd}4gKlNddso!<`narN<)fa8^qMK?ob|L3*Q*wv|8m1koG##I+cStW@}&)1 z&GOeibYN;(L+V z=?|VC;~82O`79h<`wWyT7OXsrjx83Ok1(O<(*0w%x2yN9Hdc#(j>N?7?#dsxNP53E zku{Y{Yc$#1JiE3jl(&?(lQ-|zDvyQ94lDdRK^LC&h2H(=Vi(1u zX`wTI4iy)r%>JL}NujxwPpn21Mf2hhu|FO=Y*G74-JMs)SmxM;^mJ^-RflmzLLcr} z!`R=A%ueewy#IqPDzM~rhNoiuZ@dTp8zcZ6|LPI|j{k}T;D0JPgXN!c@AMGuY;D3` zFW@|wK|Q31{Ocru%1~+|FeyhkoT^IUI^VG1`SK>MkENhgp7mm|@#W3Rn8VVn2g#V- z@yqkqzp*pS5pc@KkRQ-ruqaSDKZo~4U3#pYb#+DWwEH07MTTPp12B@M_6{gwfdC3! zd)UdxW@d7cS25x_;Sp0%U(+!5iN2mcn$rKi#S0MjXMQ1(45#oB&Yr}h=SUP_XTS7XQ08holg@6tlFvA z)4tt3AItT}L)`pC5XSp4PJ9b_->^)k9b$UB*k?GmIDSwwwEwnIfg)|Q%0%DbC#k4v zr?_F|-A~oz@2xot%vL9d>ZNsG-AHG%N6h9|<~gi(m3~Rgmqw2neS4u9MC!GnHkP1Q zq&K}1oywTDIka_a+2PM@2Mz7;-ncH7>jycE8T>L&#QJ`Q6~z7A_YDs9#Z1%~-h=6(c{XPVAi8v0l4&;VYbci+k%ZNtE*PwrJtZgdj1xArhQ+wc zah$T(aZ11Qi&0tMr%oR*vh&cKx1jd0iw_p@eP&I3>ZmE!jFW^Vf{SMh^)i^Ho{Ipsh-|L5sfXjXj5owhDY%7c0IiCZGXMm(oUCnKCi$+aLH4) zjS^ui(~&K&Q|QjBj@gIzIYz2%frsn*OL!xNC?sZ0K2wQOI^6IRyqs$!gF4UEcjc56CkU;a$ON_UUosssJ2cr z*aga(EBNW(xM3Hc(_Wdiw1PTrFJ*qb-s1a`ETM6$F=LsoM!h*cPyvdw-Y1`G6q*Mw zTR6cpRzR5@3LCt+w{((OgSO;u9{8W_4y_o6jGf;ht;GX$hri*q;5YVGI&HqS9Zp{hp@M(rU-bmpC8!g_$4Ue|k{? z!zs0MG?uE;;q*8g;kfx*7tBtb__5K_iLELaTELs*r!<=lm35W^$W)U7wwjT$CrxHF zCm`fxgE@W9Rxp%(Vs!dSEOrS~#)w&60QFU$s2onUGA>CFP?_>B^@1{a6q9aN3U%zQ zNtA=S0Lm9dUaEegFO%w&-1oe(ckiwIg*QfNlJq1aH?q2@VaW|&D*Cw9bI?PN&OWq#bImH zycm)<@W?3xXp(S4rZn71M47RRlY;>kw2=CaP6B{z&g4lWOo$X@`=GGUhYFv+8lRoz zwR!k>Y-7+VEUkK;3M=WI{+`d)oHz?UPc2>oztlAjmo>0X6k|9R z>RMD=s0?&&tpS=?@Oa5sRyK4gH`+R&dbtvqfcKJK`dbR#LyBSDXUWdGZAQawJ!bL6 z4J6-;KxEGAtTC^Cw|jF89J(?F^%@~=E7!H(GYa|z*@kg{J#O}+ybch3(Uex=|Rx&p>AVs(N& zEWgEgfDNTVFP69kJj*o`fYj<@R`;eeP-1vb0{ktS2x8W?4kc%p9)tRfj#|9uBLF^R z$-XXScejKIbHVz4jmX7CeP-Vz8#dCZ5MjAT~XJFf=T zNwDy0!T*VA>R#MykM+gZ;vf$Wc7HTmvlR$<{ z$)81Qqm1~(ZO(P!u=5&VryhmP!!H$B(%(dF7}BM4{EFsn0?Lt10xhTpY&q6mnyySE zXI)`@M{yV|R6bAG?~&M8IJA7!2$H>?;g_jT^RCptJ}hKH^@0(RHV5D2OC3IrSJG^M zQ-9p~W^=tCN%t;2f~yX^Y57bW+=CJ^_u0z3`h$0MS%&21AqQX$7R!dTl+K@CLLQ{d z(%d(Q!8dp~YV)SGwLNmn5i`0gS0NHs zYU5ehkU(tww=>~(oms)26hGU$v3WXb<5SwGw58>R`a6SFgyGFaBWa8p@xU+T3w}Em zn}?{x%w6%zk^W(R?z$F z$Vdm1lVTSxl8D4I>djs7dIse#vXH08h3v47-`!&-U;UYF?=EF(!Pzs7ASdCygq^>nqeeR1@M|9pS!zmsw?fpRBaYkReH5 zgI+pnoGKY5Qjz3Ivhek4??c!ZN9vHCy4j{|EPgKlVy}3DiSCpm$ym-_VaeQRFWtph z^@{<-Sb(-Go5`oQ-)e_+C0p=HFS>MW1Hs^i>?jl0m|D&Ik}`Tpb~9a&LLpt8A{qez z6Y?cThzwmE_7Bg{zig%n#f8tO(HO?{YmNo$T*CuN4zH1_?))?_mA`_>)E}q4pC9)T z4Gsj_$5$Y&z7|(b^sa(gxW@G^g0EVuCJFo3M|yzOvu6{M3ay4Cnrl3pZ0v4^xUDm@ zg#GU43$sj;)kOOjiTx8{1$@^2`UZ<2Y5Gk zFQS*Lwy6(U%<~@;SH`o^5?oDVoZ>5ZjMtRYcQKo0Yx?Q?3EuRlBx-wRNAbAv&I*sA zcD#nnC!S1?h2NNG;#2CCrAK(&)>iYZY<5-L2c2yHo`;LEU=`70s2b=aM^XNiC8^Y8 z99tqg4_Wh~oQICK94!QvrU?t@37SNXJFG4KpZyrG%!t|J zhpmnd2N^bDk`{FB*)7Zkm8|Gy#P96&N)|%t8c~?|g%h#sR}mtEn9V95s{7QKcViH`kI~C=23p86*E*+C_a?%7`mZij-Td!2z`B;;Oq%YzsMAfC$t+jcY~E zX2)tS^_f!VFU=s=T;?DP`p)l65k-kw)K_Ab>FzC(cYSc14O8dmM4qS2B-2ivS1{PPG~stPxMRAgTE$ zP>~A!G4t=A3nmuPZ2Wx*qBBQoyMt$7byVT&> zHfWFc4@I!Ps9(7EzRZN)7{OOwPh})-vB33NE2TXfR3nLU=e5P%-h`SD<9{;ZkgcBFrQGt zMfV_kY!`1ZpoHkehXk_|Gi)u^K9pN@H(j6I+8G?cGhpT@fQr>;OHPcIp(dy!+m+S% zbh84A$z#3+zg?0A`%w(44STo9^uT;j%7wb7`vHkKFL|NNj`wB&-AvevP4Bs899F0N z#gQ$}#JAR1Pog@Uurl$RDFerdA6R=9_8T8Oa@|XNhBZIAVV@D75GK$O1$o8lQw~@@ zsM5rYCZ3pId%Ex7L6QalOfA|@ocsg+LtwLU zeVfN~&58Fqyd;bTJqjA*3%WhB+xFLl0HMjtzOnZtW^i}K)VvVH9a#q^C2<_bradN4Q*zJ*iC0N}DF70j`NYPGW0;zrwar zVR(n7xWpOY?72R-GRZ`@Q_%P&GS_)A)wVPQJh&+o_gi|(XKCv!hyt?dKpQ){lA7345(mV6axXEN7hsV2jQ4RHdbHQL%ss!4E8rNKxm(!AiUmJWkcaI+G{40&K z8;-=hrEUdd2XtYiJHqbXXR;0VswDtt~a>BY{H+ZFm?AEA*aiiM96AMC4 zdxBaaTAWkrSE4YUw`jJdCAQ(c>oPD2NYjP!wO0|+T|4}YUE6_Lg_=rC{rMQ=nO#JM zuh^e;!`b6MMU(` z^*YPQu2}iR1-rGYdmc#3GIG?y)@S~?g?TkjMkcd@574JuBJj!X$KhTlv+W(le4d$> zu}1yx>!`8C<)JUT_tY2>4rU^hoU@&DnSBnnl{X$!H7#7ZaZw1*M1vVxP)7giso17O zgkz6IEiutq9 zM+-F=R9#9j#Qpv*Ft^_6kCr(!2@+eQW#5Zc`VFU?Lgu9VPq=w&ER!$oUQuOsWxYqt zq?3?}lvhp@;FDlB?_7Hd@KH=Y`B2H5ZML)6>% zcH&qj$im=6?m~zJo$?@elpAJc6ID)!*V{9< zmt>Ex72wxSFJ|k%Y%X6#k{SM3I=dTEmX5p0cc;bSa(V^+{yJ>(Mbc+#O3Z!etXPt( zEf|AsP>6>F7T_=i7Nov|s@FPV2GM^$t8Crgf^Kvkf#+-RqLWrLv|{kPdDppwX%*0D zRBEY==zzfR6ERxayc9Afh1J8v($s>iE@B?-({@`kFLxW8MA1OfN~3-nF`=12lN+Kz zZI|enj=#mE_mxv^LBka2i~WPtbU5x z2U4827UB|nMF0Lo4_afgMjE6lUy5`n)AJYmP^){a1!EFzJF;IlMJkDFW;b?WhZ{S* z45!I4_1MiE6uPoFKKEwb^l2uB3A}F=$qpuK5?r;YFx$+MUT5btvY6OhZ|bs3AC7NM z)sHuzvWB)Gm|B0mxD6LF0^7@RobeV^oxg7vx>F8^_~t`m%Qe>w?gtU)5tK+a`(+yD z>oY?(%|qA7ir<^_<>*PNAxw>(G@~RbD$Kl}*P7jH1^ERi!8bkjm*;p%ZgFCLtOsWL{%B6ixCpVaxq~1X~RiGq%GE=PIBVV;)@gLZMI>#M**P&`W{U+(0 z-=f`+Ly4Q<4LV@L#X$5>T|vy5x&#Jh zp8HJA4(bXvt=L!Lvw%&HkC<;gx!_6ZB5{8Wh1#oC`lQ~sr z_pW2!pupN)cVir$;^8&^10RrP;NW6OvDUy_^zh008b4iYnIDDvyyzKLlG5grA~jx)j|lhcNuC|gMmS*YGIc-IP;TG4H8UHYOgc~FDauA z0|tr!S4v(zF!pedvO17Du`aE15G%yngX@*9wC!tNDh@7_u!4m8POK0$13It{SPCv( zy=j1arn#`050>8C;y9JfEQTuukOXjgCr_E?)Mt6yEtJ#Qd&UAY-vlfGeRdPAy?xTR zegE69iu<;kLS2-h>I#tj*7O1;59NqY9F7$EXn|;ufJmAJxBat5&=|vVH^*l(qz^SX zhZAVF9Fa9ZSQ+}?h-(YgC~2O7*!L%e*@L;H!X+ZVvB25#bl0zTQN%6oKOHud%Zo&_ zBPbnIqu?y$)XNUM7MEGN^waT>!U(ghM&)Qw6IyqTD9%LNE5}(o?5&nYB*l9ochlUrElGx44XQ6H?5!XJM{=XroEZWW!ew0QTU&J zxEtp#*uurqSgKi<8cO@&I!+{NTOywOl?tv<)VY-Vz;}zP`n5$`cFo5#t<)2GR`|Eg zTGfkWs3Z`02nJcbHvxrqTfxU%;CJ)e7xsX-9yT7##u?}u($i&O)0JAkLj^rl_9tjs z#`;}j_d?aYt>e-Px9J`mfn8%?V%4|DmrOF(E$r1vWdxt?p*CWc9=p%|94yTP5V$3i z&{X}SnXc_oQK!RoJC2qg+g)y46J=vuTuO`-L7cwIiae zdO;ui9}*2wNEx4nnhC5C&iC`K?xXLI$PEDlqP|i~&W8n!O30R4N7apDVD~Nvt$2y& zkFC)B-+}qDB`E;(fqVR_Xe778mF-pVKvbgVw4>)d-x`)0Qd|Dm!&jJsU6) znLi7qB838PG7yofw)BJ#NrUzYn)c~K#Am<-!ET64sXf6Wahw%k>O_P_3(?Q!=w9Xf zY`-ye^+gvu=W`KnPNW$?;~e@HO!m~CTaQ-XBu`5t%SON$wQJ26 zv>AeKgWC8HM1T>lHpAZ9w+kC~1+5pE+nz$bfxY~T8s8&NO`K=Um6h72oGAzMrFE>V zH;=D5R2K}=LhfYSDvI*mx8b8amibl<&+|JLKRl~6&23p$&5%{s3nrEsS+tOSvWpri zn;(T)TBgp_!VwDMdmw{|H8@9TvT`~JLmG~){W#`FpoJenzwq10x&%dL2x0@bke%Nq zj_d(FJnpLZRIAU$&rie`y!FcLw&cj|5c6##L<3g10|*oN$J$AxPEp;j5&r(ZLoX=?&Bmm?A03&9R*XiO33QwZuUGszZMazwI!0hQ5qZyf zycO*VfeZW_E#-c9``y6wJ<3C}0C*Tj`|#;<>(ldtwf11Xp3lY82yP@_I}(N{X9Q(j zvAt316fRsu)g#4VArqt6I7#(D@XK&pw(<*y7~WEJp1p+mZ~k*weWykqIergIZfIAY zS^mP2QuzV@_91jiU|to}ZQyZ#rftY8wtrJn8`ltpayPQ)P?K$!`!|PaVP0kjIv6&j zi-0J3oW5c~hS8g*x?@EfeQOH2H;D9G+2$9wsN$O5vR2HoY3WDQ9N4PTpu1m<2;{=y zdW{I5w6vR0>qlHi@>DvqV+A7g^X0zdX=EFG&t+9TdS%F!F}#VCZ;NfjH!4GSdq7G` znuQZ~XF-dOqM1QD&!LP{u$A)~Cti5>i7gV_E!Lz|Fh+{I@CEK$(^K~DktTH_lTgwP z+AHHioG%D*DvUU`$qE!WsLq;V9AR=L8F+7PVe6h1BICJ(|IC}@?Odm3mScw89Ed|c z$nRN{f1{5dBv#M=4v8K!oOG(6upZxWJU+1^NMu9WdmnW-Z(ADepOeSZ02BPZzOJ#_ zYrW|dzAI*qAuD;nfVYMv*`5|kj|B-Sd$0S9m}yOzVL|gALdU+b9hJSfS5%le3T8EQ z+hxRkF;iP-nr|yW6cZ{zN^G`v#7G*hOU8W<90yHuoE0f?p|?bE0phomcn5OlB2~cY z#mP|9t|ol}F2|_4bGf3P*Zyh#*464ciW{Py%-&(lX>5YYfu@W zIw$hj;HUTNFJB<2)Oo<&lU0iM_soGWxZSQP(a^8``r_rU9wy%$WS`(^KjBRO8#S%} z1~VLve|0k)j{jdXoPUK@IP9#SSpR`|ta)Ot-iU!xfF&RLNs?I-ktz=3C#C=QLCPem zBzFYekNHWbG`gxni244f%mrB0SW^N|nok)qqBr}$+sc-lMiV)%ui}jW#HOOT6U!w3 zzQ6DPU}GATfm^_9Fvz;x>K#r>a6g~sRc`N8u7LAEe`F2Zu_H)!}7GR z4=E3ooIMX8$|(P+QFWBDA9kCjI-4rXL(*wJm*z$N3>}@~F%puN=Eqb;<7yqjreldn z$*8z_%g&Cv;T5cn4LPS$_O#(PuNVH*cgnYpmJ@LX#X8rJvx%(P&zd*)4}JR2g9M8C zgColNBJmCvL)WdnUrS1w!Px2%;+VoH+IQf$xEk zs3lohyg>`Vy1OjW)}CKpH923?g=|(} zB{5bkev8n1sw40-+uKO=UqVlyPYPr#F9ACsFOC5*i{Qwb7M(k#vO7315{}@}po=D> zm3TesPLS&&5~qExb5A+&7db}DdIQml@J6GGb=qY%7E;-SJ-r2CLUW#d2UTD zL=;pO(`!Gs0KAfpzCKDxxbh=sT+ZH(o_AR>*uO> zBwEDd+c%#1P9v{jEb#nPTh!ua+Rj(Eb^57-V#CtJkxE6+^gG`ePv%obB}Q)a&GYkw zz11D?E(dHY^jLWDq-C+(ElPg! z)Z1B*Xl#~vBG&_#tIxYN#w6B?v8DEuLYmL9``f=(?x}WjEmpV|Oh#!WIJd-`;FcZ)nP{n~$y;P>K>+mn5J!XVG-Me~7x( z$03b;n8$5U>=Ci{{#Y2wftgD>V-qo1aTjw+XDTZ}; z`67(X&SWF8CQB>b_C0js^A;s+Fx->wc8Z{FlIP+4(oEmL3w!a zwR8cK$shwebbl3MWoVBqNnmuMs7tC?HIJ(C%s4&hndnicW7czW5t#SAN2YguJiKz* z-@7_q=|?;4GP|UUgliY^r%DuDzSXm5%P{ynlw&eYcyvAy6YD_saxuno-CT{U5)UYW zt7YPB_VW4#`c%GTMxJ25t|*78W}v4$^?@z|PhGw_Ax=oI(2b^Rj~Ci;8FPU|I$O^Y zk?vP=eA*!rfHa9#nN-D(*`buDm&GoS?v``GhJxEeanZ@aLt;(h3VWmEt92I+W`d?n zlIqZLq^!)CLr2E^=CnEXK~I5?NEF)3W7=VUs$3cWChrk>sc5^N>-nQz@0(1SJeEr} z&Gy}6+t00;hS$e-jv<=-JbuF|u9^HwOBu~abI2KOjSti)-7FY+fqbtV~~hY)3J@2B0ri_V{ll5dSuUkjxx7W#TN$ z0Mv&+r)Gq=LUxu0fE+UkwuVX&*x~a+&9NV2D4`3r#eYDm!?x!b~rSDqbq3` zYui2&^htEq?|cn8N(icvm77>xIqvYm^$(U%HZ)ZXj+4s-!wh?uQPUH=ZMc1OStDeu zabtft)xn#pZV=#h@8#}*LuyE*-ys!<>6mz4yNf_`5B}(*QE;p1J|)*JT%G5$QNb!(Ku2G0QTz^fsZ%LATcmm+sSH7~w3>1LGK?8Msl6B^ z+g^0VsJODMZ8(qs0Rkkr6M}n!I|O$P?(S5Ay95jF?(Po3DI`E}3lQAh zp|C>guXIoMbkEm4|2OmYzt+sZ-nH(c>efB7@45T#v(M%{`{`F*TD5Xsk-{V|OVv=( z)@g;xbT-w4WQBt$U7t&WWE-8+)yt$~9KDNUz=lO2SYkA>w!?pskIMPlv*+Uc{#?GG zrKl*C^V`QKWQ|hwY+tR37L9Q`-qhTU#Z=v43v1Tw(zRojVp&S>$(JAmCSR26WbtqIQPI6^pRKTvvQPW+jiY*% zqW!K&o`wX+L!HM8y*Wy93#P4!kR;N>IQj_ZqpSpH7`7S8qc(XyBp8Hf6w~%g7{rCB zbI;1}yK-iB=kZsQn%P}7LLuF2^LwS}*jFow5(Aw4UP>IwCaHUTtPtk}nYm>V0J$R(txf@h6FlQKXgQvr(9WKkIp9Dz$w2 zPOXALnu*Q?c$*(eq&;F zLmX=z8InY#k-e2H;f&MxeZkM(0A>0Z(5Dxj7v@CUGf$;gz1UmCduCSg?DGKY9BcUL zo@uq1X1Fy=Yz&s{CuLX**yFC60$tml#ODdeDPqt294^_!JiQM48uZjN!HUt19h&}| zt-PiM=ey5ZkC{(>=16wDqQCn)42$+DY+%|2)Kp6%<(v3R@oDq!H!KYpVrx8ItcDZe z58t3CP50fGlvcI%UWVVA^Hm}*cI}^>@gAlfoShjvi_(hTK%|>Bj!pe;#WLHgom_>! zOyWp-1|l?|Ip&VEH<%qx8S?!YSM@0{>7`$7%YX=|ay29YjQP~+7LkrViId~Gvpx56 z^bcX;Iw#&>X4vyHaKBL|;UGw_P-3&U9WKhz3xLeSdL&zXhHtpq@FcuIy!(PdAD`UC zM<*Jii7mjZ3?pK272)oC<{xic!)37hD#g4ugH)4nxVk(up^V#~$FX^o=`j3trv8tP{88X!`ta7T`DOQg1; zeC!X(48RF+LJiyC*Ij6@RLzXgmI-l*EJ{Cm7~Lyhq3Z-$f+IZ zE+`57)c)CsmTO<7MFztLott~NqNDdI9W&CTeS+-G`WM3-dU^^PMJ37IXbJBx{pm!j zWQ_9aVVK2%$~ICq3onz>bPL*z7j3L*z>xFOSgBX@bX^UHPfNv~yTecJ$}5n>;{EM* zCBA=AeUwL(Kbr^&{aW2smH^J6#|<0Wd%0NJfjpm4^(t3^IZ-EYi3_D_XmhQ!-(Fl> zHO4buLMpPU@v-m2fdq}g2OUg#;_@ex(_i$P%68sqQ9Yf^%;KLe z6jYy=R5vsnq0xe2z2-0nu0&vA6R9u9Vo1NL;g-0fJ?W|tR9}MO=iA$kW%yoeKHPL| zx1K_w>ZH5tCMl`VN|zzSluBB*umSbj@Kzl=djlK=_)iegzb5{;t0)QYkKhRszpBq^ zRWiIqS8?Qr)R!5?Mqh2_wp-~h{5PGf{jJmUC0*1=%ZO2W2gE3zX*-n%YI`&}RA6zz z3B-j)(Z<9P24F;l8zV|PIn$ObE&TKrw#8sGQtJvKNDlr#st z!rNrhvv@`SxoF_X2md&pEp(tm>J6k$H}HRNukM|&ZO3>YCal+5W40}3f<3j0O9_kk|CKm zT7ZBlMk|;34#j;nG_R`yW=s3i;3b;ast^(hYcd)>&)HsbOl8?JS#{ACe-wW#RmBW~ z!0BMohUW<7uNLh;p5*$=LS?pLGWZY)z|{DbQS}KAH@)3lT74pkdKiDnfmIy3HP-8D z8Oq1!`no4O&X%Rw$KFsVWQkit$q8H&KG~KEZIn>;v4N|=6xn_k{xy9js{~za_|Pcf z(QAXYmv__#v0&)ql~zB5_aZZhHc@eCuV*ljG|FMMa+w@ATHTR;jf7Qrr@OMdIbIRY z_DN|zYEw5j?@mxZv?|cnLtQlb2*ux|IRr{@WtPAr@Rc_kN+cyUQ?-1|_5xcVc(#cD z`E;eI`i+i|B?i8Lzr$RWI;g~xhHpvX^mDk6-$~3UiK(Ya;x6Pv1*67bTp zLJ6_wtw?r>8P@IglQH{;Y->B!xzn?polCXb$X$;^{3&n));_AlvAQxdOU#5h z97k2vP|PF9`0${(X6mFT=g;^gpDKkIA4=+tED>)ZhCZ<$CsrH0dRe9yQ1yg4SOB_j z8_4vLMCdbMZxp@8L>ut_x{E?WJ-*5=QQ>vVaK%TNz5WFaYuj}!3{^|a1nHDyZltGl z?m6Pq`BRE7ITg7afp$LdfhDLDFX#*b1ec@5x_bNt21>mMB;8T8<# z<%BsH2V-3{Pv2Cj6X(b7&(1W-qE56gUEmbk>DsH@WSAc0tp_ZuYhX{m#1GgFcs+%Y zljrf}hSj~PmV!c~cmk81!YY*grYa|m?=(YPB5Vl5sM=xHolYkYA48Ru#oys=@?m0u zBV`qxf6tj3|CySovV4tWOR28-yq273@l}%eY*;S4b6!+lY1Y!3%EgVy*YODoSwXbL|ae#2r@@51h!VvX0D<(>wJjDKXb03h%9LU@XD5D@xmi zzIeO9_bL=&-p!WkyEab(McF}*Oh?3X?6%FL(0$gUn3f`ff}4OCcSOGEDtAOY(XSl4 zSl_h1ik`mC_nHq|+zJ=E;a2c=*RkiPCv{*uYeAmj4T(VyZ;bCu<%kF>fx*(KIXm=e z@0aM|0U1bAh+`h()3%PLS8j~t4vlZ#mDj#82w+RGXe+^Ar{l3$`tVWy;K>t?16G54 zXDq6#i>A*bR8%3&{J@x){*|&mnUE+oFZVtzRZHH0S37X^c%%I5oC$AZz;w%?=Hv|;r zqbf@Ua@UM(VjK$;6z|8qA#V2$@w$@=b|R;r3!_@ttS2cxG?&2%(dLak;(FT(&OzmH z5&1@Smvr}ta#c0U+hy9J)Mo32y?Rd}6e3F&HGFjtG~QzUAfEco6bKRVl=Tt>X1*?= zso?N)U{O1gJ=z8lBm&;uxED?i7RfQhS;gBwfM<;)(_JO|jV?(GwojmxL83k4bL0$^ z@9I=LW#<6YTocLVti@4NZ4T(Pbv|)OG*q9|^uDdSz4MHDw~Fw-82~54{xHWdzd-iz z%0K%K{+92S(pAl+Ur?`>>$!JSf}-{@>-}5l(;UD;UoH-lSgEDlJGtyUZG-;OyP1cA z0FaL8T$#%I*vmobibE_r7{_}?s;@p@Mnu8LCjCk#V3{S^8AZ{Ci{@vo-oV&#Og$^4C3=FJ-}D9wy8z#3qu{NBLG@w5*tgwS$S>OC~XE0|yfk6C;4J2@^X>;?I2<(oCyL3oN zBPcyk3Y##=96qUw3RiYufxkT=4_B7R8$Pq{L8HH2&1_2FYxwgql=Q~v4fZd`-+!m? z4eL?=`2Z3uXQBzzMzy(Dr}n6x#Lw zRllY~f3E_S?bo6B*Kh5AB`mMeB`_uf5R{X=L{)wo+KH`5EhyTft z{;9V7>8<@<0sci%|K#oXjh6mtzyJFp{6+8VpFFbveG&fHZ2qZ2{_l(Mw@K~q=KXIo z!o_iKn{DaG`YA6q;&NgnRhxCAG--l18g1*g^o2c>$}xEY zyC~&FZ{YvHC;JQ1nEm9^{8#&A|7sdDw%4!!1AQ{6RKN4dIR4Qmt9VR`DL8J588U=K zS^I25BH^toyl!d)+$*o_1pyQUBX8WB>R{Tox^XjR zdF+VB2K%I9*_nm^Ay?L~DV@f$g>f9-{?V~B2F=~|54pO2I^}lQ`7;53T>7_a{VLDz zzMv9u|FLXugHVb7P`2hQVd$lQtJWV5xej!E{;i?=_iFt+1OB@a`!#*pk3slnkLk~A z{h>1a^8x?!q5Ef#>CbEZdKbU{@^9+uU#;Q)#?V2Z#lKPO&o%tt8M;5UEB~g3|2w++ zzs3RQhlTuK$PTVk(g<_Xa2p0fypxR0%>wovSjSo zNG~Pn%GmCm3~6K22L%R4O<0tJ=bPb|u+@Xe)=e!<+ZD8F8L*JgS!nEe(cuBeWydn6 zcB4=di295AGrLn)MiyumrSd8{ez#@?S~W|pi*n6;ZA}+2H6pZ$!rGfeI+D_P_i$4q zKnwW8rU`#4Jbr~p*qTvd`hqj>W(bBBA44FH+jBsDPoVf@0?#+vy zeC}$#+|8QWy+1$$3%I9GcNXHiXc^t#3|<|5fXuw-yqgj@@`9Y>1Rg`lnYVG^mOg7_X6iwU zl-KeD)5u-s4Lm8NPCj>n30s5zuSl7Hlb2jIm4m`6-gUA@HIakMip`a$Q&V(lq)Iii z!KiF%?U39m-i2pSlV~ZTR@JY8piE`u5Y_6&wWSxDv);BQA-SQYY;*IF+ltH8y;Dz zLaU5cQRCt0hNQBaQ|tsbrlnW4TVJ%Sr#)PdM^g|0cA zO>=Ce5%%EHPVQUvyG;D#EuVO133hXP>vt zuIQxh=@967e`O#-d_O;P6F>go%xyn7nMYlu`{oFsoXnRWp`6S* z+YAg=&ObPrWuctRPT>upvc**>Co{(nPUa6w13$=|rOMKOa5DF~(ET8Drmb=M_!B2H z-OrrNG(T}NH=O-Kw){io$bV)8|EDy~7|>Mtv{0Jnp1;wBACp2E#E;Ox`Zk%)E%Q7_ zj`R99u~)XWQ~Z_{RUK`{%JRzi z#87>kAm`s2*{aW-?tiz$(5npwl#`Qag_Dw#kE{q<+4yY9WhP~CKj=J$o(UW9;fMC* zxN$z2zRY>s{*Wd)4M2RDa#x{^`@F2NR_#CAz`|qqvqdXJh z4EtgZzqPt`Lnsx!a*w76TQH^h1qVuWt8_*I^(}4u!F!JL49oV1A1em>{bcg(6V};y z{PbBfVv0_waUD`Jj5a4|Xl`aTmnv@3SBr+q z;Gn|%+}Xd`&kera3*n4P4!7kCLSaTPAJ$SY0B@vI(BzHKF;6CHi#h+%jmkOV#2=2O zkW`7H)83)}=aA1We8F=zxl{D!GwC(iQiu|wOscKZ`sfwYE^Yl>yx(Wz&$q5cvL%thj(}@pTRP$ZH3N@^-s03JR`# zYFf{PoIK;*_;tNO^;rI0xeHM!Ed;~}2;;jXHu48X_AfyCas0O<{W$(rq~A}AyT5Ih z_>WoWK?V8ELXY+J8`j@h*>pq!OC8K0J>#Nh;R8|dFN*yo$o++wDH?9X0{x4^uA0xr zm+R&Iw6jz5YUTHlLGIJw%GI>X*?l0SM76#|an7>C64*I*Jng~ZW8}%pCyBe+|==ot$h3Q+>_zqVmE9#4kwUBl@hv39~XRy%v669@ZkIihcwe%paT! zpKFgzcNw#?C3M2DIid{nMjILrSl7>T3|Q}9vdxD$S{QhZu(H@B;ZByshwyK}!bCnM z&hl=0Y-uiA4x4OhX|cF_dGpwW4Y<60(dgt%sI`JHA7Wx)YbsSK%orstl76VIIt`;I zE^p>YN%ReTucVt`THi;;MLohw3q-qmOiab8Z|mlvPJ`ooHXHSbVtaH9!RJB# ztT^p*0AkQ(VXJ4D)I`lN})nM=GaGn!CLL_G#l}EkuXwfPSWx<&1T#7kYdv-4ktr+#hwsIZE zZ1wq5eVhKg8PuHX#F_^cY|jb=CDg{2rjT@TRKEy0Iux$h(39rXwur?Bq{&_XCT%He%52y%C=RSX(M6@FiLBCCTni?doXJGCRi`at8EF4^Z++;S|Z8 z0mr^dO+?N~zXU|-{`M$5K+^HUbza`6iuVEQ#;iI$dS)7kYcFU+;KZ5{KctI_&Qt6@ z>dxmF+C&`)@4_1?vlXOq;SHNP1Olun`|%5S39m--ZWKTWNbobGhN%fNH$asqDoUiG ziYJemW_R^g+}W`~!su@a+S)q?#NEKalVJ=$zPp41SUQrMUHemE+uJm`@nIWVdYN0? zvF(-2BS=-U7?>Nm+=FvWz@(r(Rh@iCJ7fB*KU@JnHfZ!Zakb-eVk0VPSyN$e{cUC^ z5Ew!1$}zP5IHyaA1Or?MI)BhvqurBDJsI$RfmmIxQWOrwhek6j2%^Rt62?i}{^ zmt$amDw-Q+V;H1r>#>!iX&3au5u``1!V88!-T~$@9-F& zgvSjwGwCXOS9o>36~}{j_wDT%h|h|WLB`|w35fvMdQxCtKeqeK+tS*$cZ-9N5M(WIp~EEEo-SHqvKm-@GPl_o{d*e0 zF4jgOe>+IzB>U))hXL$camPRca4T|86emtqWEf%c9NINl3Vp)9E$)5}+~`Q+C%Ye; zpXf^?mV|t-g`e2ojO8Bz$us3;R_+WJP{etW_8oYcee|9&IgdATj$_Vh~SUE zj)2q2Jh`X}<+{g+z8)W|P^tWS9^!`t8arJqRUY6qiG+oI?dHFgX|D<5;adSJUls{w z(z+X+&5T}SX359%4f^MtPt}Pdx>37VUT0S(JC>W{9o4X-?Duea_B`ZarPSH|dQ#wpDCE0Q zDlvL>u_VdpXWoHv;la?%ybSvX%WKSRd6Bz)%Hh&N-o3GH0%vMrk}T?b<@k0fz5L1v zwcS#X3YgaO@2B#OQw;=D99nsWg(q6Fmca1>Fc9zCJr$kfyDJ~gYVm&WH#GNX?A&!& z3+$0fAdnEaBLg($%SSsVkmZyK_?|tG%<%2x-78c-vHo+n8v-NC8M}TZV<%@NLB+!^N-=1eA zf<>fldNMxx{k>rjz8-0=m0H55y22@iBV^FGP2`>TUS##{os_mZP2ohqixXy6bytnN zO3g1VMcLEEns`(MnpH8j=lzw{BwJJqms*vuj#Kl3O@T$iYS!aL4zVCTwXeW>dX#g5 z4FPo$pNmT$G&S*+cCNMideTM!O7hfYdo6Q8^P7TZhN5N~Boa4Fx19^8JsZS9Yw@rr zG>xQ;EqLc0CaKvAP916GMaXgU&yshY%*l@8q=d@Lv-++3$v%@RTa#Hk(U^`IjMKUE z&-sV1Q|^2d`j)&J*^sr~07oe6+E;ASNJwh~BM<0gba=t63+gvpY?+anLrc_O7l&Lw zx*&D{zJ54`btv1Jl=LPDec~*56>~Ywp=+`x0Z9Yh3Rttu=?Y!F%O33zo^YSu?!#P6 zi*$@c@hn)=(IdQJ$DxYjQ#cm*#>WWim+5JFhCqnzr%7v0CwP@KX11EW#-$e49R&&l zL+c=TGAkbihnV3X?$c85zR96l85Odmv2 zrtpzJdDk)cT=;k^$4f#*e$cib9L2(;lKw`v77>D zJPBrE;Fcx`yu2Z_y08xiEN^B0Zsv-lbmq{|rc!hAosMH!ga^=eg83_^I_ zQaUK{wWZB^0!1n$kP64sc4aiuuOd8u_r;G%-kcgWLk7eI>+QC(T%AnC=DB#s?vlrV zn!yX|w=NFq5bt(BVknj5Am88kJhs0dVnl`F$z1pi!`HcjLB;~hkd$D~WBi0Xd$dM4 zGAJylgK0FPbVK4obeiXE%uEFGC`VpfM9G%~59&5)?uZuha_z)&Ax4ULgC^@O*s?ns zYLmh<75ET$?7x$)8kzOE6aVD$tTdH_0!NG5*LG{`QNJs81zmZ{-Nq;;BJt8XXZls@ zgSV@M+n06wa;Xp=7E$)gbaS64lApvwN8A7ccSFo0!1c6|U2Ti1-fx1@OA=PE7}R6{ zadeJtz*rvv6%& zhpN4Du!!CpP(Sc1=?v*!_0H~xox;ARre@Au62*ZYq7FGGira<>D*MCJ)8xA?S}#04 z6p|-xEm~FyO%~~%pTZUSQhyp)4Bku;VoVk=kA?M1wt>@dN*_tiFf z>;xBNC0-T{KJ&f)jfn~AS=&;qlr2nzE%)~)g$tL03Xo=Unv%a_zSUU zb?$K__@$S>(MDMmBuuBF)Nb)XPAcbFF>{Y z6y^ADj)imntEiU$iZBiH?}Op#h3-K|=TurAd0`hWz!h%;T@H zLvP_!9<8P(^R^38qpNX?W}aW0MsvX}y{6T^Ivm1v1jurEOgXwG|qw2}9@hwSmb{8|q=K{$^~M4BP2UOBwGa z`bm0M;p=Oqskqs#_t2DYA%XLUbIbFKOLGg4T&v3H8HeyLxQBO6qFU8pnLD7F&4=T9 z0|A(VvV8FrQQ@i9a+RYbMGk4VqaIY^K?uYR$OQ)7pFbP2;}z5!y?$(HAWo1dS*VB) zlzqg8!W!{jp<&|$EF5i~zYgX`Z%5L3qHMwW{`i>tVNNx1D_jH~oWGcELny{lznl6* zVP0@OB+e~#5+`J7NsagD$QH16iu+ZVoUr*>M&?aKo9#CH*N&Y2*KFCudx=lyUCWNq z1#N^`)XJFEbsoMRu20(MukY^eo}R9f#s{ji+F>DOWEL=28D^xX;1VBTnIL8zfOt=< z#*a`%jx!&m-*G?qMaw*n4rrNoIp8L2_Q899XgjNh^Q`$zzDx?H;OxyLh*%Jk=2C3z zA@NuSRsu!=W=L@Cn8)GRb}m5{jL3#5NY`?3Cm@kYhw>Uu0Zs(If^oz%sNxVMbhd~< z1tk5%CXM+V0uOfPk}z3$h>-Asd|zLHg-yy(=+D=&vF5IaMsu!#LHy$I#DcJ|vO8!n zJNCN5Ow}1*)YWKGo2)~0K*ua6AQx`}c|?7CXb7apcny`fHd%@C2Eq?Mc=ia69PT~d ziR;xP$dG?cWlMUu&!Jx6>9+HP8G6(U!4u3At*t`wAh&_(2Vla3-=iEKNJmpObD=U? z$7RqNWabQF2yTFVdHt9wg9z$XK-vGe8^gSrAjbvXquxZ^0#00IruU6G4W;2z&ss=K zz-3PaA6z!p4xZNmB?}Wmc{sCfA^mrff|*+g7YGkH$bOg0I(p19K4%Tg7P62WFQqZF zYhMV);jvTMuzayej*}DeCdoH>bg-J&`-jDzN~TfNbOPzXQ&J16L^%ecOjgkBA=;+* z+EPstrHZG*Jq3q=>&|i=1ce&tJY$t5ODA-*SQ2Lkmz^z(340w!IC?2f(XG6ZI^&-hFT6ImT86s8 zWZTWIQj1LV!?zL=ndJ`>C&u1Jk^-~{OX9cS@A6_`hZ|q;+wWWNDPs^J~sAV2Qw{=jDwg#TUR`%z13$1I7Gp z#MJ@vBVfAhEjoYj?y}2r{}Q+>PiSH!$8&Rmip4YG>aE9ZtCPf#nG!*WGr!U#&`l3% zIS&uBo|(FbK%i+?Zwl=+GJU$~LsOJE)6#XnTxI|X?_4#m_$^#oLV=-HCJ4l2ak?x5 z8#GVn<_Iu5-6p=-Ww|zhbWTsiNols8k_l$^0weX(n>dDIL)OAc;J}HXZ#c*|mowYh zr;{|ITqNkA%Ion^4D-7hM%xb~LGYgV0v30H(d{E^V!9$bdd$7Yj`Z@6x9LhQ6m#V9 zfZLU>c2DcPl=;{}cosRfD~@k3U5EpjdGd?ZXzCjC%l%-SfAm5EW=V#@Q&ns6r9nY0t^Aje) z&D0<}Uc;p3{|I8L>}@b(>riI$K7EmoFb52UM{B^Ye;g1J`O35TdUeuM{SG!gl9uno zp6{C92$TM;#6%$_|MwfKj?44R8}kWZUg+_nc2bmMP0>Z;I91l+ZCdH1)QigeH&?mD zIiUVEKbGZM1N?m}vn|)Y0OtF1VP1#U+A;!6yHxvI;u6pk{Qemnm~g@a#Z3yC$7eu5knT|fp~Pe%THEOZs;b1JIs+o%R?h^EC}>((mj0%4YoRYrhdRh<^4WRwNkggm_~AJHTq^hS03Vm!9A;y zLHN8%Aj8#s5@P}wo_1Kd8=pgHMhp+SQhdKP94;}x6YkhJUO3ts{D4Zi>A6lOhZYKX zn(3hG@K}e0faEc_u;r70{JN1KM27XDOcQ+iMLd(3)$3sG^;K{rw`a|%aNoSN=1JVG z*SEfp#Jrmy6}?Y#4_5`z#5{q6+_Bx4QH9>VxnBJ9-5>ca+>P2u9*TV*^BK6GY%T>^ zprL{1U%xp-TmsI0`A96|Zswk_OG-+>15OOvb+)%a`93Z@Dd0T+jP~@F3=IeD)5l+$ za8i}${Rs|@eM^j=@H%O%PZ@!~tr(TXP2WB8^!cgV_PsJOC5S_f>WlJH%LfEBa3SwH zDkaFZmq>f3aXF`HOzE`KisEoi#igxK& z8An!c?!ukg<&|G&QK?pP4y6!gQmmfZh>hLOu+E)7Afm-q#;hbZ!t1-?dL_n!S&dSz zky_weDqjST+F{q9MP_cU2ztxcc}4&*s;*IS>e}d>=W`)&ZfC;ZzMa9{wF8;zTpkAv zW85TG=vO&DVO$uevtuCXFkBycqs>h9wcZ->#zwe*7&*5I$7Z$Kg7#_}Wn&kyZ$#kU?ni@sa=&gxc|h&PpODutffKo^CU>t#`Hn<6&x<@{i*pAf1k zfk~~ZWaQ;%7HV}Hj1gV3isz>(1l4oo>1{&o0>?Ueu30eYmBd;0nS7R+xVR{avbrjR#!+fHPRHp zT8BT{sGDE&3WdR^2@DP+Vh^{sgf$$(=nXl00!@U^XSLo=a;#yu;-Zfe7=*T=;Em7w z2%k_|Ulei}khx*h0UNc(pr&Ql%TBBF~Lf8Hl*YzI!C_C`|TC= zI2E%!>dCF4LnQvaw>%n$n*Iol+||?Z!juPg^b=cOu0q_!T;WU$a4ykcSTpp5OgJ+T zK^AD1N)YS#A!Lk?7G%-)8116s0&ZBJBk5RRhVK<9xNbe*K4VxkU8&0qJJ+3cM{m`4 zol6zeowaL$BDzD22o{+;LhNG+yF?7JawFeiIl<|Z+h1tZ{%U1WQhDzO=ZSsSpRxF3 z`uP~!{uv$_+;DO}NBW^UBIuDP``yD&QPeK52MT7r z)8QrLw42+O_M@V@w;EjTskmGEF{4r=W`u!)XA*#I?YtM9^!6#FlI}DoEq6k?J)~kXfu^Lqm)!7UIdYWOrt*>*zl=^cz<=Z(lK zXO7eT6O$Ft)40XY)+s-{SQ9gdB3NV%h5bno8-ge>SVs>AWHg##Q^U*#{Y zfdX^QEWM2ZO^G= zD=*r8=yWK&GmO$QY^kB4oy4-|m;(Lhwri#O_gVOH-!)+Ifh4%O7du0No!LaQM*R>A zHPvyrrp7oUGnH*fW}kJPtUJW_fmm>4`$JOY&T_BZcjEYdoc?#~+-;retFMQ43LWQy zt6$Vhak0MK-+$rk?D)tIJ2yb=#>+tnEIa($)h4Xq)s+;*+K*LfTX>%V%WhO_HTq6e zJN62TtB0=wd4J|2NBB(k!hM81_z-4m$CSUVwR? zf<@g{2tv3|;5U?usFu&4V^(PHQs$b$;6M_@r|EeW#k?+*FfZW*o(mx9gj>GL#h>aR ze?T~^A%pvan9W~+di^Qn@n0RY`Bzb|P`JXcsMr6Ptu_CMq4;U1jlF}NiGdB0%iQ52 z3IP2FhGMYBBUn0W!lzCOA4$xwxLzb6PxUmH9Umzev7ob8-(SD<6ZN~I&BEpKezpeP zTt{ao=Mk>sJAaPzj-%P3H2ZDnepsXcrM9hh9J{L29QnyRFWGZaX)P}Bo!e_;RfVhD zsJshYHa^T?{OY6^;ralbMrgU;&Hrs%bH(LIP4VGi^Dvt&7V1FioK>- zOB0<3H|<*$En$Y-Q-5($Z%CRdhr9a7MazIlnh{F$F<@HXtU-fiFWrypqGg!gYDpQE*e8JfGID5*d`g1O%zm_04llOPxB! z)eE%}nU+5TY-`YU&SOm@oj3*8gIX7j7}x-68q!1aCessVPMP&_t$YRyq<~0u>ArcM zX{FQ4xO$ORK2ru1z_EsOXDXZ(q6>=w122HTEMRLmq`spyxf(lxLtt9y%(P!qa7ji3 zxi6K;D%+*jl;JsGt1Mu3IJ5qFYk)q(Bfwkr+HD3@tLfGN!{MX`{j%9J>_|50C7~MA zuUZPz2QHz5DOiAP4VEFaSx7mJu!8>58+2l={$C3bR9ez>P*rmlonQ>5doHT2CS5+a7LTlk^5E6FR}xt%IdE`L1Mn5D*Vn zAzW_o(17KpBvILDSIa|QO>>N-h@U`xo;+Hg(qKyA#(76!WJO!7tyY2C_d`e|oS!xfZ^-_Cv7CL9lCmh2HjR zZS3RT7;K;UyFPQy@At^l-q*H%@+f9z_d!g-D>jL2F3Q#L0OsixStxAc_j>3q#%JYs z`n+V9=^4G^Fqxr$gtZX1aQZuzd0du)xmP}&&NXe=>OF$>7I1@acc#nqbx^RW-x!3) z>w*TytBg@wldF1P$sI%)_u&tQbfywm(K{eOkvAW9;aqBs7|;NWS6)yi>645PEd(!S zZfHhQSMRGE&)yjafqJeR)ngms{dX33b z)pWKOfT~{*I{qB$Kj0ESl&tHAlKHWr{GkRy;S&AOi-^C}EL5(dT;U@O@5?1spWj^M z+ChWA_d`{m-&nMCc5kz7ft&v90=TKDM^a(16`M`@a_T)ufja)54?@P0I|vW-;UVi z{Ff1%?CigK8~-^pwCq1}YW$M+jFaol@AmOd@r17F;|8uu%6t09V#aPQPO%GfI13<0e{ zE>T5E{g9a0yraugUw_PF=T*Cj^At9Q!SX$n`Qy>)mTMR8*eJO^mxhcB&z9^` zB~u#5ZVR;S)dv?jM)JH3kAwsQ{@K_6$yI3k zzf}L_^EW^J@z;M|@Sk7(sl_AjuSEF^4SlgsK#lneN=U*6VUF!E=NkmpUlSt>X4T(;dQ z0mW;LTgp~)Xau%+db}jJVPX7dfpS_UzapW=~rQvy6JI5=iD5xIc3+HHA527OP)7gwT#T$S7Mo*@8KCM zRBSuTFbY5*LRT8QY)i12k`5SRl5E=i)?%qCJO4paA!4y^$g+gNtVy7)-Y$Ro^|M%i zW884f{Ia&m2axKysO*e1IknDAi0ehU(;h?rFE2xW!<_dQWqb7L!E-+-yl6+@kb?`^ zxcU!F?q2{<`^jScS1<7XM*ua}H~$%#CV#)c<7EB^llv<`ttfbn#upR)6+2Alr>I{| zE-kxI)$=bxUnaG|U-(qM9Rk>158di^b!&R)>HRRcEHqLD^3BTkMbuQ7qWPmrasGD` zN-~5OM#hhP1ebUbmrX*Q-n@Y>giGjy?s?;rB-sol1 ztAQc``zo(@Z*7~h_ykMb`YY-KYgHN-4<*0Z68t~xy;W3P-L?jpg#3X7g1ZK{pusf| z+}%A`aQA{FK(OHM?xAp}Ah^3rK?oFHKp}+`kgojaAGgo#a~}GRdv5o+J(|J8ept0? z)!K8fJ>~np3AFCj8E4Mr0IWKmXX?kObcJkF_aIu(pi(q;G z2MfzZ?AVWh_WbDw9a7M~MZNgn2wMSb&V;ijg%%G;g4(OU2x_1IT?uLr{BcoeX91vT z1aKG44Te&r4r|-;Z4u6zAoAO|@mLBycU`O`R*h@hoD(iI1A(bx=niT&fWhje$Hac* z8XA`g#`Z65DqY%F6U)GSM&n}yf!BQ1Jm^`(*#U2}ongNc%fm8G5xM-1a;1YX z0q*XS)##XP%o)mdb5Y7Tfx;K_1g2gl0ylkV=vH%4hLU|-4z+6=usHF6bx=+t0WY#( z#)erz5^1&%wO@Jm6OXlioB9O@oGjH*A{8qn_$nZqTU7s~h!jj?X182GWv{n&Y3WZnO2BKx2$=AaBS=Uw5-57H`~O1uXimOCQb1|dLn zw^`ZrVM9e!_4GXPxK8G{H7}XH2Wg|IPw?`Nn|}vo^Qbjn*#XSH!LJaB0KGwBd)zg- z)j)iC5Rv=bx!rJaj`ARIjl#~kqTOCoH6VXm*{ivlevXWil=8*Z7lQz&c6s`Kju3uf zk(W>~3hfD_m`Vo~$)1z*slb?BcrI+>=f!QX>az`Lo87L-Nf{)s;`uE|1s#Ckrg!$7 z=M_I9BSqBJBpXf@mACm z;kFKThLZZcxnb*UYYcMnsp)DY{l#}myP6mG zR&=+s)y`UvmhU8CvyIhB-sMKuqegf5#s~%fRP>-5aWQoS@RFVEAt)dNo(wxayV@N;dWfu{*rgScRy@+NjJh<8WypnTkcHPBEa(L z(~kD#P7Rp*rXgki6|e&kYv2Y$?|!3aBzf&ZC)6CW+HMau%!rcdkqk&xLu7oGb#|NM z^Tw1aIKKw9FLd76TISWaE||C7o`%Xgd-6X;m?%%Mp>L`Vg-q6~hIfhZ-&Alc{b(`) zT$5%`?|eAGh*cR}@RAFijP0r?%%ptj&FAYd&~pJ_d3*Kada-yrCr>p0^w&@HvlIH% z&KN8&R6A(@KIjvW=zTpb8eAB(hNqDG3EBORR!kUP!1{wt!MVowF5f18asw}Oxf%RN zd0W>|=OrKBhPZ4_=pCh@1lZpGHM^R3UHhibH&`+KCnbJZJAcaRf z2!?$I;0BKLI_DPUy4J2T!WaYA;PM-tpBBX!483bs25nafRS;>8%SfZ_g=SnIy>Hhh zN(GEHe|TS~`i1S*=O_{4^NbK-DU=c1D|QoO)!nSRKp>j zx8<-Rwh9q={)Y(GFhKYUb$v6IE~`TmJl{TeVn$5LF>iS_tl|#=dqiAgkc~aktd~~8 zCWKSq)E6=leV}vCD8NdzwG%ftDgh2`08}$t z6I#HQddB+fSJsmos)Kwkw&pJ82B$Xzf37FjRF@Yrijez~kzihV@ZeklLjci^`8f}z z_oDetGPmmlD>mMX2t%r;R1LR>V6XZY3J(Fd_qTt{^g&aJY!vK&bf^UX z1(5JR{gMC8(Wl^l6%x+H`FD*DeqaAPI8?|*{awP3if*BlPAxEZuwCxsO^{n&$k2HRl6Fm1a~JuZ_Xj3rDD7> zoNt#Hy9a-L`qV%1!{vInXVdx>^d0pZ0g+E~G@49vpP2Bukxk7}7Ets3uxZj2-gD|Z zVA=pVFvG?zxNWt#Yr}5QUgW$jm`NvJ?|4fWuRw%IP@t#j7K!aG(v*LC*FYyntzlbF z-uafsP#rK2JXpA$sj};;(6PjQocl(x5WAG-lG%gMTY^T{?(&QCDq$s5)|9W(3Xv&M ztvy1IZbS%pp^@8Tb{?YmV!UnI!TU%UoI~quDofeyzG2Q!w#XYvPaQj+mK4(c^CJ7_xj){u2V#G5Xg#iqtKnjNn4Ez7V^>9s_J^JLaxt5jmD;2p(ezpc)BC-0m&r{-g_ zO9kejOd%`;81wv#pX;ZW^}S!eQqKxXwk}+6O%W+PQfPffoxOo%?)&2U9K6-4!jOxI zJ2?$gXC-pm+s2W=?)?#|v!EoLGpmQIk#U91{m7#o%&1ce*a#`IJesS2Z(xYyN>cdg zmblgpT<1;RCb#>j`10!v#U4rdA9Af1(tAFWbwK>sXFX=rG6gF&lvz?hr>Oa77u*=W z5l?bRk@9fb+uPsU@)O=piDy2JqMhFD8&S@jrBbv~{61!wL_}v+o}}D=r?Oh2AtKAT zS@TOFT5c;GpN!AuRnCO=$8-q`@FK4qmR`m74D91+ZUBKVrF} z^W=fT^y5{(hPkrs*@*A?+Fh^B)X`2pnYmiuaA)6qr&1-Vh)wt0SbXm_FDY~^&Ut8g zx9qJL(v7okRK9}%a`!Tu_g^x)Cr@)t zzD+jbKDz&)7P&OnoETg_K6DNwM`UW; zKc1z~#>(^CXV~1}zfAD&#U{k^Q#QZ$ByXKqJRa=Bcze}&=ed2ID)HFw*z?^Krn9__ z>obn}Jb^6>%C~t^%cFU-1%^LC=4e`B^jc1W0Wc-kqq!VL-LXDE3|n#BrCsRT`stP^ zp(xg*G3^t^?;;GkcSG7uwO^Q+UXM*Xovviw;H~;{Cq?){58WPEvp;sn zr#~rn<#36wQgW~{D@y>O9g!qIVYsRO;aB@%9H>^I=B)g6KiJ?>l4YCQ-W1*G%iG=I z3omB}>{WbnVxuwp#Ze!Ro-r3+vt~!ZyIt>BO~YKG*l5ScYbQ&eDfqQssZplLISQdM zoHu^3@J9ots6N9|vcs`&d0Qhgqa7uj#SXVP^eTPy=||*#&1xOXedSxdQ|?C)!5DB- zW_Gz?w)G8z7siXHCeN4h_svgJ`Mp(2*e5kVsd(|F-h55Po3lp|5KV10@ofX|V<2H- ziUJb7(T_^C%J?XrCEo9+XJ~TJ1&1*-y4Y%%w@2_=FD_rsIda;D9KK1_r=_$Lz{u%I zu{CeMENKwV_Dg(0row8jd7750$Y*O`$(X>*D;!j^bGOfxd(f)oMy2_~b;hB9R^ z2Vs|HMNlq#^rD2s1T*k!0X}}KRv%}sxWX*BVyf_byet%E2bQ^{_1L2 z16KB+7pZsW1LH+1|*Ui0e^o zG@nIGoi92M1wR|zJ^G;4aqL5Rj9Gdfy6!DT;k6mCcF8h;SL7scIl-jod=*9;A#~Jy zXf{_a{Dm?sNeg7TXwbXNvi7F}-Amni^GfSf5A8?^7K8kw+k5#MgD^5&0-$bp(Mdbx zbBV~?5_g?>dn|@}vrfGk1mU?EA4vnKB3V)VEklC-tXC!!iSzocif6mz%x}Cp#(>8) zWb4?_0cpwEIPmbNt62STlXIx&%ht(zwV`4anPTi|?^!KT(BU^;wW7n77#ZaClvy^x zca~*UCA3e_Ynl5zzVPnR4O_XDduW1r8r=w|La33B|K>GYIm2uQ&_*r`Xun@{JHnNa zv>j4VcA+1c5Lc`l6ZD!^`L;-So(KmAXF3R3YY1pn|0ieVzW_TS_`e-HA^2a#PVoOe zAO8|T|tr-M8CP+ga`dX4sawS3&8k zqInY_D~&qKV8R~;8>a0=EDJq5$2p%Kc7y)xWZ3dvX#e@OgTaR`y3^9qT^rE)qX03` z!;OB_Jr$eLQzv93PZ%|Cha#3bZ&#b9EEBABPxaiGF!1G?wRhVZWi{5dgGuZvq+!+a zz)x^iywx?mW!Rxn(1_CX6@J9P&j=~*#{nrtap8jn@oL|9T?M7~WqM0hJf>T^ZJUIQ zOoEtj6^rU`B+HF(Mj*JS&qoYQlikYWCQI;Y<)%(tSE^Q|76ArZ43%KWOBst{rA|E{ zuR*p+5a-^NKiCPirPWFI83XR2r$MwqYuY4T;pWG|4@jzS?sThz(N|a1SGcOXit{q< zt5nJ*=lyQ2Mq@U!!BIV^GAtw{f%2AVLWqWnM1t^yY-etvp$<8Ui`=1h3p_l z5q!li9YfHxL+$#J)Q`wlEF0gp8Kcc(R%6d@EE<s{SdyyH$+mOiq_#@Y~+YPiy95Z0?jbm{Y z8f7<>E4%q!-+p<$eX)Z zRY72##e)L)gA2E*BSF=7upfiaBxBDy*;n`4_$Z0vmyBogO|}6+aqk0~b5#4N^K?>t z8S!du>Bupz??v)^9dGVGnF-)VgXT?uyzRaF7PO2|SdHoAvnn?RLYLKTuZOE9g2Z`( zaO;t(d?O$7B|JcSNqGu}%RL@@uktEvfPXw)~pxtD#Th$~Y`hW2E9 zbK$E@B3veK54`E-kFu%9tvl!R)+=ml-(!hQ2I|Ie;I04bc7cYbSs)u6VbAafSowwiJrrvGi%z>(I!O*O_zN&v? zfCy7lN>@s&(Qtng?x=SA?D(dK7}Zs!e6`6~UvD53=kQW`=#cCx5}-Fa6csvzeGLs=Q{=>49~$wc~9=TX4kUfLX0@4o7LPHJ@z?H!N<_s<{L7 zNFIE>>qb<52pRqC6hs$a*&rKLMciqTr78p&i&ACZ6$cjzc(1JGTh{WLFqe)>W1P8` zCMbpgR5JDbX}|Ws&=rFC(q4N_`<^u}WQV&3ij}YGSEoeXWQ*sv9xvOThiF13JPJ)Z zjpm>o8%#wsfxhU~jIMWeQ;@er*{B8S8tyGinChhyOy6)im;o~S!Q%U>z3HMnbFXE?rnlqe*!==#m z_(Hb6u`}hyocEg*EoCNbkUN!8Rh4d$*jc6P9w?m?;R9uOCV2eq+$MN=(|=e85p{p# zrbBqIAavC9FeM$;loU2I->q;@w8Qxgo&#kvtilz3TjS*e0~pU zy`ndy0?wc7J1jvgznecKW%kw13r>)~>Z;?e63D#*HRz9>=Uo%wuq(Px!6 z@mt4l_=SVZN6d;%*jFZKtk1o_v=T!m%-Z;H3ylJpNSDJ`?OwU90d}5M3MV*zZ7${5 zy>Cbkg6Va=8G|sz67=nfQ(V=nQ12k7cZfxU1?5{~9$eBgisp zBLCG51@VH?)`n}Tqt?PZKWlEhEaioOn2>g7wx}$bHdr}Rur>M32uu!DO>m5CtIBU} z(%@z&?6Ez%VkiA%&Y?N)esg{D*7TQF4wC~8$ZSsF&6brEBgd#BD3oR*CtGsvy?N5s zG~fAQ_gR_K;HAfD;co8FuLr6+5UU#m9kmlsUxtOqvUP2QN#$uU_A$liSSJg{R7MI0b|#o{$3sYwSr37 zE_cEF~?te*O5E!E}}CZzcMz_=y)Gp@<3O zEMC>OMu_~%-I43pR#|5thRor8;4;S`NQd(I=3^rFC`9p;u`L!~)XAdL4O_li6W216 zuy5?1HQOL9`woL6*T(RPYyf9vb=V}bgSA6xRjjq>DRz`Er!keYw(p(N&97@Cxrtlb z{5KoSWWI(EfzGIu=3LD;2?Wsm$(EewLSIi7v$pI0eS2(b=KLJj4{bBj>Re@OuqiaE{ed%lrBydRtfQTBuQ7Hm0U1^Zss)d=$n|Yeig%gR4ORB_ zQEgbZPMkdifHGX-!)1XYNY7%3Ltbm$aeWc>4zz5?m9T*UVIvf=hCIs@r|&u~0TArP znijR_tY@?D24}z@1jRW;+#t@*^nq%=<-+Nz{simuJzcG56PA4YDUBxqXDGeFx+aQ% z!n4|MBHiz(?eS|7%s}C@8@`P7y?k@lsixpVf3m72M7|3w2f1!WzD~wU zK?*EyAwFv(iSzc5`R%ae)OMt6uBM1eQDPHlcAcuTo>@g$_}kMFJ-m+CbmdR!H5dKH z0HD&!`F62;IA4?;+cKsVBU{~(aH=m}5m2+_WR3Jf9H%t}DQn#dMdMPESt}(3j~hun zsI)rQpoAceG5caS5eLX6Y^TOFGDb8gZNWLY8jgVcxGzGb?5!Wh8R26fv*JJL+>p<~ z&@q+N0y4ST@77{dCLAwsBfXE=s=DmPja&}TXO;XWgvnHVTYeen?#P#=Up)fS_-2(k zQ~vzkIA6sX)~5g}c3X=4MgFsgJXXJT9*-4H=x@dL#d8W4X3oaqEsGBBHvt294nBnm z1O$qjuvEqpI)m$%35J_(ggv|V+>*mlMFU7FBz*e|j#2?o8rVK~g>r@`6En#|w#>2C?t5q!j|VI(DS@hhbvC-iEgSnL@rpxp zNZ~IOpO2x^Cis-e#qgX+iL2WI|XKU{eqFftBtgoHXEclv{9-Pfi1>H zIR^`)&yLSr1X(xQ%zY8RM5ZN?PUrl%9NCV;j!%+0@R!g?R~BDkMF4VVw59w#l7O*% z2cBsN2c!BQ5;Q^FD*06PFbkVb@ocQhKtU3hfGgv7 zj%hXrTjQ|T-YEfd_V0NIK^zcsPI(a)6WT7tl|++sGfLCrt4Oi(=$i{E#J+tk)Wwc`-gVyb73XR7)10M zfl^9a{k{B!kUi0M^lrFN8*n?B~;i`b5EF!7~e+CSl`=kpf+n`Mv8YBmO@wReTW!B%_h?HNn%Ttg z0RBV#paV&@yT>a~CGV!>bdIobN$#WCsH)HjSB~H{ThH6VWjp82)c6w{um<4Aa82x!dvgx~;2keU_fJ*3qG@8Q4R&U${KTldqmVL&ez10jl@-ao zYfto6+cgJo)c4B@Hg#fIT>*l})?j4%>}H=I-P82Vi4s4}d176LM@6gD(k_1X;XZj6 z(6#nk%U!%y{&cSM{Ag-@Te`;xGtSLq7yUzuZ^ay8ZtCpKH>cfTYi2SmU+G;LQ51?U z8kh~&preYZKzyagK;=Em4F|<%jfLTz)2eCf(>uRicCTE>12WllZ?|HP|EBhLS}M(Q zFDWLd#s!%Qw(!1@gejE^ra5AkMj4f$MlVWD-4j*IG*E0%D0M7AoWIQ86dk7|nt;-x zW$!3Ul|!7S#O=%RgvF|zmak^d>(^i5HVcYAxQJ+eyJBD!^n>)vHD^TqVYJY7JE!5V z{GDK`>8@Gay`B7uwAhH76gPjhim+bExPjdQ)k(Fec#%x%hjr9EJ1&-VVygy7*u()# z*6}#TpxC2tx8^RIU(I9kSyV5PJW*!wjm}+Q%g_Yly-s7CvP|#e>QzY1s^uX=;q;@m zv}xM}UqeT)uu%RUd zpg9AxJz4=NbS~T5{c-ZpRgl7~{bdVRJ9rb{7jcIa@*q`(yhxVuM(U5iQ7?U#s&{*J z1o1%Kql{-5cAht;GyO6_O*&G$7S796M2Y6j`m97}=~~>B(cw$?fmAMx#qAne>q3mQNFA;PQI$_y;CJp zrt-y&gHW>4BmNYn;SR-QJJ9kmTvjvvdecX%KPx*{sPQvAYsMonwWkr8_L5GxvGW%& zjEJ34$If@wiCVVg+4HnKgt$F#DR)D%DI1U?CoUrCY&x0tegz%#4osw~vcZ&GL(Ufy z5pbukULrhr~X!YCD_Rse%xt}@Nai>o}(1@yrm$vo5 zz@4qY%G->X&<>U5Yc^BLq^&hePoF38K0A#wdl&;}{NZ;WjdyR{#4?pXWoC@;jLsWW zDp@n|Z@YFGb3inwe?ZB8Iq#mq>`v+9ec2Wb!QHkjf$3;bH(%9bCP{YcZ!B63s#2ns zJ$Uw-4j_w4BDkjl-*PXqDVJNP9o$<0`KeMurr+lJx|`(BJjgjhg zn0IC)Qu8LQeti{u_$9O$^@;r*mQ2BOXiBLOPK#N0sd2sSrw@zs5w>)D?!pe%Tf2e$ znpYcB#+11MuLz96Dy`B8^0OR#2T(+neHg<|lmG!vx z_**UJwRPbp6~ZnU!`L5h#v#6X=27mbU89j{5a1!K3seZ22Z9Km2MFD->XHOblN}4K ze;O#Ag=tg#SrPhZdJ^;)dM6$FPYR0v0%qW!^|=4l;EVq%Gmz(Z5%I5r;=c{}0=cNa zD=6~w3I1ofUZW5GD|#7hWUIuPfQ>8d*9mWN(H=`44g3(5znt^9n*H@lb)oKqLq#Rk#8H^&uQB_e2v8UKa&zvhvg)vx%sE-eHnnd}-IjLpW?~ z->vmkPjzW8U4TsW-H}we>fVs&WLC4|A_m1Jn|47zFRZ%Qd3BiS-|vo$S?ej#0lYK#)8>RfDW$ujrh&0 zr)MXN!1E_>E)Z+%j3g&F#fwyE*K&NOVy&j~2WZuAzfQ|dc}MIcL$37EEv!&#qmDDY zTl)9zt$=)6k#vPp&0&FCm-dG^RdR~lHj!>UmP`)(GbGlK^y1D1Ee!ZMp2*mD&B^%{ zesQ{SN6vC=`vVPj2OKzeDA0Z?nE|s!YZD6gdJ-?iO@eNoXJfN_{FW}~V^K%k9PCq2 z964xHN+?raDuBp_!wCH$xT|k=*+Gc%tPSb^74n~5OgCft?7u_5(LmN*wONZr8ZVX^ z*Xa!5?`#i|q>Akk&wOScJ4gTvsmrhLsYgIbmgXs_hZKop9k$4W9&MR`;<249o-^`5#H69kXGs7#(Tmf*m zRHsoD{+{7$ua!3iA7p0wGhWTTD6iRFBV-@l>}w|fT1Np2ILio*_x=9(O}{}sn?l{| zD93yQIoW%Bx)>~H0h80tLXJst#);9l1!^ZA{vhBSA z$@vuvStj$q>kx6?5E(%-^p+})v>?%i$6R8c+p^IvDYWAa%e8hWgVtcF_^;y*SIWw$bI3n&w z3pI(n!3*pGwMND>Uj|(Ayo951L0@WXVM99D+3zoFXsY-9@&c4>a(SeG+N6# zEXtD(_1mDv$m7q-XWg}46%u8UJ%J6n&0AOMj}N4Qe(@`YiEDz0K3U3RTh*#7eYyW+ z+r)4g);7b!ASo6^XZ&H(|04 zFZ=`iNR)Ji?MxF7T7BpeSiGOj_B+hkZ(D52)h!*tQLiou`s z$Ao}l(nt%%mjyd-<(C{a{s00T+XfJT3bP@T2rJAzOXz&v0PVC^|U*gMMN> zu-qUB4VAJY+dzyYTFPP=1C)SL7>p(=<%(CXG~vXMtW<&?y>8pN?36OA-W7F?bOGOO z+J3&Bu$pD&lp45*9rBG&m`WDo8i&<54oF^>yE4E!FVS00{tTUL7}J&K)PT`}y6#ap61yC=6FI~2R1JfPgb8ExDe?@6#?->YcJRt6qJTcFB`WEcMGBvYJRn_y=8 zJMH6!6$@R8J%F@BQr*7Cm-P>ghLTlD$^$(`{8PfGmZSs*>?wx{PL4qD)nwiU0`-pw z`UEqD#xJkXqJ@%@E$E=(S&I}SbC%$Sl8bqr`u?^VL!C%0YK(@ShtJSM0Ad#Nu`6D_GqrTrx^9ZhGCvzxYWv!_-I0_960_7-wF}Rx2 zE@aDhm);+ycuY4g8;Y(hWa3cYx(~K))MKinzVs8d;>>XLLV&HgD6mish`7WEVoTi! zB4pl>Ea}>$O*tV`( zpZ>mRqYS$Okj8%c;rB{;hiOQb&Fj;ApwW*ST90)bZusGT)UllQ7*)q?FPOFSby zX9oBY>wCGLOogZHn>dU=E;uQp>CM@qtt)BE2aXPQjtNtQn>l%v9g@&F6JiQt8D^Q4 zn^uucNcvwaN562_R1b^LaOP~<8N-p#v0Q>qbP**$1 zB!(bAPQ`kJ&2nE*$|@sn!l&%AO>~^o2ED`f6J(~nwe0GyuiJ9{odQ2<4F-+o1GZtv zXR<|uqS5E&xNUPdZNdrMSz|l4R$`{F^QI(%%=9XumzVQye8pc^5`}1z?BgCQeL$lv zIN#eRu1y!nYt>Ui&320<)_XNZC}5|o-^^Lxo(c7fpf*4PxxfxyX>dZtNBPo;tDH|G zcY3L{oii$!0KG=vbX7nQtwa9mJj;=kPO+5iZ2J71a+)|#YNX0N`iCfD&Hkq z+UVE!`?>emJm#W!$(7F5&=ST}MRJhWsl{r^9V)Y!MvH*{#*ztP+lLy(Q&z!n%e`ev z%rK&nn7QR|gbN;a)9n`Yp`?>+U% zPI8mAQ=ZA$IugJ}M~DfbrY^Ml@nSsf)q9pg0Bso&j^Qj1MPX~(I*^F)hMz$0*t=H5 zw{K@f#-qgY4JY(4ECj4~KJO>^X4vV7(oBBp)KV&$ zfZ0!g+;FrKI-+&3|D(S=_*zDqa$NTuhxzU^n{vxr6xxV@LVgpd6KDPYQ|f14@0GFJ z2$|r(>OA9Hs?Xw~_2tTKBy-(9O%hFQ(latbL@!|9+w*krv%#BLd0$K3yy)2pXavWf zcgs>!;1YfCZtnuMFUR{;4@=q9-(;lC2Z*R?14J6Xw;M*w$Wm~;dW5jIw_kGP%Q$ms zS_aw_b{4$)-c3aq1tIn7tozz&(Y5NF`crIiSy1k6yE6@+cO+N!tgFY;3@kK}Uh29$ zbZjBn{~l-W(UTGKbvIDDDh8Ny7cGOq!#U65OLz1D8vYRKEHh11MBtA=p2%_r%Xdnk z2g^oblW*4VYCZNM;R9ZEvJ)guw}*+?m5U#0So zKzh%{=>Y$$3mD4JoMOdBr9S@$F?dY4O;N>}OPgK-1N2qM^RaKtiu~wwE#t?>n~yzE z&VzsQ;bU)p)QzfTX%YrM773b5l~(84(M4q5UlJ0!)f=V*T4`Bm`*!;E3OmHozEA2@ zUOt-;r4sG3KfrB`JI^y2C2v4i$Fg*CUN)Xez&3nA_MzaBf44Z7)=)qck3D7*r6RT2 zD4?nuE1J5e*tKR<&}y=C%}H^^vQ^*k-kQ(jMgf#)0FW(>OaIFkFR=$v*>tbJ12syn|mHUVzzM zd3@}A)eas9--CIR6ovyj4Z8Cp!dM3|9226AKt7A)UiSOni_Z;D=+kGLWSTG!-%TCA zfM^sizrlXWiHjEKxQ|p+@FmGdos-CCc`k*TKVu=~5|Jfx)M%$IL1|Dq->@;Ze9#zt zeinMAk>`RaOt0XPlFTsk;FT!qdol>}zdqgEr?l#EYyDDZ-ua69P*BfXFjJ-2JyhY{ zj7>XM*Zq0!uN#;(m+kcDBmY-Alf~+t<;shT{HG#12d|*y3{QY&D!cY2>(CcG1a`7Z z^{3wcfuS0~Y*=b>SwOF=*w`z6L}q(f>ELQ*`t(U}{dcoDonp0$(c7fYC>H!y)(CL7 zrNu=Yt~@B$-C1x`7BCX9h!7pOaII%76#sJFoD9t@JP6y zglv?v>ztPGK_nr_2}R3D3GnWt@|=gf(g+rAOOkzai~z~{3cm2~)FJ7f+XKD&L`??nmk zk)u-4BWN(3WN>hOGXLnsBIF?IN~QcEOFfcdSNe2iLo}|v5MFC`;=&zJD;Ic$a^hKV zTy^!MgrP-;Motk_v}U0l#}k)T{D-qd@#PWdd(V+~MW|$P73ogjR2wc7Cm2O8Qssc2 z;ycsOlZx=$T+me-M#k%idC<2?MJy41MSB8KfQA11Mr{Q~Y+fEbf+r4_C=pK_@V-T} zuo*lx+ZvvI(E$Z5T+TZGy58Lo2Up49x}EgAiJFhnz3`hXdh_}U^BeTO^w%}inaXLAsYHE0yDM?qK2?Sd z;ERAc=N|Lp6>B?(a}Eq5)3GZY7zW@PqBLd)tnjvrBr&P4Pd2Q(+FJF36$;jtYa>T$ z4#y}onvE44A}pF$+=4{v<#8ehIlir0PXK3U-JKhz4m63MMHCo^IMfa_twubj?aOTV zEOqB^op>jxwfGw1M!)iod_QtdBH{_ONxEsMuYig8GH*3sdTxFmi6h2o6cTgG(yS8C zv#NZji!6|^v9eTwG?>@zxACTZVwfoA(b_XJ!36bALKb5{WhUhaR4%hL>}3wp}(ihcZGEC9ZC&kZ58@n;iz$mE2)@g7BE07 z$?bGc%{5NGq%<&~G!d7@KWrAqje)`A1X0%jY|13oSKvi|I?tq@yp~DNlbh#khE@mL zn(ppP_WTv|eo;DKsWn?h9cpU^%04GOuJkW`>YvK0DHYED<=zREo@vU-vn@jcz0d5U zdsBD?&9VdX0jc+k)z_7K8V0m=ZL8cL6C7kh@kr=-4n~X;ao~Devpy3||29M%`||z=J3NvBNj`OGXRO;KDWl(2;%BPZt*FZ6qT^|ElpRD_{5qF?I7m|mauO-krgI}qo2 zZR=%ZHq(`?N0rML36LRkG(6RA-Y-#QZfb|E;Pz|ESiUj04oJRIXAu77Bnr6K@+=$U z6I28Gtrmr*+l9u|ewHtPMV9l`C=X+U_Vfon(kk}-MGL%{&8ch5l~)UbJhsjiN~3!x zOhSyKGzXdGpIUHjN?p`mO-&dvQ=o=Az`tN8^%EXIn$(mO0+&xzNxDN%{x(#a4)h~ILZnEH(aEHsL0V8DN(~ogVdWX#iYZSXR_n2v(}7H#gE6k!iugH9kC*(3>~!&^NxVL`FwCqsS)MQp ztiSYzhrF4ODJa({j6=Jm3N?; z&=Q*D=-9N(XSXXPVe`S+NXx{xV&?EhmFoKd%l-^vKo1QWf44W*D4tST3(~eD?sm%y zmn@DOM|$$M$);= zsj}MaeI0b`R3Gtm#X(8Hv{EZO4<|70>THHch2Usw1gC2d#gXU$${xxVo@X$GEZav+ zGO`clR%#1*I8k}KW=(O;N_9qNJkRm-mW@cEXA7u~35$Xr`@PG2U)Stn_!uR zVaiWG6MD}@)229BIPKGhW~WH2w)`QQ$Hy}Ua|Sh_^KHn)Tqz!t#C`)=rPMx9veqc< zHk{O`_H%$RR68-yAG8StMm7wjjYIZ`+Yx6oWDTmC^s_f^g=|+^+WRxqxt2V*ijYb+ zed>rMSv9wtClm~d-A$2J4B=5%1twl@Q&jTSZp&J)_srLcgpBy|>Y>+tBRzb=_94sD z@}*&|G(y3%i=DclBemo!8`?wqqk6Wn6$(}7sMi*>ie_YU1C-b9?W5R&K zsc%YPsg;4n)c{#yra+&b2qdO0qfqg$t=HL<<01VNYi!~dKz%1n{^A-1q0M<@jS0fCr~z)|yNi6FGYFi`L0W6_rUNry$O;V{<{( zeY~nAYPIASzstl|egU6|N-1Waqw(Wo1h#(d1G?W7?z}R(8XtnX!oKE!bY58v=EGYiW4bnyH@3W}R;+d}jo~C8ng@x#Qcip***~JozJ$-- zVET)w>!nPdT+Q}N31$1og=~S>WCV}ADS>c`u8uwLpEarQ`+O!6TY{6D;pG zl)l0HB@uja!9ZF^O9^bgHy4-1(M+=3y>eEGc%iH4*NotOz!y=m7gRq^@Ux+8a;+H!Zjg54+Dzh!-apjB zwp`wP5^cK(6tq&_&6+fcLB)0DTG{}8WHQ=4IbbgXB&NnJQnACuOlKQU%(6r_vgpCw z@iQd%yP41E9N|!1euWRb9QHEzo3EilT>LT zy)`zu7TouJ_wBu*xfFc3*pLTE)aeh^zRF?%@E%aXe?d;hzXXiKXd1rTLH}OK+-g)Mo-QP3wJkQKCznOjECFjol<(ZzrIrr-X>kG%hl z*WL2?Q%^tjpZEXx#V@<~YtKCR^!ZOe@ZIyzzVG4a?Qeh0L*Kah)VXiH_;26+fgkzM zh5vomPu{=t>zJA$vUv%PaAG+_UUpV`*S6=jk;@2Ph z;5|>Ai~jqgk35)vtEe|?uRe=-VNvf=H_?)`Q!61|NHrmfA!ze2mkfkzjEO> z9((8mAO6Z`&p&wT*Pcs%HqXZWbK&CWF8ZS<-}jV-3E<({%l!ZQwU@cR_VU>FHD`7f zd#84HZoK>Uawd*#U$y)C;?&OUL@*0)#u?&Zb|Tyg0$gsjmBjenRupDcd3dN?NktH# z{Q6mV4Eyut&T@Bt>#?ws%&Pyo?6PCq$4?f^Q#-db58~s=H@xDC>eTY?>E#`G{{B;| zXE$$<#?>>LH=ZdRpk!Qqb`KAno!C2lrkqv(xOvuk34JM*{&Djb3^&-yLmWWgN@`en z^~ee?$^%^Du@y_Sw-shKxcZWh5#WYF1;hcbzGcG=Z>ErfSyACiRJSX<3BLIhY{?Mp znXNENE#0dR>W_kV{|x`8{?#YtUGO%8sn2lMJfczXRzMv3R}*<$@D3Tz+FOo-ci)k2 z>fdheD%k2hp2JbyRTxx2HurDX&0h@|&*8ZKN&#`e+tpw7_Rp1HA1@q*p_{)7yZNgj z<9S?vrGPl}Z#RE6WIT`SuR=F}6?XMky~nc{*Iy|hoBLOE^H&4Lvl!Q3DIgAbyZNgD z<5~FiS8<#io<4`d&0mRb{%Qzbzy6Bwy3Mb|Wd2I{^;d+~&0mRb{%Sz~M&tS`1!S}R zMqT|?Z~q*P>#r0L2fW?<)d2q-`Sn-EazBjR{8coWzlz57R|<$j|4!zwqH+CIpu=dT9%XX)2pB}tVtYTkH;Gel17U)jxH z4Z-WzUlCq6eJ0^-oW-Tc*%@jR}- zirxHGwaqupd?OjxUnwA)`#0(8uX?WslX3l(0^)$To4*<`o|AF?RpREal5YO0@fJPy z2a|FAl>*|>zg_)R|M5Joze?QvRnpC04H(bqxc*83+1$TrH-9x?Jg0vBm6@MZKpgOP z^H)Rgj_a>dH-D9O^H&4rSLwL^N&#``->&|u_x^sxa5+{b8#`B?XsE)xmYd){K0q>!Yn7ZNZJ)ZM%{Z;e5xJ`Jw z`Ktlrx#E+K7=N2@K5fF=)nD~qALZlvtL93&O?bQcs{!kyd|ZFk5brnPooaps%KNzE z*=c?y6fL|V**CjwWB6xL@!!X@-x|oq_$z{L{;L0Y7Qv|gss`eKx0}D}@1I5B*Iyay z1ra#SuSC$zUk$9`K!tJ?P6ShyKv*T3q=ce@8ACO zXE83ny#}(`Uroku7uCt65#x~?zg;L=xO@K&vEOn1l^efZbmX@W!0VacZu%=Xe!J+% zZy$np-1(Iozg;L=xchh>;IBOM+l_o(4P>*w>d0>&FrO6T`YQ#*0qR-3{ zRY!jNfbl#ozg@WT+eK%7d*A$(7?~-<;HIplkwYyCw_ZlQH0lRJ=l@oK43h1=C_-8-T3XIBfovX zc$VYluN08Y{&_NfyYR$sUpJoJ`0YZ`!n^pZzWWPeTz-2E#G!w?<=Y30=W+S%LJ{>h zW~p2y|4*FYTlx2wPE z?Vrcxw+lCZyHK=n_x>H=pObO@l^efZbmX@W@Xwz4?Pk8+jo&UNq(YXBf8pvkJs?Ya7-MRTHNRs-2Qo_q1z z4{&wD>z6MY8+hIL?NLX5yU~xG@cPYP5niYDVAPS{J_N7d^`JSP-T3WMFMfN!@jR}- za^ts0z4-0D@OtLAoBr92-yZeixA(*A*I$|A*^S>Gb>z1Xu-|d{?KP0i@u(xeeF$E^ z{>mKBZv6JB*ZuAN#`C!TN&#``-){V?0sT8Jzddr}w?`fM?L+YT^;hP2cDuhl>cwyG zHJ-=ix7R>6kLQm3_96W{uD^2Qw?`fM?L+YT^;hQn*^S>G_2Rep8qeeM+iM`3$8$%1 z`+)u(m){<_@!O-0{PrPu{jLX1|Ln$Zk9zUj`;F&u{goTPJ?hABAJV^m{T21ETmGu! z{p|zrdgiyA{@IP+9`)k4_ZrWh`RyiNH-3B6k>5U~f5-J#3drVo)balIA$Z62S8n|F zs29Jz-*_I^U%BzyqmKOc0sT8Jzr6;sc|3RIw-3QP?t0LT-yZeixAz*)p84(gqW0nM z0o6bp`nTKu;E?|H>#qoJ1;hbwpZgj5^{-!lMR?ujSH1Y{z3}?xx0~bH>AZ8)i{IW4 zuU~&<&ad3~?NLX5`vChLm)~9k*=)ZZ`RzmS`t?`l`pE76_NW)Xz2A8D>#qo}o4@MC zZ|{ZIH^1HRR|?4H@!X5w-Vg7%{>qKt9`)k4_rp8xdeH6u_DIpfyYeUd;q~jU%y{I+ zZ;v|i+iRr#@cQ*vgxAeqb>z1Xz&kF#J#xFhJ?h18@72HK^4n`5oBeY~e*2LA_3N)p z{)XHA?UAB|PvWmU@!Nx~2-I$+_tyH?jo)7VR^M{kuo2ha?>(e{{rIc3c-{O}M}B*M zyo&$s;r`fQOM)tC>0hVypzOtOuTkUdRR{j0^u%vpgSQ5<(O=1q_qPw}U%&p!h`%)u z2fS0|ucT*wJK=T8U&)UA_96Z2$6qzJWUL2kAP)UI75_?l=C>1GH-9x5zg>=de|zP$ z9rCeqGJd=Ce1E%XzY56a@jMy7U507QAlrtI7E7()0c8rv19{ z+hxc5+Xu`iJ@eZMubaP`jNdLj-`{THb>p|o$@uNkGryhiI-MVslkwYSqKt zE+^x+%W>~-uYqiiN0af}rB{BtrGMT0)nxp3IWE6ly7AlPWc+qH?)~jGkj>+HGJd-p z_x^V2#&4IC@!RFN_qW$T9QwC1^C|XIq-TD+nZI)5x68@+?b0*9-Nft0Z6kLStw?K1W`zY4aff8ExDlkwYS>^FZ!c-`_>lkwZ-xcqjhfNUPmlkwZ-xc9e9 zH-5XEjNdLj^V`kwtbjQ5?_~Rfp^pW!HwT8 zC*!wE&-b?zUbpq&Wc+sNncq%$-R4)5@!O?me!Cfe-R^IflkwZ-xc9f$KsNj5$@uNk z6TiJ)p;h-3x6XG0jO@!N|uz=NgDZ|4x;^iaqn& zqqMp^zBw^9i`7N+oAFM@Z;!q5+ljvsyYbuO$@uNDXMVfczi#~Ycrt!_?3v%5MNw-! zyYbuO$@uNDXMQ{3by^R`lkwYQ&-`}6>o&idjNcx6=C_;n>&9=7C*!xrUit0#&X!@n z3drVoG#S4=_R4R!;C13N#FO#cW3T*nj=ygF_INUWd+eFtPUG3lUrokuk3I9-O@HOa zZ;vPAx5u9O?S$7U-yToKZ;!q5+i`1wv0iZFx5tz5+hfoCcEam6znYBS9((1tTl&|{ zUrokuk3I9-&Go(;zdfFe-yVDBx0~bH?f&+7GJbpPncq(R>o&idjNcx6=C_;qSOsMB z{CTqb+hfoCcEaoCuO{QS$DaA^CSEswdpsGxJ@(9RC%kU+tI7E7_&%}6{UcreIW6LazmH5;9- z(jrXLGFZ(;5GJs&S)}5)CSjVbefmZa$c_TXt7)Xl9;JblPM# zs12^!=roO^Wg+5nk;Kcn0Q0{2+8edOH5;9>CGC3ldo2(i&CG07uRf0p3AcBr+}JtXZMcdvMKW ze6WY>?hEHpmgRC;W*EweEaF+Sa;Ep-noYk1%S9m<`J%{0o~1!CYff8J8(g#L7m+7% zxhl&gUL%jOylYl?)CSko{Sp?bfI~(0H(cNZP;)An+K57PSqb{rL*V2?XMHyo^PW&u7io3#bjQ+4L=2^g1kDv%$iiWnH}GnoYmV7eN}W zg82g93R$MhV%B__fU!``W{j~{SQqEG=7By$u`1)NnwuhbwJK-Li3)m;CDm-Outl%a zDc5XtD&`69W?Cd^oCnJ#Si0)ShRx=KN> zQ-2L}oZ47X&8A<%Vj0Zwd^sl#lx{yHQx&B=TH$y+I0a- z(Jb1fz}2jh4m}OP)Ruj_E|95>?RF`ksqNo(DX^(c(pG7;xNqj%`f1ftTWz?t>jInF zmTH#*o7%i-mjavG;%JuwTQie9^fX{o>GF16U{guub}6u_)N8vG*i?eDRSGSJ%^XWV z4eLxSEX*E)ZZW;5QeaaT1GL)(Hg$ZyT?%Z??BmeWfK466Zr24ibzr$&3T*0FZ@U!O z)Zy1wDf%pI;^Fa=dv}SOqV#pAuKVS!>l-6f2P)NXND2+8fG5e+x~iLq>KfG^UQ^*rm{%I>2I=<};yP5^E!X_+UV=IE46MWUv^S z9PO}Jj0B5zT`ZQ!B^@|17RL}D@oLDtXvfCCP9Z*UaV$puNIN?gLu=aWkr*PO-5-mg zHSGsk46SL0$YN+sdqx&RYuZJ!7+TZ5lEu)PcA6}P*0lFzF|?-LD2t&r?N3RJw-(y5 zvKU&^9+t(>ns&7;hSs#tWihm-oiB@_HSL9246SK*%wlLw`(+kaxypmU%3^3udukFx zp+dWC7DH>=ce5B;(@vbl(3`nVoJJP{`qzlG0fK^9Rv8+P?(MZEQZE(3}7)-regq$p)(x=SPZ4<7{Fp^O~(Kd zqa!0716T~L=@`IbXidie7DH<~2Cx`f(=mX>(3*|`EQZ!}3}7*|regq$p*0-?SPZS{ z7(il-GbCmkkHTVTO~(KhLu)z)uozm?F@VL;nvMZ1hSqcpU@^3&V*rbxH5~(346W%H zz+z}k#{d$;1R@;+SPZS{7{Fp^O~(MaC2m?=aci=?`qpLRDs>1TY1Q&2V*rbxG#vw246W%HKw@C7 zFNwFS1u2sHPW3fnXidieZX8OGuHfCLcHGPVl%qF zkzviR6NuM`0xWRq^Zsjojm_>Of&n)6>eJ-*U&FrY!`ExXA;jxn4dz!L?OpS0Xzhq- z0PCy|j;{GNw01-^fbrJH64(41T00^dz_#l{Z)<)HtsM~!VEXluD*Laoxm=$>S|f(m zj)(@>R<6&ttob#xc0@G5W^jFaV$H9iwIiYdws321{+eG`#ESLPA#C2(%y0Xz;gxEl z^cpd=rdI@H{%~3~4R_72p|vBT0eo6b^1PuUN#g(I&a%46KUSoM=1%_`Pwm}S&Mvv? z^2?9UUUg@A`p#43U0b&=S3iGj`|>k;CwEWJF1dbpr^H{6pQ%}vm%Q@K;?#0+vN*km z(qAg?zDwPL)Kt6jbXn|GKU39a$9GqIcNM40*|j^1Ez}3gb9rV_u9d5wpW5BIs@i9} zB?fSxNY|qyaFhtcg*S?V*Qh| zr_0qbbXSrd!#z&>e?S1Kjx4Tbbs1b*HSMVW%y#NB5QT!uT^ue`f5DEcQ5IH=$5t8k zcdD#}QKpQzRffD|6}XKuweD$^MYyKbd>5>SD|S(<9&UH37@X}gkyfldqb#qM5$!S% zLa04_J_E6!T@QoLd}q+ts|#{k^^!2Hru{})Ua?i$WocM3&x|s)7-^Mdfl5snWpQ<} zR=W%-AG5s-i3bx$jtx7rUXE=lqh1(>>ageGXP_)K^%r7ivSI_a>tUT}=m5>($!0w$ z%g`wpQ{d+r^`cDeGPa(9FC_# zw3;v)Wx45_u-}(eYn)a+1fPl(+%6NMV#gU}YVOu9L$Zq6#cAi9R=LzBrx5sv#U?Ya>v7IN_L!f&2{58EFQr}#U?L0xcmg}*dCrB}JJ+|`% zX)&sYd-mx4FoEWJZ0A^kG7dO=ZjbF8wb(vj==5B$bg4ZIK(1%8b0~)ES?oNq*g59+ z{9KEjrxrWMxlDR)YO(XwV&|#F&Qpt>W8FjVhe^!&Fd2X@u++yds#m;k!onxs%IC6`f=WOR`z;<384l>(IksC7mBMsQj zQ!F;P9@}{uu$^Q05e}>`$v#r7UFiKlai-^D+RpXZ&aw0(`@o4)#u2ie!#v17ko934 z)vCmpm!yL29M;P?a0ZrefDXp(v7M)&Fj2jT?K}nfhU>ANr<}V%MvLtn(^JNQgbCrm z<~%(YxdN((6du=OJBJ%$97xJ=du-=8%)#Rd+fMXcTtiOnVXK7ev7O^&F5`&V&QqkM zxjnY?G-f-;nHI)@$sV=TWvDkTHv2!>RdOsK;JvX)3d1|q9 zoC@UjEOwq+>>NobdTwU1^UPxBnZ?eLnBw*qUaIi@d+BV;>=Mj1!QcAnuF0KFeJbO#Gq@hxc?OCw z*JC@+Kx?IX5!*S^2aF?PJI_EQ?Gan{nV;eR?kPaMYe950_c;a7f284oe;mZ^z@xl84JIc{sQ_#*tX;91Bsx zVadbcHWTg-OCAopDcm2HJRJ4|xIZj;xXhA=%W!Q9^#_hK6Aq;Qxt_(&EqOSk!x)Dp z50_c;aL@qz-OOU=xX*^(&yt73ZUE!3 zl84I^i=A8YaJeN9hqMp(hb0e}Tk>#lliVMcJX~(c!{wGd9H#u-AC^2E(o) zjxw@ylu zGK%LYBRfYK#dDNVJjba`i=A^G4)rW{&UrZ0v)DQ3;ZV}ai^KkgB#m+eohk6z}=R6!N-(u&S zheJJ!on!K2%u7(uV&|NPLp`>0uz1Wkhy%iG=eS$PTt}fE+j+=&IMic14+FMyT)<<_ zOK^!`QW5SCb0JXH^09lTi&M9ir;n*Sf=;~wSN(xtynVy&?jHUN94d83>&|Kye*;qr ce*+`=jy-Uz6(1R`p^J{a_{CSh>Y8K!2ZK3YkN^Mx diff --git a/dns/docs/spec-document.pdf b/dns/docs/spec-document.pdf deleted file mode 100644 index e2c1b67b87ef37d50b0154235b661e8f74315d19..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 133929 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 &XgF`qHd`tbDd{aO0g#*Hj_IntH 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 0: - print "Found public-website relation, generating dns template" - webhead_data = unserialize_data('data/proxy-hosts.json') - dns_data = c.build_dns(webhead_data) - - push_data_to_dns(dns_data) - - web_relation = hookenv.relations_of_type('public-website') - for rel in web_relation: - hookenv.relation_set(rel['__relid__'], {'dns-data': dns_data}) - - -def push_data_to_dns(rel_data): - # push data to DNS provider - class_string = "{}.provider.Provider".format(hookenv.config('provider')) - provider = load_class(class_string) - p = provider(hookenv.config('domain')) - p.add_record(rel_data) - - -if __name__ == "__main__": - hooks.execute(sys.argv) diff --git a/dns/hooks/install b/dns/hooks/install deleted file mode 100755 index 1d68bb0..0000000 --- a/dns/hooks/install +++ /dev/null @@ -1,9 +0,0 @@ -#!/bin/bash - -set -eu - -easy_install pip -pip install charmhelpers jinja2 pydns - -hooks/install_core - diff --git a/dns/hooks/install_core b/dns/hooks/install_core deleted file mode 100755 index 56ecc9e..0000000 --- a/dns/hooks/install_core +++ /dev/null @@ -1,61 +0,0 @@ -#!/usr/bin/python -import os -import subprocess -import sys - - -# Add charmhelpers to the system path. -sys.path.insert(0, os.path.abspath(os.path.join(os.environ['CHARM_DIR'], - 'contrib'))) - -from charmhelpers.core.hookenv import ( - log, - config, -) -from charmhelpers.fetch import ( - apt_install, - apt_update, -) - - -from common import install_packages, sanity_check, load_module -# from bind.install import ProviderInstaller - - -class Install(object): - - # Does not assume offline environment by default - def install_core(self): - if not sanity_check(): - return 0 - - # TODO: Check offline, install fatpack - if config()['offline'] is False: - apt_update(fatal=True) - apt_install(packages=['python-pip', - 'libxml2-dev', - 'libxslt-dev'], fatal=True) - - subprocess.call(['pip', 'install', '-r', 'contrib/requirements.txt', - '--upgrade']) - else: - log('Installing Core Packages from Offline Pack') - install_packages('files/core') - - # Place the offline environment as a config option - # Place the files in data/ and the provider/charm will look to install this - # information from there. - - def install_provider(self): - self.config = config() - - # something is weird here, its trimming the last bit off of the module. - class_string = "{}.install.install".format(self.config['provider']) - provider = load_module(class_string) - provider.install() - - -if __name__ == '__main__': - i = Install() - i.install_core() - i.install_provider() diff --git a/dns/hooks/programmable-multiple-relation-changed b/dns/hooks/programmable-multiple-relation-changed deleted file mode 100755 index 28b6e68..0000000 --- a/dns/hooks/programmable-multiple-relation-changed +++ /dev/null @@ -1,50 +0,0 @@ -#!/usr/bin/python -import json -import os -import sys -# Add charmhelpers to the system path. -sys.path.insert(0, os.path.abspath(os.path.join(os.environ['CHARM_DIR'], - 'contrib'))) - -from charmhelpers.core.hookenv import ( - config, - log, - unit_get, - relation_get, - relation_set, - relation_id, -) - -from common import trim_empty_array_elements as trim -from common import resolve_hostname_to_ip as getip -from common import load_class - -class ProgrammableChanged(object): - - def __init__(self): - self.config = config() - relid = relation_id() - relation_set(relid, {'public-address': - getip(unit_get('public-address'))}) - self.add_resource() - - def add_resource(self): - if not relation_get('resources'): - log("No rr sent on the wire. Doing nothing. You may need to remove" - " and re-add the relationship to trigger this hook again.") - return - - domain = relation_get('domain') - - resources = json.loads(relation_get('resources')) - if not domain: - domain = config()['domain'] - - class_string = "{}.provider.Provider".format(self.config['provider']) - provider = load_class(class_string) - p = provider(domain) - p.add_record(resources) - - -if __name__ == '__main__': - c = ProgrammableChanged() diff --git a/dns/hooks/programmable-relation-changed b/dns/hooks/programmable-relation-changed deleted file mode 100755 index 1de0a97..0000000 --- a/dns/hooks/programmable-relation-changed +++ /dev/null @@ -1,47 +0,0 @@ -#!/usr/bin/python -import os -import sys -# Add charmhelpers to the system path. -sys.path.insert(0, os.path.abspath(os.path.join(os.environ['CHARM_DIR'], - 'contrib'))) - -from charmhelpers.core.hookenv import ( - config, - unit_get, - relation_get, - relation_set, - relation_id, -) -from common import resolve_hostname_to_ip as getip -from common import load_class - - -class ProgrammableChanged(object): - - def __init__(self): - self.config = config() - relid = relation_id() - relation_set(relid, - {'public-address': getip(unit_get('public-address'))}) - if not relation_get('rr'): - return - self.add_resource() - - def add_resource(self): - domain = relation_get('domain') - alias = relation_get('alias') - addr = relation_get('addr') - rr = relation_get('rr').upper() - if None == domain or len(domain) == 0 or None == rr or len(rr) == 0: - return - - parsed = {'domain': domain, 'alias': alias, 'addr': addr, 'rr': rr} - - class_string = '{}.provider.Provider'.format(self.config['provider']) - provider = load_class(class_string) - p = provider(domain) - p.add_record(parsed) - - -if __name__ == '__main__': - c = ProgrammableChanged() diff --git a/dns/hooks/programmable-relation-departed b/dns/hooks/programmable-relation-departed deleted file mode 100755 index 7e496cc..0000000 --- a/dns/hooks/programmable-relation-departed +++ /dev/null @@ -1,40 +0,0 @@ -#!/usr/bin/python -import os -import sys - -sys.path.insert(0, os.path.abspath(os.path.join(os.environ['CHARM_DIR'], - 'contrib'))) - -#from bind.provider import BindProvider - -from charmhelpers.core.hookenv import ( - log, - config, - unit_get, - relation_get, -) - -from common import load_class - -class ProgrammableBroken(object): - - def __init__(self): - self.config = config() - self.provider = self.remove_record() - - - def remove_record(self): - domain = relation_get('domain') - alias = relation_get('alias') - addr = relation_get('addr') - rr = relation_get('rr').upper() - - parsed = {'domain': domain, 'alias': alias, 'addr': addr, 'rr': rr} - - class_string = "{}.provider.Provider".format(self.config['provider']) - provider = load_class(class_string) - p = provider() - p.remove_record(parsed, domain) - -if __name__ == '__main__': - c = ProgrammableBroken() diff --git a/dns/hooks/public-website-relation-changed b/dns/hooks/public-website-relation-changed deleted file mode 100755 index 375e371..0000000 --- a/dns/hooks/public-website-relation-changed +++ /dev/null @@ -1,62 +0,0 @@ -#!/usr/bin/env python -from charmhelpers.core import hookenv -import json -import os -import sys - -sys.path.append(os.path.abspath(os.path.join(os.environ['CHARM_DIR'], - 'contrib'))) - -from common import serialize_data, unserialize_data, load_class -from consul_client import ConsulClient - -hooks = hookenv.Hooks() - -@hooks.hook('public-website-relation-changed') -def website_changed(): - relid = hookenv.relation_id() - - if not hookenv.relation_get('public-address'): - log("Relationship incomplete, missing public-address. Exiting") - return - - serialize_proxy_data() - domain = hookenv.config('domain') - hookenv.relation_set(relid, {'domain': domain}) - - - consul_relations = hookenv.relations_of_type('consul') - if len(consul_relations) > 0: - rel_data = action_on_consul_service_data() - hookenv.relation_set(relid, {'dns-data': rel_data}) - -def serialize_proxy_data(): - # grab an object of the relationship - reldata = hookenv.relation_for_unit() - # no slashes in our names - unitkey = reldata['__unit__'].replace('/', '-') - - # build the config object for cache - proxy_data = {'public-address': hookenv.relation_get('public-address'), - 'port': hookenv.relation_get('port')} - - proxy_hosts = unserialize_data('data/proxy-hosts.json') - proxy_hosts[unitkey] = proxy_data - serialize_data('data/proxy-hosts.json', proxy_hosts) - - -def action_on_consul_service_data(): - print "Discovered consul-relationship, reacting to found data" - client = ConsulClient(filepath='data/consul-host.json') - rel_data = client.build_dns(unserialize_data('data/proxy-hosts.json')) - - # push data to DNS provider - class_string = "{}.provider.Provider".format(hookenv.config('provider')) - provider = load_class(class_string) - p = provider(hookenv.config('domain')) - p.add_record(rel_data) - - return rel_data - -if __name__ == '__main__': - hooks.execute(sys.argv) diff --git a/dns/hooks/public-website-relation-departed b/dns/hooks/public-website-relation-departed deleted file mode 100755 index 731f34b..0000000 --- a/dns/hooks/public-website-relation-departed +++ /dev/null @@ -1,33 +0,0 @@ -#!/usr/bin/env python -from charmhelpers.core import hookenv -import json -import sys - -hooks = hookenv.Hooks() - -@hooks.hook('public-website-relation-departed') -def website_departed(): - relid = hookenv.relation_id() - - if not hookenv.relation_get('public-address'): - log("Relationship incomplete, missing public-address. Exiting") - return - - # grab an object of the relationship - reldata = hookenv.relation_for_unit() - unitkey = reldata['__unit__'].replace('/', '-') - - with open('data/proxy-hosts.json', 'r') as f: - proxy_hosts = json.loads(f.read()) - - proxy_hosts.pop(unitkey, None) - - with open('data/proxy-hosts.json', 'w+') as f: - f.write(json.dumps(proxy_hosts)) - - # TODO: trigger the re-evaluation of the json parsing, and transmission - # of DNS data back to the upstream DNS provider. - - -if __name__ == '__main__': - hooks.execute(sys.argv) diff --git a/dns/icon.svg b/dns/icon.svg deleted file mode 100644 index b1a988b..0000000 --- a/dns/icon.svg +++ /dev/null @@ -1,287 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - - - - - - - - - diff --git a/dns/local-dev-standup.yaml b/dns/local-dev-standup.yaml deleted file mode 100644 index 9c94799..0000000 --- a/dns/local-dev-standup.yaml +++ /dev/null @@ -1,14 +0,0 @@ -dns-test: - services: - dns: - charm: local:trusty/dns - options: - provider_keys: AWS_ACCESS_KEY_ID|XXXX AWS_SECRET_ACCESS_KEY|XXXX - provider: rt53 - dtest: - charm: local:trusty/dtest - relations: - - ["dns:programmable", "dtest:programmable"] - - ["dns:programmable-multiple", "dtest:programmable-multiple"] - series: trusty - diff --git a/dns/metadata.yaml b/dns/metadata.yaml deleted file mode 100644 index 7ad84b6..0000000 --- a/dns/metadata.yaml +++ /dev/null @@ -1,21 +0,0 @@ -name: dns -summary: Deploys a DNS server or wrapper to manage service name resolution -maintainer: charles -description: | - Deploys BIND9, and exposes a programmable API to set DNS records, alternatively - utilizes a plugin architecture to extend the charm to other API based DNS services. -categories: - - Applications -subordinate: false -provides: - programmable: - interface: dns - programmable-multiple: - interface: dns-multi - auto-generated: - interface: dns-auto -requires: - consul: - interface: consul - public-website: - interface: public-http diff --git a/dns/pytest.ini b/dns/pytest.ini deleted file mode 100644 index 5170375..0000000 --- a/dns/pytest.ini +++ /dev/null @@ -1,2 +0,0 @@ -[pytest] -addopts = --tb=short -s --cov=contrib --cov-report=term-missing --cov-config .coveragerc diff --git a/dns/tests/00-setup b/dns/tests/00-setup deleted file mode 100755 index cd5b3a2..0000000 --- a/dns/tests/00-setup +++ /dev/null @@ -1,5 +0,0 @@ -#!/bin/bash - -sudo add-apt-repository ppa:juju/stable -y -sudo apt-get update -sudo apt-get install amulet python3-requests -y diff --git a/dns/tests/local-bundle.yaml b/dns/tests/local-bundle.yaml deleted file mode 100644 index 708e5a8..0000000 --- a/dns/tests/local-bundle.yaml +++ /dev/null @@ -1,13 +0,0 @@ -dns-testing: - services: - dtest: - charm: local:trusty/dtest - dns: - charm: local:trusty/dns - expose: true - options: - domain: example.com - relations: - - ["dns:programmable", "dtest:programmable"] - - ["dns:programmable-multiple", "dtest:programmable-multiple"] - series: trusty diff --git a/dns/tests/local_formation_test b/dns/tests/local_formation_test deleted file mode 100755 index 1d19eaf..0000000 --- a/dns/tests/local_formation_test +++ /dev/null @@ -1,82 +0,0 @@ -#!/usr/bin/env python3 - -import amulet -import os -import shlex -from subprocess import check_output -import unittest -import yaml - -class TestDeployment(unittest.TestCase): - @classmethod - def setUpClass(cls): - cls.deployment = amulet.Deployment(series='trusty') - - with open(os.path.join(os.path.dirname(__file__), 'local-bundle.yaml')) as f: - bundle = yaml.safe_load(f.read()) - - cls.deployment.load(bundle) - - try: - cls.deployment.setup(timeout=900) - cls.deployment.sentry.wait() - except amulet.helpers.TimeoutError: - amulet.raise_status(amulet.SKIP, msg="Environment wasn't stood up in time") - except: - raise - cls.dns_unit = cls.deployment.sentry['dns/0'] - - - - def test_zone_existence(self): - stat = self.dns_unit.file_stat('/etc/bind/db.example.com') - self.assertTrue(stat['size'] > 0) - - - def test_programmable_multiple_works(self): - contents = self.dns_unit.file_contents('/etc/bind/db.example.com') - self.assertTrue('sprout' in contents) - - - def test_programmable_works(self): - contents = self.dns_unit.file_contents('/etc/bind/db.example.com') - self.assertTrue('dtest' in contents) - - def test_resolution_with_programmable(self): - dns_server = self.dns_unit.info['public-address'] - test = self.dns_unit.run('unit-get private-address')[0] - cmd = "dig @{} dtest.example.com".format(dns_server) - out = check_output(shlex.split(cmd)) - self.assertTrue(test in str(out)) - - - - - - - # Now you can use self.deployment.sentry.unit[UNIT] to address each of - # the units and perform more in-depth steps. You can also reference - # the first unit as self.unit. - # There are three test statuses that can be triggered with - # amulet.raise_status(): - # - amulet.PASS - # - amulet.FAIL - # - amulet.SKIP - # Each unit has the following methods: - # - .info - An array of the information of that unit from Juju - # - .file(PATH) - Get the details of a file on that unit - # - .file_contents(PATH) - Get plain text output of PATH file from that unit - # - .directory(PATH) - Get details of directory - # - .directory_contents(PATH) - List files and folders in PATH on that unit - # - .relation(relation, service:rel) - Get relation data from return service - # add tests here to confirm service is up and working properly - # For example, to confirm that it has a functioning HTTP server: - # page = requests.get('http://{}'.format(self.unit.info['public-address'])) - # page.raise_for_status() - # More information on writing Amulet tests can be found at: - # https://juju.ubuntu.com/docs/tools-amulet.html - pass - - -if __name__ == '__main__': - unittest.main() diff --git a/dns/tox.ini b/dns/tox.ini deleted file mode 100644 index e4679a4..0000000 --- a/dns/tox.ini +++ /dev/null @@ -1,25 +0,0 @@ -[tox] -envlist = py27 -skipsdist = True - -[testenv:contrib] -install_command = pip install {opts} --pre --use-wheel {packages} -deps= - pytest - pytest-cov - pytest-capturelog - mock - jinja2 - charmhelpers - six - pyyaml - boto - path.py - python-consul - -passenv = AWS* -setenv = - PYTHONPATH = {toxinidir}/contrib - -commands= - py.test {posargs} diff --git a/functest-abot-epc-bundle/README.md b/functest-abot-epc-bundle/README.md deleted file mode 100644 index a477d36..0000000 --- a/functest-abot-epc-bundle/README.md +++ /dev/null @@ -1,95 +0,0 @@ -# Charms Overview - -Abot-VOLTE bundle deploys abot-volte-basic along-with Oai-hss, Oai-epc , Mysql, Clearwater-homestead, Clearwater-bono, Clearwater-sprout, and dns charms, which support deployment and scaling of OAI charms and Clearwater charms along with ABot developed by Rebeca. - -ABot should typically be deployed alongside the System Under Test(SUT) - such that it can execute tests against the latter. One of the possible SUTs is the OAI EPC, which is an open source initiative of Eurecom. - -ABot enables users to create **feature files** based on a **Cucumber** framework. Cucumber is a Behavior Driven Development (BDD) framework which is used to write tests for different types of applications. It allows automation of functional validation in an easily readable and understandable format (like plain English). It is used to test the system rather than testing the particular piece of code. - -The feature files authored by the user(s) can subsequently be used for testing different types of applications/protocols that are running on the System Under Test(SUT). The feature files follow a specified grammar and include instructions that translate into sending messages to the SUT, and receiving back responses using different protocols and verification of the results as defined. - -The current protocol drivers(adapters) provided with ABot are SSH, SFTP, HTTP as well as S1AP. - -Each feature file can be associated with one (or more) tags; thereby enabling it to be selectively invoked. **Execution of feature file(s) associated with one (or more) specified tags is driven through a Juju action after deployment of the ABot charm.** - -The result of each test execution is displayed in a web-based reporting UI; this is currently accessible on port 80. - -# Usage - -Before deploying this bundle, you should bootstrap a Juju environment, as documented in the https://jujucharms.com/docs/stable/getting-started. The testing of this charm on MAAS cloud is complete, and there are plans to test it on Amazon and OpenStack in the near future. If you get it working on a different cloud service, please let us know! - -OAI is a relatively simpler bundle consisting of Mysql, Oai-hss, Oai-epc and Abot-EPC-basic, together forming the SUT for feature file execution. The ABot OAI EPC bundle has been integrated and tested on MAAS cloud. You can find the ABot OAI EPC bundle at [ ]. To know more about ABot please visit charmstore (https://jujucharms.com/u/abotcharm/abot-volte-basic/xenial/). - -The ABot OAI EPC bundle ties up all the above mentioned charms and their relations together, allowing the deployment of a full system in a single step. - ->juju deploy [bundle_name] - - -# Configuration - -See [bundle.yaml](bundle.yaml), which lists the main configuration fields with comments about their meanings. - -# Files downloaded - -When the above mentioned charms are being installed, the following files are downloaded from their respected paths: - -- The ABot Debian package, from the public package repository server hosted at Rebaca. -- Any dependencies related to Debian packages (particularly Java and Maven), are downloaded from the standard Ubuntu repository servers. -- Any dependency related to OAI charms are downloaded from their respective repositories. - -# Test Case Execution - - - After deployment of the ABot OAI EPC bundle, the following actions can be quickly executed to verify whether the bundle works fine: -> juju run-action abot-volte-basic/[abot-unit-number] run - - - The following script needs to be executed from the Juju controller in order to generate and add the ssh keys to all the ABot OAI EPC bundle units. - -> charm pull cs:~abotcharm/xenial/abot-volte-basic -> ./abot-volte-basic/lib/scripts/add-keys.sh - -- In order to execute a particular feature file or a set of feature files we need to execute the following commands mentioned below, replacing the tag name mentioned below with the required tag name. Also in the following commands we need to replace the [abot-unit-number] with the current abot-unit-number. - -> juju run-action abot-volte-basic/[abot-unit-number] run tagnames=[tag name] - -In order to run EPC attach test case feature file: -> juju run-action abot-volte-basic/8 run tagnames=Attach_Procedure_AttachWithIMSI - -In order to run EPC MAC Failure test case feature file: -> juju run-action abot-volte-basic/8 run tagnames=Auth_NotAccept_by_UE_GUTIattach_MAC_code_failure - -In order to run EPC SQN Failure test case feature file: -> juju run-action abot-volte-basic/8 run tagnames=Auth_NotAccept_by_UE_SQN_failure - -In order to run EPC negative test cases in multiple feature files: -> juju run-action abot-volte-basic/8 run tagnames=negTCs - -In order to run 3GPP TS 24.301 test cases in multiple feature files: -> juju run-action abot-volte-basic/8 run tagnames=TS_24_301 - -- The following action will execute all test cases (feature files) for which the tags have been added through the previous command. Please note that the tags can be run from a specified filename as well. - -> juju run-action abot-volte-basic/[abot-unit-number] run tagnames=[feature file name without extension] - -- Execute tags from the file titled ***negTCs***. This file includes tags for negative call scenarios; such as *sync failure*, *MAC failure* and so on. -> juju run-action abot-volte-basic/[abot-unit-number] run tagnames=negTCs - -# Adding new Test Cases (Feature Files) - -- **New Feature Files should be added into the following folder** on the Juju controller node -> charm pull cs:~abotcharm/trusty/abot-ims-basic -> $PWD/abot-volte-basic/lib/featureFiles - -- The Juju charm upgrade action should be invoked to propagate these new feature files onto the deployed service node. - -> juju upgrade-charm --path=$PWD/abot-volte-basic abot-epc-basic - -- Once the upgrade is successful, the test case execution steps can be followed to execute one or more feature files. - -# Viewing Test Report - - - The report UI can then be viewed by accessing the below mentioned URL. - http://[abot-ip]/app - Eg : http://192.168.15.87/app/ - -- This pulls up the Cucumber report of the latest execution. -- Accessing any of the feature files present in the report shows the step-wise execution status for that feature file. diff --git a/functest-abot-epc-bundle/bundle.yaml b/functest-abot-epc-bundle/bundle.yaml deleted file mode 100644 index 1656d9c..0000000 --- a/functest-abot-epc-bundle/bundle.yaml +++ /dev/null @@ -1,52 +0,0 @@ - services: - "oai-hss": - charm: /src/epc-requirements/abot_charm/oai-hss - num_units: 1 - constraints: "arch=amd64 mem=2G" - series: trusty - options: - operator-key: "1006020f0a478bf6b699f15c062e42b3" - random: "true" - annotations: - "gui-x": "400" - "gui-y": "900" - "oai-epc": - charm: /src/epc-requirements/abot_charm/oai-epc - num_units: 1 - constraints: "arch=amd64 mem=4G" - series: trusty - options: - gummei_tai_mnc: "93" - annotations: - "gui-x": "400" - "gui-y": "600" - "mysql": - charm: cs:mysql-55 - num_units: 1 - constraints: "arch=amd64 mem=2G" - series: trusty - annotations: - "gui-x": "200" - "gui-y": "300" - abot-epc-basic: - charm: /src/epc-requirements/abot_charm/abot-epc-basic - constraints: "arch=amd64 mem=4G" - series: xenial - expose: true - num_units: 1 - options: - abot_app: "abot-functest-basic" - annotations: - "gui-x": "400" - "gui-y": "700" - relations: - - - "mysql:db" - - "oai-hss:db" - - - "oai-hss:hss" - - "oai-epc:hss" - - - "oai-epc:epc" - - "abot-epc-basic:epc" - - - "oai-epc:ssh-abot-epc" - - "abot-epc-basic:ssh-abot" - series: trusty - diff --git a/oai-epc/README.md b/oai-epc/README.md deleted file mode 100644 index 54a90db..0000000 --- a/oai-epc/README.md +++ /dev/null @@ -1,361 +0,0 @@ -# Overview - -Evolved Packet Core (EPC) is a framework for providing converged voice and data on a 4G -Long-Term Evolution (LTE) network. Evolved Packet Core unifies voice and data on an Internet -Protocol (IP ) service architecture and voice is treated as just another IP application. - -This charm aims to deploy EPC functions of OpenAirInterface (OAI) wireless technology platform, -an opensource software-based implementation of the LTE system developed by EURECOM. It is written in the C programming language. - - -# Usage - -This charm is available in the Juju Charm Store, to deploy you'll need a working -Juju installation, and a successful bootstrap. This charm need to be deployed -with other charms to get a open LTE network made up of LTE base station and core -network. Mysql charm should be related to HSS. The latter should be related -to EPC charm that should be related to either the OAI eNB charms (either simulated or realtime eNB). -In a simulated mode, OAI eNB has no radio frontend and integrates the UE protocol, and the process is denoted as "oaisim". -In realtime mode, OAI eNB has a radio frontend and operates in a realtime mode, and the process is called "lte-softmodem". -The eNB charms and UE charm are under development and will be available in the near future allowing one -to install the software to manage a real antenna. - -Please refer to the -[Juju Getting Started](https://juju.ubuntu.com/docs/getting-started.html) -documentation before continuing. - -__For the time being__ you can use a cloud environment if you have, the manual environment -or the local provider. You could use also your private openstack cloud or MAAS, -but you stick to manual environment if you don't want to add complexity, and want to manually manage your group of machines. -For the local provider you must force juju to create kvm instead of lxc by modifying appropriately the environment.yaml file. -Kvm is needed by EPC software because it deals with kernel modules. -__As soon as the OAI ENB charms will be available__ you will have to use your group of machines to use the right hardware(exmimo2 or usrp). - -## Local provider - -Once bootstrapped, deploy the MySQL charm then this HSS charm: - - juju deploy mysql - juju deploy hss - -Have a look at what's going on: - - watch juju status --format tabular - -Juju creates two KVM nodes with a hss unit and a mysql unit. - -Add a relation between the two: - - juju add-relation hss mysql - -You can deploy in two lxc nodes within a single kvm by refering to the -[LXC Containers within a KVM Guest](https://jujucharms.com/docs/devel/config-KVM#lxc-containers-within-a-kvm-guest) - -To have a look at the hss output: - - juju ssh hss/0 - cat /srv/.out - -Then you could add EPC charm to complete the LTE core network: - - juju deploy epc - -Now you have one unit of epc service named "epc" and a unit of hss service named "hss". - -Add a relation between epc and hss: - - juju add-relation epc hss - -Have a look at the EPC output and see if it is connected to HSS service: - - juju ssh epc/0 - cat /srv/.out - -The order of deployment doesn't matter, so you can't deploy all the charms you want to and then add all the relations afterwards. The order in which relations are added can be whatever you want. - -Then to complete the LTE network you will have the chance to deploy a simulation of enB and UE: - - juju deploy oaisim(WATCHOUT: YOU CAN'T DO THIS YET) - -In local, only a simulation of the enodeB can be deployed. As soon as enodeB charm is -completed you'll need to deploy on a machine with an antenna so manual provisioning can be appropriate. - -## Manual environment - -Deployment example: all KVM nodes in one physical machine(juju bootstrap node). - -Once bootstrapped, deploy the MySQL charm then this Hss charm: - - juju deploy --to kvm:0 mysql - juju deploy --to kvm:0 hss - -Juju creates two KVM nodes with a unit of hss and a unit of mysql. - -Add a relation between the two: - - juju add-relation hss mysql - -To have a look at the hss unit output: - - juju ssh hss/0 - cat /srv/.out - -Then you could add a unit of EPC charm to complete the LTE core network: - - juju deploy --to kvm:0 epc - -Add a relation between the epc service unit and hss service unit: - - juju add-relation epc hss - -To have a look at the epc output and see if it is connected to hss unit: - - juju ssh epc/0 - cat /srv/.out - -NEAR FUTURE: -Then to complete the LTE network you will have the chance to deploy a simulation of enB -and UE: - - juju deploy --to kvm:0 oaisim(WATCHOUT: YOU CAN'T DO THIS YET) - -Or a real enodeB charm(WORK IN PROGRESS) to deploy on a machine equipped with the right -hardware(antenna). - -### Group of services - -Consider to deploy directly against physical machines because the KVM that juju creates -are behind a NAT bridge. In fact if you want to use kvm, you should create some kvm containes -outside of juju with proper networking and add-machine to juju. - - - juju deploy --to kvm:0 mysql - juju deploy --to 0 hss - - - juju deploy --to 1 epc-rome - juju deploy --to 2 epc-nice - juju deploy --to 3 epc-torino - - - juju add-relation hss mysql - juju add-relation epc-rome hss - juju add-relation epc-nice hss - juju add-relation epc-torino hss - -IN FUTURE: - -You could add enodeB services and relate them to a specific EPC service. - - -## Scale Out Usage - -If you need to scale out your epc-rome service(for instance), you can add another unit of epc-rome -by typing: - - juju add-unit epc-rome --to 4 - -Since the relation has been done at service level, the new unit of epc-rome service -knows exactly how to relate and even the configuration options are the same. -As soon as OAISIM charm will be available, you can deploy OAISIM charm and then -relate it to epc-rome service: - - juju add-relation oaisim epc-rome - -Oaisim will be related to both units of epc-rome service. - -__More on scale out when oaisim or enodeb charm will be available.__ - - -## Known Limitations and Issues - -### Important - -__Don't use the epc relation hooks. They will be usefull only when enodeB charm will be available.__ - - * **Removing relation between hss service and mysql service(consider the simple case in which we have only one service of hss charm and for this service we have deployed only one unit. Same for mysql service)** - - - juju remove-relation hss mysql - -If you need to remove the relation between hss service and mysql service, HSS sofware -is stopped and so EPC running software fails to connect to HSS that is put in a zombie state. For this reason db-relation-departed hook execution triggers hss-relation-changed hook on EPC side that stops EPC sofware. As soon -as you re-add a relation with a mysql service, HSS process will be restarted and the db-relation-changed hook execution will trigger hss-relation-changed hook in each EPC unit that will start EPC sofware again. - - __Be aware that the new mysql unit doesn't have the old data, but simply the mme entries to allow the MMEs to connect to hss__ - -TO DO. Review what just described when OAISIM charm will available. - - * **Removing relation between epc service and hss service** - - juju remove-relation epc-rome hss - -Each EPC unit's sofware of the chosen EPC service will be stopped and HSS will be removing the MMEs -from the database. HSS process remains active because you might -have more EPC services(epc-rome, epc-turin, epc-nice) using -the same HSS so we don't want to break the connections. - -In future there will be the explanation on what's going on on U-TRAN side(enodeB) as soon as the -enodeB charm will be available. - - * **Removing HSS unit (for deploying the unit on another machine)** - - - juju remove-unit hss/0 - -When you remove a HSS unit, the relation hooks are -called concurrently in EPC and HSS for the hss relation and in HSS and MYSQL for the db -relation. If you remove a unit that still has active relations can be dangerous. So it is better to remove first all the relations(db, hss: no matter the order) and -then remove the unit. -([Removing services, units and environments](https://jujucharms.com/docs/stable/charms-destroy#removing-services,-units-and-environments)) -Anyway at the end the stop hook is called to stop HSS software before juju removes the unit -from the environment. -How to act: - - juju deploy --to ... hss (somewhere) - juju add-relation epc-rome hss - juju add-relation epc-nice hss - juju add-relation hss mysql - - * **Removing EPC unit (for redeploying the unit on another machine: scaling up)** - - When you remove a EPC unit (juju remove-unit epc-rome/1 for example) the relation hooks are - called concurrently in EPC and HSS to handle hss relation. - Then the stop hook is called to stop EPC process before juju removes the unit from the - environment. How to act: - -1) In the case you had more than one unit of epc-rome service: - - - juju add-unit --to ... epc-rome (somewhere) - -No need to add-relation. - -2) In the case you had only one unit of a service: - - - juju deploy --to ... epc-rome - juju add-relation epc-rome hss - -In future you probably will need to do: - - juju add-relation oaisim epc-rome - - * **Upgrading HSS service** - - - juju upgrade-charm hss - -When you upgrade HSS charm(juju upgrade-charm hss) HSS software is stopped, rebuilt and restarted. -Each EPC unit fails to connect to HSS and doesn't reattempt to connect. As soon as HSS is running(see /srv/.out) -you restart EPC process: - - juju do action epc/0 restart - -Do it for each EPC unit connected to the HSS unit. - -This action must be done only if there is a relation between EPC and HSS. - - * **Upgrading EPC service** - - - juju upgrade-charm epc-nice - -When you upgrade epc-nice service, EPC process is stopped, rebuilt and restarted. - -__You will have to take care about the behaviour of the U-TRAN side. More on this when enodeb charm will be available__ - -# Configuration - -You can tweak various options for your EPC deployment: - - * realm - Diameter realm of the MME. HSS and EPC have to have the same. NO empty value. - - * eth - This is usefull especially when you are in manual environment so you are - your own machines. The default value is eth0. - - * maxenb - Maximum number of eNB that can connect to MME. - - * maxue - For debug purpose, used to restrict the number of served UEs the MME can handle. - - * relative-capacity - Even though this parameter is not used by the MME for controlling the MME load balancing within a pool (at least for now), the parameter has to be forwarded to the eNB during association procedure. Values going from 0 to 255. - - * mme-statistic-timer - Displayed statistic (stdout) period. You can access the stdout: cat /srv/.out on the machine where EPC charm is deployed. - - * emergency-attach-supported - - - * authenticated-imsi-supported - - - * verbosity - asn1 verbosity is..... Valid values are "none", "info", or "annoying". - - * gummei-tai-mcc - TAI=MCC.MNC:TAC. MCC is the Mobile Country Code. - - * gummei-tai-mnc - TAI=MCC.MNC:TAC. MNC is the Mobile Network Code. - - * gummei-tai-tac - TAI=MCC.MNC:TAC. TAC is the Tracking Area Code. - - * ipv4-list-start - lower bound of the IP address range that will be assigned to UEs. - - * ipv4-list-end - upper bound of the IP address range that will be assigned to UEs. - - * ipv6-list - Ipv6 addresses available to be assigned to UEs. - - * DEFAULT-DNS-IPV4-ADDRESS - IPv4 address of primary default DNS that can be queried by UEs. - - * DEFAULT-DNS-SEC-IPV4-ADDRESS - IPv4 address of secondary default DNS that can be queried by UEs. - -Each option can be changed before deployment by providing a "myconfig.yaml" to your deployment command with the value you want each option to take: - - - juju deploy --to 7 --config /home/myconfig.yaml epc epc-berlin - - -Each option can be changed by running: - - juju set