From 547aebd1df695e4dbb138e80b46277c77296fbb6 Mon Sep 17 00:00:00 2001 From: Frank Kloeker Date: Tue, 13 Jun 2017 10:46:10 +0200 Subject: [PATCH 01/72] lookup role for ecs --- README.md | 4 ++++ roles/lookup_name/tasks/main.yml | 20 +++++++++++++++++--- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 0443010..dac58bd 100644 --- a/README.md +++ b/README.md @@ -343,6 +343,10 @@ lookup id by name (zone) ansible-playbook -i hosts lookup_name.yml -e "zone_name=example.com." --vault-password-file vaultpass.txt +lookup id by name (ecs) + + ansible-playbook -i hosts lookup_name.yml -e "ecs_name=ansible-test01" --vault-password-file vaultpass.txt + list provided database versions for RDS ansible-playbook -i hosts rds_versions.yml --vault-password-file vaultpass.txt diff --git a/roles/lookup_name/tasks/main.yml b/roles/lookup_name/tasks/main.yml index 97f8e9f..5922fc2 100644 --- a/roles/lookup_name/tasks/main.yml +++ b/roles/lookup_name/tasks/main.yml @@ -119,6 +119,20 @@ zone_id: "{{ (zonelist_result.content|from_json)|json_query(\"zones[?name=='\" + zone_name + \"'].id|[0]\") }}" when: zone_name is defined -- debug: - msg: "{{ zone_id }}" - when: zone_id is defined +- name: Request ecs list from API + uri: + url: "{{ AUTH_URL_ECS }}/servers" + method: GET + return_content: yes + validate_certs: yes + HEADER_Content-Type: "application/json" + HEADER_X-Auth-Token: "{{ token['x_subject_token'] }}" + register: ecs_result + +- set_fact: + ecs_id: "{{ (ecs_result.content|from_json)|json_query(\"servers[?name=='\" + ecs_name + \"'].id|[0]\") }}" + when: ecs_name is defined + +#- debug: +# msg: "{{ ecs_id }}" +# when: ecs_id is defined From 84f0bbe322e1d5742edd9264449990a6606f9722 Mon Sep 17 00:00:00 2001 From: Frank Kloeker Date: Tue, 13 Jun 2017 11:17:31 +0200 Subject: [PATCH 02/72] migrate ecs show/delete with lookup role --- README.md | 6 +++--- ecs_delete.yml | 1 + ecs_show.yml | 1 + 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index dac58bd..2c05561 100644 --- a/README.md +++ b/README.md @@ -174,15 +174,15 @@ create and start virtual machine with injection user_data show virtual machine (single) - ansible-playbook -e "ecs_id=51b6558a-7a6d-49f4-94e5-f4ec94314746 ecs_name=test05-ansible" -i hosts ecs_show.yml --vault-password-file vaultpass.txt + ansible-playbook -i hosts ecs_show.yml -e "ecs_name=ansible-test01" --vault-password-file vaultpass.txt delete virtual machine (only the machine) - ansible-playbook -e "ecs_id=51b6558a-7a6d-49f4-94e5-f4ec94314746 ecs_name=test05-ansible" -i hosts ecs_delete.yml --vault-password-file vaultpass.txt + ansible-playbook -i hosts ecs_delete.yml -e "ecs_name=ansible-01" --vault-password-file vaultpass.txt delete virtual machine (delete also floating ip and attached volumes) - ansible-playbook -e "ecs_id=f6b7536e-b954-4d73-940f-248de71ce58b ecs_name=test06-ansible delete_publicip=1 delete_volume=1" -i hosts ecs_delete.yml --vault-password-file vaultpass.txt + ansible-playbook -i hosts ecs_delete.yml -e "ecs_name=test01-ansible delete_publicip=1 delete_volume=1" --vault-password-file vaultpass.txt show information about a single virtual machines diff --git a/ecs_delete.yml b/ecs_delete.yml index b4ddeaf..76278eb 100644 --- a/ecs_delete.yml +++ b/ecs_delete.yml @@ -3,4 +3,5 @@ gather_facts: no roles: - role: token + - role: lookup_name - role: ecs_delete diff --git a/ecs_show.yml b/ecs_show.yml index a1b8851..611a51f 100644 --- a/ecs_show.yml +++ b/ecs_show.yml @@ -3,4 +3,5 @@ gather_facts: no roles: - role: token + - role: lookup_name - role: ecs_show From 389421aada2127055d7ecbd01c28807a9abbf058 Mon Sep 17 00:00:00 2001 From: Frank Kloeker Date: Tue, 13 Jun 2017 11:22:13 +0200 Subject: [PATCH 03/72] cleanup README --- README.md | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 2c05561..df3c675 100644 --- a/README.md +++ b/README.md @@ -158,17 +158,17 @@ create and start virtual machine (defined in _ecs_secrets.yml) create and start virtual machine (defined in _ecs_secrets.yml and overwrite options) - ansible-playbook -i hosts ecs_create.yml -e @_ecs_secrets.yml -e "ecs_name=test02-ansible" --vault-password-file vaultpass.txt + ansible-playbook -i hosts ecs_create.yml -e @_ecs_secrets.yml -e "ecs_name=test01-ansible" --vault-password-file vaultpass.txt create and start virtual machine with file injection (inject up to 5 max 1k base64 encoded files) - ansible-playbook -i hosts -e "ecs_fileinject_1=/etc/hosts ecs_fileinject_data_1=$(base64 -w 0 hosts.txt) ecs_fileinject_2=/root/README.md2 ecs_fileinject_data_2=$(base64 -w 0 hallo.txt)" ecs_create.yml --vault-password-file vaultpass.txt + ansible-playbook -i hosts ecs_create.yml -e "ecs_fileinject_1=/etc/hosts ecs_fileinject_data_1=$(base64 -w 0 hosts.txt) ecs_fileinject_2=/root/README.md2 ecs_fileinject_data_2=$(base64 -w 0 hallo.txt)" --vault-password-file vaultpass.txt create and start virtual machine with injection user_data (inject max 32k base64 encoded user-data files) - ansible-playbook -i hosts -e "ecs_user_data=$(base64 -w 0 user-data.txt)" ecs_create.yml --vault-password-file vaultpass.txt + ansible-playbook -i hosts ecs_create.yml -e "ecs_user_data=$(base64 -w 0 user-data.txt)" --vault-password-file vaultpass.txt (!) You can define ecs_fileinject_1, ecs_fileinject_data_1 and ecs_user_data also in _ecs_secrets.yml. Files must be base64 encoded. @@ -185,10 +185,6 @@ delete virtual machine (delete also floating ip and attached volumes) ansible-playbook -i hosts ecs_delete.yml -e "ecs_name=test01-ansible delete_publicip=1 delete_volume=1" --vault-password-file vaultpass.txt -show information about a single virtual machines - - ansible-playbook -e "ecs_id=f6b7536e-b954-4d73-940f-248de71ce58b ecs_name=test06-ansible" -i hosts ecs_info.yml --vault-password-file vaultpass.txt - list elastic loadbalancers ansible-playbook -i hosts elb.yml --vault-password-file vaultpass.txt From 3f4048daa6343102aa28f27725ef0bcfb10a8a8e Mon Sep 17 00:00:00 2001 From: Frank Kloeker Date: Tue, 13 Jun 2017 11:45:19 +0200 Subject: [PATCH 04/72] gatherfacts=no on vpc playbooks --- vpc.yml | 1 + vpc_create.yml | 1 + vpc_delete.yml | 1 + 3 files changed, 3 insertions(+) diff --git a/vpc.yml b/vpc.yml index 48fe3f7..be9fd11 100644 --- a/vpc.yml +++ b/vpc.yml @@ -1,5 +1,6 @@ --- - hosts: all + gather_facts: no roles: - role: token - role: vpc diff --git a/vpc_create.yml b/vpc_create.yml index 1bd50c3..3b9527a 100644 --- a/vpc_create.yml +++ b/vpc_create.yml @@ -1,5 +1,6 @@ --- - hosts: all + gather_facts: no roles: - role: token - role: vpc_create diff --git a/vpc_delete.yml b/vpc_delete.yml index 8358c67..7498250 100644 --- a/vpc_delete.yml +++ b/vpc_delete.yml @@ -1,5 +1,6 @@ --- - hosts: all + gather_facts: no roles: - role: token - role: vpc_delete From beb83d5242c8f7cdcfd18709c532e86289fd7cb6 Mon Sep 17 00:00:00 2001 From: Frank Kloeker Date: Tue, 13 Jun 2017 12:13:45 +0200 Subject: [PATCH 05/72] fix vpc_create condition --- roles/vpc_create/tasks/main.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/roles/vpc_create/tasks/main.yml b/roles/vpc_create/tasks/main.yml index 2b80bb4..4771efc 100644 --- a/roles/vpc_create/tasks/main.yml +++ b/roles/vpc_create/tasks/main.yml @@ -9,7 +9,9 @@ HEADER_Content-Type: "application/json" HEADER_X-Auth-Token: "{{ token['x_subject_token'] }}" body: "{{ lookup('template', 'roles/vpc_create/templates/request.json.j2')|to_json }}" - when: (not vpc_id and vpc_name is defined) + when: + - not vpc_id or vpc_id is undefined + - vpc_name is defined register: vpc - debug: From 8208414733c22b5dbfb050f3ce899fe35645479c Mon Sep 17 00:00:00 2001 From: Frank Kloeker Date: Thu, 15 Jun 2017 15:41:00 +0200 Subject: [PATCH 06/72] add feat snat in vpc --- README.md | 10 ++++- roles/snat_enable/tasks/main.yml | 42 +++++++++++++++++++++ roles/snat_enable/templates/request.json.j2 | 8 ++++ 3 files changed, 59 insertions(+), 1 deletion(-) create mode 100644 roles/snat_enable/tasks/main.yml create mode 100644 roles/snat_enable/templates/request.json.j2 diff --git a/README.md b/README.md index df3c675..07c5ab8 100644 --- a/README.md +++ b/README.md @@ -50,6 +50,7 @@ Roles |elb_backends | list backends for elastic loadbalancer| |elb_backends_create | create backends for elastic loadbalancer| |elb_backends_delete | delete backends for elastic loadbalancer| +|enable_snat | enable SNAT on specific VPC| |endpoints | discover API endpoints| |evs | list volumes| |evs_create | create a volume| @@ -184,7 +185,6 @@ delete virtual machine (delete also floating ip and attached volumes) ansible-playbook -i hosts ecs_delete.yml -e "ecs_name=test01-ansible delete_publicip=1 delete_volume=1" --vault-password-file vaultpass.txt - list elastic loadbalancers ansible-playbook -i hosts elb.yml --vault-password-file vaultpass.txt @@ -249,6 +249,14 @@ delete backends for elastic loadbalancer ansible-playbook -i hosts -e "elb_listener_id=e12454b93f304b759be699cb0270648c elb_backends_id=f6b7536e-b954-4d73-940f-248de71ce58b" elb_backends_delete.yml --vault-password-file vaultpass.txt +enable SNAT on specific VPC + + ansible-playbook -i hosts snat_enable.yml -e "vpc_name=ansible-vpc1" -e "enable_snat=true" --vault-password-file vaultpass.txt + +disable SNAT on specific VPC + + ansible-playbook -i hosts snat_enable.yml -e "vpc_name=ansible-vpc1" -e "enable_snat=false" --vault-password-file vaultpass.txt + discover API endpoints ansible-playbook -i hosts endpoints.yml --vault-password-file vaultpass.txt diff --git a/roles/snat_enable/tasks/main.yml b/roles/snat_enable/tasks/main.yml new file mode 100644 index 0000000..6dfd1d6 --- /dev/null +++ b/roles/snat_enable/tasks/main.yml @@ -0,0 +1,42 @@ +- name: Request router from API + uri: + url: "{{ AUTH_URL_VPC }}/v2.0/routers/{{ vpc_id }}" + method: GET + return_content: yes + validate_certs: yes + HEADER_Content-Type: "application/json" + HEADER_X-Auth-Token: "{{ token['x_subject_token'] }}" + when: + - vpc_id is defined + register: routerlist + +- set_fact: +# router_id: "{{ (routerlist.content|from_json)|json_query('router.id') }}" +# router_name: "{{ (routerlist.content|from_json)|json_query('router.name') }}" +# router_status: "{{ (routerlist.content|from_json)|json_query('router.status') }}" +# router_admin_state_up: "{{ (routerlist.content|from_json)|json_query('router.admin_state_up') }}" +# router_routes: "{{ (routerlist.content|from_json)|json_query('router.routes[]') }}" +# router_tenant_id: "{{ (routerlist.content|from_json)|json_query('router.tenant_id') }}" + external_network_id: "{{ (routerlist.content|from_json)|json_query('router.external_gateway_info.network_id') }}" +# external_network_snat_state: "{{ (routerlist.content|from_json)|json_query('router.external_gateway_info.enable_snat') }}" + when: routerlist is defined + +- name: Send request to API + uri: + url: "{{ AUTH_URL_VPC }}/v2.0/routers/{{ vpc_id }}" + method: PUT + body_format: raw + follow_redirects: all + return_content: yes + validate_certs: yes + HEADER_Content-Type: "application/json" + HEADER_X-Auth-Token: "{{ token['x_subject_token'] }}" + body: "{{ lookup('template', 'roles/snat_enable/templates/request.json.j2')|to_json }}" + when: + - vpc_id is defined + - enable_snat is defined + - external_network_id|length != 0 + register: router + +- debug: + msg: "{{ router }}" diff --git a/roles/snat_enable/templates/request.json.j2 b/roles/snat_enable/templates/request.json.j2 new file mode 100644 index 0000000..0fd092b --- /dev/null +++ b/roles/snat_enable/templates/request.json.j2 @@ -0,0 +1,8 @@ +{ + "router": { + "external_gateway_info": { + "network_id": "{{ external_network_id }}", + "enable_snat": {{ enable_snat }} + } + } +} From 5d8d5f4db0435bf55482365a145c4f044479223a Mon Sep 17 00:00:00 2001 From: Frank Kloeker Date: Thu, 15 Jun 2017 15:50:05 +0200 Subject: [PATCH 07/72] add playbook for snat rule --- snat_enable.yml | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 snat_enable.yml diff --git a/snat_enable.yml b/snat_enable.yml new file mode 100644 index 0000000..b57d0b1 --- /dev/null +++ b/snat_enable.yml @@ -0,0 +1,7 @@ +--- +- hosts: all + gather_facts: no + roles: + - role: token + - role: lookup_name + - role: snat_enable From beb444438df4308c428bdd15a2050cf2f20a88d8 Mon Sep 17 00:00:00 2001 From: Frank Kloeker Date: Thu, 15 Jun 2017 15:55:34 +0200 Subject: [PATCH 08/72] add lookup availability_zone_id --- roles/lookup_name/tasks/main.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/roles/lookup_name/tasks/main.yml b/roles/lookup_name/tasks/main.yml index 5922fc2..55cb09c 100644 --- a/roles/lookup_name/tasks/main.yml +++ b/roles/lookup_name/tasks/main.yml @@ -1,4 +1,10 @@ +- set_fact: + availability_zone_id: "bf84aba586ce4e948da0b97d9a7d62fb" + when: availability_zone == "eu-de-01" +- set_fact: + availability_zone_id: "bf84aba586ce4e948da0b97d9a7d62fc" + when: availability_zone == "eu-de-02" - name: Request images list from API uri: From 9531276562da4f6038aa346d9f05e0dcf053c9ad Mon Sep 17 00:00:00 2001 From: Frank Kloeker Date: Sat, 17 Jun 2017 21:03:51 +0200 Subject: [PATCH 09/72] move otc credentials from secrets.yml to clouds.yml (os-client-config) --- README.md | 125 +++++++++++++++---------- env.yml | 1 + roles/ecs/tasks/main.yml | 2 - roles/endpoints/tasks/main.yml | 2 - roles/image_create/tasks/main.yml | 2 - roles/image_delete/tasks/main.yml | 2 - roles/rds_flavors/tasks/main.yml | 2 - roles/rds_versions/tasks/main.yml | 2 - roles/services/tasks/main.yml | 2 - roles/token/tasks/main.yml | 38 +++++++- roles/zone_create/tasks/main.yml | 2 - roles/zonerecord_create/tasks/main.yml | 2 - secrets.yml | 65 ++++++------- 13 files changed, 144 insertions(+), 103 deletions(-) create mode 100644 env.yml diff --git a/README.md b/README.md index 07c5ab8..f1c6d7c 100644 --- a/README.md +++ b/README.md @@ -124,12 +124,19 @@ Requirements * :exclamation: credentials on OTC (username, password, domain, S3 access/secret key) +Files outside the repo +====================== +| filename | description| +|-------------------------------|------------| +|~/.config/openstack/clouds.yml | os-client configuration file for multiple openstack environments| + Files ===== | filename | description| |----------------|------------| |ajob | shell script to fetch job status from OTC| -|secrets.yml | var file for OTC credentials and endpoints (ansible-vault)| +|env.yml | profile to use in clouds.yml| +|secrets.yml | var file for S3 credentials and endpoints (ansible-vault)| |ecs_secrets.yml | var file for virtual machine and volume conf (ansible-vault)| |elb_secrets.yml | var file for elastic loadbalancer conf (ansible-vault)| |secgrouprule.yml| var file for single security group rule | @@ -138,8 +145,19 @@ Files |hosts | host file for ansible (we use only localhost)| |tenant.ini | configuration file for complete tenant| -Examples -======== + +Os-client config +================ + +for more comfort and standardization we moved credential lookup from secrets.yml to clouds.yml (part of https://docs.openstack.org/developer/os-client-config/). If you already configured your OTC credentials there put your profile name in env.yml or use +``` + ansible-playbook -e "CLOUD=otc" ... +``` +if your profile named otc + + +Starting up +=========== ``` cp secrets.yml _secrets.yml @@ -149,10 +167,14 @@ Examples :exclamation: **adjust your own data in this file before you using the examples:** -list virtual machines +list virtual machines (with secrets.yml) ansible-playbook -i hosts ecs.yml --vault-password-file vaultpass.txt +list virtual machines (with clouds.yml) + + ansible-playbook -i hosts ecs.yml + create and start virtual machine (defined in _ecs_secrets.yml) ansible-playbook -i hosts ecs_create.yml -e @_ecs_secrets.yml --vault-password-file vaultpass.txt @@ -175,15 +197,15 @@ create and start virtual machine with injection user_data show virtual machine (single) - ansible-playbook -i hosts ecs_show.yml -e "ecs_name=ansible-test01" --vault-password-file vaultpass.txt + ansible-playbook -i hosts ecs_show.yml -e "ecs_name=ansible-test01" delete virtual machine (only the machine) - ansible-playbook -i hosts ecs_delete.yml -e "ecs_name=ansible-01" --vault-password-file vaultpass.txt + ansible-playbook -i hosts ecs_delete.yml -e "ecs_name=ansible-01" delete virtual machine (delete also floating ip and attached volumes) - ansible-playbook -i hosts ecs_delete.yml -e "ecs_name=test01-ansible delete_publicip=1 delete_volume=1" --vault-password-file vaultpass.txt + ansible-playbook -i hosts ecs_delete.yml -e "ecs_name=test01-ansible delete_publicip=1 delete_volume=1" list elastic loadbalancers @@ -251,15 +273,15 @@ delete backends for elastic loadbalancer enable SNAT on specific VPC - ansible-playbook -i hosts snat_enable.yml -e "vpc_name=ansible-vpc1" -e "enable_snat=true" --vault-password-file vaultpass.txt + ansible-playbook -i hosts snat_enable.yml -e "vpc_name=ansible-vpc1" -e "enable_snat=true" disable SNAT on specific VPC - ansible-playbook -i hosts snat_enable.yml -e "vpc_name=ansible-vpc1" -e "enable_snat=false" --vault-password-file vaultpass.txt + ansible-playbook -i hosts snat_enable.yml -e "vpc_name=ansible-vpc1" -e "enable_snat=false" discover API endpoints - ansible-playbook -i hosts endpoints.yml --vault-password-file vaultpass.txt + ansible-playbook -i hosts endpoints.yml list volumes @@ -279,89 +301,89 @@ show information about a single volume show flavors - ansible-playbook -i hosts flavors.yml --vault-password-file vaultpass.txt + ansible-playbook -i hosts flavors.yml show elastic ip-addresses - ansible-playbook -i hosts eip.yml --vault-password-file vaultpass.txt + ansible-playbook -i hosts eip.yml apply a new elastic ip-address (bandwidth between 1-300 MBit/s) - ansible-playbook -i hosts eip_apply.yml -e "eip_bandwidth_name=ansible-eip1" -e "eip_bandwidth_size=100" -e "public_ip_address=0.0.0.0" --vault-password-file vaultpass.txt + ansible-playbook -i hosts eip_apply.yml -e "eip_bandwidth_name=ansible-eip1" -e "eip_bandwidth_size=100" -e "public_ip_address=0.0.0.0" delete elastic ip-address - ansible-playbook -i hosts eip_delete.yml -e "eip_id=c417c3bf-fdd2-47c4-a64f-320add5759b5" --vault-password-file vaultpass.txt + ansible-playbook -i hosts eip_delete.yml -e "eip_id=c417c3bf-fdd2-47c4-a64f-320add5759b5" show images - ansible-playbook -i hosts images.yml --vault-password-file vaultpass.txt + ansible-playbook -i hosts images.yml delete an image (API return code is 204 when success, ansible expected 200 and may give an error) - ansible-playbook -i hosts -e "image_id=af0a0bcf-7be3-4722-98ba-3350801a8cd5" image_delete.yml --vault-password-file vaultpass.txt + ansible-playbook -i hosts -e "image_id=af0a0bcf-7be3-4722-98ba-3350801a8cd5" image_delete.yml show job status - ansible-playbook -e "job_id=2c9eb2c15693b00901571e32ad5e1755" -i hosts job.yml --vault-password-file vaultpass.txt + ansible-playbook -e "job_id=2c9eb2c15693b00901571e32ad5e1755" -i hosts job.yml ./ajob 2c9eb2c15693b00901571e32ad5e1755 show keypairs - ansible-playbook -i hosts keypairs.yml --vault-password-file vaultpass.txt + ansible-playbook -i hosts keypairs.yml create keypair - ansible-playbook -i hosts -e "ecs_adminkey=test-key" -e "keypair_file=~/.ssh/id_rsa.pub" keypair_create.yml --vault-password-file vaultpass.txt + ansible-playbook -i hosts -e "ecs_adminkey=test-key" -e "keypair_file=~/.ssh/id_rsa.pub" keypair_create.yml delete keypair - ansible-playbook -i hosts -e "ecs_adminkey=test-key" keypair_delete.yml --vault-password-file vaultpass.txt + ansible-playbook -i hosts -e "ecs_adminkey=test-key" keypair_delete.yml lookup id by name (image) - ansible-playbook -i hosts lookup_name.yml -e "image_name=Community_Ubuntu_16.04_TSI_latest" --vault-password-file vaultpass.txt + ansible-playbook -i hosts lookup_name.yml -e "image_name=Community_Ubuntu_16.04_TSI_latest" lookup id by name (flavor) - ansible-playbook -i hosts lookup_name.yml -e "ecs_ram=2048" -e "ecs_vcpus=4" --vault-password-file vaultpass.txt + ansible-playbook -i hosts lookup_name.yml -e "ecs_ram=2048" -e "ecs_vcpus=4" lookup id by name (subnet) - ansible-playbook -i hosts lookup_name.yml -e "subnet_name=subnet-5831" --vault-password-file vaultpass.txt + ansible-playbook -i hosts lookup_name.yml -e "subnet_name=subnet-5831" lookup id by name (secgroup) - ansible-playbook -i hosts lookup_name.yml -e "secgroup_name=bitnami-wordpress-56a9-securitygroup" --vault-password-file vaultpass.txt + ansible-playbook -i hosts lookup_name.yml -e "secgroup_name=bitnami-wordpress-56a9-securitygroup" lookup id by name (vpc) - ansible-playbook -i hosts lookup_name.yml -e "vpc_name=vpc-4988" --vault-password-file vaultpass.txt + ansible-playbook -i hosts lookup_name.yml -e "vpc_name=vpc-4988" lookup id by name (eip) - ansible-playbook -i hosts lookup_name.yml -e "public_ip_address=160.44.1.1" --vault-password-file vaultpass.txt + ansible-playbook -i hosts lookup_name.yml -e "public_ip_address=160.44.1.1" lookup id by name (zone) - ansible-playbook -i hosts lookup_name.yml -e "zone_name=example.com." --vault-password-file vaultpass.txt + ansible-playbook -i hosts lookup_name.yml -e "zone_name=example.com." lookup id by name (ecs) - ansible-playbook -i hosts lookup_name.yml -e "ecs_name=ansible-test01" --vault-password-file vaultpass.txt + ansible-playbook -i hosts lookup_name.yml -e "ecs_name=ansible-test01" list provided database versions for RDS - ansible-playbook -i hosts rds_versions.yml --vault-password-file vaultpass.txt + ansible-playbook -i hosts rds_versions.yml list provided flavors for selected database version in RDS - ansible-playbook -i hosts rds_flavors.yml -e "rds_version_id=286a34fc-a605-11e6-88fd-286ed488c9cb" --vault-password-file vaultpass.txt + ansible-playbook -i hosts rds_flavors.yml -e "rds_version_id=286a34fc-a605-11e6-88fd-286ed488c9cb" discover API services - ansible-playbook -i hosts services.yml --vault-password-file vaultpass.txt + ansible-playbook -i hosts services.yml show s3 buckets @@ -381,86 +403,87 @@ upload files in s3 object store (VHD, ZVHD, VMDK, QCOW2 are supported for otc im show security groups - ansible-playbook -i hosts secgroups.yml --vault-password-file vaultpass.txt + ansible-playbook -i hosts secgroups.yml show security groups (only from one vpc) - ansible-playbook -i hosts secgroups.yml -e "vpc_id=d59284de-ad78-4fee-8f2d-d6ff335f4961" --vault-password-file vaultpass.txt + ansible-playbook -i hosts secgroups.yml -e "vpc_id=d59284de-ad78-4fee-8f2d-d6ff335f4961" create security group - ansible-playbook -i hosts secgroup_create.yml -e "secgroup_name=ansible-secgroup01" -e "vpc_id=d59284de-ad78-4fee-8f2d-d6ff335f4961" --vault-password-file vaultpass.txt + ansible-playbook -i hosts secgroup_create.yml -e "secgroup_name=ansible-secgroup01" -e "vpc_id=d59284de-ad78-4fee-8f2d-d6ff335f4961" delete security group - ansible-playbook -i hosts secgroup_delete.yml -e "secgroup_id="6e8ac0a0-e0ec-4c4d-a786-9c9c946fd673"" --vault-password-file vaultpass.txt + ansible-playbook -i hosts secgroup_delete.yml -e "secgroup_id=6e8ac0a0-e0ec-4c4d-a786-9c9c946fd673" create security group rule - ansible-playbook -i hosts secgrouprule_create.yml -e "secgroup_id=e67e7ef1-b582-47f7-a43f-6a244fd01353" -e @secgrouprule.yml --vault-password-file vaultpass.txt + ansible-playbook -i hosts secgrouprule_create.yml -e "secgroup_id=e67e7ef1-b582-47f7-a43f-6a244fd01353" -e @secgrouprule.yml delete security group rule - ansible-playbook -i hosts secgrouprule_delete.yml -e "secgrouprule_id=3c329359-fef5-402f-b29a-caac734065a1" --vault-password-file vaultpass.txt + ansible-playbook -i hosts secgrouprule_delete.yml -e "secgrouprule_id=3c329359-fef5-402f-b29a-caac734065a1" show subnets - ansible-playbook -i hosts subnet.yml --vault-password-file vaultpass.txt + ansible-playbook -i hosts subnet.yml create subnet (vars in subnet_var.yml) - ansible-playbook -i hosts subnet_create.yml -e @subnet_var.yml --vault-password-file vaultpass.txt + ansible-playbook -i hosts subnet_create.yml -e @subnet_var.yml delete subnet - ansible-playbook -i hosts subnet_delete.yml -e "vpc_id=0db2af4b-115d-426a-acae-889b025110c8" -e "subnet_id=3ec461e1-eca4-485b-a2a5-91a840968a4f" --vault-password-file vaultpass.txt + ansible-playbook -i hosts subnet_delete.yml -e "vpc_id=0db2af4b-115d-426a-acae-889b025110c8" -e "subnet_id=3ec461e1-eca4-485b-a2a5-91a840968a4f" show vpc - ansible-playbook -i hosts vpc.yml --vault-password-file vaultpass.txt + ansible-playbook -i hosts vpc.yml create vpc - ansible-playbook -i hosts vpc_create.yml -e "vpc_name=ansible-vpc1" -e "vpc_net=192.168.0.0/16" --vault-password-file vaultpass.txt + ansible-playbook -i hosts vpc_create.yml -e "vpc_name=ansible-vpc1" -e "vpc_net=192.168.0.0/16" delete vpc - ansible-playbook -i hosts vpc_delete.yml -e "vpc_id=0db2af4b-115d-426a-acae-889b025110c8" --vault-password-file vaultpass.txt + ansible-playbook -i hosts vpc_delete.yml -e "vpc_id=0db2af4b-115d-426a-acae-889b025110c8" + show DNS zones - ansible-playbook -i hosts zones.yml --vault-password-file vaultpass.txt + ansible-playbook -i hosts zones.yml create DNS zone (name and ttl are mandatory) - ansible-playbook -i hosts -e "zone_name=example.com." -e "zone_description=example zone" -e "zone_email=example@example.com" -e "zone_ttl=86400" zone_create.yml --vault-password-file vaultpass.txt + ansible-playbook -i hosts -e "zone_name=example.com." -e "zone_description=example zone" -e "zone_email=example@example.com" -e "zone_ttl=86400" zone_create.yml delete DNS zone - ansible-playbook -i hosts -e "zone_id=ff80808257e2bb5e0157ec5ca2620234" zone_delete.yml --vault-password-file vaultpass.txt + ansible-playbook -i hosts -e "zone_id=ff80808257e2bb5e0157ec5ca2620234" zone_delete.yml show DNS zone records - ansible-playbook -i hosts zonerecords.yml --vault-password-file vaultpass.txt + ansible-playbook -i hosts zonerecords.yml create DNS zonerecord (A-Record) possible values A,AAAA,MX,CNAME,PTR,TXT,NS - ansible-playbook -i hosts -e "zone_id=ff80808257e2bb5e0157ec620968023a" -e "zonerecord_name=testserver.example.com." -e "zonerecord_type=A" -e "zonerecord_value=160.44.196.210" -e "zonerecord_ttl=86400" zonerecord_create.yml --vault-password-file vaultpass.txt + ansible-playbook -i hosts -e "zone_id=ff80808257e2bb5e0157ec620968023a" -e "zonerecord_name=testserver.example.com." -e "zonerecord_type=A" -e "zonerecord_value=160.44.196.210" -e "zonerecord_ttl=86400" zonerecord_create.yml create DNS zonerecord (PTR-Record) first create reverse zone: - ansible-playbook -i hosts -e "zone_name=210.196.44.160.in-addr.arpa." -e "zone_description=reverse zone 160.44.196.210" -e "zone_email=test@example.com" -e "zone_ttl=300" zone_create.yml --vault-password-file vaultpass.txt + ansible-playbook -i hosts -e "zone_name=210.196.44.160.in-addr.arpa." -e "zone_description=reverse zone 160.44.196.210" -e "zone_email=test@example.com" -e "zone_ttl=300" zone_create.yml then create PTR-Record: - ansible-playbook -i hosts -e "zone_id=ff80808257e2bb5e0157ec8911e60240" -e "zonerecord_name=210.196.44.160.in-addr.arpa." -e "zonerecord_type=PTR" -e "zonerecord_value=testserver.example.com" -e "zonerecord_ttl=300" zonerecord_create.yml --vault-password-file vaultpass.txt + ansible-playbook -i hosts -e "zone_id=ff80808257e2bb5e0157ec8911e60240" -e "zonerecord_name=210.196.44.160.in-addr.arpa." -e "zonerecord_type=PTR" -e "zonerecord_value=testserver.example.com" -e "zonerecord_ttl=300" zonerecord_create.yml beware of "." in the end of name and name convention of the PTR zones delete DNS zonerecord - ansible-playbook -i hosts -e "zone_id=ff80808257e2bb5e0157ec620968023a" -e "zonerecordid=ff80808257e2bb050157ec789b5e027e" zonerecord_delete.yml --vault-password-file vaultpass.txt + ansible-playbook -i hosts -e "zone_id=ff80808257e2bb5e0157ec620968023a" -e "zonerecordid=ff80808257e2bb050157ec789b5e027e" zonerecord_delete.yml Full Working Example diff --git a/env.yml b/env.yml new file mode 100644 index 0000000..cf58d21 --- /dev/null +++ b/env.yml @@ -0,0 +1 @@ +CLOUD: "otc.19720" diff --git a/roles/ecs/tasks/main.yml b/roles/ecs/tasks/main.yml index 56ce71a..7b5d989 100644 --- a/roles/ecs/tasks/main.yml +++ b/roles/ecs/tasks/main.yml @@ -1,5 +1,3 @@ -- include_vars: _secrets.yml - - name: Request ecs list from AUTH API uri: url: "{{ AUTH_URL_ECS }}/servers" diff --git a/roles/endpoints/tasks/main.yml b/roles/endpoints/tasks/main.yml index bcff548..4e846f3 100644 --- a/roles/endpoints/tasks/main.yml +++ b/roles/endpoints/tasks/main.yml @@ -1,5 +1,3 @@ -- include_vars: _secrets.yml - - name: Request endpoints list from AUTH API uri: url: "{{ IAM_AUTH_URL }}/endpoints" diff --git a/roles/image_create/tasks/main.yml b/roles/image_create/tasks/main.yml index d62b470..202c6b4 100644 --- a/roles/image_create/tasks/main.yml +++ b/roles/image_create/tasks/main.yml @@ -1,5 +1,3 @@ -- include_vars: _secrets.yml - - name: Send request to API uri: url: "{{ AUTH_URL_IMS }}/v2/cloudimages/action" diff --git a/roles/image_delete/tasks/main.yml b/roles/image_delete/tasks/main.yml index 7b48e08..bfded77 100644 --- a/roles/image_delete/tasks/main.yml +++ b/roles/image_delete/tasks/main.yml @@ -1,5 +1,3 @@ -- include_vars: _secrets.yml - - name: Send request to API uri: url: "{{ AUTH_URL_IMS }}/v2/images/{{ image_id }}" diff --git a/roles/rds_flavors/tasks/main.yml b/roles/rds_flavors/tasks/main.yml index aa29e11..daf1a04 100644 --- a/roles/rds_flavors/tasks/main.yml +++ b/roles/rds_flavors/tasks/main.yml @@ -1,5 +1,3 @@ -- include_vars: _secrets.yml - - name: Request rds flavors from API uri: url: "{{ AUTH_URL_RDS }}/{{ OS_USER_DOMAIN_ID }}/flavors?dbId={{ rds_version_id }}®ion={{ PROJECT_NAME }}" diff --git a/roles/rds_versions/tasks/main.yml b/roles/rds_versions/tasks/main.yml index 02f931a..acb5a85 100644 --- a/roles/rds_versions/tasks/main.yml +++ b/roles/rds_versions/tasks/main.yml @@ -1,5 +1,3 @@ -- include_vars: _secrets.yml - - name: Request rds list from API for mysql uri: url: "{{ AUTH_URL_RDS }}/{{ OS_USER_DOMAIN_ID }}/datastores/MySQL/versions" diff --git a/roles/services/tasks/main.yml b/roles/services/tasks/main.yml index ba9e945..9e9fd87 100644 --- a/roles/services/tasks/main.yml +++ b/roles/services/tasks/main.yml @@ -1,5 +1,3 @@ -- include_vars: _secrets.yml - - name: Request services list from AUTH API uri: url: "{{ IAM_AUTH_URL }}/services" diff --git a/roles/token/tasks/main.yml b/roles/token/tasks/main.yml index 36cd4de..aa6f318 100644 --- a/roles/token/tasks/main.yml +++ b/roles/token/tasks/main.yml @@ -1,4 +1,36 @@ -- include_vars: _secrets.yml +- name: Load auth variable file from os-client config or ansible-vault secret + include_vars: "{{ item }}" + with_first_found: + - "env.yml" + - "_secrets.yml" + +- name: set fact user home + set_fact: + user_home: "{{ lookup('env','HOME') }}" + +- stat: + path: "{{ user_home }}/.config/openstack/clouds.yml" + register: osclientconfigfile + +- name: set fact os-client config file + set_fact: + os_client_config: "{{ lookup('file', osclientconfigfile.stat.path)|from_yaml }}" + when: osclientconfigfile.stat.exists + +- name: set facts from os-client-config + set_fact: + USERNAME: "{{ os_client_config['clouds'][CLOUD]['auth']['username'] }}" + PASSWORD: "{{ os_client_config['clouds'][CLOUD]['auth']['password'] }}" + PROJECT_NAME: "{{ os_client_config['clouds'][CLOUD]['auth']['project_name'] }}" + DOMAIN: "{{ os_client_config['clouds'][CLOUD]['auth']['user_domain_name'] }}" + when: osclientconfigfile.stat.exists + +- name: set fact generic endpoints + set_fact: + IAM_AUTH_URL: "https://iam.{{ PROJECT_NAME }}.otc.t-systems.com/v3" + AUTH_URL_ELB: "https://elb.{{ PROJECT_NAME }}.otc.t-systems.com/v1.0" + AUTH_URL_ECS_CLOUD: "https://ecs.{{ PROJECT_NAME }}.otc.t-systems.com/v1" + AUTH_URL_RDS: "https://rds.{{ PROJECT_NAME }}.otc.t-systems.com/rds/v1" - name: Request token from AUTH API uri: @@ -13,7 +45,8 @@ body: "{{ lookup('template', 'roles/token/templates/request.json.j2',convert_data=True)|to_json }}" register: token -- set_fact: +- name: set facts dynamic endpoints + set_fact: # OS_USER_DOMAIN_ID: "{{ (token.content|from_json)['token']['user']['domain']['id'] }}" OS_USER_DOMAIN_ID: "{{ (token.content|from_json)|json_query('token.user.domain.id') }}" PROJECT_ID: "{{ (token.content|from_json)|json_query('token.project.id') }}" @@ -24,6 +57,7 @@ AUTH_URL_EVSv2: "{{ (token.content|from_json)|json_query('token.catalog[?type==`volumev2`].endpoints[].url|[0]') }}" AUTH_URL_RTS: "{{ (token.content|from_json)|json_query('token.catalog[?type==`orchestration`].endpoints[].url|[0]') }}" AUTH_URL_IMS: "{{ (token.content|from_json)|json_query('token.catalog[?type==`image`].endpoints[].url|[0]') }}" + IAM_AUTH_URL: "https://iam.{{ PROJECT_NAME }}.otc.t-systems.com/v3" # - debug: # msg: "{{ token }}" diff --git a/roles/zone_create/tasks/main.yml b/roles/zone_create/tasks/main.yml index 326abb1..3d5e323 100644 --- a/roles/zone_create/tasks/main.yml +++ b/roles/zone_create/tasks/main.yml @@ -1,5 +1,3 @@ -- include_vars: _secrets.yml - - name: Send request to API uri: url: "{{ AUTH_URL_DNS }}/v2/zones" diff --git a/roles/zonerecord_create/tasks/main.yml b/roles/zonerecord_create/tasks/main.yml index fc786e0..470c49b 100644 --- a/roles/zonerecord_create/tasks/main.yml +++ b/roles/zonerecord_create/tasks/main.yml @@ -1,5 +1,3 @@ -- include_vars: _secrets.yml - - name: Send request to API uri: url: "{{ AUTH_URL_DNS }}/v2/zones/{{ zone_id }}/recordsets" diff --git a/secrets.yml b/secrets.yml index 5bf38e4..d2804e2 100644 --- a/secrets.yml +++ b/secrets.yml @@ -1,33 +1,34 @@ $ANSIBLE_VAULT;1.1;AES256 -61313666663139373934613230386362653539623063666637383438386433336266313166373365 -6435386635373838323462363733363433353361323633630a656436623537623634386162366561 -62343831373030646330323435653837623465633162363832663365353035323266363731663936 -3131386335346335660a306531353438653362666565656263323537303665346263386463663631 -39343363616461666561623264616139616434303564626432306562373136326463313232316138 -39356164393461353138643131326662346130656566633431633837313734373331313234353039 -34663535356362383464366637653962633763313330353461383031633563386532623733326464 -30386237306632366532353361663364373133353262663232306234356430313461333565343039 -61613235646132373065323736316337346661356636653530343464353731656563616137386361 -37633437303834313562376430363031366166663261303066643335663265396663373530636236 -39353539353064653763386630623037383339393930346534343664396564313466393663613064 -61666136623438323931323763383763393535393562663035313039303435383032656236646433 -36346662353433326366626464646431326462343138386132333831643063623732373930393137 -63623733306635646230646665336537373735363731663237633265376336336632386262393630 -38323066316363643031323563623238316530616538303231393661393865366636393333373532 -32316438613739623863623438356663653933373830326433313337626539643636313237373639 -34353731386565303732353161393265643834623865646633623237623332626134323538666335 -39316234643736343962663961356362383133326338663232613564663563356161393864336563 -65393535333837383230313937653565313166326564373936646235343630353061393634323838 -35643732356463363033653636356461333262306636623138643663633834373136313338626635 -33313836363235656435663866663433656265343430396335643766663563643064663733356561 -38623364383665313664646139313938323065363638353332613836323933346266393339353832 -35366463653461613865653262623635313130656634643635626636616465623536666161393931 -63363564326366346134663931363837656231623334626536333036643465313939656462343363 -30303531353634653536393861373134626563323061363066613665306363336662333336343639 -63313934316536656239386264333933356435386136643364616463656665373063613666353235 -31623130393533346462633163313462646530316138333264656637316535363833613065623265 -35656137633561383234393733396265316361366234373434383331323238633062366239316665 -39373161373531313330363362333230633636386461386530623832316265386535633433333039 -33626265653734663239396662663535303362363631393832663661633764613761303230373638 -62393937343032343230386661623964346131613435623338656466386336343432623362616264 -61636536323832353932 +35616631313462613563643736613164396666333464616337633639653636303131666531383866 +6664313535343038386330623361353365303336396664390a336235643138343066366163303762 +35663639366262353437326233393765313863333430623065326437393733353833326662613930 +6362636637613132630a356431386166663736336332626164336330353432316237396437363537 +66343235626537343938326532383239336435626439623135643639333333346630353935336133 +36363535316565633932633338353761326237653362353765353030613662626561336164313032 +32326432373661373762666231316230613239366130353232343936616134353032373236633839 +31306163393636373730336265333966633830623432663365633536623964323839363565373933 +32353535323237393634343636306463356337313063623764663337343932323561343661623362 +66613635303633633332336262353565666264663062653262656262643938306662326337623631 +34653436623935613136363533646164623833346535643436613333313732623438313330373166 +32613766386639393635393362363838636265383937626263306463383033376563343836313235 +31373233616235653065653763393937383035373236663239363034343032383364636561383165 +37656233383333316139613830623634393639333733356336306532653962376662366565303435 +34623938663830376466393631643965613865643265616266366530616237666135316634323263 +34653034613335303162306533653764643335393434373436616438343235396537653638313732 +35393533313865316665393039646633626362356362636539383364633731636336626632303537 +32663561306264343032383536383431626262376634623530623331333532616262316238333064 +34366631366466363461333134663935383261636635646462393464643966636238343830323832 +61396362343930316666333364323831383238353937633766356436376634363765353739333738 +34326637643162636661663235336138366561336432623434626238633465653134333536623763 +66663338333136633132373138633637373036626135623662366437636262383238626135376339 +63613365383438306564656265636330323435373435333431633632376464653866366661633035 +34613239663636303566373335633138623937313535393833373139316530346230643162663864 +30663763353065346636623738313565613731646630373538356334646633323463323132396237 +30363433336338333364306165303534323939626531303561313163633234306265333463613332 +36326666333036656437616364333262303163363938626336353361656164383164616664643937 +36666466353463383332363739343263363561613565326361313837343735643462616337333231 +65623730613866326633346363623033333536633465363964383333656462376232613364633230 +30663431383463323765666632346531633330623065396232643466333530323862316631363939 +63396534656662326430326436643136363134323866363333613136633835313462363334326662 +61343265396362363761376238356134343461616462393465663063313135316562323737613639 +3134 From cc4de1aa1de2aaf40a05e844052ba4589d92150a Mon Sep 17 00:00:00 2001 From: Frank Kloeker Date: Sat, 17 Jun 2017 21:16:04 +0200 Subject: [PATCH 10/72] delete secret file from elb list role --- roles/elb/tasks/main.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/roles/elb/tasks/main.yml b/roles/elb/tasks/main.yml index 0b67605..0fbb114 100644 --- a/roles/elb/tasks/main.yml +++ b/roles/elb/tasks/main.yml @@ -1,5 +1,3 @@ -- include_vars: _elb_secrets.yml - - name: Send request to API uri: url: "{{ AUTH_URL_ELB }}/{{ PROJECT_ID }}/elbaas/loadbalancers" From 6a3204b0f04a2398b6db41c8d6e963e2cd1dbe20 Mon Sep 17 00:00:00 2001 From: Frank Kloeker Date: Sat, 17 Jun 2017 21:21:47 +0200 Subject: [PATCH 11/72] lookup role for elb --- README.md | 8 +++- roles/lookup_name/tasks/main.yml | 79 ++++++++++++++++++++++++++------ 2 files changed, 72 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index f1c6d7c..2decaf3 100644 --- a/README.md +++ b/README.md @@ -146,7 +146,7 @@ Files |tenant.ini | configuration file for complete tenant| -Os-client config +os-client config ================ for more comfort and standardization we moved credential lookup from secrets.yml to clouds.yml (part of https://docs.openstack.org/developer/os-client-config/). If you already configured your OTC credentials there put your profile name in env.yml or use @@ -373,6 +373,10 @@ lookup id by name (ecs) ansible-playbook -i hosts lookup_name.yml -e "ecs_name=ansible-test01" +lookup id by name (elb) + + ansible-playbook -i hosts lookup_name.yml -e "elb_name=ansible-elb01" + list provided database versions for RDS ansible-playbook -i hosts rds_versions.yml @@ -497,7 +501,7 @@ This playbook will create VPC,Subnet, SecurityGroup, SSH-Keypair, allocate Float configure your DNS in tenant.ini and deploy all zones and zonerecords - ansible-playbook -i hosts dns_create.yml --vault-password-file vaultpass.txt + ansible-playbook -i hosts dns_create.yml --vault-password-file vaultpass.txt Contributing diff --git a/roles/lookup_name/tasks/main.yml b/roles/lookup_name/tasks/main.yml index 55cb09c..7886106 100644 --- a/roles/lookup_name/tasks/main.yml +++ b/roles/lookup_name/tasks/main.yml @@ -1,10 +1,12 @@ -- set_fact: +- name: set fact for availability_zone_id (eu_de-01) + set_fact: availability_zone_id: "bf84aba586ce4e948da0b97d9a7d62fb" - when: availability_zone == "eu-de-01" + when: elb_availability_zone is defined and elb_availability_zone == "eu_de-01" -- set_fact: +- name: set fact for availability_zone_id /ei_de-01) + set_fact: availability_zone_id: "bf84aba586ce4e948da0b97d9a7d62fc" - when: availability_zone == "eu-de-02" + when: elb_availability_zone is defined and elb_availability_zone == "eu_de-02" - name: Request images list from API uri: @@ -36,7 +38,23 @@ vpc_id: "{{ (vpc_result.content|from_json)|json_query(\"vpcs[?name=='\" + vpc_name + \"'].id|[0]\") }}" when: vpc_name is defined -- name: Request subnet list from API +- name: Request subnet list from API for elb + uri: + url: "{{ AUTH_URL_VPC }}/v1/{{ PROJECT_ID }}/subnets" + method: GET + return_content: yes + validate_certs: yes + HEADER_Content-Type: "application/json" + HEADER_X-Auth-Token: "{{ token['x_subject_token'] }}" + when: elb_subnet_name is defined + register: subnet_result + +- name: set fact for subnet_name (for elb) + set_fact: + subnet_name: "{{ elb_subnet_name }}" + when: elb_subnet_name is defined and elb_subnet_name|length != 0 + +- name: Request subnet list from API for ecs uri: url: "{{ AUTH_URL_VPC }}/v1/{{ PROJECT_ID }}/subnets" method: GET @@ -47,11 +65,28 @@ when: subnet_name is defined register: subnet_result -- set_fact: +- name: set fact for subnet_id + set_fact: subnet_id: "{{ (subnet_result.content|from_json)|json_query(\"subnets[?name=='\" + subnet_name + \"'].id|[0]\") }}" - when: subnet_name is defined + when: subnet_name is defined and subnet_name|length != 0 -- name: Request secgroup list from API +- name: Request secgroup list from API for elb + uri: + url: "{{ AUTH_URL_VPC }}/v1/{{ PROJECT_ID }}/security-groups" + method: GET + return_content: yes + validate_certs: yes + HEADER_Content-Type: "application/json" + HEADER_X-Auth-Token: "{{ token['x_subject_token'] }}" + when: elb_secgroup_name is defined + register: secgroup_result + +- name: set fact secgroup_name (for elb) + set_fact: + secgroup_name: "{{ elb_secgroup_name }}" + when: elb_secgroup_name is defined + +- name: Request secgroup list from API for ecs uri: url: "{{ AUTH_URL_VPC }}/v1/{{ PROJECT_ID }}/security-groups" method: GET @@ -62,7 +97,8 @@ when: secgroup_name is defined register: secgroup_result -- set_fact: +- name: set fact for secgroup_id + set_fact: secgroup_id: "{{ (secgroup_result.content|from_json)|json_query(\"security_groups[?name=='\" + secgroup_name + \"'].id|[0]\") }}" when: secgroup_name is defined @@ -79,7 +115,7 @@ - set_fact: flavor_id: "{{ (flavor_result.content|from_json)|json_query('sort_by(flavors, &ram)|[?ram>=`' + ecs_ram + '` && vcpus>=`' + ecs_vcpus + '`].id|[0]') }}" - when: ecs_ram is defined and ecs_vcpus is defined + when: (ecs_ram is defined and ecs_ram |length != 0) or (ecs_vcpus is defined and ecs_vcpus|length != 0) - name: Request keypair list from API uri: @@ -94,7 +130,7 @@ - set_fact: ecs_adminkey_name: "{{ (keypairlist_result.content|from_json)|json_query(\"keypairs[?keypair.name=='\" + ecs_adminkey + \"'].keypair.name\") }}" - when: ecs_adminkey is defined + when: ecs_adminkey is defined and ecs_adminkey|length != 0 - name: Request floatingip list from API uri: @@ -134,11 +170,28 @@ HEADER_Content-Type: "application/json" HEADER_X-Auth-Token: "{{ token['x_subject_token'] }}" register: ecs_result + when: ecs_name is defined - set_fact: ecs_id: "{{ (ecs_result.content|from_json)|json_query(\"servers[?name=='\" + ecs_name + \"'].id|[0]\") }}" when: ecs_name is defined -#- debug: -# msg: "{{ ecs_id }}" +- name: Request elb list from API + uri: + url: "{{ AUTH_URL_ELB }}/{{ PROJECT_ID }}/elbaas/loadbalancers" + method: GET + follow_redirects: all + return_content: yes + validate_certs: yes + HEADER_Content-Type: "application/json" + HEADER_X-Auth-Token: "{{ token['x_subject_token'] }}" + register: elb_result + when: elb_name is defined + +- set_fact: + elb_id: "{{ (elb_result.content|from_json)|json_query(\"loadbalancers[?name=='\" + elb_name + \"'].id|[0]\") }}" + when: elb_name is defined + +- debug: + msg: "{{ elb_id }}" # when: ecs_id is defined From e2c6bd1fba851ee6b1f09bab378065fc9cd18fe4 Mon Sep 17 00:00:00 2001 From: Frank Kloeker Date: Sun, 18 Jun 2017 16:07:13 +0200 Subject: [PATCH 12/72] lookup role elb listener and certifcates --- elb_show.yml | 1 + roles/elb_show/tasks/main.yml | 5 +-- roles/lookup_name/tasks/main.yml | 63 +++++++++++++++++++++++++++----- 3 files changed, 57 insertions(+), 12 deletions(-) diff --git a/elb_show.yml b/elb_show.yml index 3d08d3b..4536a11 100644 --- a/elb_show.yml +++ b/elb_show.yml @@ -3,4 +3,5 @@ gather_facts: no roles: - role: token + - role: lookup_name - role: elb_show diff --git a/roles/elb_show/tasks/main.yml b/roles/elb_show/tasks/main.yml index 18b5f6f..4634c62 100644 --- a/roles/elb_show/tasks/main.yml +++ b/roles/elb_show/tasks/main.yml @@ -1,8 +1,6 @@ -- include_vars: _elb_secrets.yml - - name: Send request to API uri: - url: "{{ AUTH_URL_ELB }}/{{ elb_id }}" + url: "{{ AUTH_URL_ELB }}/{{ PROJECT_ID }}/elbaas/loadbalancers/{{ elb_id }}" method: GET follow_redirects: all return_content: yes @@ -10,6 +8,7 @@ HEADER_Content-Type: "application/json" HEADER_X-Auth-Token: "{{ token['x_subject_token'] }}" register: elb + when: elb_id is defined - debug: msg: "{{ elb }}" diff --git a/roles/lookup_name/tasks/main.yml b/roles/lookup_name/tasks/main.yml index 7886106..ef15269 100644 --- a/roles/lookup_name/tasks/main.yml +++ b/roles/lookup_name/tasks/main.yml @@ -1,12 +1,16 @@ - name: set fact for availability_zone_id (eu_de-01) set_fact: availability_zone_id: "bf84aba586ce4e948da0b97d9a7d62fb" - when: elb_availability_zone is defined and elb_availability_zone == "eu_de-01" + when: + - listener_name is undefined + - elb_availability_zone is defined and elb_availability_zone == "eu_de-01" - name: set fact for availability_zone_id /ei_de-01) set_fact: availability_zone_id: "bf84aba586ce4e948da0b97d9a7d62fc" - when: elb_availability_zone is defined and elb_availability_zone == "eu_de-02" + when: + - listener_name is undefined + - elb_availability_zone is defined and elb_availability_zone == "eu_de-02" - name: Request images list from API uri: @@ -46,13 +50,17 @@ validate_certs: yes HEADER_Content-Type: "application/json" HEADER_X-Auth-Token: "{{ token['x_subject_token'] }}" - when: elb_subnet_name is defined + when: + - listener_name is undefined + - elb_subnet_name is defined register: subnet_result - name: set fact for subnet_name (for elb) set_fact: subnet_name: "{{ elb_subnet_name }}" - when: elb_subnet_name is defined and elb_subnet_name|length != 0 + when: + - listener_name is undefined + - elb_subnet_name is defined and elb_subnet_name|length != 0 - name: Request subnet list from API for ecs uri: @@ -78,13 +86,17 @@ validate_certs: yes HEADER_Content-Type: "application/json" HEADER_X-Auth-Token: "{{ token['x_subject_token'] }}" - when: elb_secgroup_name is defined + when: + - listener_name is undefined + - elb_secgroup_name is defined register: secgroup_result - name: set fact secgroup_name (for elb) set_fact: secgroup_name: "{{ elb_secgroup_name }}" - when: elb_secgroup_name is defined + when: + - listener_name is undefined + - elb_secgroup_name is defined - name: Request secgroup list from API for ecs uri: @@ -100,7 +112,8 @@ - name: set fact for secgroup_id set_fact: secgroup_id: "{{ (secgroup_result.content|from_json)|json_query(\"security_groups[?name=='\" + secgroup_name + \"'].id|[0]\") }}" - when: secgroup_name is defined + when: + - secgroup_name is defined and secgroup_name| length != 0 - name: Request flavor list from API uri: @@ -192,6 +205,38 @@ elb_id: "{{ (elb_result.content|from_json)|json_query(\"loadbalancers[?name=='\" + elb_name + \"'].id|[0]\") }}" when: elb_name is defined -- debug: - msg: "{{ elb_id }}" +- name: Request elb certificate list from API + uri: + url: "{{ AUTH_URL_ELB }}/{{ PROJECT_ID }}/elbaas/certificate" + method: GET + follow_redirects: all + return_content: yes + validate_certs: yes + HEADER_Content-Type: "application/json" + HEADER_X-Auth-Token: "{{ token['x_subject_token'] }}" + register: listener_certificate_result + when: listener_certificate_name is defined and listener_certificate_name|length != 0 + +- set_fact: + listener_certificate_id: "{{ (listener_certificate_result.content|from_json)|json_query(\"certificates[?name=='\" + listener_certificate_name + \"'].id|[0]\") }}" + when: listener_certificate_name is defined and listener_certificate_name|length != 0 + +- name: Request elb listener from API + uri: + url: "{{ AUTH_URL_ELB }}/{{ PROJECT_ID }}/elbaas/listeners?loadbalancer_id={{ elb_id}}" + method: GET + follow_redirects: all + return_content: yes + validate_certs: yes + HEADER_Content-Type: "application/json" + HEADER_X-Auth-Token: "{{ token['x_subject_token'] }}" + register: listener_result + when: elb_id is defined + +- set_fact: + listener_id: "{{ (listener_result.content|from_json)|json_query(\"[?name=='\" + listener_name + \"'].id|[0]\") }}" + when: listener_name is defined + +#- debug: +# msg: "{{ listener_certificate_id }}" # when: ecs_id is defined From 3926bc9ac76702ca6ce75023d5bd8ca3ebe2e258 Mon Sep 17 00:00:00 2001 From: Frank Kloeker Date: Sun, 18 Jun 2017 20:24:50 +0200 Subject: [PATCH 13/72] document facts in lookup role and token --- roles/lookup_name/tasks/main.yml | 50 +++++++++++++++++++------------- roles/token/tasks/main.yml | 10 +++---- 2 files changed, 35 insertions(+), 25 deletions(-) diff --git a/roles/lookup_name/tasks/main.yml b/roles/lookup_name/tasks/main.yml index ef15269..d7749d5 100644 --- a/roles/lookup_name/tasks/main.yml +++ b/roles/lookup_name/tasks/main.yml @@ -1,11 +1,11 @@ -- name: set fact for availability_zone_id (eu_de-01) +- name: Set fact for availability_zone_id (eu_de-01) set_fact: availability_zone_id: "bf84aba586ce4e948da0b97d9a7d62fb" when: - listener_name is undefined - elb_availability_zone is defined and elb_availability_zone == "eu_de-01" -- name: set fact for availability_zone_id /ei_de-01) +- name: Set fact for availability_zone_id /ei_de-01) set_fact: availability_zone_id: "bf84aba586ce4e948da0b97d9a7d62fc" when: @@ -23,7 +23,8 @@ when: image_name is defined register: ims_result -- set_fact: +- name: Set fact image_id if image_name is defined + set_fact: image_id: "{{ (ims_result.content|from_json)|json_query('images[].id|[0]') }}" when: image_name is defined @@ -38,7 +39,8 @@ when: vpc_name is defined register: vpc_result -- set_fact: +- name: Set fact vpc_id if vpc_name is defined + set_fact: vpc_id: "{{ (vpc_result.content|from_json)|json_query(\"vpcs[?name=='\" + vpc_name + \"'].id|[0]\") }}" when: vpc_name is defined @@ -55,7 +57,7 @@ - elb_subnet_name is defined register: subnet_result -- name: set fact for subnet_name (for elb) +- name: Set fact for subnet_name if elb_subnet_name is defined set_fact: subnet_name: "{{ elb_subnet_name }}" when: @@ -73,7 +75,7 @@ when: subnet_name is defined register: subnet_result -- name: set fact for subnet_id +- name: Set fact for subnet_id if subnet_name is defined set_fact: subnet_id: "{{ (subnet_result.content|from_json)|json_query(\"subnets[?name=='\" + subnet_name + \"'].id|[0]\") }}" when: subnet_name is defined and subnet_name|length != 0 @@ -91,7 +93,7 @@ - elb_secgroup_name is defined register: secgroup_result -- name: set fact secgroup_name (for elb) +- name: Set fact secgroup_name if elb_secgroup_name is defined set_fact: secgroup_name: "{{ elb_secgroup_name }}" when: @@ -109,7 +111,7 @@ when: secgroup_name is defined register: secgroup_result -- name: set fact for secgroup_id +- name: Set fact for secgroup_id if secgroup_name is defined set_fact: secgroup_id: "{{ (secgroup_result.content|from_json)|json_query(\"security_groups[?name=='\" + secgroup_name + \"'].id|[0]\") }}" when: @@ -123,10 +125,11 @@ validate_certs: yes HEADER_Content-Type: "application/json" HEADER_X-Auth-Token: "{{ token['x_subject_token'] }}" - when: ecs_ram is defined and ecs_vcpus is defined + when: (ecs_ram is defined and ecs_ram|length != 0) and (ecs_vcpus is defined and ecs_vcpus|length != 0) register: flavor_result -- set_fact: +- name: Set fact flavor_id if ecs_ram or ecs_vcpus is defined + set_fact: flavor_id: "{{ (flavor_result.content|from_json)|json_query('sort_by(flavors, &ram)|[?ram>=`' + ecs_ram + '` && vcpus>=`' + ecs_vcpus + '`].id|[0]') }}" when: (ecs_ram is defined and ecs_ram |length != 0) or (ecs_vcpus is defined and ecs_vcpus|length != 0) @@ -141,7 +144,8 @@ when: ecs_adminkey is defined register: keypairlist_result -- set_fact: +- name: Set fact ecs_adminkey_name if ecs_adminkey is defined + set_fact: ecs_adminkey_name: "{{ (keypairlist_result.content|from_json)|json_query(\"keypairs[?keypair.name=='\" + ecs_adminkey + \"'].keypair.name\") }}" when: ecs_adminkey is defined and ecs_adminkey|length != 0 @@ -155,7 +159,8 @@ HEADER_X-Auth-Token: "{{ token['x_subject_token'] }}" register: floatingiplist_result -- set_fact: +- name: Set fact eip_id for eip if public_ip_address is defined + set_fact: eip_id: "{{ (floatingiplist_result.content|from_json)|json_query(\"publicips[?public_ip_address=='\" + public_ip_address + \"'].id|[0]\") }}" when: (public_ip_address is defined and public_ip_address | ipaddr) @@ -170,7 +175,8 @@ when: zone_name is defined register: zonelist_result -- set_fact: +- name: Set fact zone_id for dns if zone_name is defined + set_fact: zone_id: "{{ (zonelist_result.content|from_json)|json_query(\"zones[?name=='\" + zone_name + \"'].id|[0]\") }}" when: zone_name is defined @@ -185,7 +191,8 @@ register: ecs_result when: ecs_name is defined -- set_fact: +- name: Set fact ecs_id for ecs if ecs_name is defined + set_fact: ecs_id: "{{ (ecs_result.content|from_json)|json_query(\"servers[?name=='\" + ecs_name + \"'].id|[0]\") }}" when: ecs_name is defined @@ -201,7 +208,8 @@ register: elb_result when: elb_name is defined -- set_fact: +- name: Set fact elb_id for elb if elb_name is defined + set_fact: elb_id: "{{ (elb_result.content|from_json)|json_query(\"loadbalancers[?name=='\" + elb_name + \"'].id|[0]\") }}" when: elb_name is defined @@ -217,7 +225,8 @@ register: listener_certificate_result when: listener_certificate_name is defined and listener_certificate_name|length != 0 -- set_fact: +- name: Set fact listener_certificate_id for elb if listener_certificate_name is defined + set_fact: listener_certificate_id: "{{ (listener_certificate_result.content|from_json)|json_query(\"certificates[?name=='\" + listener_certificate_name + \"'].id|[0]\") }}" when: listener_certificate_name is defined and listener_certificate_name|length != 0 @@ -233,10 +242,11 @@ register: listener_result when: elb_id is defined -- set_fact: +- name: Set fact listener_id for elb if listener_name is defined + set_fact: listener_id: "{{ (listener_result.content|from_json)|json_query(\"[?name=='\" + listener_name + \"'].id|[0]\") }}" when: listener_name is defined -#- debug: -# msg: "{{ listener_certificate_id }}" -# when: ecs_id is defined +# - debug: +# msg: "{{ elb_id }}" +# when: elb_id is defined diff --git a/roles/token/tasks/main.yml b/roles/token/tasks/main.yml index aa6f318..444a00e 100644 --- a/roles/token/tasks/main.yml +++ b/roles/token/tasks/main.yml @@ -4,7 +4,7 @@ - "env.yml" - "_secrets.yml" -- name: set fact user home +- name: Set fact user home set_fact: user_home: "{{ lookup('env','HOME') }}" @@ -12,12 +12,12 @@ path: "{{ user_home }}/.config/openstack/clouds.yml" register: osclientconfigfile -- name: set fact os-client config file +- name: Set fact os-client config file set_fact: os_client_config: "{{ lookup('file', osclientconfigfile.stat.path)|from_yaml }}" when: osclientconfigfile.stat.exists -- name: set facts from os-client-config +- name: Set facts from os-client-config set_fact: USERNAME: "{{ os_client_config['clouds'][CLOUD]['auth']['username'] }}" PASSWORD: "{{ os_client_config['clouds'][CLOUD]['auth']['password'] }}" @@ -25,7 +25,7 @@ DOMAIN: "{{ os_client_config['clouds'][CLOUD]['auth']['user_domain_name'] }}" when: osclientconfigfile.stat.exists -- name: set fact generic endpoints +- name: Set fact generic endpoints set_fact: IAM_AUTH_URL: "https://iam.{{ PROJECT_NAME }}.otc.t-systems.com/v3" AUTH_URL_ELB: "https://elb.{{ PROJECT_NAME }}.otc.t-systems.com/v1.0" @@ -45,7 +45,7 @@ body: "{{ lookup('template', 'roles/token/templates/request.json.j2',convert_data=True)|to_json }}" register: token -- name: set facts dynamic endpoints +- name: Set facts dynamic endpoints set_fact: # OS_USER_DOMAIN_ID: "{{ (token.content|from_json)['token']['user']['domain']['id'] }}" OS_USER_DOMAIN_ID: "{{ (token.content|from_json)|json_query('token.user.domain.id') }}" From 3188a2daea5fb88b6c077b71f572925cd6903d84 Mon Sep 17 00:00:00 2001 From: Frank Kloeker Date: Sun, 18 Jun 2017 20:35:00 +0200 Subject: [PATCH 14/72] fix syntax error --- roles/lookup_name/tasks/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/roles/lookup_name/tasks/main.yml b/roles/lookup_name/tasks/main.yml index d7749d5..8aa80cb 100644 --- a/roles/lookup_name/tasks/main.yml +++ b/roles/lookup_name/tasks/main.yml @@ -5,7 +5,7 @@ - listener_name is undefined - elb_availability_zone is defined and elb_availability_zone == "eu_de-01" -- name: Set fact for availability_zone_id /ei_de-01) +- name: Set fact for availability_zone_id (eu_de-02) set_fact: availability_zone_id: "bf84aba586ce4e948da0b97d9a7d62fc" when: From ce2d11a8c1ffe632d3f2585997dbc13f9ebaab8c Mon Sep 17 00:00:00 2001 From: Frank Kloeker Date: Sun, 18 Jun 2017 20:39:04 +0200 Subject: [PATCH 15/72] move elb configuration into tenant.ini (includes create elb,elb_listener,elb_backends, elb_healthchecks). comments/usage instruction in head of tenant.ini --- README.md | 44 ++++--- backend_member_helper.yml | 7 + elb_create.yml | 1 + elb_delete.yml | 1 + elb_healthcheck_create.yml | 1 + elb_listener.yml | 1 + elb_listener_create.yml | 1 + elb_listener_delete.yml | 1 + elb_secrets.yml | 83 ++++++------ roles/backend_member_helper/tasks/main.yml | 50 ++++++++ .../templates/request.json.j2 | 6 + roles/ecs_create/tasks/main.yml | 10 +- roles/elb_backends/tasks/main.yml | 4 +- roles/elb_backends_create/tasks/main.yml | 4 +- roles/elb_backends_delete/tasks/main.yml | 5 +- roles/elb_create/tasks/main.yml | 6 +- roles/elb_create/templates/request.json.j2 | 12 +- roles/elb_delete/tasks/main.yml | 2 - roles/elb_healthcheck_create/tasks/main.yml | 3 +- .../templates/request.json.j2 | 14 +- roles/elb_listener/tasks/main.yml | 3 +- roles/elb_listener_create/tasks/main.yml | 8 +- .../templates/request.json.j2 | 10 +- roles/elb_listener_delete/tasks/main.yml | 3 +- roles/secgrouprule_helper/tasks/main.yml | 14 ++ tenant.ini | 120 +++++++++--------- tenant_create.yml | 41 +++++- tenant_var.yml | 27 ++++ 28 files changed, 317 insertions(+), 165 deletions(-) create mode 100644 backend_member_helper.yml create mode 100644 roles/backend_member_helper/tasks/main.yml create mode 100644 roles/backend_member_helper/templates/request.json.j2 diff --git a/README.md b/README.md index 2decaf3..e531988 100644 --- a/README.md +++ b/README.md @@ -138,7 +138,6 @@ Files |env.yml | profile to use in clouds.yml| |secrets.yml | var file for S3 credentials and endpoints (ansible-vault)| |ecs_secrets.yml | var file for virtual machine and volume conf (ansible-vault)| -|elb_secrets.yml | var file for elastic loadbalancer conf (ansible-vault)| |secgrouprule.yml| var file for single security group rule | |subnet_var.yml | var file for subnet | |vaultpass.txt | password file for ansible-vault. The default password is: linux :-)| @@ -162,7 +161,6 @@ Starting up ``` cp secrets.yml _secrets.yml cp ecs_secrets.yml _ecs_secrets.yml - cp elb_secrets.yml _elb_secrets.yml ``` :exclamation: **adjust your own data in this file before you using the examples:** @@ -201,7 +199,7 @@ show virtual machine (single) delete virtual machine (only the machine) - ansible-playbook -i hosts ecs_delete.yml -e "ecs_name=ansible-01" + ansible-playbook -i hosts ecs_delete.yml -e "ecs_name=ansible-test01" delete virtual machine (delete also floating ip and attached volumes) @@ -209,31 +207,31 @@ delete virtual machine (delete also floating ip and attached volumes) list elastic loadbalancers - ansible-playbook -i hosts elb.yml --vault-password-file vaultpass.txt + ansible-playbook -i hosts elb.yml -create elastic loadbalancer +create elastic loadbalancer (tenant.ini) - ansible-playbook -i hosts elb_create.yml --vault-password-file vaultpass.txt + ansible-playbook -i hosts elb_create.yml delete elastic loadbalancer - ansible-playbook -i hosts -e "elb_id=43848329789145988d1e0bf25edb5ea8" elb_delete.yml --vault-password-file vaultpass.txt + ansible-playbook -i hosts elb_delete.yml -e "ecs_name=ansible-elb01" show elastic loadbalancer - ansible-playbook -i hosts -e "elb_id=43848329789145988d1e0bf25edb5ea8" elb_show.yml --vault-password-file vaultpass.txt + ansible-playbook -i hosts elb_show.yml -e "elb_name=ansible-elb01" list elastic loadbalancer certificates - ansible-playbook -i hosts elb_certificate.yml --vault-password-file vaultpass.txt + ansible-playbook -i hosts elb_certificate.yml -create elastic loadbalancer certificate +create elastic loadbalancer certificate (we hate comments in cert file) - ansible-playbook -i hosts -e "elb_certificate_name=ansible-cert elb_certificate_key_file=cert.key elb_certificate_certificate_file=cert.crt" elb_certificate_create.yml --vault-password-file vaultpass.txt + ansible-playbook -i hosts -e "elb_certificate_name=ansible-cert elb_certificate_key_file=cert.key elb_certificate_certificate_file=cert.crt" elb_certificate_create.yml delete elastic loadbalancer certificates - ansible-playbook -i hosts -e "elb_certificate_id=43848329789145988d1e0bf25edb5ea8" elb_certificate_delete.yml --vault-password-file vaultpass.txt + ansible-playbook -i hosts -e "elb_certificate_id=43848329789145988d1e0bf25edb5ea8" elb_certificate_delete.yml create elastic loadbalancer healthcheck @@ -249,27 +247,27 @@ show elastic loadbalancer healthcheck list listener for elastic loadbalancer - ansible-playbook -i hosts -e "elb_id=e12454b93f304b759be699cb0270648c" elb_listener.yml --vault-password-file vaultpass.txt + ansible-playbook -i hosts elb_listener.yml -e "elb_name=ansible-elb01" -create listener for elastic loadbalancer +create listener for elastic loadbalancer (tenant.ini) - ansible-playbook -i hosts -e "elb_id=e12454b93f304b759be699cb0270648c" elb_listener_create.yml --vault-password-file vaultpass.txt + ansible-playbook -i hosts tenant_create.yml -e "elb_name=ansible-elb01" -e "listener_name=ansible-listener01" delete listener for elastic loadbalancer - ansible-playbook -i hosts -e "elb_listener_id=e12454b93f304b759be699cb0270648c" elb_listener_delete.yml --vault-password-file vaultpass.txt + ansible-playbook -i hosts elb_listener_delete.yml -e "elb_name=ansible-elb01" -e "listener_name=ansible-listener03" list backends for elastic loadbalancer - ansible-playbook -i hosts -e "elb_listener_id=e12454b93f304b759be699cb0270648c elb_backends.yml --vault-password-file vaultpass.txt + ansible-playbook -i hosts -e "listener_id=e12454b93f304b759be699cb0270648c elb_backends.yml create backends for elastic loadbalancer - ansible-playbook -i hosts -e "elb_listener_id=e12454b93f304b759be699cb0270648c ecs_id=f6b7536e-b954-4d73-940f-248de71ce58b ecs_address=192.168.0.112" elb_backends_create.yml --vault-password-file vaultpass.txt + ansible-playbook -i hosts -e "listener_id=e12454b93f304b759be699cb0270648c ecs_id=f6b7536e-b954-4d73-940f-248de71ce58b ecs_address=192.168.0.112" elb_backends_create.yml --vault-password-file vaultpass.txt delete backends for elastic loadbalancer - ansible-playbook -i hosts -e "elb_listener_id=e12454b93f304b759be699cb0270648c elb_backends_id=f6b7536e-b954-4d73-940f-248de71ce58b" elb_backends_delete.yml --vault-password-file vaultpass.txt + ansible-playbook -i hosts -e "listener_id=e12454b93f304b759be699cb0270648c elb_backends_id=f6b7536e-b954-4d73-940f-248de71ce58b" elb_backends_delete.yml --vault-password-file vaultpass.txt enable SNAT on specific VPC @@ -377,6 +375,14 @@ lookup id by name (elb) ansible-playbook -i hosts lookup_name.yml -e "elb_name=ansible-elb01" +lookup id by name (certificate) + + ansible-playbook -i hosts lookup_name.yml -e "listener_certificate_name=ansible-cert" + +lookup id by name (listener) + + ansible-playbook -i hosts lookup_name.yml -e "listener_name=ansible-listener01" -e "elb_name=ansible-elb01" + list provided database versions for RDS ansible-playbook -i hosts rds_versions.yml diff --git a/backend_member_helper.yml b/backend_member_helper.yml new file mode 100644 index 0000000..cf48f95 --- /dev/null +++ b/backend_member_helper.yml @@ -0,0 +1,7 @@ +--- +- hosts: all + gather_facts: no + roles: + - role: token + - role: lookup_name + - role: backend_member_helper diff --git a/elb_create.yml b/elb_create.yml index c12e2a5..be2cbd9 100644 --- a/elb_create.yml +++ b/elb_create.yml @@ -3,4 +3,5 @@ gather_facts: no roles: - role: token + - role: lookup_name - role: elb_create diff --git a/elb_delete.yml b/elb_delete.yml index 2febd21..2a05069 100644 --- a/elb_delete.yml +++ b/elb_delete.yml @@ -3,5 +3,6 @@ gather_facts: no roles: - role: token + - role: lookup_name - role: elb_delete diff --git a/elb_healthcheck_create.yml b/elb_healthcheck_create.yml index 7463143..0f57032 100644 --- a/elb_healthcheck_create.yml +++ b/elb_healthcheck_create.yml @@ -3,4 +3,5 @@ gather_facts: no roles: - role: token + - role: lookup_name - role: elb_healthcheck_create diff --git a/elb_listener.yml b/elb_listener.yml index 6f90ec2..a3de313 100644 --- a/elb_listener.yml +++ b/elb_listener.yml @@ -3,4 +3,5 @@ gather_facts: no roles: - role: token + - role: lookup_name - role: elb_listener diff --git a/elb_listener_create.yml b/elb_listener_create.yml index d227ca1..667a87e 100644 --- a/elb_listener_create.yml +++ b/elb_listener_create.yml @@ -3,4 +3,5 @@ gather_facts: no roles: - role: token + - role: lookup_name - role: elb_listener_create diff --git a/elb_listener_delete.yml b/elb_listener_delete.yml index a57c74b..4e91ad0 100644 --- a/elb_listener_delete.yml +++ b/elb_listener_delete.yml @@ -3,4 +3,5 @@ gather_facts: no roles: - role: token + - role: lookup_name - role: elb_listener_delete diff --git a/elb_secrets.yml b/elb_secrets.yml index 3b53c26..c847dae 100644 --- a/elb_secrets.yml +++ b/elb_secrets.yml @@ -1,46 +1,39 @@ $ANSIBLE_VAULT;1.1;AES256 -64386139303439353066376465353039383636363439653934633961653363313734363163316232 -6666666133646565393233333565356236333534323262350a333065666331646439663832643964 -38326265623966343837356131616663393863373734643563336538656233653461383731663439 -6463323532663039650a613964626130623337626234363262653863656239343136393237633030 -33306164396463656463353361353432393366643363653137613532326130613863346536363036 -63656634366165316634363762343363356438636165653038356636663337323130656562323634 -30646263353839626330326234316237373264306364623336303561656132353261303038326639 -39356139646135656336326337373039643834353133346663346538633832306431663733303731 -36343066356438323337396331393734653961663966316338373232386233313230313339643733 -31376430373434663130366237366466396665373037386234396466343032613537366366363038 -63303839323335613562643763333635353537326365323030666530346566313939356437366161 -33326439373830393232353833383062373838636234383430656333616166613662623433613335 -32343330383735333261363539323766343566363637663366613664363634326165346138623236 -38616161653565656637333161376630613764666237626666646432663832653164366439343031 -31633265323031386331313161653662313561303634363038316132353033343331356663306639 -33376235336335646334383131623765653030646435393564346432626234386138393830356434 -64313135656231386164653839643538653038643562396662313463393433323833373165383764 -64396639313631353164613139303831366464636434666564656565356663396362636236393765 -66353961663935633364393264356131353030396630333731393338633334326431366565623061 -39613864303331663261383838313531653530316337353332633536613132323533663565306465 -65636132346539373434366231326561623837376534646336333039616337386133396561373938 -35383063376439376465373636303861356638306430666238646339303730626230383532616234 -34616665656438363563646538306364336630646365306564666633626438333535626465646138 -33663135643032353234306230626461363963373938333938356531356564636234343561386132 -61373766343136623064346331626339313930653364306233633066636631616132666538623564 -36666237663466343133316633393263303538393163623931353763346164343262343936383334 -35616232323636396535653261373161393763393430343539663065613161656165666331383537 -65353363383761353163666235396437653539633861616637316233336437393737613666373133 -63626266316466343637616663616630373831393461306164373961313336313363316636343463 -30653435313038373662313333633734633464616239363437386233633739363664633730343735 -35316239636638323232393234643537323362326364656339643037306331393133303462643262 -37663939376166633330393639343134393336303566626337646565633432356433333730316335 -32343130633164326339663432396563316338363665313262353532616333326136643962313731 -30366338626266326231373364616333643063356466663664356237313036353737316432353033 -39376136663466663330626335346331326561643738626235643235613736633031396361313335 -34383536623736363539346238323436313663616134636263633935353830666238393631373763 -35343335633232323539326138643037313032613734643363326432383137626534636634656536 -38613066623366346633333439333830363361343762316163383435643139666238663639333466 -34343139383330613435313562303561633962336663386664366130353165616634633339353665 -66633565636363643438383331316433316636313338633039343037313337343332346364366264 -38653166636536356164376666373334373263373939613133396234653839303230386630653734 -30393361623739653339333466656365343930366332646661663830353163356233386335663266 -65393666323834643638633562666337653637316636393837333236303464366664303866643865 -37316436376232383338393061343432306364333439363462313738666235303234363561616364 -6361 +32633362376237613035323762323665613162393664323933336236383732336135303666616366 +6331393139613832663163396365626635316133386634620a636337313031303965623732303836 +32643538323735613030303762626565646166396130646263653962633538343535363662636637 +3862333531616532610a383931323462656132666464353130636564303663653737616462343138 +66613163333336376636616134623639663733326666643630653433623637316263666364613636 +32343732666665636236396464343535643362623237613238666362336464323437626465623438 +61393361343436663430313231613439613564643737643839623465636631373230663230323266 +33326230356465373261366636326633313030636236316539336366653761366137623731316233 +39613137303030336138356338386665336636633630643666373132613931323934373034356332 +33356232333137366434666233633761376162636163636639343332643266343830323766613937 +62316166393266353265396538333763323362383062393939393334356264633864386539653934 +64613561653930653666333464343639363165356233393965306663366531333032336232396663 +66613836303737616134626232643136366235653061303335326236356163343635626634636631 +63666339376139626530363465323037653735646464383431313966653736316233386538393238 +63633762633163613930363532613730663763303238363832316336343931393063326437666562 +36633064373062643166623732333636346135386463303862333462653237346533313230306639 +31616161373535366165326263633362346162306662393832663532613632613463623462616136 +30666537623533666130663738336336643938326432353361326338396432353538396536643037 +38636636373237326264393035663234636166323965356136333763313865666633643161386135 +61303263323837646463326332653765366632353062626437316566393635393962336130326439 +36656464643366643166393031393832373439346264656366306665623532663564373965653063 +66333361383934356234613134306436653436623039336661616633626661626462643933303537 +36653262663238333663633563636330363738323531373637633833353435343539316137323932 +64613864343863303866323534386432373562346433613835383464633061633739343363643462 +34636532643065626630663334656662396134303535393035636466643637323633643232393638 +39636539323334303831346136653261636265623939616661373431643237316632303562373538 +66316533613334393231633862343035353933636433363130323161306265623661663931643035 +33396566393962373831663732373762616134363261353131633236376163393031343131343934 +34386132366530656261316337396363653534396431363037323463346330653639613135643431 +33376638323237306235383664343665373134333833636161353533363436396462343666656537 +61356537306135666632626532396233656338643962323736613561303461643464653665313634 +30313733313165613036346561613333323931336561383135616335633162623764616337316666 +31356464343133383763656336653337633063643436653064326363663538636634653130393266 +39663539636636393565376637636266656332323334613162626130646362316265636333656237 +61306135376336326239613432313033396334393335323635383866323736326132396461616130 +63306237623331633536346130376137343732326330386439643933663464346630373436333062 +37346236396437336130366436643265306132326663323463303935313934616333393865353763 +3937656431353766363533346435343133613933343661653139 diff --git a/roles/backend_member_helper/tasks/main.yml b/roles/backend_member_helper/tasks/main.yml new file mode 100644 index 0000000..58d1486 --- /dev/null +++ b/roles/backend_member_helper/tasks/main.yml @@ -0,0 +1,50 @@ +- name: Request full ecs list from API + uri: + url: "{{ AUTH_URL_ECS }}/servers" + method: GET + return_content: yes + validate_certs: yes + HEADER_Content-Type: "application/json" + HEADER_X-Auth-Token: "{{ token['x_subject_token'] }}" + register: ecs_result + when: listener_name is defined + +- name: Request detail ecs info from API + vars: + backend_member_name: "{{ ecs_result['json']|json_query(\"servers[?name=='\" + item + \"'].id|[0]\") }}" + uri: + url: "{{ AUTH_URL_ECS }}/servers/{{ backend_member_name }}" + method: GET + return_content: yes + validate_certs: yes + HEADER_Content-Type: "application/json" + HEADER_X-Auth-Token: "{{ token['x_subject_token'] }}" + register: ecs + with_items: + - "{{ backend_members.split(',') }}" + +- name: Set fact backend_member_id + set_fact: + backend_member_id: "{{ ecs['results']|json_query('[].json.server.id') }}" + backend_member_ip: "{{ ecs['results']|json_query('[].json.server.addresses.*[*].addr[][]') }}" + when: ecs is defined and listener_id is defined + +- name: Send request to API + uri: + url: "{{ AUTH_URL_ELB }}/{{ PROJECT_ID }}/elbaas/listeners/{{ listener_id }}/members" + method: POST + body_format: raw + follow_redirects: all + return_content: yes + validate_certs: yes + HEADER_Content-Type: "application/json" + HEADER_X-Auth-Token: "{{ token['x_subject_token'] }}" + body: "{{ lookup('template', 'roles/backend_member_helper/templates/request.json.j2')|to_json }}" + register: elbbackends + with_together: + - "{{ backend_member_id }}" + - "{{ backend_member_ip }}" + +- debug: + msg: "{{ elbbackends }}" + when: elbbackends is defined diff --git a/roles/backend_member_helper/templates/request.json.j2 b/roles/backend_member_helper/templates/request.json.j2 new file mode 100644 index 0000000..bf9de4c --- /dev/null +++ b/roles/backend_member_helper/templates/request.json.j2 @@ -0,0 +1,6 @@ +[ +{ + "server_id": "{{ item.0 }}", + "address": "{{ item.1 }}" +} +] diff --git a/roles/ecs_create/tasks/main.yml b/roles/ecs_create/tasks/main.yml index 9e15239..ea749ce 100644 --- a/roles/ecs_create/tasks/main.yml +++ b/roles/ecs_create/tasks/main.yml @@ -12,12 +12,16 @@ HEADER_X-Auth-Token: "{{ token['x_subject_token'] }}" body: "{{ lookup('template', 'roles/ecs_create/templates/request.json.j2')|to_json }}" register: ecs - when: ecs_name is defined + when: + - ecs_name is defined + - image_id is defined + - flavor_id is defined + - listener_name is undefined - set_fact: job_id: "{{ (ecs.content|from_json)|json_query('job_id') }}" - when: ecs is defined + when: ecs is defined and ecs.content|length != 0 - debug: msg: "{{ job_id }}" - + when: job_id is defined diff --git a/roles/elb_backends/tasks/main.yml b/roles/elb_backends/tasks/main.yml index ffadb46..6522ad4 100644 --- a/roles/elb_backends/tasks/main.yml +++ b/roles/elb_backends/tasks/main.yml @@ -1,8 +1,6 @@ -- include_vars: _elb_secrets.yml - - name: Send request to API uri: - url: "{{ AUTH_URL_ELB_LISTENER }}/{{ elb_listener_id}}/members?limit=10&marker=0" + url: "{{ AUTH_URL_ELB }}/{{ PROJECT_ID }}/elbaas/listeners/{{ listener_id}}/members?limit=10&marker=0" method: GET follow_redirects: all return_content: yes diff --git a/roles/elb_backends_create/tasks/main.yml b/roles/elb_backends_create/tasks/main.yml index fc5837b..cb0e09c 100644 --- a/roles/elb_backends_create/tasks/main.yml +++ b/roles/elb_backends_create/tasks/main.yml @@ -1,8 +1,6 @@ -- include_vars: _elb_secrets.yml - - name: Send request to API uri: - url: "{{ AUTH_URL_ELB_LISTENER }}/{{ elb_listener_id }}/members" + url: "{{ AUTH_URL_ELB }}/{{ PROJECT_ID }}/elbaas/listeners/{{ listener_id }}/members" method: POST body_format: raw follow_redirects: all diff --git a/roles/elb_backends_delete/tasks/main.yml b/roles/elb_backends_delete/tasks/main.yml index 433f4f3..9aced21 100644 --- a/roles/elb_backends_delete/tasks/main.yml +++ b/roles/elb_backends_delete/tasks/main.yml @@ -1,8 +1,6 @@ -- include_vars: _elb_secrets.yml - - name: Send request to API uri: - url: "{{ AUTH_URL_ELB_LISTENER }}/{{ elb_listener_id }}/members/action" + url: {{ AUTH_URL_ELB }}/{{ PROJECT_ID }}/elbaas/listeners/{{ listener_id }}/members/action" method: POST body_format: raw follow_redirects: all @@ -12,6 +10,7 @@ HEADER_X-Auth-Token: "{{ token['x_subject_token'] }}" body: "{{ lookup('template', 'roles/elb_backends_delete/templates/request.json.j2')|to_json }}" register: elbbackends + when: listener_id is defined - debug: msg: "{{ elbbackends }}" diff --git a/roles/elb_create/tasks/main.yml b/roles/elb_create/tasks/main.yml index 73b0643..3c1cd05 100644 --- a/roles/elb_create/tasks/main.yml +++ b/roles/elb_create/tasks/main.yml @@ -1,5 +1,3 @@ -- include_vars: _elb_secrets.yml - - name: Send request to API uri: url: "{{ AUTH_URL_ELB }}/{{ PROJECT_ID }}/elbaas/loadbalancers" @@ -11,6 +9,10 @@ HEADER_Content-Type: "application/json" HEADER_X-Auth-Token: "{{ token['x_subject_token'] }}" body: "{{ lookup('template', 'roles/elb_create/templates/request.json.j2')|to_json }}" + when: + - listener_name is undefined + - vpc_id is defined + - elb_name is defined register: elb - debug: diff --git a/roles/elb_create/templates/request.json.j2 b/roles/elb_create/templates/request.json.j2 index cd783f3..531bc94 100644 --- a/roles/elb_create/templates/request.json.j2 +++ b/roles/elb_create/templates/request.json.j2 @@ -1,14 +1,14 @@ { "name": "{{ elb_name }}", - "vpc_id": "{{ elb_vpcid }}", + "vpc_id": "{{ vpc_id }}", {% if elb_type == "External" %} "bandwidth": "{{ elb_bandwidth }}", {% endif %} "type": "{{ elb_type }}", -{% if elb_type == "Internal" and elb_secgroup is defined %} - "security_group_id": "{{ elb_secgroup }}", - "az": "{{ elb_availability_zone_id }}", - "vip_subnet_id": "{{ elb_subnet_id }}", +{% if elb_type == "Internal" and secgroup_id is defined %} + "security_group_id": "{{ secgroup_id }}", + "az": "{{ availability_zone_id }}", + "vip_subnet_id": "{{ subnet_id }}", {% endif %} - "admin_state_up": {{ elb_admin_state_up }} + "admin_state_up": {{ admin_state_up }} } diff --git a/roles/elb_delete/tasks/main.yml b/roles/elb_delete/tasks/main.yml index 8adfe4f..22cc88a 100644 --- a/roles/elb_delete/tasks/main.yml +++ b/roles/elb_delete/tasks/main.yml @@ -1,5 +1,3 @@ -- include_vars: _elb_secrets.yml - - name: Send request to API uri: url: "{{ AUTH_URL_ELB }}/{{ PROJECT_ID }}/elbaas/loadbalancers/{{ elb_id }}" diff --git a/roles/elb_healthcheck_create/tasks/main.yml b/roles/elb_healthcheck_create/tasks/main.yml index cc059a3..21938d4 100644 --- a/roles/elb_healthcheck_create/tasks/main.yml +++ b/roles/elb_healthcheck_create/tasks/main.yml @@ -1,5 +1,3 @@ -- include_vars: _elb_secrets.yml - - name: Send request to API uri: url: "{{ AUTH_URL_ELB }}/{{ PROJECT_ID }}/elbaas/healthcheck" @@ -12,6 +10,7 @@ HEADER_X-Auth-Token: "{{ token['x_subject_token'] }}" body: "{{ lookup('template', 'roles/elb_healthcheck_create/templates/request.json.j2')|to_json }}" register: elbhealthcheck + when: listener_id is defined and listener_id|length != 0 - debug: msg: "{{ elbhealthcheck }}" diff --git a/roles/elb_healthcheck_create/templates/request.json.j2 b/roles/elb_healthcheck_create/templates/request.json.j2 index c233340..240696e 100644 --- a/roles/elb_healthcheck_create/templates/request.json.j2 +++ b/roles/elb_healthcheck_create/templates/request.json.j2 @@ -1,21 +1,21 @@ { - "listener_id": "{{ elb_listener_id }}", -{% if healthcheck_connect_port is defined %} + "listener_id": "{{ listener_id }}", +{% if healthcheck_connect_port is defined and healthcheck_connect_port|length != 0 %} "healthcheck_connect_port": {{ healthcheck_connect_port }}, {% endif %} -{% if healthcheck_interval is defined %} +{% if healthcheck_interval is defined and healthcheck_interval|length != 0 %} "healthcheck_interval": {{ healthcheck_interval }}, {% endif %} -{% if healthcheck_protocol is defined %} +{% if healthcheck_protocol is defined and healthcheck_protocol|length != 0 %} "healthcheck_protocol": "{{ healthcheck_protocol }}", {% endif %} -{% if healthcheck_timeout is defined %} +{% if healthcheck_timeout is defined and healthcheck_timeout|length != 0 %} "healthcheck_timeout": {{ healthcheck_timeout }}, {% endif %} -{% if healthcheck_uri is defined %} +{% if healthcheck_uri is defined and healthcheck_uri|length != 0 %} "healthcheck_uri": "{{ healthcheck_uri }}", {% endif %} -{% if healthcheck_treshold is defined %} +{% if healthcheck_treshold is defined and healthcheck_treshold|length != 0 %} "healthcheck_treshold": {{ healthcheck_treshold }} {% endif %} } diff --git a/roles/elb_listener/tasks/main.yml b/roles/elb_listener/tasks/main.yml index 59c4d74..d72e01b 100644 --- a/roles/elb_listener/tasks/main.yml +++ b/roles/elb_listener/tasks/main.yml @@ -1,5 +1,3 @@ -- include_vars: _elb_secrets.yml - - name: Send request to API uri: url: "{{ AUTH_URL_ELB }}/{{ PROJECT_ID }}/elbaas/listeners?loadbalancer_id={{ elb_id}}" @@ -10,6 +8,7 @@ HEADER_Content-Type: "application/json" HEADER_X-Auth-Token: "{{ token['x_subject_token'] }}" register: elblistener + when: elb_id is defined - debug: msg: "{{ elblistener }}" diff --git a/roles/elb_listener_create/tasks/main.yml b/roles/elb_listener_create/tasks/main.yml index a7f3f84..5a371c8 100644 --- a/roles/elb_listener_create/tasks/main.yml +++ b/roles/elb_listener_create/tasks/main.yml @@ -1,5 +1,3 @@ -- include_vars: _elb_secrets.yml - - name: Send request to API uri: url: "{{ AUTH_URL_ELB }}/{{ PROJECT_ID }}/elbaas/listeners" @@ -12,6 +10,12 @@ HEADER_X-Auth-Token: "{{ token['x_subject_token'] }}" body: "{{ lookup('template', 'roles/elb_listener_create/templates/request.json.j2')|to_json }}" register: elblistner + when: + - elb_id is defined + - listener_name is defined + +# - pause: +# minutes: 1 - debug: msg: "{{ elblistner }}" diff --git a/roles/elb_listener_create/templates/request.json.j2 b/roles/elb_listener_create/templates/request.json.j2 index 9f1a267..0e725cc 100644 --- a/roles/elb_listener_create/templates/request.json.j2 +++ b/roles/elb_listener_create/templates/request.json.j2 @@ -5,19 +5,19 @@ "port": {{ listener_port }}, "backend_protocol": "{{ listener_backend_protocol }}", "backend_port": {{ listener_backend_port }}, -{% if listener_certificate_id is defined %} +{% if listener_certificate_id is defined and listener_certificate_id|length != 0 %} "certificate_id": "{{ listener_certificate_id }}", {% endif %} -{% if listener_session_sticky is defined %} +{% if listener_session_sticky is defined and listener_session_sticky|length != 0 %} "session_sticky": "{{ listener_session_sticky }}", {% endif %} -{% if listener_sticky_session_type is defined %} +{% if listener_sticky_session_type is defined and listener_sticky_session_type|length != 0 %} "sticky_session_type": "{{ listener_sticky_session_type }}", {% endif %} -{% if listener_cookie_timeout is defined %} +{% if listener_cookie_timeout is defined and listener_cookie_timeout|length != 0 %} "cookie_timeout": "{{ listener_cookie_timeout }}", {% endif %} -{% if listener_tcp_timeout is defined %} +{% if listener_tcp_timeout is defined and listener_tcp_timeout|length != 0 %} "tcp_timeout": "{{ listener_tcp_timeout }}", {% endif %} "lb_algorithm": "{{ listener_lb_algorithm }}" diff --git a/roles/elb_listener_delete/tasks/main.yml b/roles/elb_listener_delete/tasks/main.yml index c927868..a876307 100644 --- a/roles/elb_listener_delete/tasks/main.yml +++ b/roles/elb_listener_delete/tasks/main.yml @@ -1,10 +1,11 @@ - name: Send request to API uri: - url: "{{ AUTH_URL_ELB }}/{{ PROJECT_ID }}/elbaas/listeners/{{ elb_listener_id}}" + url: "{{ AUTH_URL_ELB }}/{{ PROJECT_ID }}/elbaas/listeners/{{ listener_id}}" method: DELETE follow_redirects: all return_content: yes validate_certs: yes + status_code: 200,201,202,203,204 HEADER_Content-Type: "application/json" HEADER_X-Auth-Token: "{{ token['x_subject_token'] }}" register: elblistener diff --git a/roles/secgrouprule_helper/tasks/main.yml b/roles/secgrouprule_helper/tasks/main.yml index 1556601..db24b85 100644 --- a/roles/secgrouprule_helper/tasks/main.yml +++ b/roles/secgrouprule_helper/tasks/main.yml @@ -1,13 +1,24 @@ +- name: switch ecs or elb + set_fact: + ecs_name: "{{ elb_name }}" + when: + - elb_name is defined +# - listener_name is undefined + - name: fetch secgroup rules from ini set_fact: secgrouprules: "{{ item }}" with_ini: secgroup_rule[1-9] section={{ ecs_name }} file=tenant.ini re=true register: secgrouprule_reg + when: + - ecs_name is defined +# - listener_name is undefined - name: make a list from secgroup rules set_fact: secgrouprule_list: "{{ secgrouprule_reg.results | map(attribute='ansible_facts.secgrouprules') | list }}" + when: secgrouprule_reg is defined - name: send request to API vars: @@ -24,6 +35,9 @@ HEADER_X-Auth-Token: "{{ token['x_subject_token'] }}" body: "{{ lookup('template', 'roles/secgrouprule_helper/templates/request.json.j2')|to_json }}" register: secgrouprule + when: + - secgroup_id is defined + - secgrouprule_reg is defined with_items: - "{{ secgrouprule_list }}" diff --git a/tenant.ini b/tenant.ini index 255b9dc..91083d4 100644 --- a/tenant.ini +++ b/tenant.ini @@ -1,87 +1,92 @@ # ini file for tenant configuration # each block for each vm # usage ecs: -# ansible-playbook -i hosts tenant_create.yml -e "ecs_name=ansible-test01" --vault-password-file vaultpass.txt +# ansible-playbook -i hosts tenant_create.yml -e "ecs_name=ansible-test01" # usage dns: -# ansible-playbook -i hosts dns_create.yml --vault-password-file vaultpass.txt - +# ansible-playbook -i hosts dns_create.yml +# usage elb (listener, healthcheck, backendmembers) +# ansible-playbook -i hosts tenant_create.yml -e "elb_name=ansible-elb01" -e "listener_name=ansible-listener01" +# [DEFAULT] image_name=Community_Ubuntu_16.04_TSI_latest +availability_zone=eu-de-01 +vpc_name=ansible-vpc01 +vpc_net=192.168.0.0/16 +subnet_name=ansible-subnet01 +subnet_net=192.168.0.0/24 +subnet_gateway=192.168.0.1 +subnet_dhcp_enable=true +subnet_primary_dns=8.8.8.8 +subnet_secondary_dns=8.4.4.8 +secgroup_name=ansible-secgroup02 +secgroup_rule1=ingress;IPv4;tcp;22;22;0.0.0.0/0 +ecs_volumetype=SATA +ecs_ram=2048 +ecs_vcpus=2 +ecs_adminkey=ansible-key +keypair_file=~/.ssh/id_rsa.pub [dnszones] # name; description; type; email-address; ttl -zone1=external.otc.telekomcloud.com.;Core Zone public OTC services;;cloud-operations@telekom.de;86400 +zone1=cloud2.internal.;Core Zone public OTC services;;cloud-operations@telekom.de;86400 +# zone1=external.otc.telekomcloud.com.;Core Zone public OTC services;;cloud-operations@telekom.de;86400 # zone2=80.196.44.160.in-addr.arpa.;Reverse OTC Zone;;cloud-operations@telekom.de;300 [dnszonerecords] # domain; description; name; type; ttl; value -zonerecord1=external.otc.telekomcloud.com.;;console.external.otc.telekomcloud.com.;A;300;160.44.204.87 +zonerecord1=cloud2.internal.;;test1.cloud2.internal.;A;300;192.168.99.1 +# zonerecord1=external.otc.telekomcloud.com.;;console.external.otc.telekomcloud.com.;A;300;160.44.204.87 # zonerecord2=80.196.44.160.in-addr.arpa.;Reverse OTC;80.196.44.160.in-addr.arpa.;PTR;300;console.otc.telekomcloud.com. +[ansible-elb02] +elb_type=External +elb_bandwidth=100 +admin_state_up=true +elb_availability_zone=eu_de-01 +elb_secgroup_name=ansible-secgroup02 +secgroup_rule1=ingress;IPv4;tcp;22;22;0.0.0.0/0 +secgroup_rule1=ingress;IPv4;tcp;80;80;0.0.0.0/0 +elb_subnet_name=ansible-subnet01 +[ansible-listener01] +# HTTP, HTTPS, TCP +listener_protocol=TCP +listener_port=22 +listener_backend_protocol=TCP +listener_backend_port=22 +# source, roundrobin, leastconn +listener_lb_algorithm=source +#listener_certificate_name=ansible-cert +#listener_tcp_timeout= +#listener_cookie_timeout= +#listener_sticky_session_type=insert +#listener_session_sticky= +healthcheck_connect_port=22 +healthcheck_interval=5 +# HTTP, TCP +healthcheck_protocol=TCP +healthcheck_timeout=10 +#healthcheck_uri="/" +unhealthy_threshold=3 +backend_members=ansible-test01,ansible-test02 [ansible-test01] -# image_name=Community_Ubuntu_14.04_TSI_latest -ecs_volumetype=SATA -ecs_ram=2048 -ecs_vcpus=2 -vpc_name=ansible-vpc01 secgroup_name=ansible-secgroup01 secgroup_rule1=ingress;IPv4;tcp;22;22;0.0.0.0/0 secgroup_rule2=ingress;IPv4;tcp;80;80;0.0.0.0/0 secgroup_rule3=egress;IPv4;tcp;80;80;0.0.0.0/0 secgroup_rule4=ingress;IPv4;icmp;;;0.0.0.0/0 -vpc_net=192.168.0.0/16 -subnet_name=ansible-subnet01 -subnet_net=192.168.0.0/24 -subnet_gateway=192.168.0.1 -subnet_dhcp_enable=true -subnet_primary_dns=8.8.8.8 -subnet_secondary_dns=8.4.4.8 -availability_zone=eu-de-02 ecs_ipaddress=192.168.0.101 -ecs_publicip=160.44.195.243 +ecs_publicip=160.44.194.98 eip_bandwidth_name=ansible-eip1 eip_bandwidth_size=100 -ecs_adminkey=ansible-key -keypair_file=~/.ssh/id_rsa.pub [ansible-test02] -image_name=Community_Ubuntu_16.04_TSI_latest +image_name=Community_Ubuntu_14.04_TSI_latest ecs_volumetype=SATA ecs_ram=2048 -ecs_vcpus=2 -vpc_name=ansible-vpc01 -secgroup_name=ansible-secgroup01 -vpc_net=192.168.0.0/16 -subnet_name=ansible-subnet01 -subnet_net=192.168.0.0/24 -subnet_gateway=192.168.0.1 -subnet_dhcp_enable=true -subnet_primary_dns=8.8.8.8 -subnet_secondary_dns=8.4.4.8 -availability_zone=eu-de-02 +ecs_vcpus=4 ecs_ipaddress=192.168.0.102 -# ecs_publicip= -eip_bandwidth_name=ansible-eip1 -eip_bandwidth_size=100 -ecs_adminkey=ansible-key -keypair_file=~/.ssh/id_rsa.pub [ansible-test03] -image_name=Community_Ubuntu_16.04_TSI_latest -ecs_volumetype=SATA -ecs_ram=2048 -ecs_vcpus=2 -vpc_name=ansible-vpc01 -secgroup_name=ansible-secgroup01 -vpc_net=192.168.0.0/16 -subnet_name=ansible-subnet01 -subnet_net=192.168.0.0/24 -subnet_gateway=192.168.0.1 -subnet_dhcp_enable=true -subnet_primary_dns=8.8.8.8 -subnet_secondary_dns=8.4.4.8 -availability_zone=eu-de-02 +ecs_volumetype=SSD ecs_ipaddress=192.168.0.103 -ecs_publicip=0.0.0.0 -eip_bandwidth_name=ansible-eip1 -eip_bandwidth_size=100 -ecs_adminkey=ansible-key -keypair_file=~/.ssh/id_rsa.pub +# ecs_publicip=0.0.0.0 +# eip_bandwidth_name=ansible-eip1 +# eip_bandwidth_size=100 [console] image_name=Community_Ubuntu_16.04_TSI_latest ecs_volumetype=SATA @@ -110,4 +115,3 @@ eip_bandwidth_name=cloudcamp-eip1 eip_bandwidth_size=100 ecs_adminkey=eumel-key keypair_file=~/.ssh/id_rsa.pub - diff --git a/tenant_create.yml b/tenant_create.yml index da3c874..47e29b9 100644 --- a/tenant_create.yml +++ b/tenant_create.yml @@ -72,10 +72,47 @@ vars_files: - tenant_var_default.yml - tenant_var.yml -# vars: -# eip_id: "{{ eip_id }}" roles: - role: token - role: lookup_name - role: ecs_create - role: job + +- hosts: localhost + gather_facts: no + connection: local + vars_files: + - tenant_var_default.yml + - tenant_var.yml + roles: + - role: elb_create + +- hosts: localhost + gather_facts: no + connection: local + vars_files: + - tenant_var_default.yml + - tenant_var.yml + roles: + - role: elb_listener_create + +- hosts: localhost + gather_facts: no + connection: local + vars_files: + - tenant_var_default.yml + - tenant_var.yml + roles: + - role: lookup_name + - role: elb_healthcheck_create + +- hosts: localhost + gather_facts: no + connection: local + vars_files: + - tenant_var_default.yml + - tenant_var.yml + roles: + - role: lookup_name + - role: backend_member_helper + when: backend_members is defined diff --git a/tenant_var.yml b/tenant_var.yml index a0f8397..2936300 100644 --- a/tenant_var.yml +++ b/tenant_var.yml @@ -27,3 +27,30 @@ subnet_secondary_dns: "{{ lookup('ini', 'subnet_secondary_dns section={{ ecs_name }} file=tenant.ini') }}" vpc_name: "{{ lookup('ini', 'vpc_name section={{ ecs_name }} file=tenant.ini') }}" vpc_net: "{{ lookup('ini', 'vpc_net section={{ ecs_name }} file=tenant.ini') }}" +# ELB vars + admin_state_up: "{{ lookup('ini', 'admin_state_up section={{ elb_name }} file=tenant.ini') }}" + elb_availability_zone: "{{ lookup('ini', 'elb_availability_zone section={{ elb_name }} file=tenant.ini') }}" + elb_bandwidth: "{{ lookup('ini', 'elb_bandwidth section={{ elb_name }} file=tenant.ini') }}" + elb_type: "{{ lookup('ini', 'elb_type section={{ elb_name }} file=tenant.ini') }}" + elb_secgroup_name: "{{ lookup('ini', 'elb_secgroup_name section={{ elb_name }} file=tenant.ini') }}" + elb_subnet_name: "{{ lookup('ini', 'elb_subnet_name section={{ elb_name }} file=tenant.ini') }}" +# ELB listener vars + listener_protocol: "{{ lookup('ini', 'listener_protocol section={{ listener_name }} file=tenant.ini') }}" + listener_port: "{{ lookup('ini', 'listener_port section={{ listener_name }} file=tenant.ini') }}" + listener_backend_protocol: "{{ lookup('ini', 'listener_backend_protocol section={{ listener_name }} file=tenant.ini') }}" + listener_backend_port: "{{ lookup('ini', 'listener_backend_port section={{ listener_name }} file=tenant.ini') }}" + listener_lb_algorithm: "{{ lookup('ini', 'listener_lb_algorithm section={{ listener_name }} file=tenant.ini') }}" + listener_certificate_name: "{{ lookup('ini', 'listener_certificate_name section={{ listener_name }} file=tenant.ini') }}" + listener_tcp_timeout: "{{ lookup('ini', 'listener_tcp_timeout section={{ listener_name }} file=tenant.ini') }}" + listener_cookie_timeout: "{{ lookup('ini', 'listener_cookie_timeout section={{ listener_name }} file=tenant.ini') }}" + listener_sticky_session_type: "{{ lookup('ini', 'listener_sticky_session_type section={{ listener_name }} file=tenant.ini') }}" + listener_session_sticky: "{{ lookup('ini', 'listener_session_sticky section={{ listener_name }} file=tenant.ini') }}" +# ELB healthcheck vars + healthcheck_connect_port: "{{ lookup('ini', 'healthcheck_connect_port section={{ listener_name }} file=tenant.ini') }}" + healthcheck_interval: "{{ lookup('ini', 'healthcheck_interval section={{ listener_name }} file=tenant.ini') }}" + healthcheck_protocol: "{{ lookup('ini', 'healthcheck_protocol section={{ listener_name }} file=tenant.ini') }}" + healthcheck_timeout: "{{ lookup('ini', 'healthcheck_timeout section={{ listener_name }} file=tenant.ini') }}" + healthcheck_uri: "{{ lookup('ini', 'healthcheck_uri section={{ listener_name }} file=tenant.ini') }}" + unhealthy_threshold: "{{ lookup('ini', 'unhealthy_threshold section={{ listener_name }} file=tenant.ini') }}" +# ELB backend member + backend_members: "{{ lookup('ini', 'backend_members section={{ listener_name }} file=tenant.ini') }}" From 2f153a328639c4cd38b7aa9730edafd4efad8c72 Mon Sep 17 00:00:00 2001 From: Frank Kloeker Date: Sun, 18 Jun 2017 20:39:37 +0200 Subject: [PATCH 16/72] add condition job role --- roles/job/tasks/main.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/roles/job/tasks/main.yml b/roles/job/tasks/main.yml index b8f8f78..51d4793 100644 --- a/roles/job/tasks/main.yml +++ b/roles/job/tasks/main.yml @@ -6,8 +6,9 @@ validate_certs: yes HEADER_Content-Type: "application/json" HEADER_X-Auth-Token: "{{ token['x_subject_token'] }}" + when: job_id is defined register: jobstatus - debug: msg: "{{ jobstatus.json }}" - + when: jobstatus is defined and jobstatus.content|length != 0 From bca4b74e04597a0c4ada5a0707f10f4a909ba603 Mon Sep 17 00:00:00 2001 From: Frank Kloeker Date: Sun, 18 Jun 2017 20:39:48 +0200 Subject: [PATCH 17/72] add condition keypair role --- roles/keypair_create/tasks/main.yml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/roles/keypair_create/tasks/main.yml b/roles/keypair_create/tasks/main.yml index df314b2..482d006 100644 --- a/roles/keypair_create/tasks/main.yml +++ b/roles/keypair_create/tasks/main.yml @@ -1,6 +1,10 @@ +- stat: path={{ keypair_file }} + register: keypair_file_stat + - name: grab ssh pub key shell: awk '$1=$1' ORS='\\n' {{ keypair_file }} register: keypair_file_content + when: keypair_file_stat.stat.exists - name: Send request to API uri: @@ -13,7 +17,9 @@ HEADER_Content-Type: "application/json" HEADER_X-Auth-Token: "{{ token['x_subject_token'] }}" body: "{{ lookup('template', 'roles/keypair_create/templates/request.json.j2')|to_json }}" - when: (not ecs_adminkey_name or ecs_adminkey_name is undefined) + when: + - keypair_file_stat.stat.exists + - (not ecs_adminkey_name or ecs_adminkey_name is undefined) register: keypair - debug: From 9e27e10d10b146037407aa949df64dd124232637 Mon Sep 17 00:00:00 2001 From: Frank Kloeker Date: Sat, 24 Jun 2017 13:49:18 +0200 Subject: [PATCH 18/72] add role vpc router info and set facts --- README.md | 11 ++++++++--- roles/vpc_router/tasks/main.yml | 30 ++++++++++++++++++++++++++++++ vpc_router.yml | 7 +++++++ 3 files changed, 45 insertions(+), 3 deletions(-) create mode 100644 roles/vpc_router/tasks/main.yml create mode 100644 vpc_router.yml diff --git a/README.md b/README.md index e531988..9733748 100644 --- a/README.md +++ b/README.md @@ -82,6 +82,7 @@ Roles |subnet_delete | delete subnet| |token | get auth token| |vpc | show vpc| +|vpc_router | show vpc router info and set facts| |vpc_create | create vpc| |vpc_delete | delete vpc| |zones | show DNS zones| @@ -257,13 +258,13 @@ delete listener for elastic loadbalancer ansible-playbook -i hosts elb_listener_delete.yml -e "elb_name=ansible-elb01" -e "listener_name=ansible-listener03" -list backends for elastic loadbalancer +list backends for elastic loadbalancer (tenant.ini) - ansible-playbook -i hosts -e "listener_id=e12454b93f304b759be699cb0270648c elb_backends.yml + ansible-playbook -i hosts elb_backends.yml -e "listener_name=ansible-listener01" -e "elb_name=ansible-elb01" elb_backends.yml create backends for elastic loadbalancer - ansible-playbook -i hosts -e "listener_id=e12454b93f304b759be699cb0270648c ecs_id=f6b7536e-b954-4d73-940f-248de71ce58b ecs_address=192.168.0.112" elb_backends_create.yml --vault-password-file vaultpass.txt + ansible-playbook -i hosts elb_backends_create.yml -e "listener_name=ansible-listener01" -e "elb_name=ansible-elb01" -e "ecs_name=ansible-test01" -e "ecs_address=192.168.0.10" delete backends for elastic loadbalancer @@ -451,6 +452,10 @@ show vpc ansible-playbook -i hosts vpc.yml +show vpc router info and set facts + + ansible-playbook -i hosts vpc_router.yml -e "vpc_name=ansible-vpc01" + create vpc ansible-playbook -i hosts vpc_create.yml -e "vpc_name=ansible-vpc1" -e "vpc_net=192.168.0.0/16" diff --git a/roles/vpc_router/tasks/main.yml b/roles/vpc_router/tasks/main.yml new file mode 100644 index 0000000..6f23392 --- /dev/null +++ b/roles/vpc_router/tasks/main.yml @@ -0,0 +1,30 @@ +- name: Request router from API + uri: + url: "{{ AUTH_URL_VPC }}/v2.0/routers/{{ vpc_id }}" + method: GET + return_content: yes + validate_certs: yes + HEADER_Content-Type: "application/json" + HEADER_X-Auth-Token: "{{ token['x_subject_token'] }}" + when: + - vpc_id is defined + register: routerlist + +- set_fact: + router_id: "{{ (routerlist.content|from_json)|json_query('router.id') }}" + router_name: "{{ (routerlist.content|from_json)|json_query('router.name') }}" + router_status: "{{ (routerlist.content|from_json)|json_query('router.status') }}" + router_admin_state_up: "{{ (routerlist.content|from_json)|json_query('router.admin_state_up') }}" + router_routes: "{{ (routerlist.content|from_json)|json_query('router.routes[]') }}" + router_tenant_id: "{{ (routerlist.content|from_json)|json_query('router.tenant_id') }}" + external_network_id: "{{ (routerlist.content|from_json)|json_query('router.external_gateway_info.network_id') }}" + external_network_snat_state: "{{ (routerlist.content|from_json)|json_query('router.external_gateway_info.enable_snat') }}" + when: + - vpc_id is defined + - routerlist is defined + +- debug: + msg: "{{ routerlist.json }}" + when: + - vpc_id is defined + - routerlist is defined diff --git a/vpc_router.yml b/vpc_router.yml new file mode 100644 index 0000000..5398b0e --- /dev/null +++ b/vpc_router.yml @@ -0,0 +1,7 @@ +--- +- hosts: all + gather_facts: no + roles: + - role: token + - role: lookup_name + - role: vpc_router From 34857c9e19dc01b009a458dc154279b0679b7826 Mon Sep 17 00:00:00 2001 From: Frank Kloeker Date: Sat, 24 Jun 2017 13:50:56 +0200 Subject: [PATCH 19/72] reimplement feature private dns zones (tenant.ini) --- dns_create.yml | 2 ++ roles/zone_create/templates/request.json.j2 | 6 ++++++ roles/zonerecord_helper/templates/zone.json.j2 | 6 ++++++ tenant.ini | 12 +++++++----- 4 files changed, 21 insertions(+), 5 deletions(-) diff --git a/dns_create.yml b/dns_create.yml index dfbfed9..3a8e492 100644 --- a/dns_create.yml +++ b/dns_create.yml @@ -5,4 +5,6 @@ connection: local roles: - role: token + - role: lookup_name + - role: vpc_router - role: zonerecord_helper diff --git a/roles/zone_create/templates/request.json.j2 b/roles/zone_create/templates/request.json.j2 index 9510bd1..a467fab 100644 --- a/roles/zone_create/templates/request.json.j2 +++ b/roles/zone_create/templates/request.json.j2 @@ -6,6 +6,12 @@ {% if zone_type is defined %} "zone_type": "{{ zone_type }}", {% endif %} +{% if zone_type == "private" %} + "router": { + "router_id": "{{ router_id }}", + "router_region": "{{ PROJECT_NAME }}" + }, +{% endif %} {% if zone_email is defined %} "email": "{{ zone_email }}", {% endif %} diff --git a/roles/zonerecord_helper/templates/zone.json.j2 b/roles/zonerecord_helper/templates/zone.json.j2 index d54755e..3d0ff2e 100644 --- a/roles/zonerecord_helper/templates/zone.json.j2 +++ b/roles/zonerecord_helper/templates/zone.json.j2 @@ -6,6 +6,12 @@ {% if zone_list_part[2]|length != 0 %} "zone_type": "{{ zone_list_part[2] }}", {% endif %} +{% if zone_list_part[2] == "private" %} + "router": { + "router_id": "{{ router_id }}", + "router_region": "{{ PROJECT_NAME }}" + }, +{% endif %} {% if zone_list_part[3]|length != 0 %} "email": "{{ zone_list_part[3] }}", {% endif %} diff --git a/tenant.ini b/tenant.ini index 91083d4..27a0054 100644 --- a/tenant.ini +++ b/tenant.ini @@ -2,8 +2,10 @@ # each block for each vm # usage ecs: # ansible-playbook -i hosts tenant_create.yml -e "ecs_name=ansible-test01" -# usage dns: +# usage dns (public zones): # ansible-playbook -i hosts dns_create.yml +# usage dns (internal usage, only in selected vpc): +# ansible-playbook -i hosts dns_create.yml -e "vpc_name=ansible-vpc01" # usage elb (listener, healthcheck, backendmembers) # ansible-playbook -i hosts tenant_create.yml -e "elb_name=ansible-elb01" -e "listener_name=ansible-listener01" # @@ -26,16 +28,16 @@ ecs_vcpus=2 ecs_adminkey=ansible-key keypair_file=~/.ssh/id_rsa.pub [dnszones] -# name; description; type; email-address; ttl -zone1=cloud2.internal.;Core Zone public OTC services;;cloud-operations@telekom.de;86400 +# name; description; type (public/private); email-address; ttl (in sec) +zone1=cloud3.internal.;Core Zone public OTC services;private;cloud-operations@telekom.de;86400 # zone1=external.otc.telekomcloud.com.;Core Zone public OTC services;;cloud-operations@telekom.de;86400 # zone2=80.196.44.160.in-addr.arpa.;Reverse OTC Zone;;cloud-operations@telekom.de;300 [dnszonerecords] # domain; description; name; type; ttl; value -zonerecord1=cloud2.internal.;;test1.cloud2.internal.;A;300;192.168.99.1 +zonerecord1=cloud3.internal.;;test1.cloud3.internal.;A;300;192.168.0.1 # zonerecord1=external.otc.telekomcloud.com.;;console.external.otc.telekomcloud.com.;A;300;160.44.204.87 # zonerecord2=80.196.44.160.in-addr.arpa.;Reverse OTC;80.196.44.160.in-addr.arpa.;PTR;300;console.otc.telekomcloud.com. -[ansible-elb02] +[ansible-elb01] elb_type=External elb_bandwidth=100 admin_state_up=true From fc53a185bdff8a9615b412b1c1b9f3b9d29d21ff Mon Sep 17 00:00:00 2001 From: Frank Kloeker Date: Sat, 24 Jun 2017 14:15:58 +0200 Subject: [PATCH 20/72] migrate elb_backend role to name concept --- README.md | 4 ++-- elb_backends.yml | 1 + elb_backends_create.yml | 1 + elb_backends_delete.yml | 1 + roles/elb_backends_delete/tasks/main.yml | 2 +- 5 files changed, 6 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 9733748..7039e7c 100644 --- a/README.md +++ b/README.md @@ -260,7 +260,7 @@ delete listener for elastic loadbalancer list backends for elastic loadbalancer (tenant.ini) - ansible-playbook -i hosts elb_backends.yml -e "listener_name=ansible-listener01" -e "elb_name=ansible-elb01" elb_backends.yml + ansible-playbook -i hosts elb_backends.yml -e "listener_name=ansible-listener01" -e "elb_name=ansible-elb01" create backends for elastic loadbalancer @@ -268,7 +268,7 @@ create backends for elastic loadbalancer delete backends for elastic loadbalancer - ansible-playbook -i hosts -e "listener_id=e12454b93f304b759be699cb0270648c elb_backends_id=f6b7536e-b954-4d73-940f-248de71ce58b" elb_backends_delete.yml --vault-password-file vaultpass.txt + ansible-playbook -i hosts elb_backends_delete.yml -e "listener_name=ansible-listener01" -e "elb_name=ansible-elb01" -e "elb_backends_id=d15e2f8dd7d64d95a6b5c2a791cac408" enable SNAT on specific VPC diff --git a/elb_backends.yml b/elb_backends.yml index f4f7901..fb6d633 100644 --- a/elb_backends.yml +++ b/elb_backends.yml @@ -3,4 +3,5 @@ gather_facts: no roles: - role: token + - role: lookup_name - role: elb_backends diff --git a/elb_backends_create.yml b/elb_backends_create.yml index 15ccce4..89998c1 100644 --- a/elb_backends_create.yml +++ b/elb_backends_create.yml @@ -3,4 +3,5 @@ gather_facts: no roles: - role: token + - role: lookup_name - role: elb_backends_create diff --git a/elb_backends_delete.yml b/elb_backends_delete.yml index a6fcda5..9c2ee54 100644 --- a/elb_backends_delete.yml +++ b/elb_backends_delete.yml @@ -3,4 +3,5 @@ gather_facts: no roles: - role: token + - role: lookup_name - role: elb_backends_delete diff --git a/roles/elb_backends_delete/tasks/main.yml b/roles/elb_backends_delete/tasks/main.yml index 9aced21..3624dd6 100644 --- a/roles/elb_backends_delete/tasks/main.yml +++ b/roles/elb_backends_delete/tasks/main.yml @@ -1,6 +1,6 @@ - name: Send request to API uri: - url: {{ AUTH_URL_ELB }}/{{ PROJECT_ID }}/elbaas/listeners/{{ listener_id }}/members/action" + url: "{{ AUTH_URL_ELB }}/{{ PROJECT_ID }}/elbaas/listeners/{{ listener_id }}/members/action" method: POST body_format: raw follow_redirects: all From cc572dfd756530caf0748377a84d655b071dd0e1 Mon Sep 17 00:00:00 2001 From: Frank Kloeker Date: Sat, 24 Jun 2017 14:30:09 +0200 Subject: [PATCH 21/72] fetch only certificate names in debug output of elb_certificate role --- roles/elb_certificate/tasks/main.yml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/roles/elb_certificate/tasks/main.yml b/roles/elb_certificate/tasks/main.yml index d2b255d..1cc2f8e 100644 --- a/roles/elb_certificate/tasks/main.yml +++ b/roles/elb_certificate/tasks/main.yml @@ -9,6 +9,10 @@ HEADER_X-Auth-Token: "{{ token['x_subject_token'] }}" register: elbcertificate -- debug: - msg: "{{ elbcertificate }}" +- set_fact: + certificate_names: "{{ (elbcertificate.content|from_json)|json_query('certificates[].name') }}" +- debug: +# msg: "{{ elbcertificate }}" + msg: "{{ certificate_names }}" + when: elbcertificate is defined From 633bd3bf34b4f579644a6bcadd4512084bb393fa Mon Sep 17 00:00:00 2001 From: Frank Kloeker Date: Sat, 24 Jun 2017 14:44:29 +0200 Subject: [PATCH 22/72] migrate elb_certificate_delete role to name concept --- README.md | 4 ++-- elb_certificate_delete.yml | 1 + roles/elb_certificate_delete/tasks/main.yml | 10 +++++++--- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 7039e7c..8410e72 100644 --- a/README.md +++ b/README.md @@ -228,11 +228,11 @@ list elastic loadbalancer certificates create elastic loadbalancer certificate (we hate comments in cert file) - ansible-playbook -i hosts -e "elb_certificate_name=ansible-cert elb_certificate_key_file=cert.key elb_certificate_certificate_file=cert.crt" elb_certificate_create.yml + ansible-playbook -i hosts elb_certificate_create.yml -e "elb_certificate_name=ansible-cert elb_certificate_key_file=cert.key elb_certificate_certificate_file=cert.crt" delete elastic loadbalancer certificates - ansible-playbook -i hosts -e "elb_certificate_id=43848329789145988d1e0bf25edb5ea8" elb_certificate_delete.yml + ansible-playbook -i hosts elb_certificate_delete.yml -e "listener_certificate_name=ansible-cert" create elastic loadbalancer healthcheck diff --git a/elb_certificate_delete.yml b/elb_certificate_delete.yml index 4248544..5799681 100644 --- a/elb_certificate_delete.yml +++ b/elb_certificate_delete.yml @@ -3,4 +3,5 @@ gather_facts: no roles: - role: token + - role: lookup_name - role: elb_certificate_delete diff --git a/roles/elb_certificate_delete/tasks/main.yml b/roles/elb_certificate_delete/tasks/main.yml index e79d6e8..a0d6b0a 100644 --- a/roles/elb_certificate_delete/tasks/main.yml +++ b/roles/elb_certificate_delete/tasks/main.yml @@ -1,14 +1,18 @@ - name: Send request to API uri: - url: "{{ AUTH_URL_ELB }}/{{ PROJECT_ID }}/elbaas/certificate/{{ elb_certificate_id}}" + url: "{{ AUTH_URL_ELB }}/{{ PROJECT_ID }}/elbaas/certificate/{{ listener_certificate_id }}" method: DELETE follow_redirects: all return_content: yes validate_certs: yes + status_code: 200,201,202,203,204 HEADER_Content-Type: "application/json" HEADER_X-Auth-Token: "{{ token['x_subject_token'] }}" - register: elbcerticate + register: elbcertifcate + when: listener_certificate_id is defined - debug: msg: "{{ elbcertifcate }}" - + when: + - elbcertifcate is defined + - listener_certificate_id is defined From 4ea3e06b06af5e7a06e974748266cd7bca07c2bc Mon Sep 17 00:00:00 2001 From: Frank Kloeker Date: Sat, 24 Jun 2017 18:15:32 +0200 Subject: [PATCH 23/72] used variables in playbooks and roles --- VARIABLES.md | 133 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 133 insertions(+) create mode 100644 VARIABLES.md diff --git a/VARIABLES.md b/VARIABLES.md new file mode 100644 index 0000000..a0561ca --- /dev/null +++ b/VARIABLES.md @@ -0,0 +1,133 @@ +# +# +# +availability_zone +availability_zone_id +backend_member_id +backend_member_ip +certificate_names +ecs +ecs_address +ecs_adminkey name of ssh-key +ecs_adminpass +ecs_adminpass Admin password of ECS instance +ecs_fileinject_1 file to inject in ECS +ecs_fileinject_2 file to inject in ECS +ecs_fileinject_3 file to inject in ECS +ecs_fileinject_4 file to inject in ECS +ecs_fileinject_6 file to inject in ECS +ecs_fileinject_data_1 data of injected file in ECS +ecs_fileinject_data_2 data of injected file in ECS +ecs_fileinject_data_3 data of injected file in ECS +ecs_fileinject_data_4 data of injected file in ECS +ecs_fileinject_data_5 data of injected file in ECS +ecs_id UUID of ECS instance +ecs_ipaddress local ipaddress of ECS instance +ecs_name name of ECS instance +ecs_user_data cloud-init user_data to inject in ECS +ecs_volumetype type of ECS volume (SATA,SAS,SSD) +eip +eip_bandwidth_name +eip_bandwidth_size +eip_id UUID of floating ipaddress +elb +elb_availability_zone +elbbackends +elb_backends_id +elb_bandwidth +elbcertifcate +elbcertificate +elb_certificate_name +elbhealthcheck +elb_id +elblist +elblistener +elblistner +elb_name name of ELB instance +elb_secgroup_name +elb_subnet_name +elb_type +enable_snat +evs +evs_availability_zone +evs_backup_id +evs_ims_id +evs_name +evs_shareable +evs_size +evs_volume_type +external_network_id +flavor_id UUID of selected flavor +healthcheck_connect_port +healthcheck_interval +healthcheck_protocol +healthcheck_timeout +healthcheck_treshold +healthcheck_uri +image_create +image_delete +image_id UUID of selected IMS image +image_min_disk +image_name +image_os_version +job_id +keypair +keypair_file +listener_backend_port +listener_backend_port: +listener_backend_protocol +listener_certificate_id +listener_certificate_name +listener_cookie_timeout +listener_id +listener_lb_algorithm +listener_name +listener_port +listener_protocol +listener_session_sticky +listener_sticky_session_type +listener_tcp_timeout +public_ip_address +router +router_id +secgroup +secgroup_id +secgroup_name +secgrouprule +secgroup_rule +secgrouprule_direction +secgrouprule_ethertype +secgrouprule_list +secgrouprule_port_range_max +secgrouprule_port_range_min +secgrouprule_protocol +secgrouprule_remote_group_id +secgrouprule_remote_ip_prefix +subnet +subnet_dhcp_enable +subnet_gateway +subnet_id UUID of selected subnet +subnet_name +subnet_net +subnet_primary_dns +subnet_secondary_dns +token +unhealthy_threshold +vpc +vpc_id UUID of ECS instance +vpc_name name of VPC +vpc_net +zone +zone_description +zone_email +zone_list +zone_name +zonerecord +zonerecord_description +zonerecord_list +zonerecord_name +zonerecord_ttl +zonerecord_type +zonerecord_value +zone_ttl +zone_type From 43d3cdaff9a783473d8679e7fbd4e78c3057b01d Mon Sep 17 00:00:00 2001 From: Frank Kloeker Date: Sat, 24 Jun 2017 19:01:45 +0200 Subject: [PATCH 24/72] cleanup elb_healthcheck/listener to use names instead uuid --- README.md | 14 +++++++------- elb_healthcheck_create.yml | 3 +++ elb_listener_create.yml | 3 +++ roles/elb_healthcheck_delete/tasks/main.yml | 1 + 4 files changed, 14 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 8410e72..1af7cc6 100644 --- a/README.md +++ b/README.md @@ -212,11 +212,11 @@ list elastic loadbalancers create elastic loadbalancer (tenant.ini) - ansible-playbook -i hosts elb_create.yml + ansible-playbook -i hosts elb_create.yml -e "elb_name=ansible-elb01" delete elastic loadbalancer - ansible-playbook -i hosts elb_delete.yml -e "ecs_name=ansible-elb01" + ansible-playbook -i hosts elb_delete.yml -e "elb_name=ansible-elb01" show elastic loadbalancer @@ -234,17 +234,17 @@ delete elastic loadbalancer certificates ansible-playbook -i hosts elb_certificate_delete.yml -e "listener_certificate_name=ansible-cert" -create elastic loadbalancer healthcheck +create elastic loadbalancer healthcheck (tenant.ini) - ansible-playbook -i hosts -e "elb_listener_id=1595f0e7b6984395ab2832a22cd246f2" elb_healthcheck_create.yml --vault-password-file vaultpass.txt + ansible-playbook -i hosts elb_healthcheck_create.yml -e "elb_name=ansible-elb01" -e "listener_name=ansible-listener01" delete elastic loadbalancer healthcheck - ansible-playbook -i hosts -e "elb_healthcheck_id=e12454b93f304b759be699cb0270648c" elb_healthcheck_delete.yml --vault-password-file vaultpass.txt + ansible-playbook -i hosts elb_healthcheck_delete.yml -e "elb_healthcheck_id=e12454b93f304b759be699cb0270648c" show elastic loadbalancer healthcheck - ansible-playbook -i hosts -e "elb_healthcheck_id=e12454b93f304b759be699cb0270648c" elb_healthcheck_show.yml --vault-password-file vaultpass.txt + ansible-playbook -i hosts elb_healthcheck_show.yml -e "elb_healthcheck_id=e12454b93f304b759be699cb0270648c" list listener for elastic loadbalancer @@ -252,7 +252,7 @@ list listener for elastic loadbalancer create listener for elastic loadbalancer (tenant.ini) - ansible-playbook -i hosts tenant_create.yml -e "elb_name=ansible-elb01" -e "listener_name=ansible-listener01" + ansible-playbook -i hosts elb_listener_create.yml -e "elb_name=ansible-elb01" -e "listener_name=ansible-listener01" delete listener for elastic loadbalancer diff --git a/elb_healthcheck_create.yml b/elb_healthcheck_create.yml index 0f57032..532ccee 100644 --- a/elb_healthcheck_create.yml +++ b/elb_healthcheck_create.yml @@ -1,6 +1,9 @@ --- - hosts: all gather_facts: no + vars_files: + - tenant_var_default.yml + - tenant_var.yml roles: - role: token - role: lookup_name diff --git a/elb_listener_create.yml b/elb_listener_create.yml index 667a87e..44a2494 100644 --- a/elb_listener_create.yml +++ b/elb_listener_create.yml @@ -1,6 +1,9 @@ --- - hosts: all gather_facts: no + vars_files: + - tenant_var_default.yml + - tenant_var.yml roles: - role: token - role: lookup_name diff --git a/roles/elb_healthcheck_delete/tasks/main.yml b/roles/elb_healthcheck_delete/tasks/main.yml index 4c47cf9..b589e9a 100644 --- a/roles/elb_healthcheck_delete/tasks/main.yml +++ b/roles/elb_healthcheck_delete/tasks/main.yml @@ -5,6 +5,7 @@ follow_redirects: all return_content: yes validate_certs: yes + status_code: 200,201,202,203,204 HEADER_Content-Type: "application/json" HEADER_X-Auth-Token: "{{ token['x_subject_token'] }}" register: elbhealthcheck From 245f8963f2628f3cfa368ee9de5705c08346ed66 Mon Sep 17 00:00:00 2001 From: Frank Kloeker Date: Sat, 24 Jun 2017 20:39:51 +0200 Subject: [PATCH 25/72] lookup role for evs --- README.md | 10 +++++++--- roles/lookup_name/tasks/main.yml | 20 ++++++++++++++++++-- 2 files changed, 25 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 1af7cc6..57d1e63 100644 --- a/README.md +++ b/README.md @@ -284,11 +284,11 @@ discover API endpoints list volumes - ansible-playbook -i hosts evs.yml --vault-password-file vaultpass.txt + ansible-playbook -i hosts evs.yml -create a volume (defined in ecs_secrets.yml) +create a volume (tenant.ini) - ansible-playbook -i hosts evs_create.yml --vault-password-file vaultpass.txt + ansible-playbook -i hosts evs_create.yml -e "evs_name=ansible-evs01" delete a volume @@ -372,6 +372,10 @@ lookup id by name (ecs) ansible-playbook -i hosts lookup_name.yml -e "ecs_name=ansible-test01" +lookup id by name (evs) + + ansible-playbook -i hosts lookup_name.yml -e "evs_name=ansible-evs01" + lookup id by name (elb) ansible-playbook -i hosts lookup_name.yml -e "elb_name=ansible-elb01" diff --git a/roles/lookup_name/tasks/main.yml b/roles/lookup_name/tasks/main.yml index 8aa80cb..4db8932 100644 --- a/roles/lookup_name/tasks/main.yml +++ b/roles/lookup_name/tasks/main.yml @@ -196,6 +196,22 @@ ecs_id: "{{ (ecs_result.content|from_json)|json_query(\"servers[?name=='\" + ecs_name + \"'].id|[0]\") }}" when: ecs_name is defined +- name: Request evs list from API + uri: + url: "{{ AUTH_URL_EVS }}/cloudvolumes" + method: GET + return_content: yes + validate_certs: yes + HEADER_Content-Type: "application/json" + HEADER_X-Auth-Token: "{{ token['x_subject_token'] }}" + register: evs_result + when: evs_name is defined + +- name: Set fact evs_id if evs_name is defined + set_fact: + evs_id: "{{ (evs_result.content|from_json)|json_query(\"volumes[?name=='\" + evs_name + \"'].id|[0]\") }}" + when: evs_name is defined + - name: Request elb list from API uri: url: "{{ AUTH_URL_ELB }}/{{ PROJECT_ID }}/elbaas/loadbalancers" @@ -248,5 +264,5 @@ when: listener_name is defined # - debug: -# msg: "{{ elb_id }}" -# when: elb_id is defined +# msg: "{{ evs_id }}" +# when: evs_id is defined From 97a3e3a30394b0edf0fe31a1153f2fce5e8ff746 Mon Sep 17 00:00:00 2001 From: Frank Kloeker Date: Sat, 24 Jun 2017 20:56:01 +0200 Subject: [PATCH 26/72] migrate eip_delete role to name concept --- README.md | 6 +++--- eip_delete.yml | 1 + evs_create.yml | 5 +++-- evs_delete.yml | 3 +-- evs_show.yml | 3 +-- roles/evs_create/tasks/main.yml | 3 --- roles/evs_create/templates/request.json.j2 | 9 +++++++-- roles/evs_delete/tasks/main.yml | 1 + tenant.ini | 8 ++++++++ tenant_var.yml | 6 ++++++ tenant_var_default.yml | 5 +++++ 11 files changed, 36 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index 57d1e63..523d6c3 100644 --- a/README.md +++ b/README.md @@ -292,11 +292,11 @@ create a volume (tenant.ini) delete a volume - ansible-playbook -i hosts evs_delete.yml -e "evs_id=05f143e0-3ca9-4ec7-97e6-733dd281c283" --vault-password-file vaultpass.txt + ansible-playbook -i hosts evs_delete.yml -e "evs_name=ansible-evs01" show information about a single volume - ansible-playbook -i hosts evs_show.yml -e "evs_id=05f143e0-3ca9-4ec7-97e6-733dd281c283" --vault-password-file vaultpass.txt + ansible-playbook -i hosts evs_show.yml -e "evs_name=ansible-evs01" show flavors @@ -312,7 +312,7 @@ apply a new elastic ip-address (bandwidth between 1-300 MBit/s) delete elastic ip-address - ansible-playbook -i hosts eip_delete.yml -e "eip_id=c417c3bf-fdd2-47c4-a64f-320add5759b5" + ansible-playbook -i hosts eip_delete.yml -e "public_ip_address=160.44.195.18" show images diff --git a/eip_delete.yml b/eip_delete.yml index 53c8fba..0570893 100644 --- a/eip_delete.yml +++ b/eip_delete.yml @@ -3,4 +3,5 @@ gather_facts: no roles: - role: token + - role: lookup_name - role: eip_delete diff --git a/evs_create.yml b/evs_create.yml index 2f18d7a..f756cc2 100644 --- a/evs_create.yml +++ b/evs_create.yml @@ -1,8 +1,9 @@ --- - hosts: all gather_facts: no + vars_files: + - tenant_var_default.yml + - tenant_var.yml roles: - role: token - role: evs_create - - diff --git a/evs_delete.yml b/evs_delete.yml index fb8f92f..c7edd14 100644 --- a/evs_delete.yml +++ b/evs_delete.yml @@ -3,6 +3,5 @@ gather_facts: no roles: - role: token + - role: lookup_name - role: evs_delete - - diff --git a/evs_show.yml b/evs_show.yml index b7a34db..6c14f15 100644 --- a/evs_show.yml +++ b/evs_show.yml @@ -3,6 +3,5 @@ gather_facts: no roles: - role: token + - role: lookup_name - role: evs_show - - diff --git a/roles/evs_create/tasks/main.yml b/roles/evs_create/tasks/main.yml index 860e45c..6b7837d 100644 --- a/roles/evs_create/tasks/main.yml +++ b/roles/evs_create/tasks/main.yml @@ -1,5 +1,3 @@ -- include_vars: _ecs_secrets.yml - - name: Send request to API uri: url: "{{ AUTH_URL_EVS }}/cloudvolumes" @@ -15,4 +13,3 @@ - debug: msg: "{{ evs }}" - diff --git a/roles/evs_create/templates/request.json.j2 b/roles/evs_create/templates/request.json.j2 index ddacebc..8acf059 100644 --- a/roles/evs_create/templates/request.json.j2 +++ b/roles/evs_create/templates/request.json.j2 @@ -12,8 +12,13 @@ {% if evs_backup_id is defined %} "backup_id": "{{ evs_backup_id }}", {% endif %} -{% if evs_shareable is defined %} - "shareable": "{{ evs_shareable }}", +{% if evs_scsi is defined and evs_scsi|length != 0 %} + "metadata": { + "hw:passthrough": {{ evs_scsi }} + }, +{% endif %} +{% if evs_multiattach is defined and evs_multiattach|length != 0 %} + "multiattach": {{ evs_multiattach }}, {% endif %} "count": 1 } diff --git a/roles/evs_delete/tasks/main.yml b/roles/evs_delete/tasks/main.yml index 3a7ac65..824e144 100644 --- a/roles/evs_delete/tasks/main.yml +++ b/roles/evs_delete/tasks/main.yml @@ -6,6 +6,7 @@ follow_redirects: all return_content: yes validate_certs: yes + status_code: 200,201,202,203,204 HEADER_Content-Type: "application/json" HEADER_X-Auth-Token: "{{ token['x_subject_token'] }}" register: evs diff --git a/tenant.ini b/tenant.ini index 27a0054..9cc768b 100644 --- a/tenant.ini +++ b/tenant.ini @@ -2,6 +2,8 @@ # each block for each vm # usage ecs: # ansible-playbook -i hosts tenant_create.yml -e "ecs_name=ansible-test01" +# usage evs: +# ansible-playbook -i hosts evs_create.yml -e "evs_name=ansible-evs01" # usage dns (public zones): # ansible-playbook -i hosts dns_create.yml # usage dns (internal usage, only in selected vpc): @@ -12,6 +14,7 @@ [DEFAULT] image_name=Community_Ubuntu_16.04_TSI_latest availability_zone=eu-de-01 +evs_availability_zone=eu-de-01 vpc_name=ansible-vpc01 vpc_net=192.168.0.0/16 subnet_name=ansible-subnet01 @@ -37,6 +40,11 @@ zone1=cloud3.internal.;Core Zone public OTC services;private;cloud-operations@te zonerecord1=cloud3.internal.;;test1.cloud3.internal.;A;300;192.168.0.1 # zonerecord1=external.otc.telekomcloud.com.;;console.external.otc.telekomcloud.com.;A;300;160.44.204.87 # zonerecord2=80.196.44.160.in-addr.arpa.;Reverse OTC;80.196.44.160.in-addr.arpa.;PTR;300;console.otc.telekomcloud.com. +[ansible-evs01] +evs_volume_type=SATA +evs_size=20 +# evs_multiattach=true +# evs_scsi=true [ansible-elb01] elb_type=External elb_bandwidth=100 diff --git a/tenant_var.yml b/tenant_var.yml index 2936300..06d64f5 100644 --- a/tenant_var.yml +++ b/tenant_var.yml @@ -27,6 +27,12 @@ subnet_secondary_dns: "{{ lookup('ini', 'subnet_secondary_dns section={{ ecs_name }} file=tenant.ini') }}" vpc_name: "{{ lookup('ini', 'vpc_name section={{ ecs_name }} file=tenant.ini') }}" vpc_net: "{{ lookup('ini', 'vpc_net section={{ ecs_name }} file=tenant.ini') }}" +# EVS vars + evs_availability_zone: "{{ lookup('ini', 'evs_availability_zone section={{ evs_name }} file=tenant.ini') }}" + evs_volume_type: "{{ lookup('ini', 'evs_volume_type section={{ evs_name }} file=tenant.ini') }}" + evs_size: "{{ lookup('ini', 'evs_size section={{ evs_name }} file=tenant.ini') }}" + evs_multiattach: "{{ lookup('ini', 'evs_multiattach section={{ evs_name }} file=tenant.ini') }}" + evs_scsi: "{{ lookup('ini', 'evs_scsi section={{ evs_name }} file=tenant.ini') }}" # ELB vars admin_state_up: "{{ lookup('ini', 'admin_state_up section={{ elb_name }} file=tenant.ini') }}" elb_availability_zone: "{{ lookup('ini', 'elb_availability_zone section={{ elb_name }} file=tenant.ini') }}" diff --git a/tenant_var_default.yml b/tenant_var_default.yml index 2631812..a58167c 100644 --- a/tenant_var_default.yml +++ b/tenant_var_default.yml @@ -17,4 +17,9 @@ subnet_secondary_dns_default: "{{ lookup('ini', 'subnet_secondary_dns section=DEFAULT file=tenant.ini') }}" vpc_name_default: "{{ lookup('ini', 'vpc_name section=DEFAULT file=tenant.ini') }}" vpc_net_default: "{{ lookup('ini', 'vpc_net section=DEFAULT file=tenant.ini') }}" + evs_availability_zone_default: "{{ lookup('ini', 'evs_availability_zone section=DEFAULT file=tenant.ini') }}" + evs_volume_type_default: "{{ lookup('ini', 'evs_volume_type section=DEFAULT file=tenant.ini') }}" + evs_size_default: "{{ lookup('ini', 'evs_size section=DEFAULT file=tenant.ini') }}" + evs_multiattach_default: "{{ lookup('ini', 'evs_multiattach section=DEFAULT file=tenant.ini') }}" + evs_scsi_default: "{{ lookup('ini', 'evs_scsi section=DEFAULT file=tenant.ini') }}" From c5f5bbebfee7dbb509b925f2ebfc4c5a44c4e099 Mon Sep 17 00:00:00 2001 From: Frank Kloeker Date: Sat, 24 Jun 2017 21:02:15 +0200 Subject: [PATCH 27/72] cleanup ecs_secrets.yml --- CONNECT.md | 1 - README.md | 12 ------------ roles/ecs_create/tasks/main.yml | 2 -- 3 files changed, 15 deletions(-) diff --git a/CONNECT.md b/CONNECT.md index 8e46849..0788aed 100644 --- a/CONNECT.md +++ b/CONNECT.md @@ -119,7 +119,6 @@ cd ~ git clone https://github.com/eumel8/ansible-otc.git cd ansible-otc cp secrets.yml _secrets.yml -cp ecs_secrets.yml _ecs_secrets.yml cp elb_secrets.yml _elb_secrets.yml ansible-vault edit _secrets.yml --vault-password-file vaultpass.txt ``` diff --git a/README.md b/README.md index 523d6c3..9d663e5 100644 --- a/README.md +++ b/README.md @@ -138,7 +138,6 @@ Files |ajob | shell script to fetch job status from OTC| |env.yml | profile to use in clouds.yml| |secrets.yml | var file for S3 credentials and endpoints (ansible-vault)| -|ecs_secrets.yml | var file for virtual machine and volume conf (ansible-vault)| |secgrouprule.yml| var file for single security group rule | |subnet_var.yml | var file for subnet | |vaultpass.txt | password file for ansible-vault. The default password is: linux :-)| @@ -161,7 +160,6 @@ Starting up ``` cp secrets.yml _secrets.yml - cp ecs_secrets.yml _ecs_secrets.yml ``` :exclamation: **adjust your own data in this file before you using the examples:** @@ -174,14 +172,6 @@ list virtual machines (with clouds.yml) ansible-playbook -i hosts ecs.yml -create and start virtual machine (defined in _ecs_secrets.yml) - - ansible-playbook -i hosts ecs_create.yml -e @_ecs_secrets.yml --vault-password-file vaultpass.txt - -create and start virtual machine (defined in _ecs_secrets.yml and overwrite options) - - ansible-playbook -i hosts ecs_create.yml -e @_ecs_secrets.yml -e "ecs_name=test01-ansible" --vault-password-file vaultpass.txt - create and start virtual machine with file injection (inject up to 5 max 1k base64 encoded files) @@ -192,8 +182,6 @@ create and start virtual machine with injection user_data ansible-playbook -i hosts ecs_create.yml -e "ecs_user_data=$(base64 -w 0 user-data.txt)" --vault-password-file vaultpass.txt -(!) You can define ecs_fileinject_1, ecs_fileinject_data_1 and ecs_user_data also in _ecs_secrets.yml. Files must be base64 encoded. - show virtual machine (single) ansible-playbook -i hosts ecs_show.yml -e "ecs_name=ansible-test01" diff --git a/roles/ecs_create/tasks/main.yml b/roles/ecs_create/tasks/main.yml index ea749ce..0a35e2e 100644 --- a/roles/ecs_create/tasks/main.yml +++ b/roles/ecs_create/tasks/main.yml @@ -1,5 +1,3 @@ -# - include_vars: _ecs_secrets.yml - - name: Send request to API uri: url: "{{ AUTH_URL_ECS_CLOUD }}/{{ PROJECT_ID }}/cloudservers" From 8a92506ada4cbcd0ce4b1da2c07d59083eae32fd Mon Sep 17 00:00:00 2001 From: Frank Kloeker Date: Sat, 24 Jun 2017 21:02:34 +0200 Subject: [PATCH 28/72] cleanup ecs_secrets.yml --- ecs_secrets.yml | 24 ------------------------ 1 file changed, 24 deletions(-) delete mode 100644 ecs_secrets.yml diff --git a/ecs_secrets.yml b/ecs_secrets.yml deleted file mode 100644 index e4bf26d..0000000 --- a/ecs_secrets.yml +++ /dev/null @@ -1,24 +0,0 @@ -$ANSIBLE_VAULT;1.1;AES256 -32313362313136346637646362346335653965383165326164653230303833653936346461306261 -6564396434383834386231303839313462323538613961620a326566626331636537333463343135 -35383761666630666337336533363530646534373861373430616463663665353966383932643339 -3231363266386137390a623464386238343939613436626565646161636536393565623033376262 -36363063656161383835613539623730623666323139653363356338316330326263643063333764 -62623434396266633938366130636164633939643564313937373138663734303432333464323934 -39626662323931663762336239323435373933656133656530303138366139303964336238326130 -64363337326336303364363262373137633664386135666566653835623863333239386136323635 -38373832336263356531326365623138303332623637613164303263306439666165643263303966 -31376331366336356265343938333534303065663833626436393133616633656236613930323539 -38326137383638373036363530353935323164323764383430666661363965383834353631383737 -63383266353162313363366463656162333563366339633536376333653461636661393264336561 -37636363636561313833623136613634613164636464323233366166383065666332373131343262 -33633565376336313034386130356461313964653637313339626162323530653565323762643134 -63616537313135336563396435643062383265643965396265336636363162336664636464663162 -39353634373662386262653064373730393835346137356435376531613035346538333832313363 -36316164643965353436646137633463653265643730366233613334643638653966343061623237 -36383666666535316264373461613932386132386464393634336632346136383034656432633133 -64346430393835323837326436336238643938383236393439333363303361373936316264613661 -30616464636535616431323935353939343632333334386537383232363762326432613966303936 -64313863366131636439653638386635333663303762336463623533646563653365353363303064 -34306261333362373763386661323361373433326266626230323839316536616437656665363537 -633863653731383566303630326364326466 From 3b0d35dba5af5617a1dd075b2667f58b51cee121 Mon Sep 17 00:00:00 2001 From: Frank Kloeker Date: Sat, 24 Jun 2017 21:03:00 +0200 Subject: [PATCH 29/72] cleanup elb_secrets.yml --- CONNECT.md | 1 - 1 file changed, 1 deletion(-) diff --git a/CONNECT.md b/CONNECT.md index 0788aed..a05bb51 100644 --- a/CONNECT.md +++ b/CONNECT.md @@ -119,7 +119,6 @@ cd ~ git clone https://github.com/eumel8/ansible-otc.git cd ansible-otc cp secrets.yml _secrets.yml -cp elb_secrets.yml _elb_secrets.yml ansible-vault edit _secrets.yml --vault-password-file vaultpass.txt ``` From 200a453e1f3c78622794e588f280832b8a2baa5e Mon Sep 17 00:00:00 2001 From: Frank Kloeker Date: Sat, 24 Jun 2017 21:12:12 +0200 Subject: [PATCH 30/72] cleanup subnet_var.yml --- README.md | 9 ++++----- subnet_delete.yml | 1 + subnet_var.yml | 8 -------- 3 files changed, 5 insertions(+), 13 deletions(-) delete mode 100644 subnet_var.yml diff --git a/README.md b/README.md index 9d663e5..21db278 100644 --- a/README.md +++ b/README.md @@ -78,7 +78,7 @@ Roles |secgrouprule_create | create security group rule| |secgrouprule_delete | delete security group rule| |subnet | show subnet| -|subnet_create | create subnet (vars in subnet_var.yml)| +|subnet_create | create subnet| |subnet_delete | delete subnet| |token | get auth token| |vpc | show vpc| @@ -139,7 +139,6 @@ Files |env.yml | profile to use in clouds.yml| |secrets.yml | var file for S3 credentials and endpoints (ansible-vault)| |secgrouprule.yml| var file for single security group rule | -|subnet_var.yml | var file for subnet | |vaultpass.txt | password file for ansible-vault. The default password is: linux :-)| |hosts | host file for ansible (we use only localhost)| |tenant.ini | configuration file for complete tenant| @@ -432,13 +431,13 @@ show subnets ansible-playbook -i hosts subnet.yml -create subnet (vars in subnet_var.yml) +create subnet (subtask in tenant_create ecs section) - ansible-playbook -i hosts subnet_create.yml -e @subnet_var.yml + ansible-playbook -i hosts subnet_create.yml delete subnet - ansible-playbook -i hosts subnet_delete.yml -e "vpc_id=0db2af4b-115d-426a-acae-889b025110c8" -e "subnet_id=3ec461e1-eca4-485b-a2a5-91a840968a4f" + ansible-playbook -i hosts subnet_delete.yml -e "vpc_name=ansible-vpc01" -e "subnet_name=ansible-subnet01" show vpc diff --git a/subnet_delete.yml b/subnet_delete.yml index 3549457..04866e2 100644 --- a/subnet_delete.yml +++ b/subnet_delete.yml @@ -2,4 +2,5 @@ - hosts: all roles: - role: token + - role: lookup_name - role: subnet_delete diff --git a/subnet_var.yml b/subnet_var.yml deleted file mode 100644 index 60f1c13..0000000 --- a/subnet_var.yml +++ /dev/null @@ -1,8 +0,0 @@ -subnet_name: "ansible-subnet01" -subnet_net: "192.168.5.0/24" -subnet_gateway: "192.168.5.1" -subnet_dhcp_enable: true -subnet_primary_dns: "8.8.8.8" -subnet_secondary_dns: "8.4.4.8" -availability_zone: "eu-de-02" -vpc_id: "0db2af4b-115d-426a-acae-889b025110c8" From bd46c33d5382524b4873994702600384b555637a Mon Sep 17 00:00:00 2001 From: Frank Kloeker Date: Sat, 24 Jun 2017 21:14:52 +0200 Subject: [PATCH 31/72] cleanup secgrouprule.yml --- README.md | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 21db278..c2768f7 100644 --- a/README.md +++ b/README.md @@ -138,7 +138,6 @@ Files |ajob | shell script to fetch job status from OTC| |env.yml | profile to use in clouds.yml| |secrets.yml | var file for S3 credentials and endpoints (ansible-vault)| -|secgrouprule.yml| var file for single security group rule | |vaultpass.txt | password file for ansible-vault. The default password is: linux :-)| |hosts | host file for ansible (we use only localhost)| |tenant.ini | configuration file for complete tenant| @@ -419,9 +418,9 @@ delete security group ansible-playbook -i hosts secgroup_delete.yml -e "secgroup_id=6e8ac0a0-e0ec-4c4d-a786-9c9c946fd673" -create security group rule +create security group rule (subtask in tenant_create ecs section) - ansible-playbook -i hosts secgrouprule_create.yml -e "secgroup_id=e67e7ef1-b582-47f7-a43f-6a244fd01353" -e @secgrouprule.yml + ... delete security group rule @@ -433,7 +432,7 @@ show subnets create subnet (subtask in tenant_create ecs section) - ansible-playbook -i hosts subnet_create.yml + ... delete subnet From 2f9970936dfeabf4926dba5a979f45a8ce333198 Mon Sep 17 00:00:00 2001 From: Frank Kloeker Date: Sat, 24 Jun 2017 21:19:24 +0200 Subject: [PATCH 32/72] cleanup secgroup(rule) creation --- README.md | 6 +++--- secgroups.yml | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index c2768f7..e3892dd 100644 --- a/README.md +++ b/README.md @@ -408,11 +408,11 @@ show security groups show security groups (only from one vpc) - ansible-playbook -i hosts secgroups.yml -e "vpc_id=d59284de-ad78-4fee-8f2d-d6ff335f4961" + ansible-playbook -i hosts secgroups.yml -e "vpc_name=ansible-vpc01" -create security group +create security group (subtask in tenant_create ecs section) - ansible-playbook -i hosts secgroup_create.yml -e "secgroup_name=ansible-secgroup01" -e "vpc_id=d59284de-ad78-4fee-8f2d-d6ff335f4961" + .... delete security group diff --git a/secgroups.yml b/secgroups.yml index 125d47d..0c68e1c 100644 --- a/secgroups.yml +++ b/secgroups.yml @@ -1,7 +1,7 @@ --- - hosts: all + gather_facts: no roles: - role: token + - role: lookup_name - role: secgroups - - From 276c99ee48520e3307f015a12f3bdfdd9410ad36 Mon Sep 17 00:00:00 2001 From: Frank Kloeker Date: Sat, 24 Jun 2017 21:23:13 +0200 Subject: [PATCH 33/72] migrate vpc_delete role to name concept --- README.md | 2 +- roles/vpc_delete/tasks/main.yml | 3 ++- vpc_delete.yml | 1 + 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index e3892dd..9af307f 100644 --- a/README.md +++ b/README.md @@ -452,7 +452,7 @@ create vpc delete vpc - ansible-playbook -i hosts vpc_delete.yml -e "vpc_id=0db2af4b-115d-426a-acae-889b025110c8" + ansible-playbook -i hosts vpc_delete.yml -e "vpc_name=ansible-vpc01" show DNS zones diff --git a/roles/vpc_delete/tasks/main.yml b/roles/vpc_delete/tasks/main.yml index 4502a61..94fbeb8 100644 --- a/roles/vpc_delete/tasks/main.yml +++ b/roles/vpc_delete/tasks/main.yml @@ -10,7 +10,8 @@ HEADER_Content-Type: "application/json" HEADER_X-Auth-Token: "{{ token['x_subject_token'] }}" register: vpc + when: vpc_id is defined - debug: msg: "{{ vpc }}" - + when: vpc_id is defined diff --git a/vpc_delete.yml b/vpc_delete.yml index 7498250..225a89c 100644 --- a/vpc_delete.yml +++ b/vpc_delete.yml @@ -3,4 +3,5 @@ gather_facts: no roles: - role: token + - role: lookup_name - role: vpc_delete From e6b3a9dcf0c28f8890c9051419e8d96ec8e487c7 Mon Sep 17 00:00:00 2001 From: Frank Kloeker Date: Sat, 24 Jun 2017 21:27:09 +0200 Subject: [PATCH 34/72] document more variables --- VARIABLES.md | 58 ++++++++++++++++++++++++++-------------------------- 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/VARIABLES.md b/VARIABLES.md index a0561ca..fc861b0 100644 --- a/VARIABLES.md +++ b/VARIABLES.md @@ -1,35 +1,34 @@ # # # -availability_zone -availability_zone_id +availability_zone name of the availability zone (e.g. eu-de_01) +availability_zone_id UUID of the availability zone (static) backend_member_id backend_member_ip certificate_names ecs ecs_address -ecs_adminkey name of ssh-key -ecs_adminpass -ecs_adminpass Admin password of ECS instance -ecs_fileinject_1 file to inject in ECS -ecs_fileinject_2 file to inject in ECS -ecs_fileinject_3 file to inject in ECS -ecs_fileinject_4 file to inject in ECS -ecs_fileinject_6 file to inject in ECS -ecs_fileinject_data_1 data of injected file in ECS -ecs_fileinject_data_2 data of injected file in ECS -ecs_fileinject_data_3 data of injected file in ECS -ecs_fileinject_data_4 data of injected file in ECS -ecs_fileinject_data_5 data of injected file in ECS -ecs_id UUID of ECS instance -ecs_ipaddress local ipaddress of ECS instance -ecs_name name of ECS instance -ecs_user_data cloud-init user_data to inject in ECS -ecs_volumetype type of ECS volume (SATA,SAS,SSD) +ecs_adminkey name of ssh-key +ecs_adminpass Admin password of ECS instance +ecs_fileinject_1 file to inject in ECS +ecs_fileinject_2 file to inject in ECS +ecs_fileinject_3 file to inject in ECS +ecs_fileinject_4 file to inject in ECS +ecs_fileinject_6 file to inject in ECS +ecs_fileinject_data_1 data of injected file in ECS +ecs_fileinject_data_2 data of injected file in ECS +ecs_fileinject_data_3 data of injected file in ECS +ecs_fileinject_data_4 data of injected file in ECS +ecs_fileinject_data_5 data of injected file in ECS +ecs_id UUID of ECS instance +ecs_ipaddress local ipaddress of ECS instance +ecs_name name of ECS instance +ecs_user_data cloud-init user_data to inject in ECS +ecs_volumetype type of ECS volume (SATA,SAS,SSD) eip eip_bandwidth_name eip_bandwidth_size -eip_id UUID of floating ipaddress +eip_id UUID of floating ipaddress elb elb_availability_zone elbbackends @@ -43,7 +42,7 @@ elb_id elblist elblistener elblistner -elb_name name of ELB instance +elb_name name of ELB instance elb_secgroup_name elb_subnet_name elb_type @@ -52,12 +51,13 @@ evs evs_availability_zone evs_backup_id evs_ims_id -evs_name -evs_shareable -evs_size -evs_volume_type -external_network_id -flavor_id UUID of selected flavor +evs_multiattach EVS is shareble (true/false) +evs_scsi EVS volume is scsi device instead vdb +evs_name EVS name +evs_size EVS size in GB +evs_volume_type EVS volume type (SATA/SAS/SSD) +external_network_id UUID of the external network +flavor_id UUID of selected flavor healthcheck_connect_port healthcheck_interval healthcheck_protocol @@ -66,7 +66,7 @@ healthcheck_treshold healthcheck_uri image_create image_delete -image_id UUID of selected IMS image +image_id UUID of selected IMS image image_min_disk image_name image_os_version From 0e92b1349ddad4915cbde51b68402ff61d0c4a54 Mon Sep 17 00:00:00 2001 From: Frank Kloeker Date: Sat, 24 Jun 2017 21:32:56 +0200 Subject: [PATCH 35/72] format markdown --- VARIABLES.md | 265 +++++++++++++++++++++++++-------------------------- 1 file changed, 132 insertions(+), 133 deletions(-) diff --git a/VARIABLES.md b/VARIABLES.md index fc861b0..231d135 100644 --- a/VARIABLES.md +++ b/VARIABLES.md @@ -1,133 +1,132 @@ -# -# -# -availability_zone name of the availability zone (e.g. eu-de_01) -availability_zone_id UUID of the availability zone (static) -backend_member_id -backend_member_ip -certificate_names -ecs -ecs_address -ecs_adminkey name of ssh-key -ecs_adminpass Admin password of ECS instance -ecs_fileinject_1 file to inject in ECS -ecs_fileinject_2 file to inject in ECS -ecs_fileinject_3 file to inject in ECS -ecs_fileinject_4 file to inject in ECS -ecs_fileinject_6 file to inject in ECS -ecs_fileinject_data_1 data of injected file in ECS -ecs_fileinject_data_2 data of injected file in ECS -ecs_fileinject_data_3 data of injected file in ECS -ecs_fileinject_data_4 data of injected file in ECS -ecs_fileinject_data_5 data of injected file in ECS -ecs_id UUID of ECS instance -ecs_ipaddress local ipaddress of ECS instance -ecs_name name of ECS instance -ecs_user_data cloud-init user_data to inject in ECS -ecs_volumetype type of ECS volume (SATA,SAS,SSD) -eip -eip_bandwidth_name -eip_bandwidth_size -eip_id UUID of floating ipaddress -elb -elb_availability_zone -elbbackends -elb_backends_id -elb_bandwidth -elbcertifcate -elbcertificate -elb_certificate_name -elbhealthcheck -elb_id -elblist -elblistener -elblistner -elb_name name of ELB instance -elb_secgroup_name -elb_subnet_name -elb_type -enable_snat -evs -evs_availability_zone -evs_backup_id -evs_ims_id -evs_multiattach EVS is shareble (true/false) -evs_scsi EVS volume is scsi device instead vdb -evs_name EVS name -evs_size EVS size in GB -evs_volume_type EVS volume type (SATA/SAS/SSD) -external_network_id UUID of the external network -flavor_id UUID of selected flavor -healthcheck_connect_port -healthcheck_interval -healthcheck_protocol -healthcheck_timeout -healthcheck_treshold -healthcheck_uri -image_create -image_delete -image_id UUID of selected IMS image -image_min_disk -image_name -image_os_version -job_id -keypair -keypair_file -listener_backend_port -listener_backend_port: -listener_backend_protocol -listener_certificate_id -listener_certificate_name -listener_cookie_timeout -listener_id -listener_lb_algorithm -listener_name -listener_port -listener_protocol -listener_session_sticky -listener_sticky_session_type -listener_tcp_timeout -public_ip_address -router -router_id -secgroup -secgroup_id -secgroup_name -secgrouprule -secgroup_rule -secgrouprule_direction -secgrouprule_ethertype -secgrouprule_list -secgrouprule_port_range_max -secgrouprule_port_range_min -secgrouprule_protocol -secgrouprule_remote_group_id -secgrouprule_remote_ip_prefix -subnet -subnet_dhcp_enable -subnet_gateway -subnet_id UUID of selected subnet -subnet_name -subnet_net -subnet_primary_dns -subnet_secondary_dns -token -unhealthy_threshold -vpc -vpc_id UUID of ECS instance -vpc_name name of VPC -vpc_net -zone -zone_description -zone_email -zone_list -zone_name -zonerecord -zonerecord_description -zonerecord_list -zonerecord_name -zonerecord_ttl -zonerecord_type -zonerecord_value -zone_ttl -zone_type +|variable|description| +|+------------------------------+---------------------------------------------| +|availability_zone| name of the availability zone (e.g. eu-de_01)| +|availability_zone_id| UUID of the availability zone (static)| +|backend_member_id| +|backend_member_ip| +|certificate_names| +|ecs| +|ecs_address| +|ecs_adminkey| name of ssh-key| +|ecs_adminpass| Admin password of ECS instance| +|ecs_fileinject_1| file to inject in ECS| +|ecs_fileinject_2| file to inject in ECS| +|ecs_fileinject_3| file to inject in ECS| +|ecs_fileinject_4| file to inject in ECS| +|ecs_fileinject_6| file to inject in ECS| +|ecs_fileinject_data_1| data of injected file in ECS| +|ecs_fileinject_data_2| data of injected file in ECS| +|ecs_fileinject_data_3| data of injected file in ECS| +|ecs_fileinject_data_4| data of injected file in ECS| +|ecs_fileinject_data_5| data of injected file in ECS| +|ecs_id| UUID of ECS instance| +|ecs_ipaddress| local ipaddress of ECS instance| +|ecs_name| name of ECS instance| +|ecs_user_data| cloud-init user_data to inject in ECS| +|ecs_volumetype| type of ECS volume (SATA,SAS,SSD)| +|eip| +|eip_bandwidth_name| +|eip_bandwidth_size| +|eip_id| UUID of floating ipaddress| +|elb| +|elb_availability_zone| +|elbbackends| +|elb_backends_id| +|elb_bandwidth| +|elbcertifcate| +|elbcertificate| +|elb_certificate_name| +|elbhealthcheck| +|elb_id| +|elblist| +|elblistener| +|elblistner| +|elb_name| name of ELB instance| +|elb_secgroup_name| +|elb_subnet_name| +|elb_type| +|enable_snat| +|evs| +|evs_availability_zone| +|evs_backup_id| +|evs_ims_id| +|evs_multiattach| EVS is shareble (true/false)| +|evs_scsi| EVS volume is scsi device instead vdb| +|evs_name| EVS name| +|evs_size| EVS size in GB| +|evs_volume_type| EVS volume type (SATA/SAS/SSD)| +|external_network_id| UUID of the external network| +|flavor_id| UUID of selected flavor| +|healthcheck_connect_port| +|healthcheck_interval| +|healthcheck_protocol| +|healthcheck_timeout| +|healthcheck_treshold| +|healthcheck_uri| +|image_create| +|image_delete| +|image_id| UUID of selected IMS image| +|image_min_disk| +|image_name| +|image_os_version| +|job_id| +|keypair| +|keypair_file| +|listener_backend_port| +|listener_backend_port:| +|listener_backend_protocol| +|listener_certificate_id| +|listener_certificate_name| +|listener_cookie_timeout| +|listener_id| +|listener_lb_algorithm| +|listener_name| +|listener_port| +|listener_protocol| +|listener_session_sticky| +|listener_sticky_session_type| +|listener_tcp_timeout| +|public_ip_address| +|router| +|router_id| +|secgroup| +|secgroup_id| +|secgroup_name| +|secgrouprule| +|secgroup_rule| +|secgrouprule_direction| +|secgrouprule_ethertype| +|secgrouprule_list| +|secgrouprule_port_range_max | +|secgrouprule_port_range_min| +|secgrouprule_protocol| +|secgrouprule_remote_group_id| +|secgrouprule_remote_ip_prefix| +|subnet| +|subnet_dhcp_enable| +|subnet_gateway| +|subnet_id| UUID of selected subnet| +|subnet_name| +|subnet_net| +|subnet_primary_dns| +|subnet_secondary_dns| +|token| +|unhealthy_threshold| +|vpc| +|vpc_id| UUID of ECS instance| +|vpc_name| name of VPC| +|vpc_net| +|zone| +|zone_description| +|zone_email| +|zone_list| +|zone_name| +|zonerecord| +|zonerecord_description| +|zonerecord_list| +|zonerecord_name| +|zonerecord_ttl| +|zonerecord_type| +|zonerecord_value| +|zone_ttl| +|zone_type| From 53b9ed9676c2ecf667a5c5e4c28064d4b029261c Mon Sep 17 00:00:00 2001 From: Frank Kloeker Date: Sat, 24 Jun 2017 21:33:49 +0200 Subject: [PATCH 36/72] format markdown --- VARIABLES.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VARIABLES.md b/VARIABLES.md index 231d135..5438d5e 100644 --- a/VARIABLES.md +++ b/VARIABLES.md @@ -1,5 +1,5 @@ |variable|description| -|+------------------------------+---------------------------------------------| +|------------------------------|---------------------------------------------| |availability_zone| name of the availability zone (e.g. eu-de_01)| |availability_zone_id| UUID of the availability zone (static)| |backend_member_id| From 62e8291f652b33603929593407377a83bc77697f Mon Sep 17 00:00:00 2001 From: Frank Kloeker Date: Sat, 24 Jun 2017 22:03:39 +0200 Subject: [PATCH 37/72] cleanup elb_secrets.yml --- elb_secrets.yml | 39 --------------------------------------- 1 file changed, 39 deletions(-) delete mode 100644 elb_secrets.yml diff --git a/elb_secrets.yml b/elb_secrets.yml deleted file mode 100644 index c847dae..0000000 --- a/elb_secrets.yml +++ /dev/null @@ -1,39 +0,0 @@ -$ANSIBLE_VAULT;1.1;AES256 -32633362376237613035323762323665613162393664323933336236383732336135303666616366 -6331393139613832663163396365626635316133386634620a636337313031303965623732303836 -32643538323735613030303762626565646166396130646263653962633538343535363662636637 -3862333531616532610a383931323462656132666464353130636564303663653737616462343138 -66613163333336376636616134623639663733326666643630653433623637316263666364613636 -32343732666665636236396464343535643362623237613238666362336464323437626465623438 -61393361343436663430313231613439613564643737643839623465636631373230663230323266 -33326230356465373261366636326633313030636236316539336366653761366137623731316233 -39613137303030336138356338386665336636633630643666373132613931323934373034356332 -33356232333137366434666233633761376162636163636639343332643266343830323766613937 -62316166393266353265396538333763323362383062393939393334356264633864386539653934 -64613561653930653666333464343639363165356233393965306663366531333032336232396663 -66613836303737616134626232643136366235653061303335326236356163343635626634636631 -63666339376139626530363465323037653735646464383431313966653736316233386538393238 -63633762633163613930363532613730663763303238363832316336343931393063326437666562 -36633064373062643166623732333636346135386463303862333462653237346533313230306639 -31616161373535366165326263633362346162306662393832663532613632613463623462616136 -30666537623533666130663738336336643938326432353361326338396432353538396536643037 -38636636373237326264393035663234636166323965356136333763313865666633643161386135 -61303263323837646463326332653765366632353062626437316566393635393962336130326439 -36656464643366643166393031393832373439346264656366306665623532663564373965653063 -66333361383934356234613134306436653436623039336661616633626661626462643933303537 -36653262663238333663633563636330363738323531373637633833353435343539316137323932 -64613864343863303866323534386432373562346433613835383464633061633739343363643462 -34636532643065626630663334656662396134303535393035636466643637323633643232393638 -39636539323334303831346136653261636265623939616661373431643237316632303562373538 -66316533613334393231633862343035353933636433363130323161306265623661663931643035 -33396566393962373831663732373762616134363261353131633236376163393031343131343934 -34386132366530656261316337396363653534396431363037323463346330653639613135643431 -33376638323237306235383664343665373134333833636161353533363436396462343666656537 -61356537306135666632626532396233656338643962323736613561303461643464653665313634 -30313733313165613036346561613333323931336561383135616335633162623764616337316666 -31356464343133383763656336653337633063643436653064326363663538636634653130393266 -39663539636636393565376637636266656332323334613162626130646362316265636333656237 -61306135376336326239613432313033396334393335323635383866323736326132396461616130 -63306237623331633536346130376137343732326330386439643933663464346630373436333062 -37346236396437336130366436643265306132326663323463303935313934616333393865353763 -3937656431353766363533346435343133613933343661653139 From 0f01c370b68045d8ae97116c00a17df0ea3b6216 Mon Sep 17 00:00:00 2001 From: Frank Kloeker Date: Sun, 25 Jun 2017 07:11:13 +0200 Subject: [PATCH 38/72] image_create role for creation image from ecs instance --- README.md | 6 +++++- image_create.yml | 1 + roles/image_create/tasks/main.yml | 2 ++ roles/image_create/templates/request.json.j2 | 11 +++++++++-- 4 files changed, 17 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 9af307f..a36a13a 100644 --- a/README.md +++ b/README.md @@ -58,7 +58,7 @@ Roles |evs_show | information about a specific volume| |flavors | show flavors| |images | show images| -|image_create | create an image from obs| +|image_create | create an image | |image_delete | delete an image | |job | show job status| |keypairs | show ssh keypairs| @@ -304,6 +304,10 @@ show images ansible-playbook -i hosts images.yml +create image (from stopped ecs instance) + + ansible-playbook -i hosts image_create.yml -e "image_name=ansible-image01" -e "ecs_name=ansible-test01" + delete an image (API return code is 204 when success, ansible expected 200 and may give an error) ansible-playbook -i hosts -e "image_id=af0a0bcf-7be3-4722-98ba-3350801a8cd5" image_delete.yml diff --git a/image_create.yml b/image_create.yml index 4623905..b3436c2 100644 --- a/image_create.yml +++ b/image_create.yml @@ -3,6 +3,7 @@ gather_facts: no roles: - role: token + - role: lookup_name - role: image_create # http://support.hwclouds.com/en-us/api-ims/en-us_topic_0020092109.html diff --git a/roles/image_create/tasks/main.yml b/roles/image_create/tasks/main.yml index 202c6b4..87d6530 100644 --- a/roles/image_create/tasks/main.yml +++ b/roles/image_create/tasks/main.yml @@ -10,7 +10,9 @@ HEADER_X-Auth-Token: "{{ token['x_subject_token'] }}" body: "{{ lookup('template', 'roles/image_create/templates/request.json.j2')|to_json }}" register: image_create + when: image_name is defined - debug: msg: "{{ image_create }}" + when: image_create is defined diff --git a/roles/image_create/templates/request.json.j2 b/roles/image_create/templates/request.json.j2 index 7edb3a1..de529ca 100644 --- a/roles/image_create/templates/request.json.j2 +++ b/roles/image_create/templates/request.json.j2 @@ -1,8 +1,15 @@ { - "name": "{{ image_name }}", +{% if image_url is defined %} "image_url": "{{ image_url }}", +{% endif %} +{% if ecs_id is defined %} + "instance_id": "{{ ecs_id }}", +{% endif %} +{% if min_disk is defined %} + "min_disk": {{ image_min_disk }}, +{% endif %} {% if image_os_version is defined %} "__os_version":"{{ image_os_version }}", {% endif %} - "min_disk": {{ image_min_disk }} + "name": "{{ image_name }}" } From 7a64c74cd85092a0a4002f7ce4e55c512e54ab23 Mon Sep 17 00:00:00 2001 From: Frank Kloeker Date: Sun, 25 Jun 2017 13:34:53 +0200 Subject: [PATCH 39/72] re-implement role dns ptr record create --- README.md | 9 +++++++++ VARIABLES.md | 2 ++ ptrrecord_create.yml | 7 +++++++ roles/image_create/templates/request.json.j2 | 2 +- roles/ptrrecord_create/tasks/main.yml | 18 ++++++++++++++++++ .../ptrrecord_create/templates/request.json.j2 | 9 +++++++++ 6 files changed, 46 insertions(+), 1 deletion(-) create mode 100644 ptrrecord_create.yml create mode 100644 roles/ptrrecord_create/tasks/main.yml create mode 100644 roles/ptrrecord_create/templates/request.json.j2 diff --git a/README.md b/README.md index a36a13a..c77f226 100644 --- a/README.md +++ b/README.md @@ -65,6 +65,7 @@ Roles |keypair_create | create a ssh keypair| |keypair_delete | delete a ssh keypair| |lookup_name | lookup id by name (set_fact image_id, vpc_id, subnet_id, secgroup_id, flavor_id)| +|ptrrecord_create | create DNS PTR record for EIP| |rds_versions | list provided database versions for RDS| |rds_flavors | list provided flavors for selected database version in RDS| |services | discover API services| @@ -308,6 +309,10 @@ create image (from stopped ecs instance) ansible-playbook -i hosts image_create.yml -e "image_name=ansible-image01" -e "ecs_name=ansible-test01" +create image (from obs image_url :) + + ansible-playbook -i hosts image_create.yml -e "image_name=ansible-image02" -e "image_url=ansible1:/xenial-server-cloudimg-amd64-disk1.vmdk" -e "image_min_disk=12" + delete an image (API return code is 204 when success, ansible expected 200 and may give an error) ansible-playbook -i hosts -e "image_id=af0a0bcf-7be3-4722-98ba-3350801a8cd5" image_delete.yml @@ -378,6 +383,10 @@ lookup id by name (listener) ansible-playbook -i hosts lookup_name.yml -e "listener_name=ansible-listener01" -e "elb_name=ansible-elb01" +create DNS PTR record for EIP + + ansible-playbook -i hosts ptrrecord_create.yml -e "public_ip_address=160.44.204.87" -e "ptr_name=ansible-test01.external.otc.telekomcloud.com" -e "ttl=300" + list provided database versions for RDS ansible-playbook -i hosts rds_versions.yml diff --git a/VARIABLES.md b/VARIABLES.md index 5438d5e..1475a64 100644 --- a/VARIABLES.md +++ b/VARIABLES.md @@ -86,6 +86,7 @@ |listener_session_sticky| |listener_sticky_session_type| |listener_tcp_timeout| +|ptr_name| name of PTR record for EIP| |public_ip_address| |router| |router_id| @@ -111,6 +112,7 @@ |subnet_primary_dns| |subnet_secondary_dns| |token| +|ttl| TTL PTR records in sec| |unhealthy_threshold| |vpc| |vpc_id| UUID of ECS instance| diff --git a/ptrrecord_create.yml b/ptrrecord_create.yml new file mode 100644 index 0000000..1edd6ba --- /dev/null +++ b/ptrrecord_create.yml @@ -0,0 +1,7 @@ +--- +- hosts: all + gather_facts: no + roles: + - role: token + - role: lookup_name + - role: ptrrecord_create diff --git a/roles/image_create/templates/request.json.j2 b/roles/image_create/templates/request.json.j2 index de529ca..08d6c9b 100644 --- a/roles/image_create/templates/request.json.j2 +++ b/roles/image_create/templates/request.json.j2 @@ -5,7 +5,7 @@ {% if ecs_id is defined %} "instance_id": "{{ ecs_id }}", {% endif %} -{% if min_disk is defined %} +{% if image_min_disk is defined %} "min_disk": {{ image_min_disk }}, {% endif %} {% if image_os_version is defined %} diff --git a/roles/ptrrecord_create/tasks/main.yml b/roles/ptrrecord_create/tasks/main.yml new file mode 100644 index 0000000..d87d9f3 --- /dev/null +++ b/roles/ptrrecord_create/tasks/main.yml @@ -0,0 +1,18 @@ +- name: send ptrrecord request to API + uri: + url: "{{ AUTH_URL_DNS }}/v2/reverse/floatingips/{{ PROJECT_NAME }}:{{ eip_id }}" + method: PATCH + body_format: raw + follow_redirects: all + return_content: yes + validate_certs: yes + status_code: 200,201,202,203,400 + HEADER_Content-Type: "application/json" + HEADER_X-Auth-Token: "{{ token['x_subject_token'] }}" + body: "{{ lookup('template', 'roles/ptrrecord_create/templates/request.json.j2')|to_json }}" + register: ptrrecord + when: eip_id is defined + +- debug: + msg: "{{ ptrrecord }}" + diff --git a/roles/ptrrecord_create/templates/request.json.j2 b/roles/ptrrecord_create/templates/request.json.j2 new file mode 100644 index 0000000..f6c31fc --- /dev/null +++ b/roles/ptrrecord_create/templates/request.json.j2 @@ -0,0 +1,9 @@ +{ +{% if description is defined and description|length != 0 %} + "description": "{{ description }}", +{% endif %} +{% if ttl is defined and ttl|length != 0 %} + "ttl": {{ ttl }}, +{% endif %} + "ptrdname": "{{ ptr_name }}" +} From ce3a6fea8b94bbb3ba4bfc11fb0babf91272555b Mon Sep 17 00:00:00 2001 From: Frank Kloeker Date: Sun, 25 Jun 2017 17:36:00 +0200 Subject: [PATCH 40/72] add role ptrrecord_delete, ptrrecords --- README.md | 10 ++++++++++ ptrrecord_delete.yml | 7 +++++++ ptrrecords.yml | 6 ++++++ roles/ptrrecord_create/tasks/main.yml | 4 +++- roles/ptrrecord_delete/tasks/main.yml | 19 +++++++++++++++++++ roles/ptrrecords/tasks/main.yml | 15 +++++++++++++++ roles/ptrrecords/templates/request.json.j2 | 9 +++++++++ 7 files changed, 69 insertions(+), 1 deletion(-) create mode 100644 ptrrecord_delete.yml create mode 100644 ptrrecords.yml create mode 100644 roles/ptrrecord_delete/tasks/main.yml create mode 100644 roles/ptrrecords/tasks/main.yml create mode 100644 roles/ptrrecords/templates/request.json.j2 diff --git a/README.md b/README.md index c77f226..c6f922a 100644 --- a/README.md +++ b/README.md @@ -66,6 +66,8 @@ Roles |keypair_delete | delete a ssh keypair| |lookup_name | lookup id by name (set_fact image_id, vpc_id, subnet_id, secgroup_id, flavor_id)| |ptrrecord_create | create DNS PTR record for EIP| +|ptrrecord_delete | delete DNS PTR record for EIP| +|ptrrecords | show DNS PTR records for EIP| |rds_versions | list provided database versions for RDS| |rds_flavors | list provided flavors for selected database version in RDS| |services | discover API services| @@ -387,6 +389,14 @@ create DNS PTR record for EIP ansible-playbook -i hosts ptrrecord_create.yml -e "public_ip_address=160.44.204.87" -e "ptr_name=ansible-test01.external.otc.telekomcloud.com" -e "ttl=300" +delete DNS PTR record for EIP + + ansible-playbook -i hosts ptrrecord_delete.yml -e "public_ip_address=160.44.204.87" + +show DNS PTR records for EIP + + ansible-playbook -i hosts ptrrecords.yml + list provided database versions for RDS ansible-playbook -i hosts rds_versions.yml diff --git a/ptrrecord_delete.yml b/ptrrecord_delete.yml new file mode 100644 index 0000000..5d9d410 --- /dev/null +++ b/ptrrecord_delete.yml @@ -0,0 +1,7 @@ +--- +- hosts: all + gather_facts: no + roles: + - role: token + - role: lookup_name + - role: ptrrecord_delete diff --git a/ptrrecords.yml b/ptrrecords.yml new file mode 100644 index 0000000..bea7580 --- /dev/null +++ b/ptrrecords.yml @@ -0,0 +1,6 @@ +--- +- hosts: all + gather_facts: no + roles: + - role: token + - role: ptrrecords diff --git a/roles/ptrrecord_create/tasks/main.yml b/roles/ptrrecord_create/tasks/main.yml index d87d9f3..12b826c 100644 --- a/roles/ptrrecord_create/tasks/main.yml +++ b/roles/ptrrecord_create/tasks/main.yml @@ -11,7 +11,9 @@ HEADER_X-Auth-Token: "{{ token['x_subject_token'] }}" body: "{{ lookup('template', 'roles/ptrrecord_create/templates/request.json.j2')|to_json }}" register: ptrrecord - when: eip_id is defined + when: + - ptr_name is defined + - eip_id is defined - debug: msg: "{{ ptrrecord }}" diff --git a/roles/ptrrecord_delete/tasks/main.yml b/roles/ptrrecord_delete/tasks/main.yml new file mode 100644 index 0000000..60d75f0 --- /dev/null +++ b/roles/ptrrecord_delete/tasks/main.yml @@ -0,0 +1,19 @@ +- name: send ptrrecord request to API + uri: + url: "{{ AUTH_URL_DNS }}/v2/reverse/floatingips/{{ PROJECT_NAME }}:{{ eip_id }}" + method: PATCH + body_format: raw + follow_redirects: all + return_content: yes + validate_certs: yes + status_code: 200,201,202,203,400 + HEADER_Content-Type: "application/json" + HEADER_X-Auth-Token: "{{ token['x_subject_token'] }}" + body: '{ "ptrdname": null }' + register: ptrrecord + when: + - eip_id is defined + +- debug: + msg: "{{ ptrrecord }}" + diff --git a/roles/ptrrecords/tasks/main.yml b/roles/ptrrecords/tasks/main.yml new file mode 100644 index 0000000..934087d --- /dev/null +++ b/roles/ptrrecords/tasks/main.yml @@ -0,0 +1,15 @@ +- name: send ptrrecord request to API + uri: + url: "{{ AUTH_URL_DNS }}/v2/reverse/floatingips" + method: GET + follow_redirects: all + return_content: yes + validate_certs: yes + status_code: 200,201,202,203,400 + HEADER_Content-Type: "application/json" + HEADER_X-Auth-Token: "{{ token['x_subject_token'] }}" + register: ptrrecord + +- debug: + msg: "{{ ptrrecord }}" + diff --git a/roles/ptrrecords/templates/request.json.j2 b/roles/ptrrecords/templates/request.json.j2 new file mode 100644 index 0000000..f6c31fc --- /dev/null +++ b/roles/ptrrecords/templates/request.json.j2 @@ -0,0 +1,9 @@ +{ +{% if description is defined and description|length != 0 %} + "description": "{{ description }}", +{% endif %} +{% if ttl is defined and ttl|length != 0 %} + "ttl": {{ ttl }}, +{% endif %} + "ptrdname": "{{ ptr_name }}" +} From 9a233ed101c9367d736350fd1acb2f503cba606f Mon Sep 17 00:00:00 2001 From: Frank Kloeker Date: Sun, 25 Jun 2017 19:44:13 +0200 Subject: [PATCH 41/72] add condition backend_member_helper --- roles/backend_member_helper/tasks/main.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/roles/backend_member_helper/tasks/main.yml b/roles/backend_member_helper/tasks/main.yml index 58d1486..753b0a2 100644 --- a/roles/backend_member_helper/tasks/main.yml +++ b/roles/backend_member_helper/tasks/main.yml @@ -20,6 +20,7 @@ HEADER_Content-Type: "application/json" HEADER_X-Auth-Token: "{{ token['x_subject_token'] }}" register: ecs + when: backend_members is defined with_items: - "{{ backend_members.split(',') }}" From eed389cf0a16c54e213c6681fb3adc69a83a1661 Mon Sep 17 00:00:00 2001 From: Frank Kloeker Date: Sun, 25 Jun 2017 19:44:37 +0200 Subject: [PATCH 42/72] add ptrrecord to tenant_create playbook --- tenant_create.yml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/tenant_create.yml b/tenant_create.yml index 47e29b9..04e7fb8 100644 --- a/tenant_create.yml +++ b/tenant_create.yml @@ -66,6 +66,15 @@ - role: lookup_name - role: eip_apply +- hosts: localhost + gather_facts: no + connection: local + vars_files: + - tenant_var.yml + roles: + - role: token + - role: ptrrecord_create + - hosts: localhost gather_facts: no connection: local From f28ad2536da0e01a866ff0fb8baa85785a5bd137 Mon Sep 17 00:00:00 2001 From: Frank Kloeker Date: Sun, 25 Jun 2017 19:45:04 +0200 Subject: [PATCH 43/72] add variable ptrrecord in var file --- tenant_var.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/tenant_var.yml b/tenant_var.yml index 06d64f5..6569cc7 100644 --- a/tenant_var.yml +++ b/tenant_var.yml @@ -14,6 +14,7 @@ ecs_vcpus: "{{ lookup('ini', 'ecs_vcpus section={{ ecs_name }} file=tenant.ini') }}" ecs_ipaddress: "{{ lookup('ini', 'ecs_ipaddress section={{ ecs_name }} file=tenant.ini') }}" public_ip_address: "{{ lookup('ini', 'ecs_publicip section={{ ecs_name }} file=tenant.ini') }}" + ptr_name: "{{ lookup('ini', 'ecs_publicfqdn section={{ ecs_name }} file=tenant.ini') }}" eip_bandwidth_name: "{{ lookup('ini', 'eip_bandwidth_name section={{ ecs_name }} file=tenant.ini') }}" eip_bandwidth_size: "{{ lookup('ini', 'eip_bandwidth_size section={{ ecs_name }} file=tenant.ini') }}" image_name: "{{ lookup('ini', 'image_name section={{ ecs_name }} file=tenant.ini') }}" From 7a4b9abaad8f5f7bf455be22e7ee0e3363e9529c Mon Sep 17 00:00:00 2001 From: Frank Kloeker Date: Sun, 25 Jun 2017 19:51:36 +0200 Subject: [PATCH 44/72] downgrade necessary for ansible due the condition error --- CONNECT.md | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/CONNECT.md b/CONNECT.md index a05bb51..b340990 100644 --- a/CONNECT.md +++ b/CONNECT.md @@ -7,7 +7,16 @@ Install prerequisites as root on your Ubuntu 16.04 machine: ``` -add-apt-repository -y ppa:ansible/ansible +NOTE: In dev branch are issues with conditions in lookup role which breaks the rolling tenant_create playbook +It works only in ansible 2.2.0.0 to use from wily repo + +``` +cat < /etc/apt/sources.list.d/ansible-ubuntu-ansible-wily.list +deb http://ppa.launchpad.net/ansible/ansible/ubuntu wily main +deb-src http://ppa.launchpad.net/ansible/ansible/ubuntu wily main +EOF + +# add-apt-repository -y ppa:ansible/ansible-2.2 apt-get update apt-get -y install curl git ansible python-openstackclient python-pip python-jmespath python-netaddr libs3-2 jq pip install python-otcclient From a088800a0e25c8eb4a6acccc9f4350fd2146a315 Mon Sep 17 00:00:00 2001 From: Frank Kloeker Date: Sun, 25 Jun 2017 22:42:07 +0200 Subject: [PATCH 45/72] change supported ansible version to 2.2.0.0 --- CONNECT.md | 12 +----------- README.md | 11 +++++------ 2 files changed, 6 insertions(+), 17 deletions(-) diff --git a/CONNECT.md b/CONNECT.md index b340990..92ea382 100644 --- a/CONNECT.md +++ b/CONNECT.md @@ -5,21 +5,11 @@ How to connect to the Open Telekom Cloud Install prerequisites as root on your Ubuntu 16.04 machine: - -``` -NOTE: In dev branch are issues with conditions in lookup role which breaks the rolling tenant_create playbook -It works only in ansible 2.2.0.0 to use from wily repo - ``` -cat < /etc/apt/sources.list.d/ansible-ubuntu-ansible-wily.list -deb http://ppa.launchpad.net/ansible/ansible/ubuntu wily main -deb-src http://ppa.launchpad.net/ansible/ansible/ubuntu wily main -EOF - -# add-apt-repository -y ppa:ansible/ansible-2.2 apt-get update apt-get -y install curl git ansible python-openstackclient python-pip python-jmespath python-netaddr libs3-2 jq pip install python-otcclient +pip install ansible==2.2.0.0 ``` Follow instruction as normal user. You need always username, password, domain data. diff --git a/README.md b/README.md index c6f922a..70b5a1d 100644 --- a/README.md +++ b/README.md @@ -100,20 +100,19 @@ Requirements * curl * openssl * base64 -* ansible >=2.2.0.0 +* ansible==2.2.0.0 * python-jmespath * python-netaddr - *Ubuntu 12.04/14.04/16.04:* + *Ubuntu 14.04/16.04:* ``` apt-get install software-properties-common - apt-add-repository ppa:ansible/ansible apt-get update apt-cache policy ansible - # should have version >2.1.0 - apt-get install curl ansible python-jmespath python-netaddr + apt-get install curl python-pip python-jmespath python-netaddr + pip install ansible==2.2.0.0 ``` *OpenSuSE 13.2:* @@ -124,7 +123,7 @@ Requirements zypper install curl ansible python-jmespath python-netaddr ``` -(should work on all other *nix systems) +(should work on all other *nix systems, check the right version of ansible!!!) * :exclamation: credentials on OTC (username, password, domain, S3 access/secret key) From 64355ec2a6575d6534ae36e93637f675bc1bede7 Mon Sep 17 00:00:00 2001 From: Frank Kloeker Date: Mon, 26 Jun 2017 08:50:22 +0200 Subject: [PATCH 46/72] add ttl for PTR record --- VARIABLES.md | 13 ++++++++----- tenant.ini | 2 ++ tenant_var.yml | 1 + 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/VARIABLES.md b/VARIABLES.md index 1475a64..2ab2f81 100644 --- a/VARIABLES.md +++ b/VARIABLES.md @@ -22,12 +22,15 @@ |ecs_id| UUID of ECS instance| |ecs_ipaddress| local ipaddress of ECS instance| |ecs_name| name of ECS instance| +|ecs_publicip| EIP of ECS (0.0.0.0 to apply new address| +|ecs_publicfqdn| DNS PTR record FQDN| +|ecs_publicttl| DNS PTR record TTL| |ecs_user_data| cloud-init user_data to inject in ECS| -|ecs_volumetype| type of ECS volume (SATA,SAS,SSD)| -|eip| -|eip_bandwidth_name| -|eip_bandwidth_size| -|eip_id| UUID of floating ipaddress| +|ecs_volumetype| type of ECS volume (SATA,SAS,SSD)| +|eip| Value of FloatingIP (EIP)| +|eip_bandwidth_name| Name of EIP bandwith resource| +|eip_bandwidth_size| Size of EIP bandwith (1-500 MBit/sec)| +|eip_id| UUID of floating ipaddress| |elb| |elb_availability_zone| |elbbackends| diff --git a/tenant.ini b/tenant.ini index 9cc768b..322f0d9 100644 --- a/tenant.ini +++ b/tenant.ini @@ -83,6 +83,8 @@ secgroup_rule3=egress;IPv4;tcp;80;80;0.0.0.0/0 secgroup_rule4=ingress;IPv4;icmp;;;0.0.0.0/0 ecs_ipaddress=192.168.0.101 ecs_publicip=160.44.194.98 +ecs_publicfqdn=ansible-test01.ansible.otc.telekomcloud.com. +ecs_publicttl=300 eip_bandwidth_name=ansible-eip1 eip_bandwidth_size=100 [ansible-test02] diff --git a/tenant_var.yml b/tenant_var.yml index 6569cc7..4c51d82 100644 --- a/tenant_var.yml +++ b/tenant_var.yml @@ -15,6 +15,7 @@ ecs_ipaddress: "{{ lookup('ini', 'ecs_ipaddress section={{ ecs_name }} file=tenant.ini') }}" public_ip_address: "{{ lookup('ini', 'ecs_publicip section={{ ecs_name }} file=tenant.ini') }}" ptr_name: "{{ lookup('ini', 'ecs_publicfqdn section={{ ecs_name }} file=tenant.ini') }}" + ttl: "{{ lookup('ini', 'ecs_publicttl section={{ ecs_name }} file=tenant.ini') }}" eip_bandwidth_name: "{{ lookup('ini', 'eip_bandwidth_name section={{ ecs_name }} file=tenant.ini') }}" eip_bandwidth_size: "{{ lookup('ini', 'eip_bandwidth_size section={{ ecs_name }} file=tenant.ini') }}" image_name: "{{ lookup('ini', 'image_name section={{ ecs_name }} file=tenant.ini') }}" From 3d4928e7753f26370d776667c923143579468374 Mon Sep 17 00:00:00 2001 From: Frank Kloeker Date: Mon, 26 Jun 2017 08:51:44 +0200 Subject: [PATCH 47/72] fix condition ptrrecord_create --- roles/ptrrecord_create/tasks/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/roles/ptrrecord_create/tasks/main.yml b/roles/ptrrecord_create/tasks/main.yml index 12b826c..74aff8b 100644 --- a/roles/ptrrecord_create/tasks/main.yml +++ b/roles/ptrrecord_create/tasks/main.yml @@ -12,7 +12,7 @@ body: "{{ lookup('template', 'roles/ptrrecord_create/templates/request.json.j2')|to_json }}" register: ptrrecord when: - - ptr_name is defined + - (ptr_name is defined and ptr_name | length != 0) - eip_id is defined - debug: From d120d5b882cdbb87484e1e442627fb10f5c3526b Mon Sep 17 00:00:00 2001 From: Frank Kloeker Date: Mon, 26 Jun 2017 08:52:50 +0200 Subject: [PATCH 48/72] fix condition subnet_create --- roles/subnet_create/templates/request.json.j2 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/roles/subnet_create/templates/request.json.j2 b/roles/subnet_create/templates/request.json.j2 index b003634..debf327 100644 --- a/roles/subnet_create/templates/request.json.j2 +++ b/roles/subnet_create/templates/request.json.j2 @@ -6,10 +6,10 @@ {% if subnet_dhcp_enable is defined %} "dhcp_enable": {{ subnet_dhcp_enable }}, {% endif %} -{% if subnet_primary_dns is defined %} +{% if subnet_primary_dns is defined and subnet_primary_dns | length != 0 %} "primary_dns": "{{ subnet_primary_dns }}", {% endif %} -{% if subnet_secondary_dns is defined %} +{% if subnet_secondary_dns is defined and subnet_secondary_dns|length != 0 %} "secondary_dns": "{{ subnet_secondary_dns }}", {% endif %} "availability_zone": "{{ availability_zone }}", From e158229c8533d943ee17a0fdc67f25c4a7b5a496 Mon Sep 17 00:00:00 2001 From: Frank Kloeker Date: Mon, 26 Jun 2017 09:16:23 +0200 Subject: [PATCH 49/72] start document build service --- BUILDSERVICE.md | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 BUILDSERVICE.md diff --git a/BUILDSERVICE.md b/BUILDSERVICE.md new file mode 100644 index 0000000..6d8c5ef --- /dev/null +++ b/BUILDSERVICE.md @@ -0,0 +1,8 @@ +Build your own images with ansible (WiP) + +1. Download Ubuntu Cloud Image +2. Upload to IMS (private, generic name) +3. Boot VM with this image +4. Login ssh, install, configure, doing things +5. Shutdown VM +6. Upload VM image to IMS (private, customize name) From 845bfb21c1942d95a55f757ed0d275594d45c999 Mon Sep 17 00:00:00 2001 From: Frank Kloeker Date: Mon, 26 Jun 2017 09:18:05 +0200 Subject: [PATCH 50/72] cleanup gitignore --- .gitignore | 3 --- 1 file changed, 3 deletions(-) diff --git a/.gitignore b/.gitignore index ae30ac1..007aec8 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,3 @@ _secrets.yml -_elb_secrets.yml -_ecs_secrets.yml _user_data*.txt -_tenant.ini *.retry From 0add3085ae2089b38f334e1289bbcabdd0d5a4bc Mon Sep 17 00:00:00 2001 From: Frank Kloeker Date: Mon, 26 Jun 2017 12:23:43 +0200 Subject: [PATCH 51/72] PoC DNS resolution --- DNS.md | 25 ++++++++ pictures/otc-dns-api.png | Bin 0 -> 95640 bytes pictures/otc-dns.png | Bin 0 -> 69585 bytes tenant.ini | 122 ++++++--------------------------------- 4 files changed, 42 insertions(+), 105 deletions(-) create mode 100644 DNS.md create mode 100644 pictures/otc-dns-api.png create mode 100644 pictures/otc-dns.png diff --git a/DNS.md b/DNS.md new file mode 100644 index 0000000..49eedac --- /dev/null +++ b/DNS.md @@ -0,0 +1,25 @@ +# OTC DNS - the complete example +============================== + +DNS services are provided by OTC since months. Now the complete stack +is reworked so we can take a closer look on API service. + +![OTC Dashboard]|pictures/otc-dns.png + +The service is located on the dashboard in the network services area. +There are 3 main features as you can see on the screen: + +* Public Zones +* Private Zones +* PTR-Records + +Private Zones and PTR-Records are completly new. If you have older +implementation with setup reverse zones, please update to the new one. +A good thing: it's simple! + +![OTC API]|pictures/otc-dns-api.png + +Docmentation can you found at https://docs.otc.t-systems.com/en-us/dns_dld/index.html + + + diff --git a/pictures/otc-dns-api.png b/pictures/otc-dns-api.png new file mode 100644 index 0000000000000000000000000000000000000000..32d40279c3e7a4880a39db308656e48cbc09f335 GIT binary patch literal 95640 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 @Lg$R<7Rgu zj{!1XZqB~{$wr=pmDCAqkj5r)aB|L@2k8Dr3P*0@zFvY4%p-J0rd~hb_4fs4B3!f= z37i11L<)_G2`Hg`k~DdUTWl5AYe)UNwnEtH*cRsq*;xvz>jxBvf7V8U~(e& zjc91!98{ybn5fAox^{#}T|!3cxwll!C{)JS+}xa&j9f@JljBuLX2(;I^CA?Pg;67H z?+EnAjZq6$QPWG;?%ox^n}3HU9V&$o>9rwQD^v|jE=Sfi7>$oT8%^7qarKdzMs`zY zBwfs~)Qb)1b$5eDy@tbF@j9@V`mA!U4!1oGVaE9rOadLg(@6J+wU8K*tAbz@$%)G8uUXK(o5fN)C4-p^( zAa)*P;t(~ZCar^|B@IqXiFy2Z5d}f7wX2JULLf@O%7rbVH*OTlnqbNmpGQUMY08#@ zny+$&)Gpi%Q6fBK+&Q7xy@Jo9!x)&Bl8Opx-7!JPD&x#2`*Vrd$vySiJEnt(aAQl=De#p z&3axi{H*Xc?JZ{gRq*6ON?JsL8$V-+vex4b;Mj{%Nm5Y&Gs7o|>?)@9>q8J+*zO5H zZokd?>*e~ATcX=)Hdod~$QtjDI1v}(m*S|TBtl1mcvN=+`iJ&GBxxw;77?;N=xY6Y zb1_1n0S{bbToD}Q2{R6iD{>A%c?-us{=$Tr(X}`rWLY zmDO6Sw+_qUyStF#{YR3+ulbmD_L9h6wcl}|VLJg$u>Ji&6Qe&Dc%By>y^%1C;JFu5 zB-lbKmvW2QYIdY*#>3+M7-0yY#3+ECZV3J%Pv06bnN78P{hCuSNzfWpAv4FO&+?eT zOtB4}APIG7_X}ki_E&O?Ov&ihrbIN9Jg(UZ_CU1nh~S`sNe_qJDR}^-Mb4<@pmY@0 zV!W+R190IU1hlBHy#c$Tw)3s2(FTQ$>C%(ZmuQ_J@VImfTYi)5U4A8mx!MGsp2n9Vz|CNpe~hFJ zxSCMBQL+#*fyk(gZdhu>KacChLCs!Nkq$6@7i|Ggv2MBVZhJ%g%~Kq;Gch@tkGc_{ z476w721esM<4W%(k`+46X1%JrzbrCUBvB+V;64lV8(5=z(c&Y!&>OsVV_C6c1uJ@W z9AQl&F>6L1Fbx!I0qFbR5QzvxFmNRLk~YVL2dA4zytGKF#I=HtGduBG5%H|1agMZf z$YogdI=w?H&<)|HJksVL!lX*?S8kP@uo{(s8fgAqX0fkwWT2z|s8&j@@<&6P;!irvZ9)!xm3B3s2Wr!InKRz(_*8&C0&$ z;o7KCc5WWGTp-njB54mxm(kNJ0eO?XDD5R}c_RqO!ILk{N^)xgrHumv z_`UO6tP3hCLLwDM7s2@V*;XN(H`sNw=X&$=h4QgoTu5Qlrf*pEGzup zy~|id2TvSBqe;M~(7Rqb*J~yWbKTdWDlhQo^bEkvArE|<+-<>tk$#d)v5?vxMUKU| z7wR$iD!pD_+j;JT4v*W8zf%~YF6cRvj=fv~!xovwx+HzA69kerr0)@b5H|<)Xa7 zp?(0Pmp+^r>IW)AQ8Y2(!dbSOkMAJ?5IE-&1`&|q&lo{X&H)Q@MifzSM10)pz z4oLzjl-MkJlTRcv_cXuehv6x;>`HVh5U=+_{eTt^ zEIPmYW0<=QdNf0;pP?cg>zAB!;*g+y#DO-bGaFhBLfIOJi(g0CM%=+6n?H#pjEJ~#N~b%V zIwxRNM_TpQ1J~|o9TKl2sUB8evynG$EJC7+>>y;m3OYr2%lqQV23}}}5RwCJIKHhS z5u|5nSR2q+=){X2173iaJA&F0#AN5`4gCV?qZ9kqk2F{a8e~5G>a>*EiaG8m@?aOd zszw%h{b?k%mSDGTR>k64c+Jt zx7>?p1W+G`3+nTrBJ2MaieH8&)N-U+6p{r1)_tM&37)tVCrvUZ%HVnCP!+sm}b>{hBDTGl(b^HkDA|eeve^_;rIz)LRd0*5s?c+Ccz$3)H zWQgtO4?CE^Vb*V@ZfsRI2}+>=E&q~TOqU=DTt?t$tJq=j8o%M<-TVsi9?z4F^Na{J zPJRQT13KUg3{9$TfAu_Msxx-Hy0-``QmyH>AZOD3@ z92d6_ZE%zW+NiV^1+)=q$zTmv=g!TZ7yXU?)E8Xna@;wD|Ju7xw9n(2?lBvOI|40q zf}*z$Ca4b#iPqRRSlm4OgDaPFli}Y%sM#UF#uSWnP>QZ*gK1a{h5R;YX;y5jd62FH z%UWjIDAR$^Im~r+-vM=F=X$qi&%9bFiXGG(TNsi09&PdsS zeQP5;9;($tUk@6Go)Dq>`o{|{EsKLbGUL>K4Dc3})Ie)b^x+tde0}w}&KBSjt2iUJ z0vWzSl$4smj2>AxN%Ot4IyHGE!Z>-2?se z*o%xykKV+Hs@27t*R%NU=C0S><6-eVlfBwy&xim*GU`bpq0D#L*E#guiGk1Ph8Zwc z(u?L#Leg3I0=y85sHiBblpP196#)QM*|SVJ)pH%1=(^)ae8wGxv1`@MN|drM4_B^N ziU|gH@{}mP(GeU0S^>=>g3?taX;W}5Pt(%kT?QP_oGAk2QJCj7Js_22^qLKDMYjoo z6Mw(mAtMuoK`uhxc(3w(uafGQ#%YUV?{WAwyuWk9o4jE*@Px#z~fwbBkGr$gK8DD#*wEW0m3TN*j=hbY6yD?NoE z5DNak`fBM=XsoQh!Y-Af-dguD^JJ=k!SzWiJ3AvgnRu}bdmeG;a+?T^<}9B8HN!kY zIUotbao`O?L6OuVbgYUHKNrna>YT;hF9L%s{e>*k%?Ft-ovwcVr5yVh4a70DIX;dDGmS ztA6-p)Z{wxHB-al&#&grV5o@}#4-kDxJ5+?E#l@AQ(T@JG(JbxG%*v{e&BU6>@iz~IFym-i< zG!~N&w8sJcbYbYuQYWS%^unvDP`!YmAg~&#UMee#F+kD^h&pd}iVh=||0Y9B<*rt-d3jX#OB z;T_X|e)4)|GC&AvG|^ZAhrMQoR2F1tP`i-<3=|qDRGD5zoRzccwucY`<%Ssm6~L~0 zFdM-HSQ@8qJh@{NtMhn3bL2R{eO7TxdVo>ea5@7@iYO2sZjDrmrw__k7)%2JV`gU$ zMl%r92sUFIu3cJ2#uO@kG`2nl!YXJX_&H|6*do9>JlUzhdZ$FwBTjy2F$Y;OvE3rf zzd6MpMa^A_i75EUes66riV}#Th*)bh_dZBaka4E}B0;aAS-~;Z7x$5l!;Nm_Q;lsfdA<$%*0t5dp z$kV<(lMOm;ZEoI{S6e&bM&*jhvm#j^atcBECGz##`udhXoM87s!OJ~+|K2@=N0`IH zNiFh2c|V~coNpUt6g+2Gi69Ox7?;U+#zJMqcoSCA8R&&YUS#9plX8I6)ld#>#O;KX zsoiTW{*MTaL4QsBVjw*5w}>p+tMhwqv6twZr-6%jf>*Blar>BH)aEShDWPZOk~M0( z1bA6iF>5e$8yvo-u6jU;zKA#D!FW|^xna$_OE(WaIGxYCgjt(LpXS!|w9nFCq8pw3 zkF)$717-H^>%hI~o!!mi9F_7?u+jN^t~UIN_Wcc@*Zn~eSnIeEdw!7UVum1*Ll#F~{keAQ-F>9)Y}Y0#S@0RGJ5FyB*olD2 ztU-q+l#fgZ6NI~X@Fmq(v|a zVa_?@(UC07a6LRQa2WQ*;eDWFzkq=O zYmU_6{aUso2||(NKz5t^xX>H|6_bhpJM3CnC!3QgxlU${GBea)RYS)nvp>VKf|Y|O zYK?dG&ch+B?Cc%DB@1z%G<0=oC}azfsdLo31(;WbG@ZFc&JBV;_!ey6XT-*z45dbp z*80rs><;vWrY8mV+lmyvNM8JD>}UanTc zrQwPrbe_)rpBdmWfhRAGu}$wF<|^z392|#;Mbr!PBcV(xjDHzcjp~BydD>=R=Pq#3 z{`~dJ|Is6E^#ccxUTIdX!k8=yNXT5MwY0QIL5sw|aD&mwX@o|#3|JIC1P9TODJXJ_ zaS#uYDgqO91+@n&NWID%Sd7f|s>zJ$s zxL*;?@$AgVA$(XqhMq_j0%Y+ik{!{k0(W_f+5z%@UjPLt=2J?8JLeGy{Hte39BrWq z>z!OkRK#cx7y!$MH4zC!@njq zqSgfbyCH-SldM-@pDIGBbmw0$4(9FazW-i_;| z&=pqx%W>ns`XusQ;`TORIuCMoQBXPr@jwSr`zjKiFZl84+8eKJib9&<^mo5^b8s7G z%W-ED+lrwp;1|39*&Np$VAL2gGi-{fd#^&WzM2j4hRX^3+5k682}}$6h%x2f$$0fd252f z!=5i`8&Lth9p>gqpmG5vK(}crjR0hn}h^4MOs#NKK+6G6|g&tzb&3F+Q0cN zge?7Iw?b~ub*&8Dve!vq8rAw6KtOmY4Fx*5MF6Z<)pf&}g)A6P80}-M*(WVcltPpN zMAlN~9}q`#nRU@Te{QV`86e3TXyQUTQR*n7{~_c*iU1yQ@l_yK2v`j?2@)_Q$z>GC zAunM<#XG2^76m+o0woK{$tZ7ucPv8X2i$)dX5aB}G2+MoU?Ui#A!PP}vJK8+M7aV9 zAc$%o$Q&_}4Yxk%sF_$;pUy(&CRknzVlVzL0;SlJi*`x@Q{M)B3dJb zfKr%sN0S6C_acNF%=`vM7NGke(xPoJm`}Fa7Z#jivnQdvCUpSd=gFpn{1qx?Di5Yh zP%&mS1HbNDtnzog;7D3u1V%l5r~x#N9)+*~GOsJZ1%o0_5NSRcXF;kY0}Soaod5=j zi2y)!KM3-^Lfa0!p29D4d;R+LX{JTXNhJQ&;Dd@UQwkX=d!Jx4=%X;Ys6yP)`RmZVglW-jG2@UVzzhknQoJzm;RjQGW7EJziFa zUD-KVvqcwzXo4e>;nyUCtd%In3Napg=5ca=PR!@tpy_A}V^#h+qH-c$1F$F}+C;+1 z!yGT;zJHBqOn85StR`8}W@bh4>j66A&+rZ-nn%cjRKE)*kK$Hz025O|xd@lM!=1M@ zJEQs=KI&NZ)xD^3(X>j4xSIQm(e@WLPt;VwpD<^w@$GchHyMa_lFM+^3N;{H${VK- z^8P@t7R9l8^mcag?sWNqx)A$;5_ctY-l zsv#QGTEtof<-1IEm5S}KaWAknf~9@gB@a(Dv_lk@Xc?pd;VV&a5hwJ-j=!Y(Vzx!jFnM!i^!$FrW;mfW%Dbn@^usqNBMf#~5$6PpD4k?DMT~ z=pk@5xFu95!FBzB#EP8Ad2M8ag#5c1Pp8xHE- zsSSG{ZFMpmgLbuR4^Au2XBwd(bXn4UhYWFg;+G2}ap-UZ6j1p@=j1?xiCGeY`l63jn|wA@rC5vj1ZG#-BB(4iANH%8g? zVr(5deR^D|(tSvH#)lJ^COFyN^r#QX7^jiM!=-t5T(kw4c8{DRHUf%$_nV z858%wFLJ-`^vC`5rjEWpY^1~CI_*!!%>8xJMs(%&>p+zr(Dh61`h=C0QTm4kR>mS$ z0``$g2CK$;czTWsFxaWSpU-gy{9RORLVeILT%|8UE#ZozeDSVrxRHxKeiW^wttex4 zbOh};N)PA}@HH?TFSntgXK6GUDLjITvs7>$EP8wn4Pf|1t3k^Z0AOlGIzsC`D?+V@ zlqII3F0@G9i2_WNWVE0q_PZl?#D;ubhGJGA)A8dkW25bOYjl2dDB|pxue90w>|Ubx z(i8nAiB4z3zTy!`hVc-=lX98vy+Q~fihf0GUR9Lls;XA4?}ip^u$pd`5o!}qc)N{L}(zj~G^OkW(GI(-g?0Qg{&0Dmn2Gs%o;_}U#QyvH;B64}=Sv<29F(dT& zT*QSEmWPM|Pr5sMk>TAe3V$~F#^b&Z9yI7U(EW_amNX-%Fn1!YY@(ua_3>k~e%{O` z?bxZ)QyTr?6-Q&9nR!!N2ysPxQLE0KJF^~}sCisCckVLPpJ2%c48U9&&UuMl&e8pOLTMx_l z`PcSYeNdx*1cFGod|6>xijEi)PQwuOMQojzQyUo3x#JpH7)_-)mxft3kA-xvuT}SF=GwnWTtge)zpo*cW1jE-u*qxTML{N8_MQDjuMeYUK>P-y@9 z1s9i($$S41C|A)xR6J(#A4Y9|)wUIH6)~b33{IWV=_!MqcBuY1OmgD9w)vN#=STij za@ub~0YxWM$E*~2-;&aRYYd&X_%`oQ!!S{OYJ_VZJly6#U(o;G=A+_L|LbM`uicKS zj6G3(lLN^IS7%e?;9txcF5YL=>xkZpU-~%p8`Mh)9d0$A)h5+JX5F?l8_Yd(p%tKYX6O)4maR zW6@dS@&y2-P?L%dJIfVH;G`1Xel#uzUaTQW4eSH2B6dF?ty8kqo1_g1{z4e0;R!i7 zkM_lG$Wqdo$sC&=x=v{6|Np&x_yM zjGbD2Wc&~A#|(e{joC&3-ApwykEWQn-n=xKSfG>PSS==nrwjvxcZlW?#z)TUtxM1 z)8IgS8KtK+3qO3mJm#i)`}UQoPon;hjhp@BiT`NSW?bX&&_aN^-WKHpWTB#*p5na| zSohirz2R#Vqk`^=_w%z;tegVYj!!xlQ>vT!M@bo_f>GgTmhQj=I!WNc(Nm_>1N{`A zU@kkk(XG*E=SX{S%Y$;6kcFO(H?Lh2m-<)}V{)YgRY8UUHhu8mL4(*SJ}c5@gy>tI zc6@9^fOnQu9_KflPnvu`PVc^hhl}do5jRG$1#JEv3v2%5<;yc+Sf>^z@Ia?MS)BdR z$KBn%C~chD;+_*HOlZiU?rxM@zSo{#X!qTLIJ~Ke>$Gn=AxnATc4jv{?pBVtF}?Tb*K)x=yKCput;V2V0RbiKE3sh~;Sf?-hzcpmu1Bu*rK{ZdwDc=sjf zR08nbRysZ2_vw#s@jtqt(wU<5h?K&_7zrYSyxRN6L+6rlTo(X}T#Jg?hITwr`n>!s zZX4dyknoGIiVqx$ESUMtKJe{fkCPv9j?hEqIT7S0lehjs?6*;-w5AOLW=2h^?il{1TP`qTi zc(Hp^?y?K_U}G5fZv8dibsqPobZ_nOAz*4y22txZ@GFJ$H^8LrMD)z%G6k_2AK%W? zwJf^eY@mL5;Qo7NGqp~c`&*mXi6L?wr+uq}USN^y38DGB@-tsB2 z&E{Vw9n+d!QKOMHS~oK!{mh(?0V?-WC*5}_NMj)xsH>USPM_x;(peq%fj#zz&q#2+{JPzfzSgYXHD=8G{z%)7t2{|~3)DmkQNBCTl`caZB^;oRt z_~Y9e;U7A7tV*s9BiWm}Gc_mXFBw@h${HIQYMmw;Zw8qHdq2RxP?AS3>-ATW7%1e&QwS#qgVLd z`{wsPddXc8`EK_0_Mbfn@6opUYW3KNmCMw`C-egn8>;c_1S`FUrJ;^vlSfkgpY>uwW(Qcmk&b)FD*v_d zYYBmFvE(cO)xKsAt7WD*gi!++rSIK&=u)Ah-8!n8jRZg|$5~_*r07Z@ALpHCYE#m7 zonn?oUK%QldyDsO7ZwU9Px0b$0W0**1cqc;I++yT zZfYH17ZGqqzpsX-<~B%B^!t*1#VM)#uf=r_dea>_8Zb2vnfkFaXR9Dz&P$IRbVz0$X zBKiVaY|f{#W5-rwKPqPHdJ1eh24%9?=W?llB*y}Dyi=Qa+C;wgEl>d zldb4#b+rx3@m#$lWCmz``XIh(9CtM8jr{!t4IlaBKX0j5y%`~x_71vwWri;hvOO_{(ZPR{S*TsJx z#kdX{PzmSbeVipOlK)rf{^EOU$z@Q9BjKzC58Y;TC{m@acs3r~YWsA3{UQdUo{$IT zL3H@oJVut(qKYb3KZDb-pUg8UzCQQ)_&hc?3$%7aoT9;{sw%SgLB6P}hq8HX+i|X2uipuH1MMKrf6?R2?K}JF!V^O`W5zC8o`Zh zzw^uT%MIS12~77V6e-u=J5@_Bz3;q)sukJqYF%cVPIhNUNsB&gXA&Oswi8HHv*Ce1 zCY=g>|9-_+c21O=i7?4V^IK-@`8+0IwejQC@!Ib_$!5T5*_LvTtqk;|uKvvY!@IS^ z^t=4bzd>tIq4dGxDE@ii7P^KB8-`5xS>=~+Wg^nuk#Wg!nsy^+PJ$M#h1UcueJ&w8 zL-LLn{sVa_`SQOaFM5SBF`j`TLxL){k?uEAA5RS|?;ZMk4Ztlm!k{$qYoG}QNa-xW zPbHs`vQx~PklN$wsxpk^$D>CA%&bZXNslTI+ z_oU9F`3oWYh)+42YBrPcDbtLMNSQGdQUMq{*Q-{*s#Bks({L$S#jYeiB!8!y{Z$(j zpnF5Ke|^7j=^|A_o1)}|FRTybqFhc-Z!FAY=neAj*&&p)?6GRi_niZ(pLGnx9_X;5|?o6hq>`D};lN*s#RCO+5UQh2ZV4%sgkN z`xcgY#5}UX>t6-wR8#je+Le!vSEC%Z&I-ChvMkYW!E?$FyWBQnJBf|El^ zTue+`JBLNCiz#izuZB5cEU`E|SWv{r+&Di;bhIVi;^ zf$EZ6rYqpolA4SGt4Xb`H$Ogn@SrGt>Tr=(!I0IcSFc$%h>Pf8uomx)`a2<^GS=A3 zRBH3AtZI?*$w6|i?GXYr)RsKHpJ!v^Mkgo{ zR18V+M|kD$HP7yIPn$3SjfoJ-3MiyK4P%zD?R-Dlcu5Y)duI|65SUqLhM?ul+KltZ z46{<0$!5?p9r>;Vt5mC2O~z;dLZIjmDA7_D4co@R*Ezjs_+&oBWJMzFMjs!aC@S}* zHEPw`1Zf9|ClN?M>L)mi&8vroNcG@Qa4ust2jafIRHrt!>r0DHn99~)+`NWe>zyOr zM#Wi#w_}oxQbA~e*NQt&>E34eYy{)5jm)q(M;?P@Jn(I%P%fZ}F;kRZJk6DqlC6)vL?2LbMI-G&D-W{6KWhk;X$K;xNT|F^R#0?|J$}*v1VNT^Gm;ERZK%+es7#7<7F+Jc0Kva%{=73+4dsUrIJ!QbR%(ZzRFH2mxDdlZ%UL({bN58@p+| z_d2Uj^p4XHZ30>qMM99dAru#XJvm=wy}N@`!L|-g0fV#Etn4jU!xMGv(&g%3f7KAs zW$i^z|A2?XbElu=D)3 zzmZJ;yL*1&;uXKut3I#w```UfC@1A3IH~{M(817v)lSv}$8BD$ptBC)uWj2NPkOkFuD>d~ zEOlL=sriYik6QNkoQ#N8AMxsZq?Fbb@6A7p_{WGhFWH+yMfjAb;l+(@uWJ4TNYgl? zQ`!!&fZ<1iVA0m-mEEHG^S7WR?LQ);EhbU6cy~UEIbAtqY$8als@B67MxiakSqZ{}pPLVCwKqx!#i z_N*!!7zb$o=DmuB9H_uL+g8!3JEgZ*2jXei7W(rLxBR3MwBYx z#BxSs7PmrS;T{zllQ!VD79eU6xc#5DZS6-@Ea;56qB2H0T}fdpwk7LFHeRMS4M1hd zmb8h)XoAR_vmvn?i{sy5@->110AI~n$1NIV>A?T zLUGi~o-Zm%247LNRsG`+?41FIul24T>}OdEMam{1jhNIS0Wmu660Q4B!HJloT(bp6 zsE8gyZ^Mx!<|Z0!?$^x15+~&Bg4#p+!aSzPm*2YejpO?tp>~J7+Ya*y^#5v!E6;*t zYl!-ntQZvN2@EB$C3Ej@@1ZJqODZWM2BYd30|w4s0Pa-VPd{KyT!_KL)%WfMOwbOr zzNH!-XE4a;3ap=%P^pN{U$O?f7wfM9+2(99FV0P>Xp@C|C1CDO z`G!;ZxA-*y5rBw$nqF)^c5Ku56(p0nK94{MBPa?`tl|vU)gE6_zh?P%wG;_mCQs`! z$BYQx!G)C(3wvfxC{&+u>tn#x5o5MnA?qM-y)KU$kxB(7?_4+~1CU)3nysu`Ms4O^Le|y5gH0n_vQj4doqLcr5L&JiyCh&S{dz zt0)rsAjJm9)>XRI+tDi*3bjE`hAX29FmY{#K#7ex8(T5H0eaP!?O8-5-0RH}tg*4J z)$7L_MG|Gzm47V*V-QHn%ZMaRv@`LJ!;p(eycsoNb&kATF;idphsZ|mi1W_9ttgn1 zKs_Y-kmYJPRCNFnV5k)_<%z2Sgkz`KAgX0U=;j(jQqoIzKWJ(FR zVeDG9iv&5M%k<$9qRZG9Hf<)UMG{LF7ef$XNW)>K6EwqgO#;)?d*1_n*EzvXhx(+9a=8Cc*4#p zpy0=E2y+<9slYm#nY!Fb)6exOPpFlvFpT8wE;jj{!x64k@qg8vL0TtEU7P?6&TB8| zJtMT_C%zPZx^T9|1uuH3vv#qe$Fk+qG2!vuioGYN#zZW3($dlr8=t}yzcwQI!Vbzn zVUacsu{iPeCt_P6EHc>jwvhnCl@vU` zG7U&Z`1o3Hpj%R$S$bFK-hm_S3qQlRCh#^#O(mX8%Fw_`kM&IM7Fgw-9Xi}YcPpx9 z=FrV4xCESxfcL5lQ@~a#ZHU}h>G6;UNy;waowOSVw8qi034BERJq*>DEyTKQ_qy$^ zqASRD4Sxz>e4Vbo^*M&qK>lpw04b0rl}5APa&K|8F~#|(6P}%|S$v|GhsV=^Gso@> zRTCT$8VQy3Hjd+`h^DJ5q&6*DLj52JV%FecXU(wT10Jh0P^N3|z>wa0dBmrAmAdqq zR$Lk!9=_7UT5o4Yi4zqjPFvRkJQvMDq(ukH*(0a^x*z_nM%FS|^};`Ua*0j%Geh>` zjpMN=KBpukyKL#19<3eT&rbgo6n6i5%=vP58~UV1AIfM!UYqpR|2W}-X5myZ@eQ`Z zb5}w2`T0Gi=HYvAeq{GN?R<}MDqaT;WGr@yp3}R!9H;y9!tz zR_e{AevvB%>W6gw&f1Ri0qB(R0drzkOXepf$c#3BkOaEb!(&F= zsc|LNxEM>B8(;Kb<*m$s-g;N{{;MX+;85V*+qcgpr;wbT+5AwVo~}Vl+7Tqi!(|I6 zV<-E|_r3oH+U8N55-h!=+>AQ>=;xRrw7)4Fc;q5i2lJFsE?fyI zE8EAOS#D=(X40~YonwB9ju)I=LzxA{w%Zq1ax0VqT)z~IM~@yAS&o}mPC&u!6IJTe zts6$HHL=4~sx8U6$-et^l1{HGa18BFR$EE($yb+Fl5t23`af%--1(r{JZV9GGxDL zGO7~VE_XvnNpQ&PSND%)u30}DNj4|9!F|najmJbyUE!-p3tu=jE)Pw^R^XByt@rSN zhv>yb1P>>P3r~{{p-o~FBlGGCP=UqLC;1?WYO)Qy+YXwjak#pP)sHg&TUhKa6Hd}C zYcIA}3;>z0hGs%VSNNB-wcCK$i*>_!r(O~$cMgHTQm4UFTRj{OXvJ|asnjSV@K<(? zyWfJGKXDid`VI(O1*E}k_v5nCx9#j4%Tx(-fKaW^Lx;@w>h0SNxO`c!GXIWUs2$=l zaL4KjlYO3w2zY1`v$bS|g$N(W#!7JdQEnDIwN3-Q>-Z<=ljkD&(4*GF1mSXBa>fN^ zWV`R9p|O=P1Hgx~!wf+t`k;Q73JM)J^{nMnw=&N1Jhu2Z4O!lhAe%3Y->!wi8wh<1xP|H3 zr+@Gb^nG$_I!GizN^|GVRX7R+ai$-1jC(j-j`Al@=2F$P0m_tSK}J`>*k%>18}B#$ zu4(4X^4X6IEs^fS^%dch);;al<3-sEaYsej6*Qxk3tAJ+qY4TCWdBrcNw^h$035Tx zd#O!>$y1$WH*zzM8>BXDRrS zB=qgh9sS~Rv&omH$5Gr;S`jRefK~@#WcLusG@$Lkud;=m+4`XqyqFXvup!wwz9wnh(6Ev0Gy@zseEqmY8Q(b->K%cuf2K+4Qb0AASET(V8q*QJES|KGX+ zY2(1WPg$TFp=%_fS;o-GUH>mbFiIao%`&=1|Sr`tC~`=v~G3 z+01Oo$cVfbFScc5WOVilh4^L!)yUpnm}DgZ4vU24#<1_x8ut!IHTfWR&D5jXFL=L^ zD^~P_C~mPf_nU9c3tn_o$qRiPlIQA2!2C)&1l!fsn>TmA90ydE-%#Z6df|~|edFDa zPkm2bWK*b_v{|08Ur4d9UFh0aFWmrCLQEZu|Ku^3V;xaL^X4b3>8lk!2d*kPw#>tS z->4Id(!70pef#R-zqrE5qG8QT1>Ct7I2qzuuqHlozePGXc11@ImS9v7K>tLkt{`}j z*PCWm==aOE<*e`aYN*2>x7=OEJ01OW;?NTy9XHo>?TprHU)uc(?g7%Wzc{~6r3#>> z0&KZ{^`_Zb4=yT?wr?vawum3VO#-f@%d{Yx>6XuOX#F+p_XlgD_uay;i1uZw_Lr*Y zem0d|E`ycGrLd&avdxas;gZ6qNiY4yTh&KfX`y9r+vg z`nVP_&PoRZ5Iw;`5-oS_>i$TDopAld4>yO7*VVPkN$tKrGkX6Ay}BAIu#@AUa~;2x z%tTsBIm@(*=s6*FI`5=$1mz<*zK-$)^^O35Sh-a1Pq6!5uX&&2*OCA;fBv*lM-OHt zJCbnsITL0tMlv9MqS6+h2PxNrIVuuRoB)w47G9<4vIdXXoR#Bd@s4t;NhT}!mx#o~ z1Sbq?ixxEzpDH78t;qaixG;cSqGlC#igA2n%Q=8#j8|8o5foC$uW+-#%8QsVkc;mf zj3nc)l}Nm$zhPRt1P{U@@nmG#YuT&|*-h~YF)C0C-AiSK6iS)>jX6T!;=VWsIFbbQ z=D4e*sKJlS6>_xJhl}%0-OZB zTTBFeWeCTVU9-7|)=loy!=7M=hX!gWnoT6!QMxTKWY&~53odYM{oBF#xKHIy`Mppl z*Cw@ftav|S@`pHX+}Y3%+IbVyzw=J7QzDpA<}O-PvAZhbu7<^|#iz`oZMl>hcFeJC zv29`j-JPgGD=DJOq3PVQSTGcO?<20Ru334nQ&-vnvYzc(&SX{&?OB~n*`<>>CEQRp z$|M&gI6?hhs!!`%GTQQGF)E3~*Qy`gg3)6O(2D&2IG4YcEnH)_6Je7Vx#hX9-co5X zAUf_|)Ia=@i`D$cA_gwLzbt;n>=1+QqW-2G@-BZ#dI*)FWC|z>1q3HYWu@5ZxVhs4 zT94yFL3Ly{&4mkVJS-xXJ~aAzHz%h#b+O`1MT|>dcTP63zHv7v=dX=+{D*|?vAIcZqqa9{F z&LJj;8tor0-E*j=d~6y7OR zTVM<4;~dH(b4H%k9aKu!tp$UG4`R_tj~pnvr=**+?W7skT6^x`K)`%R8dvWS_*2A+YKiqq#+6X%vP**O z`P*tceR-}T(dY`%_%r;blVl)1;E|XnsfL(yfVlg)K|K}U zldUSIm+99r)7K^RMTjqWw-Ma*w0I|{Ib^CRbI~^w;y(}6S;Dv>MLC;>p@uS?a0a|p zmMd-9EZ?uQc{zZ^%d8>HdzCSH5HMi4 zMS|l7vD2jdo5T1YU+1ok$8X&pXaA;+_J*8iA4(tp_&)P!^6^Vdp(Rp&&P&%9ZD!Dr z$m7I`t4Mme6lDkdFRa(2(d%Bg^r`3S=(PUHLwam%D@BH0c%Z8|jXB33Iea#eb7@AF zmzP%u@IHhiObD6iaf`Ktl^}o$#b8-}Y~IWh)37!Njcpx2BI!VF_SeQ|`pZ`=dvvo3 zPUtnhTn1Ro&U@H2AI5;W*}FJEW){3mK2@P+Z@aDyeMRv5l`DMm8p13OP zd!05~2S;{dp`2883e7;2)lixaj!DhCD_Q@I9;c=4q1OJalesr}XAg_)i}~{`&ioj3 z^Fr924R_vl)2X}BJ3gc1u+I-xLi&sb*|3iILC-1HT{84#_H>=DY#3>caa@TWQY3J+ zuhPka$?Pm3kVC2$8j&jg2j6xZG9)H-{9{zF!R5ns8&sV`Pik>ilidPYMmrSSB(K|l zjW4}6-|hW?2SDaNL1z9fyM^pB`EYi-oA>v;L64JlKTL={(?Fx@r6NjcevA&JE^diR zezQ!l=6_{oDN_T5sADb5un?IXhIv$EAwcXj9mgd)%2p~m33V+erhKx6gJ0@mH88lu z!A@5b5|)=1&65GD;Lsw6kbqv97a`tAT81;Wb+8r!IiKW(fQdIkkD%+MpG z-9p>epa!@CWWd(fm$*A1nHf7KP<4^{iZ+!w+OYKUJo3euryl4w<2?vnO zyLfR;MTTq9Q7Hz07h}<%^&dWC37$dXB} z&3e!2`XZ212r^E*uQhqV-L%e}I`xF7cPj-u75{)NZxY@61*dM1I3~8cSDAq-GMkZS z%7Xn>GM+0+<($|x>abT&iQF;0>*vtFyv+2fnwRDYDWAe9Vtgu;(V--Te zmv^NgW3~ZP9PY$Sikp%@-MwV+@Dh6j=iO(2a@VimbJC1D>!;ja9YW#OEIo9?cDFKn z?oC|5Y3-5;-!^b~27J)^7|^mTBYm7jP+`=)ki3`&(`kk7Mm5azL`jar|3|Uf4~^V- z3dax0)5q}v>QoNgO`0b|P1)}>F zH8+2p07Bnm={uT9COk-NGwSfQK(1Ty>GDS>PMy+`Kca6vUGf3wp(-2LQx04aal?yl zLAhL$6MmGfZP+L8)Zp`a&YLZ;IK28(p05Np z)5Hyn%V(dMR)gkUUP$8O5I(l+)X7iRk(9*tj$Q7>t-sJb9t5Qp)JZdZj`BO4inuu^ zBgz&%JC_T)xP|s{HvbKeQv+W8^N+i+pQHTqzdAh$ZE>(g^AXXmJ6&83tLQXtuGVLyy%G7gu}ncdOo}m$N?<7ZuOFIoRiZ`G>&^pX4pnwjj1QnLhw%}yx!PXAr=J=Y14kT=2nv37n<-W75@xR-XPahW%%YRa|bl9lgv5UQV!klC?nTH z896&Wu&`y7o!&jGg?ZknH&L@@a~x_oeoPz3^(^`dps|Z{G<o;Z@oRM z?2@Ao83LnLU37guFTmvVb64+B3+pAiEvBA+e%EUIfgCrr;imduPWO#G5b9SRHTYJO z+;^KZRz3Li(=vAN6x0c2T5j&9cB8k60*vRF59~1_1bmnG`Y0lZg#c`(eM(m5%(QUn2^W1 zew7ul_&koMrOUnJ9u`w@`grtUjuvQL~0I6mKg`ZTNY_lK|b76;$U{=x7j& ziUEXU2GCP9iu25#a~x0>)P7w^3H&jAYVFXv)v8GJI$A1M<39us<98XcrYLv<>Q&oZ zNm=6A6j=~boEl-RfX9ng(o{L$^GFwjhk3&i{e&?FOeruL5zl@rpqldAcUb{)7 zDm#07n{0NsGR}SbcIl^kNG_Ol!v<=Y4)l2_?$gJQTbLGV6dbuWj{ocBmA%ZV$vxxm z;paHm5^iBc($LUQoUyP{$SblqS{hH-nV`E~>fps}n zuCQ|mggYl{`IVr0z%R_COeiS(;!s|AK}OpMt_iVzZ^?dyyA3Xsz0CKrR5hbC4_|%6 zbo%FpPz<|42S`fNUGeuR(}lsUx|JNbGtk`9#jSQ&p1CDZNKNBvO2qvQvxdCL%S(SH zcP}e>%(qKkce-S~KbL_wcKA~xQ}fqa1(Zj2%e!lIspP}5YcUP?t~&ZN+~lO@ zYuXJNi={jv#YR%0lp2B8DQ+r_SfyWz%z9LrY-8)s!q!jk!l3jgY%$b+5-)ZRk{X0M zeTjb5B{GtZ#APiSz|Q%H5?=E}S>Q$`i{ss7iVub=~vQDBnYCx{~HB21GJ4ca)_YK=otUTBiG0K&*% z!HZAbUB|IU!B8u-NB8ciWNRrDs3-_U*wju`JgaH(OB(M#sc z8oD%~r}vcY_^0ZiDdUz31R$`VYl`a7-G1k98BF{b7opWNqJ&fguML+vNjv zvETXr*Pj=JK$k!e0e<;t$x<~PUp*$DEw>o@OJlqWFtCjK;UfKmf^?99INzIb9`-+x z-ek>MdwX-l&J-n@OPiKtSw+YQ3=y`hMip56 zd8Ad}e;ux|B}?)m z^;BSEHhYGRzC8r^q+gBSSeZ`87G0c-cW_$vsiCtb4Gm9=pQ=dSG&Aa#m0hk#uVtb~ zd~hO^?!SJaefxd8etiPG1JkE(rnX)BO@^G=vuf2U-}K4eLp;9Os`YpCj?lhj-1k!Y^8t3PE+q9eik~9JC1e4x zqS>cnjAF81{(UI@N%S0`UEx8MKFCpo)p9Do|G4PP@NeR5>L0rrOsKTL?8i@`e9gXwuOn+m(LbQ3$kB>;DZgL)HZX?v_l0!kJ@LbCxr`We`SSOM%ithzJKt^ z!6PoxdQv`=%aWBUbL80l1jzng?CvxdT{NDV=WY{&f8RI%$pL52ir4F=&v17c<0ij5 zKy~uw5A+eG)Iqj%=gr;5O0*M|t>R2NM12B0o80*doty;4we#>{Rhb%J3dVv?&GzXA zcpSjzt@!dGMRc25}8GCuHkf*agW4d;08I$D=(#O-5O`0|pr=1iwK&&zs7ITbp znP~DNp#MCc3Xz{;`JkOfLRMw#sI=oZmR%fuOH(rbR3*JFXcg>}Huf$>fRwBT#n!iAXw_Zzw!cb|ABT>uvfQA%-P0tmTJvak!QqWGq{aOFZq4e_oQ z8uv++B$JjICc5Q8B^M58pLJR`hFLkidrw(w^24!Fk5KK+SDg8MS4$e7ZpS3IB-MS# zj>XF#-M@eTO>|C9U#oyg_+Qdq9*A%Z8?bs(!2vKYG%a#YKu27;xbDN2zU@5EM68dS z!iv%Zr~?WnB>h#1z2v;MZF@Plgz6a)ehMd$KxzET@DB`{RB>}#d~wIyE^~hExi)uv z-L-d$m}U5nFZIu!J$nw8_3`%x`>xOH)4T}9&|HR@*S(|t{;v)a7d2+g9}Iu5m`TV$ zH%_+EK;_?AUM%>^k2id%t4N4|(uaH$t+H3X;)4|KQsj~{ISkwR4*Jr|iv}bxtX7`= zURhsIp_TGLxdQ|UT_=de&F9z~Ax8-9=`*qahSKG-@<$Xt3x|7A=AvE_$sNNB+(8v{ zIWk#yn>o2DIQ1Z3nNp+(M~JW&m>mr3{_!d73YU4p1UwV~m-0Yqd-&1$U105SB)a}T zC_O44UtJObwKsWkU8cpj&`JmtM(@Td+@GnLosm&v|J#x-juA$^gss8iE*vj9uG_Cy zjdBR~@IVVoO#o6+S1iVCi+c_n*uos?n%S?ZxNJRC|D!1jp4Easl-aa`luG)8sKro* zk8yYPu)|D`xOkq7-y%!w()TiJ>NQUJ1gQTV5)~_JjED5@6V+~o;hB&1%N9Y%Cp1%7 ze)e&u2TOxv-%Wnk#rpZ7RJifDGl}odH5r=z-Y+pS%)+4O)UcNaX2rX>wVsYIRVp3? z7F#_cv)-Ry9hyB5*63-v3MIVguaXK#sF&?7FD#|aXh0#9dp%X-lar6UGW~rtM@%4^ zh{+R4Nq>lJh0yRVD|tY~D-M2=be;qDc0q|Kt*E|a_>|;m$~&WuVrYXiCENioFXKs0 zwCXhH65k}j@$1$NMxrj`i~e8jx?=S?MZEhhPl;AT3hXBr;(BrbgBf++uq%e>tp&zr zN<@!3Y|2|ac3?V$w_CP{<~{zLE+nUP?_Ni>(FJpUE6xxpVlQVeqnjKFi^{jy0AP{F zv7I~7;q4s0X|}9WP55aV{AR1q?v!X+I|@2u!vEB(PC%Gf{Vg zg7s<>sE)Ut!nvBlz+!oN0NUN!#734G>#7}kWX#25$ZzWKD5v`^16Fr0Pn%x#jl=dg zx}UsTFH9@x*mU!6?KM}QYH&UMUYzM;&^j4eq(m5N!#qqHG!uP|is{-)>X%B}OQ;_D z1(C-!o4I6Qmq$9E8(lzARdJ>TEInCCA#5K*)ZaJPlO}iCj!ApmCvJVkwxFin3N}^} zN{T^!Y8m$hTA4k`>_W*^zWps0B)n1GpEp`aD;l1SNRs()2vSGUX7xN>{NcmsdGp$R zv>(RrZu2s=uSxZukg|`dVmFTsa|JF5Uj+j+9?La`tx6e7{pJSjVxO)(yhoE-C_lze z`2ipxK0EBL+gM)G(9#pi#w<${!OatL@#d%#VrXL0>tcw{V-dbc9gk+K`Owu%HpgTo zo!t#KaIR6z_vkXqOq1#vQ-&Ogy#GO7 zEj^fd9g*Lg2xuD6wYw?rXtNBRCcgIVZ4z^w@e@%Odb$k`cs}9v8tZklc6z`5kdpP~ z^e(-L>tdp#cW9qUEE}`rjZ1KbM?cTthXo~dTMcT!p8SU*TrLI9#pO4V^sr8s>`6{Z zIddg{{_)j+mU7a&?H-sG_q#&2+Ajkw=G}TkYwJsOvEb$N6$NTrHay!qPR9O1-pwNq zT{69KJ4jq<0(ch>Wy8+v3JQqvLn$b~$FU*A7X_VRm!p*ieD2WmvqLhq~vrUf#{i&mCA3dj@JlLbt)&o(I zTV8l}@LQIcN+~h0<#u{t{`K!88$~<>_82xV=|73@PRZ>%-OhOPLC@(wF73T@$@A5_ znDqq|8`81A(hlo2;~3yW(ubemL${vKTd1k2mmO_X;DsR3xYbZ9{w42cY1@DFe-&ry zLwGDluy|wXeda}+(*V7a2xm5$c0q~NoJq_(PBV#CY)SFj#t*ywtFJnR58?@3@JO=q z$z<SKiPbxLRPG19zzvfi*UdZ6p45| zB*I5xekdK2etiEt{5+zI-xx+eb(5HmOQu#p7aNIC`~tQ}mCgqV-Rry|(WI93>w;9pnd`xOV@_rRD9;EUQzTMw_ky(YHP-HN zl3m9Yw8ho4+x}~cf=D2d`#lYad9^lXM>K{BI};!_Ya6d05ZQBLP*OxEjSH>1IrK10 z27bC>evpm=gSJZ>l^)jUFZjfNOpD!TdM16gmZ4+?*>XHQZQ~Et^1aHbzJ=C1zK6tK z(EeU(k@n4O^1CxHJQwz=AC{Bpq24Ldt&``OoRzvkW{JsDRwn-$vHsyc4&`|yE;4G$ z<*8|n6bEt#1d<$Qk`F*7 znc@FYlz%m9IKvr|=GkW5vxdTRiZe)5?VR@QC>mtI5K6!b%^_#Dgg*%6N)y@BII@m} znur4hg9j&5hTs@4kF5_Gc}P-C-d|T20|nSV%AN6NKOD38aQQpOs2B?CF8MI`B0}a4Pjvr3}ZT_mK7| zPbhJvhXlw1Xm)1k3#ys<;Y%o_#cV(+gVU?o?pTKMl58U;Y{}1U)28=^i*W&#hZ?yS zu!QfPw3r#xDj@HQ5t;%Sv2}2eZF^J4FP2pL#~}jfSafB1a=*oCGN>uGCLKz!EA zxR>}WIu@f>4d2&d@{ft0Z;cu1IKP3*+xNp37Fw9hHzswkC^$yvCvGzMzv3nucG*91 zlPz)GcD(v7oGr=yfjuUwHQ6wwp_AGYhu;`O!~~;Y&yj6UE{G-nG4*H62{b=y}Q%B34WFtWK7X=cxT&66(~q0q+rE^}CE0 zU?QWM?Q?ZRJu;sG(b|F(&S(iiCguCZZqIMDogFAwDQ6d?WCt5pcN?3Y`}pX;;C1JqLI1L8`X?>F zU;WQTENy|?xazlSXVYcMK!v%h-_UfCaa>TP@jGlU^@d0-AwO1ne1N*r=o5F42*$b9 z0`M|{)1Mq6_Mweveu*j*0GsSWO)aKsF*Q`Vd~%HIlQ(ae!vc`o#b-lhMLZk$$d`rv zE#0_)>pWkT6uR%LKp7I-_4F{iw+OrZLqd|BD^-#7kt5HIb*m`S8UUuY^SVx@x)H(< zU%$cmo17p>thew)RG_Pl^bBo(-JD6v@=uHo<>YLbCup+mbnF&GHBiVj#M3)@00F4xa80h z$zK8nv2!b)>(1+!W#~=Hoxr4GGZ%ybT=3TnRZg{>N$Q5TpmasPb+l9z5b|@fS<*GL zejgY&D(OmW;3~T<7cAgkRHn{Ttm2Q0=QQC9wM`A<0a_n#j~1-_PTdy$s>1M&UpVB7 zb$1-6FwT{xm5*6TX6CZd_ASpHs8DTGg8PHBcp^oT3oe?aP z7_=R-@qyJB^TnLbqKH1VnBcW2K0A!r4 zdE9$4VG@m(#aV`L2pd4s4QWY@Li&H39dvVucgSoT&%?(m@KS+H{df*5D-d9n5Tl0Q zZC1lh01zsm6KBuXQY4;L3;(U=hgJg?HGmH5KB<`#6RgDn_Ns*QM@mAJA7oDN_E9+P51dOEip#9E18)QSL{3ycC_o>y%yBW5Z*WK#OUh6&7RDrq-W+` zB?dz*UA|Q0=8#0v1#slaxGTx*?X&P^tF*6)cYx<>;8saM<T#q-+y)59=z#wvlQq3_{Rr)O`jQgbg0qfi=iJ(0!$6Po z!Lugeh$vxvw$*df}7p&9b!3+xEwQ98s%&{S=~?We*?-?ocHXh1y$P1s31J@?_;-ds7M? zY-ATpIY+YXfuYR;*EjUZ%IUJ^q}r?><Cbj`1Im+?|wfns`Rz(>3o`)w6zFX)(N4 zX2r|Iy630HKF&)&S{Cu#c51Jy$9_|K-Kn7Hzz+_0(gx(HxxW0~@Qq+QK;+xebbrlL z`{8_L?g)W*-opk%r~LHxA_N8;uQILNQj^utuSXt&KAA9p>a)7C7C2bYA2|g$8#o2V z@|`lCXQ}5~OqW;qr7&)ga+!TK3`x*MjEnnHp&Y4yk`bU+hq2_%LD+bJ!KtQ}ZnBuz z^SO;`57^^&+B7|y=~fX&Qr^+@2p->e(e1XNI8X7uj3tu;&5#R+6C0H|xXz;Nkk+6N z^>|g@@M?B;cFFjJXbzq0JHd8i^vIg|t>7f&m~AzE&PRU1d0a5fC2QtR=gu<+rj;=w4aPAn5i z7}^DZtxA$rww0>8L+8$8{o|Wt<(da-oA|vzyu)luym!V?zRy5OCQN*VD0c=g3##XB_Xd4!UK8_I=k)KV{gkoQeY7 zs?VP!akf<^=!`Vrk-irsQ&OmDF+L?EswyI?GhB{{sCvQ{Tw!OsU$Dhwbt4 zai@6=??)=&Zpq76AQS$;o2p#qo(nA|NwlIlgPrS?ZLAx8T7WA+Wyz_Na2C|j4_{rJ zY!Sf{3NIZWFq4B71d7K>^9jcz^ocB25u~FvuDMR&Ol-UF_5!Sv5hp-j-Lm;?azxts z_gM!m*lWty{8d9H*f#5t?n%j{_2t?AwFSW!Dv~oB8=0dQ6FT@JvlQ1NZ4iei;O-HV z{_K5;DHS~nz>}ogNX7!;yOOaWlgR7U({#NH=yX_18;6!yQqWcume3djb#;m}y@@2fsPf|C0vBvSfWP)gu=&&Qr_5E>}_7 zQsThMH*oCD0sCHr8Enc`6vTarHF{y!lD(=6yIC4F!iGr?T}1)%m%LW! zmb8MYKV!%MF6r+)p~U97X3OADsdA@J9k!JxMKD7Q^)A+{S4S=nj*oZRpR$lv9_mC< zMD$Ia&+f*M*UfOyogb>3+wRp@v+nyRkUlrqCc;L)Y);QYG(1>7x=mW^ULg>=vS7W# zh7B@${OMyjBb!f$78_5R9k}^Gsm7n3JY<)h^BId*c(~`lN z-{w@@`I2FNgKw?2y8pX*8CHaBT>Wd%w64>pPp_f?(2T{PL}^lA0oFyu=sMd7(F;0| zQD7B3Xlq~lc~PDB-0;Atx0mSCK!EnmQS0{nFbzo~?bJH-o{w+c3LW#_^Kj%+JEMoE zwk@7CIXX7>NZhCQ?>CM0?HhZE3RFrQbIF`?T{pokeAc1Uc9Dty0jt%`u!=}3VBQ+F z;orG)CxF>gfHzc8S=iSb2p>?=b=R%!ZR~4c&y+Mmq#H7FkI{oq*y(`*#_xZt5*9T+ zz*MMdY!(UnccX9HWJ8}F52t74x9#NU<`9`hhe}T-d z4o`klc;kIPlO5e79t@!_p$lq0WK~ygoyZU`GC1Id)pUzHx?f&x@GetxL;TaM%Pgtz z7{{514=bToT-m9_yM~9%9h4C{?XL{*-oAZ%X6ZpszqCb?WXFbf*yI9xht{VY3!=Iz z$xM)g!Mi>GRGndEhUNc%)B}0V@jUSJh7k*f4`N~X&ou~}hCQ?EoSC#X=wCVyYq_LE z*4;XL`<+=Mb-$k4y)NKZHR~@sO2Q)|jn{{0e7dywTHkgaz@uDD=Zl)(qWydbyf_u` zuG@`uk<;%d+x#4#@M`1y0jf5pjX{_=mFBGw|3gBG?PSu>8i9X``?ZoHE>6awV!SK?5=f_?fj$KSQfWp1J8c_On;KzS9XKPCW$+RohAL zGt^f?a|tt>Mhu6+j2ZL5^yKTH*cbuhVCuJ@kAM6XuUEOZ|H`v+MSE=)j6LZ`9Y3rNM54OoFySVhL zi(g%&Jw2!*A5wEGZHFWWC0**~upqczR_@Gf%>WD2{O~JAj#*{1g9;vaVhk9^kAWTFSm75!!+LR_>E$<8UN_fBQcY4{B*Q;gF^c^j7WEL0drei z8Kombos=SElhthSH9U8CvXude5_346`2~ z85NW>zJLDN2o>GZ)kpGk!vVRDEQY)4m@}Ty!To|u=o^zJR%SK$%O@_`pnED`S%%v-4M`7h7}-rtiayR+QQ!bcCP2OvWxPb%Oo`th+aN*ACki zE3EW=qNAc*vaT3?&A%rDqH{Jx4tR05?dtkT%oF~^V!t1ArsuGkZ<7zs;uf89v?Fbu z{A=+(A1`Vy6UVNmzk0Agod+eq=Q5gs0Kai zVr3ImIJ(CbkDuzFkHDxlA zhHi;H@+11*(Xz}4y`4&zp(nJJX@(Yw6VF^Pa`DAQe>i>Vsaog_e?l6L zw11wWt5p!VWn%3~=kfAAwD}D}+N6p+6*I3g!p=?u4s(0xrLtN`F;XanY0Tl?w(`(3 zB|y|BSjE|wkVRKfq?_cGr14u+=ZH)m52OSo9z? z&@w%Arp2-o^)koKJsvph`Xu#eic^mvio0_mpho&YL)&f>||)nNUfe8izm8;t@c>g>S23-(s>yDL_0!#LhR+z; zt@_0SE57=k>ChxTE8FOA)#)s6y@^j#f8)NA-UOU1#XL1B5cn7f@qd>k?0JDU9p*%Wc19Kh!|7!d4cr4qs-J6sK zp=2sUR7hlOLI_c!R7fIIQsyMe92rX`QYv$187h>BjD<{*P=pj^NJTR4<5KTh@B4k< z9=`qC+aGJK-%3xzqqqH0?NWhupBC#UhpaZcT1+JQXF zjxLV9)OjYWfIXv0@}h9?O}sIEcO(l&-RDRBbS_rj=(M!$xaFfH+DhqW$}4osD93=U zSc)9(#r-@jf1X+gI+^xGm3uqt&Fnhg#<-rg5%)>6jc z6~>+0_2P+-x}Tk$w1MamZS!Ky$YoD2JB;5-XV{n_*=>Jn)6>Z_N(b!d+_-zON$s$p zUK=EP87#O{Wa!htonDz$*qs+>;j6KrH1m7%mNTl4)|(5o96ow*ZB7j%qhtZgz%dCX zqY#}sL#|$-R7piUy1j3cD>|QS+as8rOM*gGoYQCINwz!b=IqyqvvSF7s z=vvi)r!7zZ;gQB|wgBPn>_B$q`<@imiVmY?h-nm-q*!W2P zKQYfy4H-*ZLF3q7Ga2hN7BKsi_WCAOlbrR`fM~kk6(0s%@J(C#fs8N{NBVVf9`9J* z7}KQ&tKNBH=#zQTF`z%ePl+*oA|via_NZ2smiFk8sUTs|%KPi89zMAL^&GaFy*K*y zA25{V+mL|A*_1cpuukC2{%0v2HwqqaFdr|!JGdZl@T&gY2GHZV>32luZ3mLBpBBL2#jrhCOJ#yD4r8_@q2;KINqp;m9UWgZxSJn&pf)BGR0>$* z;?QT|-)%oUCI33`dnOxlG8M&drK-WjPB~79#t;=aitnJuV0|U|Yt<(EFSS6)h z4DKcG5M!4W`jC@$XkTYzyzS@g?9*5I@LLDc*>wvN4fEtUZSU2Di@#MquX*lz-}D!@ zWe3`X*mw`lzIwg7sKoe`>_shSS1#cU)!s}_H^aNi0k;P?>6rEg1%rkY7hdwGmS{?Z z1~LY@dN4_4zAOq-&40nWKKfr$xJgro0kt0nr?mqU=t0Z~TMCGw@P-XUFW4Xlk$id2 zsAgaKg=MNW@`j6DIb-gJn(XG?r0u|&y7c3#l)Yn;Q?a4Tm(+&uun|JZno1-6Sh$-=Ci5-uClEj_rd^4j-ay&qTg54c4Rn2(=6#L5=A!AE^#DKnGn z3VprlRb45}q7T;nu(j|nQJ?2`R>(U1P)o%+_a?R;v(K}Juew~W(}9GsfqOd+ALjH?bYy2` zWnE-6p`^)z)A8RWp^cMB#*sWmcr7%}WA736S+=fO)p7jHaQhzfP2+2g{O1>PZKDDM z#(8QK-&&w2SUH&3viQyXNy>rqQeJAzEiMNCZOZo&B|1`7S}*PEntE~9@00MTko(6@ z`(MLGH6Gz{9}SjOeVl0-xY<>C+ulHKaq3B*H-||X;iv}QH%5azOvl=ns=gn6(0kT1 zMrwt_`=S;{j?d2&L}4U5q~_+Is2ch!SNVzEaR{06SBA-dw|g`C&AU7B}elX^i{K97XjXBRhhz)$un#$<^rCrAD zRXSk*+<^PdHm2mD>-{Ak#EKUgqiu+jHA(j0erOUaFNfAGFY)86Rg^C`gF>h3uQ;); zJNlFNswUED`{b*9Wjv%QuovUxKJMZ05I&!+&XjaP;;Qp$sp{03T&Db45C1A>snvE# z-iEW6?cL16PHeKQU7-|xT2qLLO8sf1w6nzIIoAN+a1-O_yl8KYlZT!xE#Kq1diX8R zrcU;Gs%!3SQJ8mlvTe87bjXHew=MBw0-O!3jABbo6!K`nJbX-h6W2(7c#b*vdEF}2 z(hjN`t&HVrsr;BTvF1?Blo|7#*=e<{>n`nRy;FDZs9k}fZ&i+3*SFRcTRr6#RkUAE z?(p3G!E{a4(P`Sfi_EBq^fA2@M%E3ly1uIW|V` z_4~$DxId>q>}rNvE6r9LcedKuUo4g$1&gKw(*_164WqL^v;R@EWdH?x&YkWxKDQ+~nCDMpHjy^2KhnzjywV zj-xqSl#<0#H+xJTdAx6w+|t{&->5#PXrJl#8waK)Z}!B67A6XgXhY;RVdW8nP zEPcKl2F)L6v@};~yT(*d@fm*8e_hJ&&o6)ej@#tX0owSC1(FHesXyK5 zw@z&?v)uU3YvzN_8~-yqUS40$QSEs3T(U`e(~+8eBPXbbb@)oE3y%rAYM1eP^WT>Z z69_GusELj((qlN|pk4YSPhaj%|;7oogB0U zEEI=^1x^~7@k>Iu1NY{CcXNF_T_419xMa6!KUknV`Qi9;w>zDI$!*3ulE=4=QjfTB|SS+t^NAbZ5Y5)CFT;>=S$x3_u(xT|tq z#KSB5`CfNVE%TfK!Ccdsv;9x8f&BuX^#4e-^1p3J<8;-aB0wd!)FUw$uD1+;kfmkA z;iU%pkN8%bfujPJC&}0rL=T@Hj6QG#&KSB!N64J946S`9`bS*?29fbd!JHc9hhfSd zvmU-2&+&rCKkIs*Y`cuDbql$;INeHkJwW1QvYLlNTnSN6a0KYu0xv1J(VubJ-X57q z`aV4fcXNwQ`Se>OvIB&TweOzX8*!#x-?Ay8@E)J)&k5)BT(&j*aj!9c1TSTxqhM|j z7UpQ8ZnfQX$#RiNXa@u4uNO!)jg318MP4_0>3(Jf{R#o zJS|tn+HS@_sMU3qhDgA?O3|5yFCD07V_t zOaki>iagk#aX70eFAUcHIRYe8ILI9`7P>2v5IKm#N5B zYD4}8K>R?A=s{f#*&&$5D;Ss;E~X{YjZJ=gby2PBJv{KC(lZ-)63(O(*a znnDIfNPo-nM!+iz6w7$8R@uWK2v+0xE{llwA5r=T-UV-nkoAZUG7;>8@gbGP#6e;x zCnkt3(9(()a2T^OmxCB(4YF4`WF75@Gc}$z;g=n+xj7duM(kmJ{#1h^g(8NQm}5!b zo)Vuf^?+;y86_3SMae=!8C8su3#wx$Lc=HjKW-I=^|fP7$@`D96oRS&ZEVrk{B6Th zrYHX^?muNbYA`*iJg~i~(fxS;&8APiL^(J-d>FO!NRB8vad}% z@oxIG>vWWk>F5?svoG6Uv^~gtT*MNXb67;vEGp%t`^LNvTUSC%3R*six`Z7X=teEa z-dl?QLh0p8g0Nl3RdD+l=PG$=;nw;T0Lc;t-W@tK@#3! zT|LR5l4H1HV$7>JrzL~>sN{t7;>v#$+)dofEi9z;ergdyE7BjK6@qmI;R!Fz$o?~T zk|+dm{%gq8pB7qiP|Z&b5nOV604S6ihpvSsGD%3Mp}?jgdeGLf1uxGZ+_3?xpMl)9#HkBwU_xSAisXck_*0ZCCVRpbVB-uwobOLh+Ca@K(T4+auMV?}u#%~S(* zjiS&@9QS0-fh={03uYKy@eyDNf#gE$w&qPz-;iVtc36$>gOk!LvFI0Nf_x52B4@fQ zxEZ^~Z;DbRh5|aN<1J0w=ZQ$TGXl>7_Z&0hj)Fy7U|Mkp#B(3bYlw;TFM{~CA+t{-|SdW9@F!oqkb@i)pf1>Pza=W*Wt zhXQ#2F<01$wE)&Ctm;c*F*DOS4~$wz8vdiq9S{zpw9Yn1#`4Pr>_aKR?PJJTmcE#GH$!7|h|X zZ_KVBr6uXG*%8ZgkX_Q`xt~~96{X?(tDM zOT@bWG{ozYX2ET*f8uNhzkX26!J!Y@;qC=#4YzPQHi1^qX_9-FE^-z_o`$5w)3ft4tgG2MBohP=ZtT)pU(cB|< za8;<N_9Sc-%49$&AZxdovt5id%r}SCS(MX?#eK6gwv|pN7>7EnR9-4F zPcqfDvcL#Ii3Hb6b>dl)INR=5J%MMZSEqJZ31?QTlnuXUrzh^j=s7>>gA_!P8fOa5 zf{+ACxv_CTz)bAiFRF+!B7>835p|p3woAeZfdE}qPatU&7>YkHW?nX}+@gK-Mqt2E zE{!>Hb;Id80GvoBVxU{raN0q$_e?}UUwQGLEc&^wv4;8YlIi0Cf0I-5OO^diY8}F9 z`iBEg{@!zj-JG=i{4Tn%NaUn5o$cDT#?*)~9Vh(7iH@+Y23tNSUFveE>c@Dar1-~n z;sV(RWN$_(-1%eI^80fn{wDQ4=6CbAu>d}ZpI=h>_f^al+sh*O1pX!;{|{e#R~56? znFAZQ`%T@Ooi8G?`c#&8&wVDo?+&i1<_mo-GP@mV!pk*RKKS&_Uge#3xYVfkfb3(fuf zSdPb?8tl+|YDIJreO^zz3Eu;iQ&roACT#rLv$b|v>MM+lJSdX)y7j8l-&cdyKai%m zLw=95+9PgW%rUgron?N=D4E%o#ppI2c*U@F^9%jgtY2uUTX&T1I_uHgzH%gc&0mD! z!nIom=jp%;BKU2&TGzuL^YosJ&`|ev&zLB*F{Os)MX7V{^v?`?niW=@Klx?$gsbR> z@T^rcY>Sy@MjaW)FW6K?PfOiba=(C(Ayo@vyBz3H$5e zExsR3XjVj9t2+Jgkn2O4sTFpXn@L{p3O#SSzr)D>Q_)kr2d*}(VdEUWx1%<_l%mQ% zwQIR;lG8YN6!o`YnVd~W@SC}%67N$NBLD>$SjIsTAt3YZ!AVYER^5F5t z0q@aql=*3nTu7w=X3U=0sjxc|z^XyNJJFURumXY?T^WzWHXyIW9}W{u=-( zxxace+1&AV+}+z(H#>OmdAoXH75_1{VYPdP(WSjf&$Nu{R2uBItX`24k~cwTFw08Y zGOtwY(XgcYfzw)62R)x3b@ut5|Frk=k+WYH5BQB2pBZL;o_=Q}S9nyslnBBpG<^5C_^3l1+N-pm-4zYhVO;E`ZnP>O$`^xX!cD;tx*Uvm31r42 z@_9Zg>sh@bPacJf`$_@e5=lEmGTTGzet~8OwG$(m3ljfiQqL2Sxq^a`TnjP>DhOE< z)xXA_t;^;N^y&+XW?>*q6azT>ZgfB0Lp(cS#W;=!lJugrl@oSCmN~cBcksu}nqWcu z`o6v2ZF3CEryG>cglua)dq8djSSsIg?p^N#c@*Q66hZg;l4 zTWxi86}8&4n)`EF!Wye3Oh)Hls)_PmA33+GaPqeP@fY{}cMU&U-XD30&uW}0c^f`$ zaHZ}W+r!oQSMzMN^ji*DJuBJE_|iwtr=6W=YRP8aJa6A%NCHlqFsFBn)IYK5_?g7# z`9S`dom~Dhu{X1V)(W=sm&l$w`Gnq8`OK7R`G}iickZN$h(TRz^8E^0;QW19@xbam6I_3q*Zu`45#ooJUY(iXIPW_vYnQ++9w`w9B6{)4iL1_?e=@&sfE*)m+qXrR(C{ za(BJS;mnHo+cpt1W%&Y9yjA6DXLzOrhK!05b?1q$PHtkYsIK3}Cn+E!vy%AZLnadg zN^A=(w-^{BtkL|z?DBF)^r?5(GD!#foiAipJ?3;o?T3IRnKLlzCPYum zreq>s0ak&7D`tmJ<1<>cHRT^)CL9bphp-o7@C^v%x?tA>ksuC6-z6-t_PmbmY z3VmvJd%5d%^>rO;Xt|Iky9jlw%NO&$wp*d0YdqM5Um2wx@K3F%(A&;X{z9veC}L2l zw+w{7RZ$UO>GQQ;5_;jt%^xMNjSk*r8!D7Hs{g>&FH#rfr1;#>Q(fpq6}zNMSy1(N zrzm!ZmOE!3#^$Tke@MF!*J-u8DGbv`VL zfcl|FsR~0&g7{>Kn;{Ox3wLg+oHE?sxRkHQXSllgonA!Sf$g9AZJta#J@6coP&cEm z)#iLwZ}*u_H@1nqZk6vT9aY+`nz2{*q0y&7Ik68S+a~(Pe>z;~uI}%xddW58b2YDD z<8kk~C$&qqUF69*BGj<(=k$$39Ae6!$_#*o1hYp8HX-H#-;!_bS``D`v3#hANvMR6 z3hP)j&AbJ~gb{e)3K+7?lK+iC_zJ`>U4Ul<;?MLTac75XA<;1rsxd={!r9t~3hgjh zR{}Mt{7XLs{6fgEihVok)pz(uIdF^k7N7f1QyWgu0V_p8z7Qvx_kolZcu_B)y~8&# zfvvt0QbNH{T>~D~m%+Nku}R{a2>$d6?bOD}B$$0JMy{#Qp*%>i`Btx19EYNo+(V*m z!PIDDA#Zgx3osx79%80a0^*o2elr#rT?uyYs^%AOawG)>)sPRr4D?4_zy&C$76QE! z^PJUtHu}2$(_`z$+2fp+uX=Fyt|rkyKtG?_18Ud-JuB`5p@KvC@FSde#fr7I&Ttxq z_CX1*jX-(Wz~4;TGS+rVT6>G~^xZf;E8g~|H^c3D`(XS21h$XF^VS4#cQh$cPlq9_Gp?;iJeHLO=a1#$EfqU z_thlHt&2IS^iC^pZ(gyW(b|YJ9Oa(OZ`p^QaED@0zfE*B_Su*F)cWl00A59N$I_LUZ!%K zbGN3|n=4;;s$MyBb*tX9`ynh`0t*zMi&gkMy{+XqWv7?l8eg%+FxJ40At%>karMNe zn7%yD+EwxrDe~8Dy-pr6XGih8UBvoWR)5$0(Uk@4W|#xi;*zQOm*t(=_aJD-*4GVVDfyY=2@vjYV`B3bz3K5LCh)uM|J zss_~_z%s94(%R*i`by!Yz{>i(wg-YZjuokFXFfMQ1O2N?bl;vmL{^AD6qq??z#l>w zIY{~yb~appy96c0^A9uDC!T=m6-jpoRO@FG@TL7y;C$wcuTpF={v~2Igm-ApWn$%K z5%&s})}kv?PWRm0B4JDpV&R4}mjnxWIILdvAOx^J{jd~yiAbCrq^4fL#lLKCfk}kS z>1Bcf0+2z2A9+-cmsE}5m}WePc53zejDtseh2Wss?Uj>2nu1>OlSqUU38o7u)TAy~ zY(AgKm;BpGuLg&N`XSJWURzgV=0J`E$0sKLQ!~Bcd$?@gf6HW!G~Zl!t2FV@>36H- z={GzYT9x{sX6Sn^<)tsH;OXe&cH&V-PZcTJooDGO=EXsLuJ1JEj-rcPG3^vTrI{Ea znbTazWs>#I$){knmPO>$Ly4Ozxmn>sgY_Ousm+B)iW8le`1EeOw>hdNXVEur&8vre zKkbkur8m|t@f`&E>cb|gp6Qum*<3O`d1Xc_*t2ih{s`=GW^gY8bXgyv|{KDgkU^d`-$=y3o zHOl9vWFJb=hW)N@bTlsr#3QgKih)B3YlX&bWT3!YCDrY+@Ig{1y!lk2h3z?Q;1^}S z+X6aObk&6g>e6SwcDbBtIyK}hSREX*E>19MTFQL~GY50({gs<6iAMvH)bWE51`dQU zQeMCSyI>M;8gsPRYF@)D0fb~g)kSV+cU=)|R)b(zL>#h6J`Yp~nBu_2kz_wm#ZYPz zUt^;GPz2%%WcZi%=MP8+hXKIIo3^53XWs&X{EF^#9)*i3r+WxD0SHt11!_0jbzFjQ zpHsBuP0oEB{xXBX7cah&4OY_0%u95u)jau%7{sH@40-{73?;}b=mACp8R~nL#DvHX z(Wo!r#uITb3be2p{;$B-AZ_ufm6)EI(g=!tn$P)J@OofqO7<-5eW4~^KqbVNS1*t9 zGffox2y`5#wW$uB_cXYpRIN3FL!Y|PC<3S=xRL(Zop*19`xdfV*7OC@Z+}fAxhTDD zi{DFsQKZo%)xeT~*dwCAENg)n!NNFMoiqrTx=<8~S|ZDY^)tx~z_toai0Re28~fnv z3&ChO;G@b=6uw@9KPKKl*RQh@I|UTmgr<+bK*}8wnnavwfG`ruBZNv{A#USGHmiEV zx4DPc~*;?WED3y5D(CAn2N(ddD!0gPma;0FnUQO_KL z4L5KjloxG2z}4caDiZP}qfoS^mjib`*v2YZv(2(4BluKL1sRGk)6u{vnGEnqz$(?( zlF3V)9PkZD>{3O=0^)}SYYr;xGA$!a|F&jX9|~-={)Pq2wnsD;P|_>d z#5q)&-=oX!Ru)XQ1YoyRUc)zF+W*s~KJzaZ5qXa1s|4?m-?`X;L$D9rdk~4v0Bm_; zG8d)cVmTMReS6Dr!U-$ns6cHy4{Z6Uv-}?PELco)5M6`RAwWN+j`zC-`6qb)!t$ad{*7gl&~Hc)=CDky9O8|R*2ZFN5D_sd zm=%uJMr)}2xZi!}H|boZnJD{!ZK~#-vbTR9#`L2J+IS`WOBFo-}P?7|+V*Ovu0N(HYajZ6X%?09GUryrU5*qhkT_n4Ibro?w)K zWDt&pn9}3|aJI4h4>lZeog@lXRCE)cdw+j=0Oka?!I*H`?2L@d4AV{xxuf z=-YaG5)LqMh!@|rf$1oz&4_UpDb=767(=)-9JMGf)%a&H%pnQt)*S_$0CY%q3H(As zpKxh<|GNJ_E+JI|$-&(zTpPA+LxJaWfFBN((Jv&>A?(1=o}ysbO?g4dNfm?RjNO>@ z$y71+!X-HAu){|@nWwauJeMF2)PnHM z0s^AQSA*9CX@%!(35gvnMZDdxOEXZ=i&wynQr~tK_lwADb8~a?H0lp0+0DWdmzd7u zLyECQhk&nvrV4daDRNoy&|t7r3AMU7h>BD(8l^16U<1wC|7jeTPy7oAK z%(m}R0vs`q5=U4J5g4Os+i)m6H>m2K5Dz51=s@(cP3OZW5r2y&M}KLsst?%n&tAUN ze47jJ0~mwtba9a(f^X~_aqz$EKG6rWe2N0_!9bx-PCl5#hy)|6#)+mTZQ`tXqVet` z6nrGO8xfDs8XK=g!eQX3zQ)Yc)t?0}z+<(!4|@y)(%wQMuu*fBl$H|o4GL-KZcT_8 zcU@g299D?GKHw7KTnH1X?p-<*luhGs1|xrfL{T&7R*;fQ8Qago^_=jKVcYJHN+$oe_(}6@HNCwS{+rA+dJ(A`Gd@Z3YJhkSo2A zV$D#%W2RQa*AirE@)(K9Rae)uy?7_ZL&Lo0NRFYPMbFS25pzO{WEPXe zaANpD2=`!i^ski-4GksyY6+TFj3cpBZx)0c)lJ7s^tz)9=C z0a_AcO#?27J;ZXTW$&qiuyJNgM`#5JYUpf#^ClqPTC=sYqvPluo*n-x^j*3)L(92F zRCK9j&Po!firVyhnR!Gbj}6-Q3pi-7`X~zMJIuP+TcQyI3egtH45QuyAgF{w>J-vu zL4gltq1#amUUvDhas220G+oiq*H4~2F+&0}88DK(b=V`2lzPNNnI;MH$K7E2YrZuX z0)Sgjq`5+ot5DABS*(IX7kr)qhmo*SMjsw=-xJnbICTj32h)0ph41}fl1kCGXh*rC z-h%Y-ToNS@N$&fPACG5=L@F!{4zp5seTbMMV!~HfM;x_@_c*auI8})L1?`7~+qgYG z4JJ+T_(wGox14OGL!Cf&4Py29w%{BKhUW0RlRW7Tjy}oYBFT9q@tKI6Q8v0EHemqZ z){n+(;&j*n@k89EP#@9;tInf{T`nAQ5+7(n=8tY^b?tXt8U14e+ix=nO!U5SgB?`{ zUMDCP=TV5lRI#*7y=kBcFHOCDJD7iL9LBsDaHS8LgH@t?Zf84ap1hI^VcvY0Lor`D z!WJ(HmzJ!D4^=>kAe%JCwivi$7EezR02Fu346dy(0w>R^YiNiOW53YXa8V&Dd8{y1 zToy!$UHDya>!7I!<-UVgO#QC`U#z3LpS=5oF>L5b8XJj_2c+=Dx(B(HNXZ0jNP`7* zK|_EgDMZ|S7Jt?Q2>K=>sENLi*mr{Dg%_My)~jCPKpezEw173n+uyx?3+K^eHIE!9 zYQVg}QHb)4Sg&AjFhP+I zWl||x*YcAHfdL^oXaL*xxK8&r=NE&?)3NbR#EwH z3}^p3Ajx0-zaFaovq1UhEcluvoi?|}co#h}r$;CkM)M^QjMv}Y9crPUh|L<0CmWcN zrHvJ+Lk&Q$cdcEHETcHwO2VCLa0lmvoM^an95!m^?T%n~IaC(7{@e)o&kxl@w%K20 zTZDvwXMaR{jukrzNe2^4N%(p<4g+E%2pAnN)x>~Ip0>V8FH@3!qNL7Xu{m?K( zydbQJ1&}D@7+wTl2EtyWRa za00ef!5&LIPa#w{#`-lqa2lNkvPa4=p2&jT8E7ozEJdIWvE-qMDcf8?2+2x(gPVqP z3=rD|TouU}>TL@Jn&LS-*-2#k$W+GcGiI?F+1yib3xQ}C1L?mS9=-q*Bb=x4pmoVq zy12FZqXjjAy>Hfo2g>kLA%jkzQIau<3^K87If}64P~ITRoY)?cjtjOgC1?9(1^;?~ z0fs&|a!CX#poLAz0lULCAVq*x8>YX1|4ycNq{0NeS_-}l0S2gI5Hz)t_HsjROpm3h z2Ez=mM!FP`Ct0qWBX!CbpfFJ-4;OrA1T~I^0)7psR+f+(AuA*%*mi~}f{2R=UNqwu z*Vch?SOQ4t(rX2Pd8FV2SylK_GyL(s!l~U1ze%=Te1M(fB+5o>xSA0bB2wFk*9=PV z!uG}9Uc_h`=HFRxxG+O-^+kO6YJ3kIri8C3eCbB^X{Em|yN>DdRDy-NKqx2l@ZIb} z+!1YHk-p8;YCy3|%fol9NG>Ds0C(V~0UW^SS38aF%F*JJ!fJ zzBx(v+i>Yd?CHk1olBg~;+=QdNEkfOy`Nvb5aRgMvrIog@+2V2O$5K!6Qv`3N9dxV<#tMKo@b&zl&rHwb{5vvv7KZb>{F~TzUPDn8K{qit!yXzcl z7QYVWA~DHDHd~|(XjsGup>T8(=*b9d0WZF4z}yH;hN8u7*aUFNI6m+5pol*`EaTX) zTM@56D7lTTtd5v)5vNTAiDGk75(p)}Q@~+7go$kzEqD?Z>9{2>YVoPs!gxv&A(jC^ z#BiFy^BQy7v}UXx8Y*BmyznV*H;nv{aCjbXWw@*^TGsLw)O)azqP#E$A>JMaNz?kb z3k$cC!n`gDkH#CgW-Ru^-rO|oT<|75+OLO~xW6D%Cyi~zV&A>8Ij~myyf&)(qqc`Z z>+_G;{#L#-;tz>I{Zew4^Y`ki^TCNlB^2o`MqLl%okx39v?UdFrhE1ai{5aQ1|^oB zOl?s@@&U3!NkyE;?`*Wjc0wKwnePHVB0;y^KEc8FHXMZa@47wVgn70TryTUQ{>Vk* zla|(gE3j=_YoFLCnz}RB%9jVy;d5C#d6CENiRfG3Ng72YUEpEU3JMC6CYF%R(XVW{ zREk|i9M%BRHf<}wx-7xJxs028TKn^Ec(#BtRCg0a3d#?9_#UG(0~icTGA48(m&e~r zrf8>zI@Y{+vCK#o)?g67;_=gJO%%Nas*6^kcjjji-qEd|GjinBAt~F!*Q@TzyiV0w z0>=k}Shf*LD43|mi26Wx0}|Zv1iSF1IV_5gL!P1dO4dmWf7V0p{M(x2@UjCp60Av? zcC6^dB$K7Eu&~wvsKVW(q>IB#gIB&jl=1ZU68{Lyrns)I?ye28@+8o5kF=QD6^!_8 zm4w|oEBe3WLzz%g0+=KK72q#B$H-i~m00kBbyKiF+<^DDY!s7Vz~vy>8>nC@C+_Grm*~$230dVz~7AS`$`JDp`tj?g`>F z8=rp3T>oJ0b@dT2dQE4he&IbuJ*^GhqlZQ_EGTH-uDS!ie&I3&hzL4hw& z;C3m@{<_j}e`94u$)Es3YO{`{Oj;6^4y?vIopYL z4uYBkFtKU)gC-i;kaq>=BlmI^mU5R&qoSv8-O9n~_`CetBK zsRgj^9tSU!I3b39!4Rt~BnN%8k^3kse5X(%#7ZSo6rej2LvV2fFWjuCz~xNCCy7$r z5eWOmG*Z|g=_U4RV8pDQ7RQbix3(U3<7H-!VX?D8UkDSus~tv5!dr17!<^)v)cdk} z53C=wD=T=z+=#t99i29ZKakpDd`4+>MSy{eFA@EdTir6(Ha5rC(R70l0XlTO5rja zq@%#XC+=0)33ehMsy6tMJb0H#uoi=imZGPphnq|c`dyj!Da4Z~A(jE(J>@|GX8!=9 z>B;#=SUay@zb1`|ipquEBaO~`@dgAOT|rJk3=Lu3NcJE=37F_I1Lq-@W7sSi5z2}g z@tSq(=3}8Q|2YW5rpvqvm%)!mvvsj3qYc@8TSr^|00!~1A(=139L_hMGw*cyA}Z09 zo~eNoTGgSz0gT?Sv3kyeZ7 z>+M4%hZ{wuFRXR~cGm-RI8|oL36V;$s+nV?N}T7wHDE=6bO@S#bHy;0X}?@`&ncV- zqzC~YL!-kEs4iHmh(TR~TU$MtCdP^U3*@(*~#SigO=4BrhydRn&a#1anG01bu4lem`-kCAo-frnJs z%oP_foy$VlVSLa@+N%%W_b1NO90B4~n=7U65}h!=^BSyQJFi>D39V zvFUue9b)m!JbAxv^S(Po3RNg{)O}P3kC>vsuRV_yOhb_~5YAj9f)h|*kQaY1?L$UN2j7kthtlizrzpB$ zNw`}?P+VNgZR3TMGeQUd3)JJ*W}6`3B*sJ~f+z@dfi!_CreB}cSG?{I1%nr<7jgJ@ z#4=6ztvt47%=Nl`!bd@eX2A1J3ANswYXo@!&LDpvjd3{kNk0p~%JC!bYj&99`e17* z0a!BTwZAv1v2RH^;QE+<;61_14IX$BDx0*Y7}XI$BgRRs$px z5vWO81gYbF&vHrrIV{w+?gC>V9%YWqWd47|NY==6oCQ>ZzLq4gczb(mWL#O_OIv^V zc)Eu2APjUG&ppo?^SP{fPdNhEmeOy|(VQ=Lq{Y54&1C{1yGR}?URh!?#UWuzhhb^; zc8hq#%q|Zs>&pkghJGj(hdaP`5=KbE+tF~JlD652Cfgs?i3t`vg z8xGpaNSjSgV;kIw(H*JLDMf(1U$KSqOG`JM-~#t3R?0k5g&$~|@9Kp*89104dO+`e zV2>;xqGCr%OXiQKI3S6cmB79zh7@gr&;^}#BrRD(btyP{cz^odJ=sE)G6AB?6$Pz2 z7?lA@7{-z(St9_AA?ofha@;=iwUb~pH`2CermZ)ZuSNC*Op{nTt%^41Fq)&_B5rJ- zKKTu&72DZixU_>yCZSzXbw4o?Q_|DOkvdk2R z)2F)-x%h1i-=`AB;9A<+bQHveVOt0NvbVxbAP!E%sQZ_h5&eVw{_Piof{@3$*W+ej zz^<~8KhqkaNOck)0n-I8Ya5&WdA5eA&tu*?;X~D6YK&@wh5{6=iuHmQj~?YC;cOhq zwSI&rBb_`L^9Zx& zSNXanu{nkE7#oD#r+btFFgXzRMW&?j%gyyO*vQVSWVoN}>D0G9q;d6LO=1vGF*LI@ zKfo@yW_}6&>etyaeYk;=&>(quxqhtK3Jh;=WYSvoKaA_xA{tR^4vtREl%NCPY z^E`OW+&u8~tD}z{=3_G^FSw7gipmt@=7WFn=KI#6Tc4Y=9hAl-!M`qlHOTU(|NY14 zYcAr8V(P;Mf)+M2Rg4EXImpga@n>q&u%`r0n0L~Mc)W)9xBh@^P5&*{{XYSsV|)v} zi^b(;F?i>oi?ek6Ib?_>CCA1az%e{%U%5{43wTT%KFc`N>jl}&guEA>7qy0CCbO;23_2Rjd5(I)20)Zf?qauSn zwr0y$;1{ydOBpH1J?vj*OMWcagJ%C+(-8u}-husxdpr0p9_&PMdhzNR$_g?m8W&~K z-1=*hLfb7jg6_T6WD}6Bppl*olH%rT;DiZP|3V_rD(!xiUNU9L0(9StGLbX zF1WhkPh6q=KKNd1?ZH5e_Jr$Awm%Y*6!dqYUl_s9RU~mJ^j)|c(z7yx+nG&N9>tGV zlHI{7a33R`p2H*l;I*mpb~W?yEuJx%c~$uiG2qfCJ!Hc$Kb7C{*LRcig{lKw5$#qT zINX0;+)NVmc^rIlQh&crc=HE8ZJ%nH#6tf)NCXob@82zn|BqXoNSBxYVj(LO}^8W7up`^6`V~d(Q#qXcb3f?N3 zkn~29cdZ1V@H_nXEPP&@P?v+*pv+9F$p4;<5hI>VyH@84I5;;565u1GRaTQUET@s!z-%Fequr2hj9F3D7X^^`vF~A1d8s< z<&U}!=SY%7XR+pn8K}~jOLX6b!S`$j?l@IkFMTq4en}rF^n*OPwt@i8=rx3kjQ?MQ z#1Czxv@BAVEQnLP&n_nI?b9z3BI*Y<7z(|wbf`7K~-Nrl7fQ|==$e+q~N|> zf9w*dG5=YvV;OKd*HwP&MCMRYGZaa^o?@Ws4%sFgRqP2CLmicXB8hcXKlpd#yMkkj z!c1j%`dmk_qHfuHbmNoVXYXu{~Z7wa23D954E4fXXx;o(axhe(-QdB}MCV1V6u_8w%jJ-xWc*wb`TQK|9rA zGfil3MUM7g<+{9-w+lvtYYue#6 z$?|k8QjbZ<+4iG?G7EU>KEY;lecuO)???3F5g#uhZroY-Nk1GP%*kw^Cn@DZ(CIzO z;>)!h){ciF?{W7>zqGL@>kFnrA8}E52Q3UR9_9yEo7qtLm}_r!nJ*e^>JT2agVdtz zMR1`zeyV5HvQUirYdtSUE_FKbcrrM?M|=wS?e6ToMX$zfjIvso`n}UVtLCpiOEnJD z^`ZiQ(H&0kYcy&uGIH&3W}z%Skr=KJ-mzw_|AA?L_YtAq#PyPwtIP?PuAcMd@_Zsv z&tvg9ZZso_q$aEh8h_;4kW@Q=n*}si{m^k*3j;wAA3EIGQ5w@r<~I-^z=L-Ej>WK3 zdyl9sR7RX$1m;;O9|SwgmfMLUyvj#BuPKvzy*$7OJo$=lXUUm@tk%c%BxW%aEHSuJ zzO&S|v9P~wXgjX)8CUjF6VXu4rF|MB1iz=Hldh|R7sK$rl_Xgnt=vu!soo^Cg4$0@ zs^5f}%(Du5^P77FXB%?;3-ft`QdwnJ#ukRE5@PCgRo3*pxYE?j1HRe}@!u`FP%mF~ zzNih!$5oQ?#Mlkf=xfXmm#06W-DXCjx}ONt%ac+OB@3GhMEw+B5JF!WUO?Yc!Id@h z!nuqhvnZ99=svzRymQ0(4(<$JVFt6D9IE7IZVJ^pF?CBboJ2Hf)|#i-0zKj9;XA~g znFpIF&SJm4)jKb?rRyV!#Nx@`kM!CF-kqJX56vJ>ZP73iG6Wi6IeE`fnRIhQoh%vU z(UjC4f2Z6F_TG6T76S)KW_^$9o5yCoBj4`MNCeiZR-3cad>(#hyURX}N#f^@tJnZj z5OWR`LX|>R-j9yo6gblBDdwsBl+Ps%$ppz?4*^)-*v+0j`C!O?!epr1TdGu)O6rH- z{p7Lw(7%VfwpWNg?C2`>FExKjjJ+X&IZCsg)&V>>;Mh$h_xpo_^9tDtGH3?8#YuQcLrTZ&cBK$UQzjESm;;r#( zRQ;aOagb-fD}XW8pu|_3*4q4^(CEvXpm5fCF)J;m<~7+MPn2apZLTQiVo_=xR-Zu` zbhkgGM{8cRI-MA_%Aqedz;l)0S&B%fP#k=0A>!1cP3_UIh`2C|JhXNRf%N>6+IlM5 z-nv&k<+Cj+Q)SusNMcNcqBJ&GE2GS*5EZO!_cSyVR~*_Ekf}iiQ_t+BUgey<4sN5`w_;7xC zDD}wZhpz~zBC1v~hQ8{g+K3mAC*DtAVi|-KNV%4~ZXK`>F72!8zuJ&;_1qT;=?oDG zGIFIZ(0?c;4B-8^_gZ#AG*#PWa9lxVu?^f>L@3f>G8#A^zV$GR^gs0oPx4>#c!kZO1oR>5ER2v zeKMJ|6BjYoc2W#)x0mS-rfBSJ{su1DjVl9;fku^*(4fXA=WcK&Iph-2$o@>_(dlt^ z{-p{`<4?@~ zi;om83#Fo{D)Eimtjz~KhLAl#6!#;hM4zz;So0IqVEP)QGR|9sUy6sM$c+}%=!~iI zer2RODn!TV^aDP2QZ5M4)I{3Uh*meY*&3N?c>oSt)@OwElYE-DP z^@)K;Ey;dC9h2nKC3ngLn?mH^hy+=@i??(0R;ct4xDDC;o6Zy-+ z4n9(HQL+T;Wap*%oe{)OdL7$^Ay_hv6hU0Sl)XU`cU7&?CeJVPkjKh)g!oe3R=+S!X{wQnt=uxR7T(lnP?TIu zt_YnyA?Z74`JFuwrFlo?#G8MmyCM6ODu2+5NY&@3b^!E7Y>H_rgWzs3|H)sO`!xa{ zPYG{l`tG`Wn(sFnKA&MkNP!@icqgSYTs=b1BxO`r?HMC}Q_)ukXW;fe*NDSbWAi2K zt*!mKwmZb#w9?=;^^p_3Xk>fDQ!D=ju&9o&%>Rog!zxRSq=BXIb z`Go2m&Ir(s;q4VPRpu!3zL#KRX#wrZQy^zIVvzmG%u0NrB#6bUahVOAxM7bZ$LI+o z_wt2zCi-zvGrXBQ%}+9ImdC^%B5F$bBHi+P$VqvDE}9|&MI}2fzL$WrgpR{f*v=0aB!qwum?O7~Le?v%z%T zhf$tn_F=N*$YiRk+D8+GPhk^VXqZjms+rUC-*ofCmIXj;ta^@`H5+DQ9e-BSF+MLeOBMv>%6q9#hF4%~tn5)=ub-hv06nQw?tuIW>kAR{Idp-QQtDkqWe>B^=7; z^rJ_C_O(KM5hHr85FP*^Vxk4cr&xKa;viyd$#LYe>q#4y1RKOh%D~ZPVu2}QIWdnIJ1*tdkDM$?~z{$f#u|cv#72w8C z!D!TblC_rr?|l5cK(Je~LI6OU#XWV)fa5^8Xc$D?7qePK`nefz!bmTOpO48Q)OSEH zAqB10ePET^E5};VC_{m8I--){4iXG zFuq8eKt89?bt!|*wYRo#y*0jn^=3j0D&FPFJ{Oi!&0Y@?BTzwZKSpVqcmK#7OYj^R zsE{56_y)kf&CF;d_UwM4Z)&<-rO905#0%Y>7#;M2gtZ zjV_)8ufr)9?o;%9XCKrf(jjYx8(d)1yl-dq`*M4f$~?>xr#L& zZs6wm{;3>ifkqmmrzyhjoV@znesUyQVy^zv|JV_dx(7Ap`IB>9pC6DublHq+G!UGP z9N{70miMf+`de26al%lq5}}qd%5(ij^@9r|J<k7Y?=30XIpqQMDv%MMtw&nI}Bg zs~(*Dcq8WBrlH0$^QW{gULF0mi6ECj-$UFbN<5=ZOw45$ScJ=$G885*5cB>u_gTs+w;- z#SfFd9~Shjl=KHc{>O|G@rJcO6>9p&2|#}sKOG9kaSED*gA0w!V$WBCYQX(83cnVT z!%j-v4?Iojimr@^R;3AJC<$gBTwbj1Mjl%wFSp}I0`#Kq86mO$BBbI)K|Tvi;Bb5A zc_dnc`px`$c{m1Oi8-o{}Hgq=jcKDv$v&yeU)91X#=6O-2&PE3r0K_USch4z6YAn>z7ipZXx~1f|zguBd z_<#k6-`>EBp%hs$dz=U?cRk)c(U}itsEeT2g`CX~%xa1Uru2yMFomR(Usw8hYr@qu ztkpgB8SeL3bXRq`6LI*6bS4`M-}nPVICkxFp-pKa?zl8(HEvnpjlJ(FT?8oO_kwX`183%nbj{}6^?(T6I|W6mnq}wbP^+0RLE9TKpFuo=&FO9 zTIvEhWa2UI2+lt0<0`}<<4X{x6+tvmBg$lKcKCOCL@O~!A*!#svbK^S96X4H7m5XC zp&>r@G63>I;tw_rs8vuF2EQnBhx_k^7z4pfjFn50GihZ$7HP&}ZQ}1mq@igPOJw0I zZp(j~y-2Q9QVnJ?j}4(7TZ4;@tW4DUm82*mu&#J zH?@5K?nh|<_#Z(o0{R~gD)oQaRg?*O@Vay;)~>^5P5frw)Ytc1E{PqET)@d7gz66`7KmC~ zHawU@!LE26N{R}?$EN@7-Sxr6dE0$Ny0GVJ5Rsmkp&>Pe=Pt*GhrHMaE$0;3IXMrV zZtssX?hQHv(JaRDB8CN?z5d;He~a@nfu-X~-MqiP&m~fo`Eamw&fyFXEiJ-R^Pz5a zeeWK5@jqj2 zt@E37;6gnP^a_k5h2~~_hXW7eu^tcE|jN^v52jJKyfzexn)qM#-5bZQ*Zgtt6W3BJU>in^`yN95)NBvf8SQs&i~!A`dLiuey^9 z>?3cYR3A61;Uguidm3mp;tegBXKCm{*Ce1$R@#Asn+P@=Qe|t6@&s8I{dO%0L3c%h zCjkN;Ppar%vgI9)i2 z>&=7GwmXmJvso*}q6Oz~LqlJ4qC~=?qoFLCm53ide#AP1o31ifxVhRXQ!QPpv!h(} zz4t!Zn^C`=DuScG8HNMK*eRWTyCSyO~xcd5W+M zH}uFBl+2c2vQjNiQ3?Yu+D-&UKff`45o5qbic3a@nI_+hYzsMZa6G(C<_@*{fv8nd z>m!ZL9OnNce=>Xbba3(8x83qvH!^_NXq_Z*q*?_L{i)n0wWlb8lq`?6^GL*i3$BbWFd;C9jfU~FgQckcScV+! zI$O8(cy*z7h7gO{>c@mnv;t))z46z2qk6NSg+nPQd^APanME@O?nTXq$cnXU2C8ln z^?lxqz}dqhMxp4sc+~ITzuRmSqu|XsKmDF}9HxHq1dE6hTOnsOQznWciPJP}%KT#+ zf~pZ-cj;AhS}tydR*6Q15z=ItK2CTKyf}2E#!CGIF$esYFJG)Pk#qI@yG-xvW(3)b z39~E-x3J6k=i&Irn)jf_5>SpN-}dzZcY6HAV~z*%LP)R68ZJ=IJC|U(Rp`en=_OqJ z5R9O6ub*g`MS|V5zs3xqDg=&%gtv~L)eSUIN5wv1y+tu6fW-K(`j+@JQ5Vlb^1K7C zaZJ}mUCvG!tW@=9R{d5ezcs^m8A%XR^+#jOZ!8NKimi-9;kJ3hBUYR_vdIRS;bXet zIJoriwi9VtM+M>aMu(xg+GE*_%h6*k zPhE7QHVQOVBfeaCJakG8xa(jih~MGoQ&bY?C*zM>bM`y)PNjdB7f}&u8|pRU0fUCK zM_DV@rNa=+NP(jLHRtpE@CDOX7o_408HJow%nw;$ zWv9LPS8(+*ke8Y@*Zr?%KrseD`Zg$%6q_%E8b=rFS;FhHZD|EcryC!_Nj4lkMfRGL zPn68?UDnyQUxzmW(~g(a?CmT(O}i0f(2)p~$X=jwY`_<+7*n|#gO6kBXjnLHP2eh`C`}MS}z=AO2-lF?sC}oDs3kx3*7yO5GrP7 z4D67c+2NOWB3AKpHCBlV_HF}lk3-+T?|3>av{q&J;su&?B-#54<6h1Jvdk+>g@6vf zDA7CO5FE;2D%8-($gc=ELViVMt&Kz5w(P{2Q#-g19{ezvJO9SkCZ(rG@oO+8q=509 ztuQt=cC^9K9EL&cT22WQSTtAT&5Xdgh(&#bwCZg4R3{_J1j1h~XcuFu^kUy?j4FA-L=5#D>q?C@-k_w&>?8A1Nf z2|J12rRB>?G3c|v`+PPobq4oI?Y7cemV(Mry)dmDh;%0ynu@)oBBA?3lu9wo63`9(~osv2z zKEBGD2-wi5x2Kj*69@v<@PH=;D^c(@e#_nMg3taR>6cr(!GVrmJ~gyk$~CzjtT8Mc zF{_uFtd0Kw@N=|S^C^o)Ipp})V8DWHFGvGUn(5)$j7c2E$VSG-99EMoCMG6`)k}3t zKH;BVCK~--?jYu|CjM?#wX}y##s>-SS*`K{h9+m_0Jq&J93B?NZu}L~X?GeIh6I0n ze#v3>ofsg7T=|WB&1#F+@!q*v)75YAg0acB(J&M75aFUkt{7PeG0yhprVO^N*ol9i zj49+O=gVs}yK#uz{FcOg$b;#)G1Rla&~$$`RWcONRqIUf2Ia)n$*-}&RYtKVNWxd4 zr#SA+nZc4pS+S^@o?h8{sL|6can>NYtz}dMliagXQE30=@J>O4By_y@TAQ?I#ozm7 z4`s~=$CUVpnBSV#`Zrpu5Spnh79v$t=Se>(1zmyb$(}00O`~YWue4| zspg>n<*WiHCnvkljT@KMB<4ZuwQY$sq|RoJ@OWdm7q}j~^R@~fS>(6v!G+8F3?3U& zm;L#zOCns{4p&teVvhLGVIU?e8#Yz>^QQ?uAz|fP%VYQPmG;pbnYUBAuHsO{0u?5( zRJ)bwn+=*(W^RW)M7TsmM2dd(LhM2&eb5i2+ye|rYMS}-X}{;}S`l_@Z5OoiX_X&_ zH`)%HeoM$w$PgiBH}GSqpKo-wlFJYw4<~$5Sk<8f=mb`)&GpGvfK^$`q^}n(BkJhr zDAu<7Wd{nSLAxK^=2BbR#;o)9INI9Rh^{aK<~!JJ8k6OV`ssdGwlneLfTuAEH9of?T9vXDS+QhGD+ zA=`_9CeD{Plggmry7JVvVe{yl-U8$}q{%BmrkNeGZPw~*0+5i$T^XSDpaH3)ZiwWW zUkQz3;f`jHb4vx14#WHxid2+aeRZ2C!tyeO0Pk;)8ClW%wbs$_cWkX+uZ0>zK%dId zjXnSi%5OONh8r-gacxa2>Xo*{o81MfQZBhzw{z-CinPypkU& zu`#@V%BZ7yJsASBvC!GH@$=`;+kt1XZ8AUs%YwZhA3| z1_AtU;#6+fg+2KK(IH#Xg|R~J2f)*(0m!zk&ssem zYxTY!DO4>YJ?AEkND}oGsjRBX8nv*Wv#DE|)V4dF88YEC9L;{l=lBcLL{mD7LiqQH zB#ygJgNsjCXaEY1(cWwg%)=q)3Tqo6dN(%<1qKEhOqUzNP0@mU0bKXpgk{jT_09#? zrHB+;2j5Z(VGk}?7>Q?A&(6(7JYV!=o2{`5LBb%+EH1{}o2%=sf4e4hO2%tT2Fq-) z{9#r_VrXcX#P^ow!Gi~x6PAa-UM}Pi_Z%8nckXZr34`zN&fAi>tq9EqlY@x#y>Ops zU-?9qk`docwcV*2H?(-1c7Ba`I9g|?KU~?_DKWk0k}=E$z7>Oz#jnG_@6n6;+q1dR zd<9BKyX%s7NW?>42`FN2Zmz-2#o>qrmnv&nsVF`Xk?~@)J0l~bpITAR@0BjVM2uK; z>dU`++)TjR3xOfyx`6}znUiU}E$*bfc?S;f=)jtNKjL@|B`9UVP7B_$lB zMt#MZcV}O=(}m+_ta)~4YcTNW<$gb2OSURKY;-LWJOeO+!~4p~>tZFOT!ZDgoLu+M z{@ARstsA|%DM^^@z%euR8M0LuhiiUEY%D(8~r7q07lNtZ=fNA}%{AKhL zY=tm?hsiP=P?IJG{Y$kOkq9oXh3MjC!9LF!GC0?T@K`AK4t4d6jvtZ{N;;#aPd@yt za1Q~UJI9`F*SVnuII0oS*tG=q+K1Ec6Y`y<7QT-y|ImQ7oRqy?Ef0~^Y3!_ShJdRn00zo z<ic3QB{_=Q(mW5^4|J&A&&lrpD z8{|TF7i$;4yKp!>PBzScuK_1WGb&`oGqSTGTgG23@%s%~3fM|}9u z{gSOk<@mOY5oUDQ5IM7LL`T@k#CsNp^Syo+wMnI1as>+!yq-00Q9 zc8y!~)^oLkcw{1;XDl?dv@Jf*JbryA>HhjvjUU%Dg~uj3Jw0-<#dAudYA_+LV3P~zD3SmdN=_GEm(LK10Qqn8wDpS~lDL?h zoLqbF-C-{!hv%8KMU57VRyFF9*D=<9+r3ZQR7qZC0-Jv1+qJK{G3Ht=*bB-gpTqi6 zycUC7Q>N=u{XrJUSjTnTFW}_U)Xu`Qm73W}0g}97Y8Bc?yTzG;rR6>(Ggxa5;5Ns4 zS?KK7eV0=cQJLCWU3@zXkp1xoh0|fL3&eTzv$--OKda2SZu=`7loFWo@rOtF&v0|3BVfDV zWl4ri@`=&&@)98jz};RqG3cuoP1N7tT~`2q^V=RR(-$4iRm{rDvKY-q0y7pmaKAp? zK>;n=Ac(7#Xsv`Xm;OfVX>>h^tghx#u-AxrH2=1jJe~h#Y0H@wAf3p-;(7?wbyidl z^nGs$fGPDH4~y#Wd2R!DG(P&-uXivQf=Tkj0KRJ-m~MvqFA|tG@c8lLX+U9MZmES7 z{A~f`&*@h0CiCp-674z|uebm@rSKdvCNgn^w1OpKntTWfg&SmNvZQOdBLKuuA<;BS z4kJVzR-($v${ScZNE$SYOGxl2b+)%>N5&=##Syv4{OZ9KxNM)fHa>7)Qmy!2!3P*&6NHmUmr~#pkpMUG%xOvEU-T0XpgFr4cGb&-+w0 zLw{%1nrHZOB2NM7YPUjz1@7|@=Fe#a6zr33sBQD1v={JTA{{m0wQMs_VOTca*Nn~l z8t1g3lF2*gv#L1e7k6ZZLGBl!NfS6%9ceeEQ+kR1N%39Y_x6bDy0yfGlAGNr2B6#d z=R9Xp;$HDRavKfB+ohWExHESSfXtgWkAHpt6fjw$ZM-{O0n(+9IT;Tz zCb5VYpY#Xf*J~WwHI_jDOdEj+oQ^{X1g#AQmOos?Z`7i@GYF%?e3(LEnGVR0F!4O$ ziKM0`Nu%>lFc@=%^&O=1A%+$)C8e09Jtbx37Lkp>pdj)5v8?)f;?dEG`)zJP!8wMW zXTZsX#bc7UM@zJMVR^gn`}aV`u;&U2Av$l^71!e|Jz5rbfQ6M0`-L4xO;nn`G%+=G zhvx$43@2g>p0MlzdFrPtqP{7xaJS8@5FxxNIyySTi{C3)mBT97P&SS7_Np5Y;X7%g zgp_uUJWjWR0Mc5nowJqn@ez6S;^^|z%}kZ~$N2al@Jrpx#km1I#qe%?T&3D}ffUS7 z5@0_2u~)p*A|kB=>l3FTySvsIY*Jd)7PyMSq+w#SPeJa4;mwbl&8nF zui4nDC)5BmT-@V~ylKG|+GRrR&;l(fRKe<^nxCM#hY!n(55=w^!g(!@_NIgHcZddt^u@tQ@R5647uZsHnCbtW}} zoy~DWZ&D{MFi;CQgqxiCj+pT?58yPHKgnG{3#*cKiPO-&Hxr7NX@FfJTEh^bszaeSwOQ!z9=)K^1X#u5WTIQY6KV?*et@!MAp-rLs@k#(mA*Qxnnc(VM1_3Z`gUPr~Ptd zqC9*Y{wU&NrHpNL!{ogzNt9xZip~R1zL2hWAi6<@MXp)&d~coGj#YK!w1mW=%>%_k ziuBP2BbN={w?6yjbB92`HS7+>z1R@FM-zv#gIrqHpFB)?sD4>Pg{n7~fFeA!)Y{yN z9fANVI?`o&EkZfG2r$;U0#pG>EiF=dh4guYMP+6mA9qra5@7JANUfx^!EwX>_H-&o zzs(2kKwlH+o`C0LA|n9ixlK~z=|ciw&DU53k*&H9zzjRBd5q8Z7u-%K)Hy6hsOCYa z1c1Z73rqD3004PTWRJP>kV-+Av z>kcab7sA#MUce>Okr_!z?UjDa1YJcb1}3QnZeUfZFuHo)IWL1!ByK_(^O4Oq$`#J+ zmzd71y=25!bM>SMT|^U*j~=cfi_)^-X^U|yXiGUw^oD>MWXUB53(;SxLj86OWpE9apu$dLXYO3^^oE+xYNOEmcDy5AnV#Nx zONT)_fNyV?+u?FQe|A4B+F!)_xmZ>#vOjfYz>|WITwXW9WFB zO?>Z(S6V%KFHH1T}t&CM_$W37va-}o4q+Nk%? zi37vttkmw|_+=OJX4DQmdKQ-5-t2`VcU-H!eZC;P^}0G@8FAQy!(9be`NjsPOH=I)-Rem5BOu&LwL!3TM zSDJ9!viUiifv$`M!G445l=B`7lvXQbqw}4NFk_!~9|XAUKZ1 z(h@^vYi+{G1zjKzzB2j}`N1rD|B@7%{Q1NJdHu5wM)KOa2oOZzGqN z4Ki@EYYzA7mO9NG74+IX&usxBccae%+O=*8Lf87h*h?;afCMjA!`WEtOTxm!%54`K zNymauc4zRGywAy{UZ3Rp@U`8;PE}ggwbEW``VC4%gpx8C3^>yiUaAmKWU{lf3t@H_ z8Wkl2E~xx%j;H;n8SAe=>kt+%+1?(F9v=rG&JSGvOBfy%xeWd${JS0DVm(p6++|Oz zUWXMhscc}CF!3Z9hjRZ?=}V(0Z?va={#b{#%e`y^%qV|@NoK!F;UUtM|1;hVNJo$c z0bf`K7kgH{08oFq)0WaOTOeDlsCug{*4|>larJ}3K>NVq{iq*X`*tru&vta|6_d&! z4Ylb*IGJZCSiV;Y0xv6;=VODKix$tCSg!)Efi-x>Ux$74IREKXW>a{M?3%`|qB-1G zw=XsQQ%~d@#udxaeTbs_g1P2lj{Zd(>mcpu$;s1+$tOo8;^milc&}P ztl&m(p*SV&_B~>eDoU0NO8bqKcmjN3pz!B;Vem0Y(;|qC*z7^a=k5zky3!~wbkkLC z-~*ovcbcDvtbYrLb{v3ciXYaODIR4|Qdy`*A2Y#U0^}Pg>T1=4wNykmPQJP#5Q76@ z{B%YBfz8ejnjI9UiCax*9DwW9cHds(eu$k!bntsj6BL%3reY)iP#Qv6BffwCPRfwV zJeBwAAz*xy2aOErZtyU<7et0-CxN#>t;+&%KxFP!DjzNe5o=1Wf^6R=Gn9 zh)Or8Bly1=%QFPZt|~I=FvB-a&Zkc+K#NLTe7rmTgJM~j6Ow?22*#PXLZx=T+ca9umBnwJKWyLRUA?21P@&LqW=tOBW)6rJp+61#nuDkrwTRmZc_F zyXK<-=K76vPj;X|3$*F)NV>T20HN_F(x z-~AXsmnjWg&k^RH%opO*IXfea6WXGnFWp%{KTP@RUi%T#>}RzbTG&jQI&ZWA6GWqx zKd`fjiNfm+zV>sKZ!jnuawn+z4E@4!&D#8^%59sG-HhGP9adamZZ%(fbg42mlt(A1 zD0KERqZVO4rwYrp73VsS$!uB$4OZlo=vDIy|22I;Uai37180;=5!59bv>xjXf!qqG z;a>NkW;nhOI(4eir{FtFm+bI62VK(X3S)G*Z`(e+V`3dYs40|nCx>f)%Z`F{yA+VB z{mWIGgBtU#=!c_z_g(P#(@kr5i^BuY(L!jG$D7140mxkKH;y?-+)(m)}U7HD%-N|c)M~@7EK?9lY+Vrgr!836!t>mfD5tyibx;+8Y@d8@p zi3)|D^a53#b@SNC$jB%SQHCwVvt!B807wm+<6rPHgt5f*Y*}0NtB?<0C5shJOoHcC z+tY|>x6`Om8%u=c?C#OQ1HjQ(gs30IM@}u?jT{PRwk^0$3g7Oji}5stbb@9`E1}4< zZIeDa?kFBr?(m~H60Uk$h}c-a=h+KYYg8J&Ch}V6eVv-ZixIyeRq*iE<0_`@vqNrM z<;zsn!EE^1MQ@ryzuFQnk%=0|zMQEJ@Cf3Mj1I-?No7=uw^)d;0|D2cXEQd`tHt|u z?%L`8h*I_wIcQMuzp8r%8dpa2*2y0c(4H)wj@8THqlk_WRQBtJEE}p{3KLOlXs?|r z@Nr>%Rp;%z8l*li(5XRnPSHRFM;R)6h!?9!Q%w~*ki&`1@#CVQYB{PoYIPeVXGsO&*Kw)o;YTaayy_}sf z(!yi^6(cEVcanic6P=`J0j&(?AJ5U>KJ)kYhfnvb_wuyVK@@M#6c1px7|E1&Y=y-; z@AHKau5u@$zk2~dhXe&688GSql+(*Pp(T*M3{N&kiK^{jwd~WD3Xrfws7Q}min*#E zW4fj+aJrvQ7hBaUC(uDyE(!D#2^IJM~r|-oFwd7y97g`;G|RvrB7gGb{dDZ z&6c*h`FR~!9}{3ztce(a#t7QBoKCh)@Tc=TBEuwom~tg_)JNOA>2;=a#@gR!NON_K zhK1F#?5)d6Lj#Asyf?fWH@5j`wCoC7q>z$iGZF%NLD+aWm{c4ABJ zus|4$E~r=z?P#kD3`B%U%f35~){D*jgseJ!;e+PGON5|p6#qA+H;B=K2hDg)@q(wp zV_QI25Sjb+^QS>yGOhtWps2bvG@z35tC8!=z?-aL*ZpxW+Ix@ue4>hMpp+toYEezeJdJeUm z$9fJU{qeUV#I6lA&>$RD8Y~0D!%=)u3%2SRBHpNke}2edPLKGpE7U{f2+N4V8Qp0{7>8D(sJBu)o{^UVeO14#a=H zh5YY$u$ly9QLHjO^%|9{Nigbq7ozYzs;{k`Y%2!VDk0(j-Hd`B{CyMHYZb14h4t%}(^YJvOOx)47caor z*P^#Oo<`HX=i_fBB?Bz2tW0aP;!{(}xN!a)26b*Z73SqhgR_HCKq9h>U!elx z#I8#d_-WQF+uqr620gx-EQB%Le~xQM`|#7AVdKzEMOXJp{@52F00J$fdZrc(i;0N= z>?Joi7+Fb!|Y#?FV4ympO}~c7p9`6rPbua$4X5t4(41_QzPJU zVv)j-_m{O*v&D3!MNjRhIy={pVTS-7ZBpYR$Nfo;{&Q|_pKV=HVWF&izA{iV_mXq} zGRqMjV&=@)LpPzq;^Mg8wby25W+8OJ2%rdRYGyXD=K>o;5fu{lmz_8ArIYPTdA&DW z;%lp5X>&c$yKgHjWCGLc-*aK6qk9Iv9E1meZd=BIC^oZqRxoA5lM?^&W7hVzrK*}5 z6D@7P+t5EJ?8gdQmX(*704*@`^Yd@7gIR+VQ3BA&mgxOu6cZCuw$_ksoz><@7O9|1 zF<^24-4aYV0NX75^5wOu>1QAde}&@xxm;pI&m8LUetWbO78b?~nuQB+=;-N{`rfQj zCX4#E78Vsr<^c`l4S+0HfQG7SYZo~V3#$VyLfPHDA$RS?`svP;6)2c_fXV_FXaUjn z+^ZoKaB51JI%o!~ z1SfMo>SwS5=={!oNc7&TfbBhW_TVq$1sG=h!OGT+;mRc3=nI1C@7qccH|mvT(C zQ4jjY4ujSW;_OD>8>FcurBRV-bq2E{lN)D`bP5A!i z7&ro{ut)W;p|m3X+imr$?V>Uh&QB-d`Zp47mxFel*m#C@bIya@Q=8!Ht3WTp#K=e{ zP<3{e?(gbKTdypE^795%GaCV7KLxCm=W?at~kV&0JOxekAtyzuSG7;J%VyA%VBIX}gvr!?1_wV0VYDI+joVf{k{O&+G1*PVJwZlzNUmXX0BHPYu?O{wVXvNY2 zWi8d$uRrD~WKf*eUY{S7E#3U$jmZVNCop(iLo01)$_LTW%O&_B|~pH+XA*sdD7QA_I&=eV|vM9 zs#e{jdoEU>wEm>9Xi`H)yPGB1#x<|9&a9<2_trTtNs8&E=c!;x&9KdcsB^+y!PA;7 zypj)tTST+Q`i-*Rp`v@+;AZmPqV3%WTCkN6=x={H?q(|Y^#Cm`H&g!Lp@4Le%b$1@ zwwM7w?NQ$T5jk;1#Xw|o0=meXyPHeSl)!c|gazn@9Qgiy9Q3$O z?sCed^1a?jb4>@~@pLmM>dII2`1{k-A1~S70ApujWt{{Shibbek*?E63=H44oCda> zlqyhx>8YxyK91kNikcDB{=X^N_b#`Nxbv04!iC`K3_3b^bh0p+<6sE#`+ zOE-C-O(hr5iULp?lw8Ff0`>}XEE(P2k4B_#eD_?MQsvE?ZaA_^j3x-`DA`P<3qXTO z?bch|!7K0M>Pe0XwOeKL(zenOimj)iB;Gskoj(xo8GG(|nirzX`rh=^F{k-98GYBD zb-@FybjlxgX+jhVO(GW)rrUu~rw_BUS|EH%k;$)u3icGJPMOxtorZ|sh!g49i;tX` zr@KqXoZKGx>I2rx5pN7=knHp4BS2BAcHEG^KM?jf83E$?mzA!NqUL5DU`397vD_l9 zQCo*^1-N=lINvO{zdLdh5^(tWJjITUjje3v8|W%!5*BV*$iO~+%*HnLf+6iu%HS{2 zPvBk@tA0I;q@!3N<9B$pTmMVine1`it35kVj1$ad)nZoDb-q4SGD8$}`)!ayK{Q${ zYM(wSlBd<=yZ!xiieQ%38<``=PWWvkZjQJ0u=j!7sn~T?*@a8Zv7LIjyH(u+=#41K z%?(7i=$1vD&FET*1~~yF&J`iR1ja!J0#j^%F7Orw&_jmt|1kHKVO6eO*RZu!3=9ke z6_HjkXi&f)m2OF;Lpqc;!2nSZkVfeS>2?c>fONNrw19N8zHzDh+50)Z<9L6)KVFZ0 z@B6-GvDUS&>pbTibBrC6ysoS-biA!$*QV#N*zZ3J$yMWa zm#x36sPLq#Z7AvtbV?wu`-ywoGH#Ro@Zo#9Q-4UlN^eQYY1y$mS z^XJvzm9B>~M=wnU$)6Mc#I`Xz1fC@IT)D11+XiUTzTVz%FFn`{8blHSw*h6#ige0> zg9k?ul3vl{sTnrL$^u8IzSDgYEg3YRrEF)HRZ&^lZsH%wd|PPcJ81+i7_I_;x2Ng2OPwF%aJ_t-o4ZAjzjLZ+ zb+j!#1{TX>cqu##i#=>Ag$|kUO~!-bl);tx(EGl6Wr!?8fDPf5 zPus0Irb_rfk0;aB;*B`%eyAYsi+iIUi*Z?9`io(qIVPdvMjc&)s%tgyedo6}Q3pK(U#UKu-k)WK? zjQ>4bCrT5Sm#2!7vHrS2$o|Pz*@Kk%h9e1a);f_^w@Z0h+6A(b zmL5e9j7qd{N>;Trw{Wm_4MvZg>||r~VfkjyUVn~yR6z8pbzf0UOPNiR-Tvow0$c(q z=$kkZ8+OYqBs+3!OQ;su-5B8f*E$!M1#>^Pysy&~-K04oa>6>&aYKZWXN~o}!KwlE zzC#-M8J`xjSyRp&*N}hMVP-PVbnl$G6pgBqQuFs&Ir#!jbA|czNw>#vzvksVE)oBM zL_oM0=!atjSL5I_+09lXxHh>)pdBvt4w})%~DIR1?&;EZ(f-C`TDX53TlBbl)HKJ4VQWMEtMir_5J<* zM)20rGM$Ss6_!`Z?npwTnvhP}^QMPn6t7;tj)i)%#gvYdQ}NNGM-CttGeLR-wDbrv z%0>2{JaIxdy-fijU$f9*v25Es5N1Mx{MD;hW$w>|*>3GSG>6tj zKm&M|_dMvSQRM1uQcQe&#m|(_fE0meZ{CNbm4NWK;2yr!Lo~AQRG{LHiIxDSPlbuEZ+imA0`(~Z{CyR zRn)qP(arPus70DMe7o_5!VUU}5WYS>5pe3eH)?$qw*0l$+0^sB6jJVajMT^O6mFog zwUO`$wr#$CRZ_BPVX_ak$quii=%umuSq2#{DJ|4lPWPrMkCYVYK!-@f0=6aXW40&` z>fq+s-(X1j@pRzyE%`x_2&>zTyexqNS***C#HMXOjZ#G{w)X`_bI#@F_IufMdngR} zji<}%g$k6EwNHcK`=h%)hv&mWShT%p;)GJ3XY$)%wL)QOpmAfO1YR;i>sgH&f zo0rGudE~qjbYc6?7f@ZOB9OsBUbH%9o}D96L@kRo0CT0Ft^>m8IK0uQELM^ES_oID z9X}ZvnH68ij&V=Mzd{ooXGiwW2q}Ht>4-2^-_XDy4xBa8U6_wIM6SQZ71U{#ec`B= zl;EljNsEN+{$cIqaZb)AlJd77KYlQ}UpjQ*PAbejEdG`I8(loJ_Df!OKFCxQGXcVg z+U~*xgn9x=z=KZNO&v|AP6Hr7}O6qwrX&4rb+MnD-cZB$=1E~`rKQ1quN8E{ujvhhC zHa0Yrf#U@Jl|5N2Mn*=GM}4q#!SMULQln(pKB5zGUG&nWd!)_p-oCvY5D-8+7-dmr zo6{=P(f!tly>}#Kl7=~gmyyo2gb11~pH@Y3@;HpI)Ro3W=U&`F4MIiEWA(MtUx z#kh(`mY`|P8cS-8kyOuaNeP{%#=Hn#ZjlqWMT33b4(oKh(=r?>sBKF&@fa{kF$we= zx3;dN87?>!_XRO;K-0kPqg@k~Q@>YKwOadJ5TBPwx=Xw3_pII5c0?3DHyoe!nFtM! zTYeN>(GqM@&@4c*o0)x7SE!&i!&FyF*Suy{|cDe#}s}{Qy%E`&u05-XWm;?9{RS2(DBwS|QC;9`qOaqk?Ro)=-9lLee z0ogcjrvBROCu;K{_~sfB2dL$^e9FJO3jaO7#J2mn`4`h3pU3x5N27?=OK)2_Qf%W? zyY!xvt#S%^97R4`fW#84MhxPyWg)_bUu(RP?1B^hVVyv1tR0M97TZ-IJjeR2x_21jLr7{2ZMEES%^ZasM z?IZ)D$LBYl-HjroR}N5|vD3LWI(vzjr>x!XoS9;v!b~@O*N~N5=NK=EgivEDB*e zjxyayQ_7D_a)q1O;;%(;eP5msn;af`z3FM=E-og!d#hP>7W`?i&d7x+X=-*Su4N=G z#k>y+j2>Zb?_)U|{j!_fWaPpDNoU~(cS_&$^=YksgOP?ytyw<~9y$93`AsjQg{`}9RdZ^Zgt>N1Exolo9Wyyc*L5vZ*mdui z^UJz5A%{CGw6wH8YHuIo?R_m{(-D>V0IAaWNcar1AI9 zFXNMv6bUSo0ip!liO}jM2W)%9hq&}=zH3SeoZl?=R5gKeV7>lM2sSpvv~7e6sAAdmo{4PlS3rG;&%+{2^rpkY`6WQ;<8 zIw&D$k8Ghd)}Qe&x0`bE*{XmbTUcs}u-_G+NDgvdxXowH#!gKhJIN@7BS2^;xX zo#0>H%$$m%B4zTCuQzue{wU`v*`&;IJoCYG9wy<2vMjyg-9|;tk8i7Kwqy+0y&Znm z{_d1Wr4XNaR>qg4+!~EEjZB3bi#l=B&Bn{DPu7nW_n-5a<$IgkB3Wn(`doW(Gsjfq5}kD$gbgoyXn-19!<4N`rCJiLthHQSTZ5;uT?{ z%r03|%eoanZv?BK+w{WPqGmu2A^=_W{Fu3`8uplGin70k-St*`7`3%s*_P18zBPXr zSCFJs410Xc_1$51W9?D!5`&F8!Og9V1V68pLBnSwh8Qf%aL@^RJvuS*)b%7KPzU=D z9?Wi^G~f!dZ8wTB!mQn-cS%qX@Yl=Ir2ta1Dd#Oh;Wj{~Q>~vAq#A2Yy8?V6%Q_)1 zBNL7vJUl|fLww5I*mSW#A?i1@hA_v^GBGl8Ejl>dIKsWZL2Q|N{9B}@RqK4zbx<4i z_4TRd@*K9o!cP^Yq@-jL=f=B6wY+2t@ZSljTNjpvfCi?|1prWhQXtJCE*@#vvJagH z8E`qa-O73)xKD&HnYt`G*YsHZimk0JfrLw!o(;VAh|&;2rVuZ+AOQATmTRJ)N{ut) zi;4sci#^RY9iP=no9igDKmPH(A-|{B$AKAl(>6iQm=rIY3&Io)DkCYJ)#)!Qy7v4N z4+8nl*bIyYHg`%TnxMcFD|;0ZqW)$y&dbgOVEZ8MF3$euAemg(db_gotl#_i7lP zDJI`jt!7@xFuV{kSyzzNC5w2T344b#>cWwpVpp+}S=-mFt1?HWc3=^JR+;Qk8T|fz z2u-oCY*v~gQSlh}SNN`Fp)c*Vev%;YDN##Q_?|@dZTsnvfguz~+LV@$@ksTqvbQpl zpkNL0sV69+cx;B{1mKgX8jTxc!|g$_a$pZS!&bId96C5SXaZT2*j=Y5d_qGBf(jV* z5i3k)Zv+;;ZbX@hifFNzr=xKx{oOm1@zt@TIdQK=+O>NA3aQ)j6V#cas%kuJao{$L zVFyKCqLrTfwFrEx^cx^)5*q<%9;ls_Tbb4DNL8K60X|i0q&=7srROhrG!j;YN z?Bm;&m6cenkX?YW(SdiksayE&jz+$t<=-iHALGoEsdg) zl;cx+i&c^S=_+=#S3GuB<*+myU5m?gE|l%rZ7LzjDF3U%YUZgCG2U{-QtD~6muaO7 zZM~C5Z(cORIpc9oNi{zPIY9%P?psCyIvU+ATuhRI-vyQOujX6jcfr@A);j7QJ$J6& zp23fg`|0%?^(^(4dsIHDEFWlhy)~HIob&v#!hqQPCXoh@&~DRl&d|EdYbJ+xA zatf=3#RSo%j%C>qtLnPC`j4@?h%GM-w*Z$S-q7}Sir1q&oDG?967|lw@%Q~y=?t3# z7%N^^U_WhxRW|CUW=0l(d_(6`uFK=JSydST)JOW9aOJ$szTM?mNt`!7JhW?ThNprx zy$!>dN9^)55x6Bo_+}qI&x8u$<$3A|Y2+uU9BnFY6X@2US2btJo)AMj_M-Ywzo_o(;8&vp?G8QH_ng zORBGz>mL~SWLuz|q`H5GN;yN?mZONrE?1r2ZT-4n?)B>~?gl~=J?d37Emv$Nl zFC)v{1pAci{3i<81uzB4VA(5!`{ zy8WxF7~!-Kxg~FHX_U?{E-On(aCKPP7xXc@4KBt6TRjD1nQ7I3m3R@J2*EKw*cC{6 z&YO-_XV6%@x2#?>x!!By?9r1tJu}gx1=EXJt+~IZLmc5%TtydM3)Uto z=b9-`5h?>737|JC+z2BDl$DhHj>2#B zSbCB#L`u9KM*7RqguJm-W$r>TcP+H&O!Rn9v7oMMU&^OHTEmbNG`0;;cD(n^(B>J2 zpxE_Z<39CBdXv;(=09El5PN3Dt9GV6MH(rEbAi2624T)wDCTU+j@-t#Ia&|rDZx_= z-`Q{nrkqyvo72m5G&A64Wz6hr{oh|F9WI3tfo zwXX*yuXi|v@x3K)w+bs-nYuRCT^K}BN@f>2x??PyHLK^qs(E7ntx;=(f`#4#MYJ2% zUAzDkOtv5(XYlH=XX)rHQUjIC&^WpR6}7eBx{#K8sFRD4`~%qyo}k0xegty@S&8@B2PE;SPTSWzI(K?`M$x`6h0Sz&sd z%T!|?1veBjIYQcy3W-N&sW~rPCHjsW_6rI^|$z)vT)aX-p zN(l48wf0*s*SefKaIIA+VS+`y$AY{HWa9}`_rbxzsSWJx?6#Gx*+YZ^w}$fGT>`9h z3xX|!I4DrQiGt~wO06|H(o#z?2U;SNuBVI8IM2v_wkmqZ$iVPO!aBdpt=xEBmA}7# zi~cCc1-76LlET;JtxU(uqFG0a(mFdk?GBxY<)4o$tf1zRHaXg%pB^whOxE6#p?}`6 z%GpCtV{*3+By);GUJ)5|T2`(4>3^+;zI;h5jx=bI?yd_@k}tgx%1O6r36KDU!h#N| zH``C=95ZY`a=VijVFvQfW*SPR1ec!=Xoi}UwVF`TUunLsq{KMvs(kFNwanvdD7npo!S3k@FbO%XP_gzclM{Acg56}~m;y3lsuK?9&xC|Bu8!SR?w3j_ zYG_D&a=mOKh>K==WP(AntxE3g^}I>l^!ig}Eqijt1j#w5{f?}=d*`E1$&nX_%@Psm z35^Mlzy`J}eTFVgr%B;iq#T1_OSfJpMjvr>fcTa5)gekA;xNT)+H));z^zli>x2$M zFHYda&lxGjYpUJ~GBOvfDBVEC5vm&*1*2hoq&P?%KAWKz2u56Hov|Q7O)vrnTSFd- zJH2o)qxk;)`ypo_`oOKxjCXsBYcVEXBO%cUi7wNXE>Vr)WeXKPAkp72FO;ec}jtN^DjYm%k;p%Q3b6eH{ ze^NxKfxs_Bw$@g!;Tt_gvXkZ;ruX&pTWaaE;p?~Hp+Dl9PK#Y}+W%zqyF(0a8mIjQ zmVdCryemt5Bz7!FgSPK>9&_w|l$5@-XSqBp51X`kaBy%C(3l{yXA!iAK^Ri8={}~9 zgO>rSCizp#8`?Q>T!-Tl>kglXv_Z%r@Xe+?p+;TSWqn3pT67~=o~LzJZ_>CEvaf(dNNztp|CU}e>u?BTrfSptG-#MtT=m0G3k}|%Qc># z3uA-LI+~v4XGcR_4rl}z)C*qyu*)?&sXf6uKWOHnYkGm^u=$pPylI7W7w-3oExV3t z-7YLk7@T*U86}y`7mPFVPtKB>59XLgyxX`AOK9waaVCd|F}^j+SPLK! z9e$t?2Xt=}a>ghuq%FS6nk?v$M^koodCLcDYwKSqhdu@sPqMreuW)vkT@XPpSA_{` znHR5S9d%j2$nBQmW)xN{()TwJDjfY{<&n?BHf$otSGd~|-SS8aUn(l(oSh5Fk6wr+ zG>5Ra6DWK3?tLzJdaqGn4Lbi5j=C!n-L;HA0fppsQ{!L8Q#~>qJtWYnFj)t9KD!puGpN$b>sYR3o%@ zwHRc~^U?-qWMsUq*&;c6a?Cc=EQ!ygYOUPxSKvzTRFq!?P8= zzR5i0rz%}(O2&fdkMOSr?6vQ2%63?pSnLemeXyEh@;Swb%DdKlSf*w7X^TDzFLU0rk_$ zt#9buc2Y;z2$Ix^KJ5G6X^3o#i;F;TEhzC<%D-clhlXvFV&0E;mG{lstMt>Srl(7a zq@2nb{D=Cy1e<}J`g>fk=R?t}TnGos($@#OGJyNw3_~F`5}%M<4`^`2`%ZFvd_9sF zEh@pO(s%HpRrWvS9ezZS_#llio~_X2!Yk5+nA5gr`g zl^JIuKM_Q5Ew1{zT|OspJU|5XT6gE{!)tsTiqRI!`^~?|+!? zsPy1*+3!ek=yAcjq7^0w8ax*6p3t<^{4Cg%E*^2LY{={v%41{#gf3c#n~?wn$p@ zg&?(6QE?w)N(NvfnWQKsG?5&Pz$L?1c7I@&KV-jAV9Jrd7%9NA^cCCtf2X;Y0!5*s zynM2l&lR&EsD3?ilMYE=n4(H8iofVv*UG=_)eJLFGCd?NC8b0#+2(+O$l^bl%z`Ro zojeI46s>s`)zwP+`q9vA>4C@L#wb(BLQ`;v%hK>Wl~YSA(>1uz@E2BpeJXGrVGwBT2GCV>V#EH*7Z7|E5Vwda zhuop#<&~B4zDe)FMc?YSvO2oNYO#DL-%pz|w zeCM++Te)`H5VY*IUHu%{w~BhU?4*hW(skdFhDkHj`@+B)j=*w0Mzt}5jpMoGwb8P^ zNkb6Mvc9vBAmD18EcTSc1}4-B{q#p3bTq@1CZ0U-mdEDgv=*;{gK$40z#d3o z>wWw78G`2~gmVWqkuojoZ?(ZipJKH5XM$Ly+xleq!RcRR3T14zJ&T`P7-a`?o!G) zH-mW)ME#+ZtUm0T>G0+CIo9)kQ@)b+hM$%Yb|eEi#8GoR;lAqBEZB?_!b?Z?9x%)YsH6dxQSmj8K!K3%Y2Hy?5DjTz-o_nGw zHGD8^i(v0+;f3^V4H9`dG2+Et!?N%C7cBO&JJOg-ld?M^mTw2Vx^-?i@Pdyf%jC!p zGt*^}oA}k4-F%|%R0;6o?i2|WYafS6J1;E1 z%>8JKfS0r0)zZ+Wq2F=uvHA<1gQg=7Z0o8=gAA~Y@<6iPk8WydsqD3QxP$Xv7OLdv zf`S62>&mvaoa;?RVO#T9^py(dPt+__MJN9AMa3xbl)WxF@?Jl^{1%t{5yu5LYO@!RB32fscb1W+3XKc2KR$h3 zBt}yW$1Q_6;ob|P1T{mjQMd!AbG~kv9d2OyuHT`Z_E6)%y4uXcU?9=B@RUL6nn^+~K}n4-5`FUrenCoP@1JM?oK zCtm_eN+M_=!w#pieIFPw##EU=4kK^Z0*P80g0D2x=rpq3o!@mjsOkXMqGOkB3*km> z&o&Puh76H-iUqLW(NJ4NZ-g;ptfygcQ_FB(wga#pf#$R$U=8)=2zJLS`Y!vy5ZV&? z#^&aw`9Hk^7h5jE^vr;7S5%~l!gA;~)a&JmVl5yH)gq_dqs}vRFPJ>eU&@6WP1r#r)^f~SYbM66^sQwZ#YIUS1GEMa2CNRw?; zZr=zN83Bo4E8!U9a_qn-Lt%MIuVG!q2}AIVGzPi&67zCk?Io<1zuqHF<-VUFOuT6F z;$o zW{A7s=->wfKy2Ma7|dpYpMFUbfCRHp4rIJ8Og#o_=LCD6bYD$5kNF)#q%+GSb@C#IToYn&pU6_SR+qWCL71bzAyBdr>PqzJO63RP< zONcuDMIW+TEP=Zyh_2mtp{{H8#JhM$LVD65xZhc*CBrbqC}?Vi!ap2wemsZn8$~I0 zkL&8F)+}n4tKOI`dw>uadX~HAZI;Kf;nEdClW_bRRDw}Fw4H~p<>0yF5y6K{$U(%P zyQjY>{3D$W(F5@UFrp^_LgRtCE9S7gG-K{zCiq8^Ie2+_Vc|#g!;wBx_3-m^z?`RH zRnCCB(*QGg1VI)NBT@)tDh_K0&?pRjKJ=(?MlmI?j=^93ctR7E-07a2&m#So%a~7| zl-JbMOh`Q0N%N%B#*uF&}gh5z|!igLU z8f)a0ycF4av?D2+nkGTE2B}KLW$E!;^nc#BvAO)_N!sxOtZ>5PsG*UBI-z8fDJlev zSV@F){TiRZrqaJ3Y_Xjgl5J{kuIRTyq!7n{Du6%%9eq0H7^2Y^gEH@s$RE9h=kFJX z$G?LcMZE%(t)jeq1U?z+8q2wJbw9rGC0B*{{dF z?mgM#?-mDdrnfc1>I6dsK06*lm|cDs$;L9VZ=EB7xulRaKKEK+cH#+ER?o;kO?ejg z>3BOoVxArrD`AsC(-GlCz()RU*TaRKI`9nt1{LMXvlW$TN(&qVblDxO_{YDJ0ska4 zoj@!C+u`Q*`+C-W+k}Rxid$SK@%8a%EyfjmT(DFO?kf}+h9lD3mIwUli5@Ti;NVkO zKhyGMe||OZw08;9I-hcSMD_eGsegJ0g&v*zr_u2LneMvlWS0i#&~7sVpYtrKxJq9h z(@_X#qlV?j{RHy7Kj4Z7r6IaPbl$XMbQ&_U z6j;p=_@ndC#shnjkno?o!bFJ%tPMmo#H1<1zwiI$0A|1z`G6U|749w@A5g&`Q!6iE z&;~S4*(4HXPB)x!ncTkf5StL348OW{4zcSKcr$PE6E%8(E;9VD_r@b8h%HiBP*4Ks zF(TFG4MztN-ePoCR0|ej0xb~~aG-@v@1uhv>e=A`i4F&o%^YR?azJ;N%$qucu@k8U#LjM> zT4_IKT@xT3;}ltc%v?L2D+G?`+`T`&Z&GLJ-Scw@#u43gn5Dx z$G3|n2tAyt(TJ&!L`}E*#_zZIx=Gk-Vbj-H2R$zLlnS&@8A02D;nud)7Gr})yj)iz z2(+l-Jln)~=V!PJn%G7$Z#5GskBsg2TUgwu#f}A*#g(x@wEHmwm^8ZFLn=bMm0)?V z1xIcXL2OcUOF`j}>Ausq^9YZ{MM(b#|Mwlx$hRx+w=#k$#%)^NWw9U1Jdxq9M5FZ! z0{$b7({yxK*!92Ef>*Km{&~-RoX$b~3BCx0*b?)H>tE1bX~bB*5_^G1|v7^sA@} zq5cQ*T1S6^;jqa8-Qr3_s--RN!KA~ba%q>hXy+jsQ~)JqfyfDL1B9v!hr?Y=z1sgr zUH|pX*28Sp^4CH2W8%nlc>8*k?X#^qUc(s;QqLUtVKOAv^(VO?# zgOOWtFaaan6MKIK_|gzv;0k}@yv6-PFuUhX*iNzf#!#;No9})$zcy>L2j=y=KE+LV8>A?$^}n)u)< z%opz1rVG6_BqRyG5F_**;n}^FBNolE4JnblVJ*WU56Igj>Swyi@_1^J$H_?4k~0@W zL{lcrYtm0fD*H zIs_M2s?SdhK^sJTIeSCJ%1a}%P`JXjvFkSOn4`htYMQ98X-UXCF z>iKqK^$_OY;Qfs-%!vv%{qdYW^3@*Fs#eT%c0Q$`3%u$|N+P=JH^N6dG^6*Wva*5D zW|fodwN( z6xkqkp6Dy6H+NlDBUDxBF(%D<247y+4Oq@P6cX*hHn=y&fa+^K&|;I&YciVOvzkmC z=`eyRt1sv=r9!P8i6_C2Hqe&uF!?G_dJAR@+n@!LkPG_=_mm;JQI=5e#KH%!h8CV7 z8%vQg6QA!E%3?0-&!R0S3MC-nXU4E=z0WhmJqA^SI#@o)mlmZ4VRb}4yYz@y+js8? zN0Ae|bHu12)vPYco^dwSNQgUOO}@82WCsZrLs30T!dD2T=A?-m?!63 zAr&PGgud>OA8mHrazs^SM5eH;WCBdUWP9qH6NdKt!2JL~ zsFqP^I9^DFzU~mS{4uUB+)><$ZRcx}vb*CU8>^P&ME-|Kt0t%MWNl$0tW&pW)faH#tA0R<~E*cg2e?g=;&`M9F5M%BL1z4kPbL6}Uum_9N?hNay5 zY?~gp+x0I+ontF&Iq34gb^G{z2Xv`)Vw^qIupPcaS*;czK+bW{fcO zJ5C|ayKHMe=DJTKufo7{5Mw9>1O$XcM7%>otBGM}IM8Anuuu%l7#QM=$)E^4(c(Ug z>UlO~cR>i=#U6CW&7|@vf7o^_8s!Z33}cIbV0O{!oEkxQAfoq%THc0ggCj>?VmiQ% z2Y+A0>JP!t-F*on0&|cf;S)&-K_=EjAC^ zowa`{)H)??{B36MYoF7cocj}+t{NH|-W!D^e3LBzbNY&-SM9AQd>fFlC3JP^u}8u& zN@CHk1EX_^0Y2Cc_87WG$zyR0(P)k@`zIFWN1T^-75lf&I1_`}rqSs$1-k8kgW#|y zl<^B4i|B{zMLwTg7;VoS>-;KQ}oU`F7f=Cj7|j zYifMFee|n!Ep*&Ts+rn&_i;V2EuSzr2UC8W<}ER19iU)z4Ti+Okn68R)TLsk!0p}b zX4&azs^+sD`QG-#hyx4pDJ=QB;;fW5FK9XC2lNr`C~j^FaJ%0dD92jj0{ zBP8e;FEL*>D|)`qZQz3GSd zAPhANF>$^Qki0rfknmUBf$dscTKbGd2b!{pzxUFMy6StJpTCPP?5FR%Pj^L;V8Odp zG=)`w2m7|>2(bM5^XHuCX%LTXMb3_)mj;3>3kr#Q8NzXI_i>_`RUgB^kP{vwlvC{A zFAO+7_%nxqgbLrZ#=rp@<1_f>ekn*#gGahb)D0uzgC zJy%6S6K~Aqd5n=ItC#FqYu8Q*^YYrY`N6%`*47Oz*REZo!%&p3U%w`F`*Y80w!xoN z4jlM%aY^ave7y$J&>6C0a18WsohCFA6xi1F)4EnmTK>gwy~ ziM9<$q*(N?MrJ!XJ5S%j^qe1cM**So2CS(L9(3i!xJW!=9r@$I~#j`#x?E8-&{D zG=fr1@rvgHB-RnfSh~8BHdKN~j6Uz*fLr{Dm{uE9Re)k0mw3RTQ(0iIH3NnpDRHomMZE=*+ll+OIpw!gV3oV(d zNtmu67q1YfKJnM4O=mGD0S-4|_^nYlt;SYyhj5F;lC zg3RB>pFkDBi#_+LtSsj9{0-sU`3gJi1=%<`1_I?lEG-9Gm}2GT;zGjCvjDZ@G~v1) z>gJS_AiSBt3R9q>Eip?ZcVm>jB_*q-SJC)}2J;_jKa4xBr?fy! z2vDEsFDNbwTvnevGf!8)>GI?IIC;qf#vUX{kGISm=-{c{eit*K^X0Fqo3^8Y;QmML z$ptH89L9h(|Fa;~lJ5{?SI6T8IE~5@_}W4JE)E(vp%^b@WyQu9@*EPw6oBXV_Cef! zqq?_`mai4r4A zLw=wgpHCh*`lZ7$Nfn$w z1|A#=9y?IC1S17H-`1(qnV{>c8V5>!Lh+H-F-FV~1xZ7U_TP9G<9^{vVIl^~fSM_< zsL+N_`c`|!6Et|_&bMI1IY#KV=2+5UN5Fb{79M7(h(n62I75OsF$f7{Csz=0PzdBO zp&K`jnc|eoW}Ur%_P@`tl-rniRN36V0a8f$8B4A?#X=-p@+a zojCX)EcrWQ1Uu!9FM*uT5VR(V42}SX15&E6d$;b|H3&QqiHlN*lj{$h_`4u?KF_?EsRqQMAGfL8?%rL83DlC>+Q-q~ zy|BED*@=JSh2XVmSy-NcpOh(bvd>U8eY5y_`UHEi9VT6KxvorZ!4ua5N(>fs4F$4h zM^7wNw@*<1`LlA{?T#X#3$Fa!NX$M}O7O)F0d5pEG>k2C`1g4}Lh}R#&If#tr~-Tj zOdu-zNj)ak5Inun) zl)l4?tHjy>3%r2XVTj#`doQf02uAH|vbp6yG3fGZ4w)9MYi?KiT@BJHiv>gWIM&~p zy~w8(obKH(Gpt$tKHt-cjN9KyEEbnSSl%1s6`!GABhV-thHz!5vW7WLkzt{LRm~GE z&>Z7`jVx&oii8u?pmdRSe`B1yJ7U;P4yr>q*9rz1189--3=C%Oapf%SBy9 z1x~t&B)<-ariDOw&$Ai6j4@>dtU{5t7q^rExR0K1t4{b*pb)R{1bz!y5V9B>C5553 z#7S)|it+n!k8K9m7d1O}L5`oonN(@Ox&SAlcnwao!g(*9I{fAn)*n4D@!lQ}4%C`5 zr7iV5!^OqbqH}_jg{G7}6ci<>LBDSz`-8)5?fAONC&;*@qRy9F#BLheZ@ge^wC638 zOiJh3Sqbb})HB49WmfeMbEHm}mj0YwntgRbg9-;gN(BmSmF+}G4IcHs^84)`?rsqk ze&+V{K;KR+>1Q90-@8e>rpITntXC@7ux~u&DLc#u#~zn| z+yR7dY>0FJmRgv)NAvEsk)ZnHmmeoIW7;vg6klJkw|Ya-^5??w8;@Q6BYk&Y-WP-b zeU0unh$k+ilU~;_?05Y9y`4MV=y^{4&qokpm6m|Z|Nc#>so4FJ->?1sHkQ=hUsM0D zzlBYUsdW6)u@p+S=JB#TCaS^3eYHllezPU5@3sxNkG@UX&1@j_@AueBoBUvO@8PVm zCybL(DXBEl@26Cpot8L+smFJ5ZJA>i*LAu5FtWTy$K~w(yX7IZe%oKL)0Gw<Io(^nae9+pxoco0d}v-fPji<1NQ`XmF!?N``SFDTIre%T@( zFiCN6JowsYgTk{}@y89c9;+pm?D^-u%==w_OfC9kv%CDk6_Jq-rmx!hc4+j6Z)j;+ z2-^DEO7N3i(-T*lLvKu$ZWwBcSHJl0?R#~1Z_ta=CZposJqs^WJvVOOxnoe*SUKdY zm*d{~R<^?j<@6 z>J+-s5{=9fM@+Qs-_o+kbMN7aF?ng{{HE=~;X!X14?YVX*)I-GOWjX;Vw-d2RQEM4 zRx~hAw=nP1>QgLq}zG2QqH9IpF1uazV{Gs@M71VeG%l={_V+sN$qbkj`zlbAH!!`KeZO| zT`vxPKRC?TqD1R?zb3s(jAXNGAlY_(D$Vo$`p0K1qjm39-Rrq#ns%Xk-Q7leHF34M zEzIFw%#yVq#*;brUu>R;5f5G2XngfRo-ysFGh$C*sx(3BU-;s$*Y->MDbHCel zpGMQ}b;15@IUF`Svx>Ff_m*}R9$lYeYqOd>vEc)+L5@YdwIj^s_Gr7twW#0^Mc(L$J zIkLLrY0tCd8@Jot&yy|N?S3j=7WKwNEMS&>t#88GKZwuuBvxto0_Eq0kzt214SYf^3U zH-_E29ie(eooYd_Q6k?d{?UJ)_@fgqF($u>-0iTXf$*hCPR+IHbKeVBo<8Z^_uv3$ z!kzmePgB+pM(vn433HJ=$H6*b{O}mN+mc=y&9f_284+nimj;ak?;3?nu9~yY+quxG zzIm-)rf;7p&^#8yKi7AD>gfDr08Qcp#zyzVB&M{myoak6r(8NKh18dEf!ma5h(57rKQR8@?}Xon+uPQT&>cMP45r+ zi`euV`SyCs0Lle9*bX6Yc5t9`7Ti*M3xg={h=|6{D}_NjpdV@ zg%kc>D!Z=W{xr>!cSf?p8oTgR*T_wmg7tY7m;9*{^9AvG%F;rGiW6hELjQC8w-e`LI0Q5I^Xm5L9A) z$?mL5dA{b;x8anwkDAFY_WA4R+6wgd?-2NJ0uge#O3!o4CHC~8goq1%EL1z6rkrc} z+`QtabK5)3uGn!yuF2c@NSD-&mGYkJQpYbmD3W6Nva8whFZm-p z+|xfL&2{{Hx@Kg->a&*ocjE$si%T`uL&lHZBxr2w2n(IyrKq=zzFzlGWm{rH|EIha zX^pD0iH;HcK|jP2P1w%=C#gMT7AoK5PGf$t*~?Yu{GGbOFP5~Atna1l)|j>RvYl27 zovV|Y*%MXQw6!ogkLPsci?d%{hfE&b9Fb|Tt&MU@6aE;kpCfXXNpfxd$DXZrOvZt} z*nMx-D0DONBi7NuuId?8xr1g`ByCND7FYKKTN+WSDTyevY}J^Rk?ZLiP*qkGKU}!J z$6)+)hLnAsA{l=6I3p@w{bSoYBY_jazd4NF|nBPa>6z>1>=y3GJ?(bk}4(lCQ{bs9KmQrYOG2+BGpc z`szb{KH0&*FCr8J&!P;jY+$tcT3^fgs*QBKh)&INvS?mr2j4zHy1%#QJ7()7-FMY> z{B~6HDf_vNlHma_paqsuul%i}PPOt^+{7xm-Ii!_&Pke%>IA-ihM@;#IUGaQa@}ix zeM~HKb#hG1uc?UnZ!nW(?&mm@=hLnv({rlGJLH6=*O@ElhRHOic%EJw?b1&(D(xl- z_;pMEq_+18ru|>fYV$#D?FENbhpWD)J^#MJ9FVbho#DRAese;SRmZbRj{k5iJD4Zl zY|fNhd+X?c`^Y!$x4yxw|LYxS^Sn=~PoCSoBupdMy%ey$YT)Lu(ynVYZj`DDRx!>p z^68&DZ9H)QQ^)wFzjl!RUXjt@W&h9p-zt0GhM{PS=l!0ctc96;pNw6%y?Mr|u|6YS z&yJTjdnBFE8P^PZ;VYf{;M0G9kM?S{?Xyzj4^BU+yABlBnO;(~WHfj)l*xRcF!-kI+q^@xf5VBM_k}M-m`m93>WeOKcqGD5Ey)Nge9?{vPP$^ z-SFfga^KMXW*+KgS|Qm}P4*|Bexu@VD}MKc@e;RLY+_|fxx#Eyc=(HP8Kd3ddWM|r zsthF4M5#M0Cp>v~I$0GC%G_)XRp>VTK00Y`9xQQts`;gjm!9SI6RekQJYrABT{UCe zd-%Y=cb_{+n2F6k`e~e4hm&o0#RQq%0LRSVv9gcT`3{kk`PLlRIh`^|S3|Z@+>m+Q zQ@g)F@m{ukpicnX`E-{rf4G zJ2UJwPN?!7&EG~7R-vu1lU>Bqq}4R`Qc1d^Z3Gid%YJZd$o zBPJ;5FC204h@w-*)V+f8&!2DpyA&)n#>6wG>*7xBpgL=^U!;knFGc>2r52TraR3>+ zajSiK^?6r=shjdGTbu05*o-KmBV6A1TQoOb85T0&39vX8@?_6}xqf*wsVzQ78fO~z z>iSdf5&Xvs@Ke2;Sw<9}ti1dTFo&61S0n^~5P=7-mgR-6e1H{m2={2NoM>dbqUQNKi=6Mh zeFB2Tti1vku$YlU2nmA!e#mVL>oUb$4Cw~bP#uj`Y* zqtKooSxI9t{f6-mY|eWm=`nWx;A`Kgnc>T<$!4YUFB;mHK%Y?YxzOmSYgkCthGPwJ z?|$B4lpj%jEnDO6UP1NRo_^!q=u3QS5;p5n&y-8KU19h*Uh&L{r%CEz@gPr`WhU#x zAWn&=sW(`|JA<32f4Lf*rTy^bRo;R7JGW8mB=RVz zSn@p^zP)fZB6Jsvov-r~yBfa!bW^ECU-=gt`4yru@BTl&-UFWNzHcA?H8iAg77ZmS zl3heLky#npJ7q=LWQ7I|Np=z?WK%|FMk=A~nH7<(GLrp2KIeH}_y4}1`+1)8^}4Rt zb)DxKzu))!8SnRT9Pi`E%Lpm1bFe$weiDx~cvROV=Ydat1@BAYDI0@~ly;XmES*LU@SY`T%=n4a2hVq9eT{70n^8#Lg#?dyYS&<=v+tGtg_R zwcOct*Li}v<>EZ+pSkloYZ|wh)*U{h{8aJq>@8dVYdOqqqO5Qecy zwpr$5|9$H5&4~d~*4y}=Pm0*q>#v#}%icS%WPCG2s1*5-5w0;fJ zV3ACCMj7}-?|CmrwOaYKlM$ES1M{F^m0^18yb*hc~iNAQblY`^{=$Pb7qYG^hqOO@%87*NPFt%8l_?1 zUaF;USTy`Jc{%tGXK1qc+}5Zq0!N-GmK2?REIn|!AXwKkx@_(}YZh4@@1D6_+doD{ zWB>a_+Noc`e+vI^`I~3`cr}Dks%p$#Jn^#PBJiGMpL4dMp!vRUZbw@4f z9>fbJ^~Jtpvv{)2%Q2YQ|JeZb2{4!aw`a<9D{nKYF#hjTX0nzRzO)+mzLbUrxQBP( z{s=*b0)*oCUJ5zEbnNN}jvUz_qZ;=LwZy)J#!65)1c?Vwj(pWHY2Y)q8yg#IbMAbi#0tdm-TU`;Lv>8QfAswLC9Mr%)o@2H4^gLj zoJT1)R&RbU#UPY2ajMK&2CA85)U&+KG>E!>HpEcF2Eqq_B!NF zkysY1RSFz0?T2y+j2Zd1)Lq@fj`nz3NY)m?RH3Aq*6%_!VQwvO}gRELeFRD6Xyb zTlocxF#iF4V;MM;v*7E8-~)vji#QH->0KEug5JJ$NlaROm`x{m7 z=&=dz!^;4J*yU#h;^IkPla`;LRI2kij3X%d% zXrE?voE}mI5r{Spfd$16eg&6Y5M6j4c9DNTlkYxyo&vQD>wyE_Fiy#WtOM3U@4!Tn z1q2xeXpHYdmg!1S!bLD7pb$nJOBia9YAB5M2wdJR8ykq{UsvMgg_D7GGxx0{&ai_w z#vm%#Ihe$W5XP;*dEBk}y0LA11$>f^Vo)z0KM?ww1n8N3mecHLYi(4ywkqwIvq+S%(*@|s zEpl`-s$6hlfOC>54meH1l)$kEl$J^cc)1@sb}SIqyWe%AA3s(E8;95U*n?dru#@`G z?W+($KsR~@dJmjJCOG5v$GT(Exf-9+723N$7#fST5lT*;#BAkDt5$B}@DL4?^jUXk z?^vLYc=QNo>>5aKg6r9(Iw{tn_0EK1gUnu9l@^-M4;KT(G{<)EU}6s|q8b#=syp}Z z$6*Fxl4-lteCCBLtY1iC{p&6IA%Keo!{lRqF{>SUpPMiViaqw-wd86%EEa!)^ES@W zb;YCr0`Cev?%c|I|IZ0j6}c_d(F(husDM(nbNB9#M5i!% zgP7!qLhLxXkuEn1(kv)-6!6#}JnDnD1rjFmdnAzS2MruI{uYv3d>=mCh7m|ru*R6yI4CIih|q;NUbpSv&&_uD*0%S3_@vNb3W0<)`}|l2 z7Xx({vX^*a@ix2T=~K^nHqdIE)60p_C=nW0HCC2Hw2>>b@rD1Bwd8U$QaN~uh1`F_ zS10bjM_#y$oSa&UCRc!IyB7$uZ*Y|<>*;xlq}Tj#h=VG{R=sVerlIMNFpus<3Jho9 zy0OND0C(r^lpOGaSWs&H2lBR^D1_BLWN?+AKE*+`RP_6c)&A?o-Vi*kPG`IT%8Qzl z2kIQiNM=F4k*JQA{re*^o({lVY0k-ibj_I=$rtss1KEWzDVqHyNSsf23x zXPHU~u~^Fx+W?m7tcFyA$lv_pm4wt3J2AAl{|B+O16$mFu^rxSdyprB*Kz?P8*_%K zaE!W%=Tb^HgxDocWw9Ar5ZC6ou6&a*8%E{7|aSyeARsolF`t0fG+ylZ=H2p_6==G-)MJ8RU;Ia_znn?-d~6T zg^h#+vLIa-O(m|YzzRqIhrwJf;!)w}GxX{ys<&=Pk7tEpzVIAp*3gU_;`CfG{3N#Q z1==bX^#!>tAlRyj5}87n53OGNVC_?w34Z>jOKmIRc?~a#T|H3}qks!?$53E~6?5Xv z;;GWHjfX)LmBTr>q;Cu|R0*X7kxD>$CK#n8#DvE1os%Y3AFRYTY}rCg?(*^ku~{)r ziICJ=KAU!ZkCbk!sJM+Y2O5y$0SGsr|!j*U&1B94H*(v*Ap8^V0VsBTxOATL&B%V)y{J{sUFZ~JYPtfpn z?y@3BKCG-KoQOLiIi||ae}$-(XfUv~w|Zmy)uA{fUk6t%*_X`Bw@9jw_^&?SSI`*3 z?u$Babslu*M{Jj##D2dw0x5_na->{~bN{Ja4+ij3Gu^w4*S=5GY%Dh05E)`mY|Y-n24hzzlPAR|&dK zGGmo|J|XxdFz_VVi$7A5-84$}lOSGXGCn^N!fo&va{gx@%X(oakk=?fM3m5NXW?mi zOwGT4_ih^l!&xL-tU@_?pHV`*)~GRycmp@3>ICC@bO{Nb`b)!Kwj6dG!(D|ZU_)WI zJ^_+0V_YW`INV>vBc?z#5{Tg{Jl-uBGQNOe^Hie}!yDy&zR1;ZneDpTwd&zwgN#1P zI}{Q`#ifXY+ae!JvUX}cOK{~W*jCtcDJv@W;V>XY06u2br~-#io`z}B{F)a2^&8qw zNYTe`gC+#W={9dBX|5*HMvPt2${0gKv`^61o0L>ILi1VE zbh9n~B0|Ym$~RbVO`GNl6n0JQr%d6Vp}lm_mU6SK6gLWaR}>d$dN!S1z(HKBa(~hX=xi6*f+1vrb#oC zb$`$DOxnlT{hVvAYNgU~7v8Q)J-G(4u=l1(*&FJQJulE%zSzU8*uR5u`}VgGnhNB)KXSf=FOqv#vlCg^P^+sVoY7l$>^UdMlrT#55=d) z&2mIrV=kWsO)@i_pvkHB{FrwQQUc??i zcS6G;d|d1C9T<|;qp{QMk-iL;gdG9dfpylo3SRx_;~a5Cs9BhsCM#o_~7R zbL;0xMhaZ9@2Ym;aPQuIyS%qJfFl{I;Q9h5E7Fxh?1YTnVElGHJHs<9@r`nV0j$q{ zXfs^NEl9i@(bmFvFlW^MlbXnR`g$q)WQw1X$>bS2wZPX9wTAWp%)K;pQ#E#*xfAa) z0EHPdXjWE2h3O8LJ_F3j@ik?&4ddtY0}-5V8q+kdt!Re~>ec&Z(l6KNjjs8sZafsN zzO2@AfUzo4{%-%ibj`HK3&%d9%tsIGtCVmNgE6k_d^I!%pxlDRN!Ot}uIPi3H8>)S zKH9GffDzDyUloV!;?6<&eT7{$h!}E(JjY=vGrGrcMLf`~D5yttU7j4f=+9X=X?crdidE$`Vn7^GIwU z!khh0+xwgc1h=!U3Vq&r#$zV5kgq453I*~EnH zzYLa`i&xyYPu+6XHgjS+UwhOsjXm&J)z-EZLDtP0-Ui`gGP*tKZz~cqcWad2SN&EH zwu#@u<#qC1i5=r08SW{r+P~$*OTK`mBa&0wx|}j*2M04PXcJ@a1&D^a^RWefwS|gU z?C`}#i{nr(UxzZUKYtlAwmrNi8*tXZIIQ6fnV`sqCai5Vp>aw$~ zNC#$usrPNv&$l<8zI0}f?80Wq!KE}2A42MdSv`)VmazzG)q7tF_r@n44~wd~(6+yoxomw9!>Q%a znmly<)&2nS2^`Y4pc9qVq z0e}-7#2a-k&(!RU95A_f-(NiRzEAYGv3YLsh|0E-hTX4N{fF*X%jt(K-Lujg zPJ-CroeVCEIE~P*z!6w1%yfmA}ou1h?ZJgUiA3y|Rg7MM4O*Cd7+J1-r`WB^zN_m@EtJ4`N_qH8a5pGI-@ z>8Zq1h^O09E^^?H0cV+TL#s^WktiMhUP?$=w7oVgV0j8yVDTtMka``3Hv<2%?-ufx z{Z?Elgn6U%<2$sLpF*q{J^J?IppwwZzSqI?e-zKTU-NyTd-Th_pc^0eLKeCDD{N2R zniMR4G=M64p$9!4=0{)Qkg)oqb@I^dtUdm+fGJS#pfeIW`8OL{G>OiFl73yRTbw0A z>|p|WEUI;PFc#41@XBRD{pRx{6P07cwx3nLQ(#|V&2=>yFRyOp)0S2~&y)7u`gYiZ z{a|G>uf5IViXL-w1GZy)QS6%n%z5L}efG?FxFn`tkC5Y~SkT9Pt3B1aqkAD}%tTQA z-10{D{Yp7IdVjcj38ugHvCZ;T_-kh>RY+vx+!@y17dNCYRH#M$8KM1tVw$NjCiCkL zn!~3aMVxDudU?|L;(fZ?)Er9Y$CkD*nl()^y-&~Nsh`kE@OVA*XJ5aX(#eJ~cE+fx zzT$K9`ZJ%COz%AC>AR9{rB2tE${v6J@r9(TN)8W9Cwr0)&&rQ|lB$tW54!oi%?P7|gfV z0p>$0VldFiV{lB2cIn#)YwB06Kk^5oi8Yl6rb^bXU0a7j=ju^@k(V%7H$pzlXn8J@iD`IA6zlS2UDvYZdPJv`HgnS_a-eK3ubZa^HAR+Obg$k9v z^2tuL>@3{@bUDh%J_+9l!*58Ow40k!nQk>>>#F#IES-SY8qO?5Cm|EFqp z6Q00D0uSQf&R)`x5@6^&n0aCl(SC?RC)?-K4#$BLcc!W&EZR3cEPrLPs?lAif9qThP- z>n7oB0N{4{r^z%ei=ylBTsV^`g7|^@ol+ zHe0QiH7O3OR6fKpz3p-G-_x5h5F4x`)17f-KZ^j>MJ#PhsGb1cbP?Ea_T}~9SvXnT1vD}WJnl*c=P zTEeaS7h=6$!dHvmDz4!_T7cI!JxVA=$<$paht}`q7b8!zw^RA=)6{5=I%kPqyEUGX z!}4iJBQ66M#B#>*>}Uw@I)5!9canOWd0|+oZB2nmYg*nyCQ6TjR#({aI&9Xj%4f*? zUP@qcG@66d8;RW2uyySYIA5gk zbER|WnS?5vU(OSo8+YlwDdTULUD~1ZDfP#}&f48Qo;c=9JsoI+pJBsa^P- za)cV)a=FEElK+7IxE#De4NxJm{lA=rqdrB6gkzq7yiOGSzJ?hX0L#>7y}m=Lb9YU& z*ywy8b?w6HBY>ol1CVzUpf{r#no|8dm1(1@Yu5~n9{c#`aR3gdnAgzu#OmIAUI+dE zh0_`_N2uYLS~xO}vCXr93GUH`Y?tci5|MCqaS=qBY>M#TbU)4DjsCJX+b_$;)??On zJvq`bN-L+LvJLZ_d}xu4vFI`2(4BJ4bmgUnimeN3r3v8y{s1Y3>5HumtfmrG+)(I|N927`f~c{?Mck;VBxm|y_A^5vS5 zMUI>J;eE8U7%@F8EIjWEY7P{hKxA5-Rps+^j#H|F&o9_MQ99N0yn)L}KStK4jk7i| zIXsnpl`s|65%ZRxYN`IZp1R2kmO^18Uu#c^oL%h%5?=|@1Bgi;dc3!2M{Rxiv@Ml? zj?jg3AV`15PZ!Y}+pYK~nSPf~zUefWc0bX&FaELF1WlDe$-i83EY|_w7EbMo2Wv&Z zl{fur-KqCx)y}7%o?GQzf5bxW9vU+eu|9RkO^AMzrbYg5h#RX zm_k$6Fa_H{fEJ*s1mPz<3m)R8Cz6UA>>QA+IHBK-hPBcms0uCq0QK664G-xS%>-jjjYPi z%&>pLYUzKx`+JpK9zoo}K!6HkI(|F^kYH@<=v1m4h(YAY_9G<5j;8~x9HJinT6c35 z#@66DcjxI-M)VBeIS3?#en3%HcHPAf_wCkCZQUnWiXVrmz4COyqNY~M{+?VZ_ zwbNP-2HdCZGYjqth{8||&Y(4z4tgZoC!ut4eA9P#{vs$F79nglOTk+SVTvhToTa2}6)r{9BE)(9oSm(m@tQyb=p@0g2YosQ z!vDbR)8UGZK;+zXqcVabSxc7ZQ{3k;`S^2YhUL(qs}E|AUHp)jv4ZsaaV5D&XH{uI zOHD=Hn$lg!Oo1O4pe7l^6B(Yesv#a3z0{+%nF~FHJ3?j+X?bD$EUyDEu{g#;D$_H| z^>jwA-)r(`nZdo%Z>%Ud+RyKM&R3{R29MB^t1Uw z(~&qK{X-5M+S!Iz798JKA9AWS6Wqi#sGGf|neUsta44rPEzj)W**5_%gxj88Hn-*P zc^>7r+U(JkS;=#kVMVv~D0Sd%a1W&XOUz1vhI)@45c6~l&V>w-pJj2nB&o!##)(vt zELQ=l{r1=a&Qi=}`el!UM8WK8hsO9UxRF^)F%1nkVDI*CU5*b93;O`X@e<4nVn+94 z{+Dnjpu|Axkr^!@+n-Np*8T!J3Yli7I+?tu^%T|N0xLaw`hjn0za~Y~e zcCGYJNEtZ^!WBWr7tr)fUf-4LVI+XU6SX9KEeCM>aoX<9YCnUIO(>N)tQ#`shDL~r z4CrDMc>s0WC%&b#zcUwCvycf@T|!cNC<6ZevI2XH8pknX!X55Y_J%RCWcgD4(X9b5{>v(q8Ys)S@h3) zg=MXL{`m1Lu&9d|)x+u*9PM%->em7T{_mQUN80wh!3&DRVk03vuiJHR z4NY<-2&!jW$iE`!AzeYq^QMLWH+73o?iDUY-wC(zBal##RfT>6EW_-cOqR0s)F3VF2V4Tl%E@tJ^0XY-YJ0H$r3Le2cBO09r?= z71Uu#b%7o@Z=96Z?bxY=XDxBP3c1`d7Q3LB#8;^iDyojge^qbVtAKzHii;;E%`t*bXoDE@W%nfA7^ z#KIPhowiz9Qh%LNd3?j$d)p0pH~FYDP?7VQ$upglimY9jnGDFsj9u-UtM*S^1s#+e zj~hwZ)=$mOYQgt~A>hi7O3P*uPMs(9=*25_Pb763$=tqwoQ-W-vhx99z19Pkzi=d z00VU5$lUrGHV+yQCnUXGgq_$oet)guf~}^ZH64a~ueB$cz`^qR`uz{92O3ad!is=H zUu8+%lw;IfN=k}-ktP=}l6@g7bLj_O7O|prAIU>?PmX=FXM5IHSVd$e}6gI>LhN3@guneI$< zs?igTC3fwhPnPHM-%oQRa+fPlbL7|S?g8!q8t&E?RS&pK`SygiI3&C4s=9ynt@?a+ zQ8xUcWmLxD@o1NOFLN`^RaM=0Inkv5cC|B$3KqG-8&phI=uva40}Qu%o8bQ?uC9N|)3 zyP*us5J@hGu4g+}xsSwQHa$CelLWmYn&1*-EQS(xYqJTU+V+vBD~k!%1;dBo2=#ZkLxsdL6I}V= zFY>7|0M*c9j8#f9V+xvDA2o4EkA`vKv%`51%{~Yr9?buLye#0pb7TnwD?hqb`)NX%W4s9z(8V@h;IsCcFqMF)TlJ`}8?GLf7BtA`H3l(`Nn&2N; zYNcmp_5xQ>zpY@hs11HO$+^g=`(T}$f-4Z4jChCfn^uKbDi5sEzk`wQMEGte10xvt zzCHuS-fKy&3vk|66p)GF)lR~_@(HodVn29L3P?(zI&ah`^n%&eUBuz^8pvj>;zr=? z&6q+3jBzz64$_PttO=oKoen90!p% zuqcaK^aXYB&GAv1VQb;w=>?-qUz$DSRBz6^Y`XLbg&aukPcE1XlXEdo=U1E9WPe*2CcImj`r zmEwV%A_)iccMUUCA;4|J&6|H_;yzC${@Oe|%I7L?D1qz{85pVn$B#U@`W=p}EG)wy zsY%4SdCcRlU(drzPS-ru{l!}*v}9>lRxT;9p}Ehc0kfg z66`!WGm4co{@phG!?lph#KDlRO09z6Bb(wI0u0)o^z2hrS~+2QZ?opm{HwL)lp1>3 zEo%f*uwxQ|Rq4a3A__;y3&m=H#|rE^YX=-@>FC(td72bo%B{VL{;N>D*$lz zrYcqY6-kDVe)vWm+l*wo`S0PAB?_g_X2I_-)PtN-c4b(#YE{8Nm>zKogvBJ=fdi6g zU&8T^LD}PKpaMx-LrWv&F9!;5dT4#*sJByo^8^18qSn@xg;+(8cGUW6*J^ z?j-RDL$&wj`&7Cj;vNU5Ix5R-lN!8T052dROWx%c0v3yxP=6!WVIHyq(M>i_ zQW3V$s_)>B#}Aps6usBWE||ZQQyKSw;x5%dMbFF=0Ha}l{01*0T7~|nj8OQS16O0D zjYXD!q()_BihuQ(s6c!ONfF1@Fhur)q-5zHi3&)IbcQW>aJAstaG!tw+?rI4;#$29d+9RGvN@yrY8Cd@I7+Phx;QiX>Y{GGNB+pW z4^@^qylHE_vpBf9m7sGZS*7T<*tuZx)AZ6<8h+X^q|N&>;{^lP0+3y;RkU&*xZ+}fYJpwD+*TF4bQwmj>T?0prqD>72K@&=ze8n5 zJ^ae!pXa`=`jLEikaPFCuN@n1uX^GXA1jaJQnR;DI$qK1pjH5`QtWwOy=lcbX-#Kv(>TRRbQtr2qVqPif( zLv2y;fOsC_II!FXbGoc&r|se$yjM#)5jV%6Y4CrVDIVB&R>upD9zCi80KA!f0h`+; zXX8)0nkywAqjV0Oo|WySp$x8dz-cj>UOaF41~#9H3JUc&_}G_%h)!66#lb3!+xlE6 zmZQcWzA@M|#&;gHJy)sxO8JRk3;-P0oldL0M<8;^fwRUlysuF}8LglcLXeAsBP0ob z(h~oa92&>C!%t(Al3#(lcV?M<#ljNh&` za2t+~&V=nvG_Z>%U~{$lA1NvZfaL1YKj)w$CqCy~y4i_#42bW<)(>D-fCq3mG^>{S z6~Gh`&!>ySZx}A7-^)r91ij9LG7Aap*F;T=b=9;va6Gy?-|zv=$PQ2lR_NgHil(jsgYe2r0BWY&{?St-Y~?_7g^mTTEOK1#d6uS z?gDuQq|OmTHUKf)wal)OMl}jjXw(&*gT)*8T2pV{oaW4cM@l^FnJ?i?%GCaIkGiL? zit`~e-HJ`gy_MXO$sa1?eOQl8HG`|HS9v?c;0`8-Wbyn6htI8B7U+5DYC?sqS~%h9 zXob2D9F<0>6TB;1_|BfvA-5r(g=mJFni`HXPTYUVs5PVCYfy}K&3_iOmf8$9bm(=5 zbtABcc=Rnpu={QTLp=?sfn7*Q2RTr%Bb*feaFhnRq(rpgNQ6>Bw+UG-2fuY^-rM)j zXvz_zH;vFkh+3ra2MWMqDYr|ko;FP+FvASi{RmlA#H1g=0kK~yNTX=j=t>f$a19jn z`tD6Os34yJ!l@(_3nS3nZ(S>1;q0Z=KJlZcX)4?&8M;DQ^%TBT!PNV~n? zEm1ecVU45=an^?im?nCR256f3A!I|z4i{t+_kk21xK~E2zGGryLUFYqJ?JR7k61Vp ztO25&Dyydbl%|kMlLl0Pw2RXC%xFu(luX7yh`xTi9zn2I~(?|^=>pnxV%lb==+L_QS8*#Ljj@(3%j^+ zuVE)8fsKT*>0Mk{bbI#9-M-n|*{M(bRlmG6z6lFCeiXU(D~anU{2CdCV#piUKr9GS znp>eJ?ERu2zn`@Hvi&A#~S_);*+Z77MfOS+s8HILA7aU4Icj+EI}IM&9HfUnDKFRLzM$w6B^ zy3vQ69I%X2i?+g&bpv#UNRc>5ONVe844z%9Z?d%8drHgDI{o)}SIV=m=dpg?8h@3# z8gsk+T%|=Q3R&`buz#B&HBLt*n1}_5k`ubvCcw}+?z7eqpG%__qicbVpPpDEp~r3g z6v2mZUi324zbY6Qz^IpLY(XHxzKkyC6S5iT$AuZw(O!vVZZ?ie@_25_qX*z2COw^f}1> z9LdMP-GDIUm%u}b*(P+`@^}E6N=gri`isP8p$Z4^!V9djug6SdePa z87sL*twX*Yyr#bJaVcR zu~M!?c*k~c4%Cc zF_L2!zp38G*SD(I)DH#8^v|!Uh9t6X(+c{t7Z;S^!F`2VqO}E+~GFM`@<;T z)4nNvHMB7TKP7C^msYNYr*K?coDNsZ9*$1ScYCE~>pvT%STZAINv}UCR688Dc-HqY z|5laq(yW#CjLcE(NX=9)=3TB|T;!y^X72p{wsml|lfB^O-Y|+FBOZYz)at~ym_!xh zsaoLOArEgVB=92R1Rg{Fu$ug;~u6LT>zCGBLPupicG)hy!R^Iz%yBU9W;X3%=T)?W0|Djj@2o zSM}z9`2w^iNyzBZF6!yMU8L!1Y|^6it~IpZY#P3w zxoy~Hl-SD6wMS;5Q`x70_rvqw#w8UxSze!7>sN=Hja9YAURj+J8|223DgHU8#Ath> zXoB8o!#;Xin;FZV$lHFj76MJ|@AT8hL&rI+q}^VLi(7^>C8d9k`}KK^deoo%wgFA= z{J77B_D^)3^W+c5#yzmxD6l6;z+8B`CFUTN9*xYx-hvVx@5TF7KT^6_&Mq)1kC$_P z3F)nS{@`SD!2``Qvo~IG@?LfYbkll~+Yfn`tkC7y)F{;6s;sZExm8Y_uPGG4*vxTj z|DNc<9ImvF@e-{$$#nxQr`J=@m@EV(Q5S!997=jaHLn*bX+UpW-@=)Y?$3Wo?zJ?P zQm#4ec(nfQ35B20$>()bB)gUj(wDmrzpQdo4!d-<`XR05u_msn=dSM5qP@1ojcM=K zpB~xByH`<>tEEh|m2Eb3!11&VBX>g zX}CsqtB;S~*?RPnwsa)km|Co4R{EXHM5~aepER;F#`wzSex&-8qZopnZ!m#dapbUZ zah=8A!O;cB&nWa%@rTduw)rOm#5QJA7QJp8(?=`4;Tw*6y!%G>j}Jy9A2{~*xt1q) zh+aedUpucoywIBS%JWP3Xp^@~inEzWlR(q+ZGH+GpR-jo-mo&h=6<8y^<447D{Ze} zdj{(P+CsV!t?kLq0h3erL*C#U)&AfKiDRD))#$e$t&tU2(mca;<8##!ZV5VR>{PF= z2-fgI6Hgo|zGf?88Q_$wCr4kF;G#l$-u>Ww(B<(R=G$0WG_w zp#CtuHC~%aQ`{MX$eXZME&K9xu<3Erg*UEw@?JJO64$uaPsP zd2h|KHgd)xT5U_NNl?tY4SvOJ%pa8C1;o>Ba+ z(RslX-Z5*Lr|QbXqshNR=_~|Hc;i@qhkdH{JX)JnHdDppw(Gn3&|PO1rh-PUH^KUG z=D+DI%pxV@cjnJH$-eTead4~ZI5W_3o|oCHJaKaV<|p62HD{t978SIrYDzssaFQAS zM)sD5n?9Di%#vPS%X``6CRl`-v^R)W!R%2gDizk?$_0alB3B304yQ4$Qip;&1D%dE z2?KF#*3kOp`0w}StzY)t^5nSB$roK|NBx+j&%L1p2c7e4=6#oHhArpuOvOhjcilTZ z<-+bvd(XWrMa;+AvaZx_OfZyCPaOT0dM}fHUZ|%qdir_LuUrl6eVcT(r^^>wvo%*u zp4j6yKKNFxdsi0I<}~x>@kj?2g+NVdt=3xQnW(%1y~B zO^#qR6wfijV}Ywil;Q8M=k0gqLY|CKJRL`^>UL)9TIVC;rExuGtaVuAZ(I{q7~X#3 zRhT@ncb=^_{_x4~66!Wd84dTE`K#QEO#HTG;Z-D+4r?ahy({u)mf4&%Z?K8I&!Ul^ zk$Q-Vn}4f7b&tuB+Tj*o$J@y}BENj{9m6?t&wumqme3m6z!2TkS+%i(={NyS3Cq^2 zIu9%{`S_a_ z)W%p?Jy&k$a}ohSFn6N1SNkVBCQrWYlkbP)uP%*joy&}1mI#ii{JchVq41RUbW`{0 zoQ+&})3`VCkDiZ6+N9LNT$frh@u{)M`J_gRp5+zYSF*<^4clr|6t)!{E05_u=2=j9 z_iVu8L!OjMm8|o@y0^pDHl8$#ZMbIYAZ!q_G`KH=+GwImKw&{^tpAof+A0e}> zEoSHJi%)M}bG9I-xMY8KM8W~T7|E=dU!SjJM$q_Mm^4cF4061?aHPRps0`m-t>i!5;DUxx0F$g>i5Uw(umM@r%1tk%54Gv@bYA5FAm_JT{U&#CYGZ=~yUo#W3 zLQylo-|JMNm}}VD{@jxm;7LQdwuka$;qSV;^an(h_~wM-Ykf`n2qbfmY%>?t1K_*CKgg0%MeKi*XF6J05PrJC}O%yL!r(p`H z${y2D39DDHR$34REo|Q3O<*+AoRF}rUlZu3$zLi~mMniSUG9=ad%_o?o}X`gx*P}h zWM%LLMXVHY9sF2(d;6*@9RN=ETvBUiW5J;thgysz5##-Zsq?0tLK_L3QT$3zahF=l zxU&_ep-I&ZXpbMLT^DF9dLjEbqFGT0!klde^f4sjl;GLSp`0&g$yX$KSYmG3;tOM0 zYOn3UrOelQEytp8cYlqDGYd?a6nuLi;@EK|_Y&gVfT9?-PNJtE9{mNFDB}l(K(}+b zc=h+;>^1*s7l!p9IbgP^^dA;=*5E;YX zm*B-o&km4UqA3jD5?G=<#zAMlEMuB%2+VmMf?M9aHbQzWVvQz?x!V<@xKx-Csslc7$lE$5JjKO8oD?{`l%{ zyuFE`wQEa~4cCZ4j_I`1Up98>IwKy6J#>ooA-4a5KhL#-VJ!A6o_g5sdYcK^=1 zW%FhY2zi_UKH59&lkn6>`Ax7+=1L$@07sE`Z5n^Y6TSyc&t*2DN&K9JHsm5{>wUK{ z=uW;obJ(9z!jOEr)}da)d|T!y2Du?{2z61E^MCt}IuE&z9FZqHB9Nl$ale3o)}H0x zCkQ3VQ}pu&Ns=Qgj^t30pmKapD-_eDhXETC14n|EKZ}rJ&fdxkO-j5HL<{r|Z12Co zaUI}4k~SzG1k&OK;1xrJ%RdAjM_SkLYK`;<0Fy|j`D3W=%p-v(xVX6Z40aNb1RXg! z>GZ|$=0c2jM_+*%2#zRp-&zn7{N1@nFF_0n6jV6P`QMlJlObpOmI~l#(2Qt=k!B3x z9f^Sate}vbN5{>qKwdiu_9M3oT5)v zlny}UDv#2feIi(Iwbq6$ygD9m8wssSxQywzx{Co~FWA&x9ARdDXJyo`@~3|sfLe*f zDZlu)7cCpMJeyupt9PfT3{_CPi)knneGf}h-Mip0EO7`n&r1eBdX$J-#r?`&2u=VM zkx=*fth7w8wdmM+DbE85;qm2x$#Qo*jU|IfbJgFs(syss$D`NWfa15;jaf z;T5@IEL5!5_SC6c#H=MEvlkbD8sJq>{+) zqRatHlZc_#?5(B*4h8v3Xa~_*YfMSf3u2BpJ+@$+q8Q5HvxSnA=2+}8e++QZv?+x< z9e0pVtfsgVKrLBB4@8jzl8uE$2FT($1jazxr1V6P30bIk^vPgP0O>r&FJ%TK1jgYA z6a>KhvFREKuLk1+2m6cUDn-I@k0to!xFFR$Jk~b^kp_~B%xgI;odyzLiTgngg3+}7 znDhl|cm^j^9XMsl zUpC#252~}onRxI1eG+#?yLYdi=^Wu$ffQpY%ws*JdwWx&cdPi9WbrX<`R6kE+UH;| zsGm}9fAPYFY2>e^KnX$aBl4+mbvBL;sv=jD0Ef6INhsoQW;J&s{`ZD|(}^k1b~cZE z?9B`c4n~~a(rHv0>WYf@@I_5?J3oTnr#4oH9tyX5Ah|Az2Vn(Z_bLKDxZ zss=)BBIdA-55DyCpmP2`LR1)7Z){`ut2R{P`~5p<-^j!kJl#}KTB$GP*1pg zrO@0e-EfTd6Qdf}p?^~yOQ}W&Zu+1~O_QxyT-a4uTw`ltTybD7k|w_0;PLXcVyUIo zO`>fcN?#n=jWwnHgu`ydTPxI#anu!7%Fy?4ma2WnSNIu{G$g1n|Lc}oz<5n-(7Uhf zwRXvwd4fmVdH(sP+}e7)4E*drY){+lXK(2LPFk6uIExg8i;-Wl`K#@ezNoAD#$ErV zc}3iRDx3OPN*h;{ZCUYK+SBYRLYGu5NbXRZHwrs5aFCNepF zzMAs>Ph_@GW6ZAKyGC+OpO#G%pH+)VzyB)nTH6o1mNCXDr|i=BJ%y7q7Lk(UiE-VT z&Vz!@2j8V`NSXbu)VtG(I``v5(2J#q3BQeZUa7LRP^~yHUS((5-g_{DhCnX=AS-dZ zM!&^6?_qBjFLw3*9^LeUkwfTp>b()6<~vH^dYQWuuCDkQSW`Xnrw>mX6Te*JIFu$W zL;ZU9z27Gsy|O5CdqO1!?@j!A&3U_9R6h;{y(W{{vV6^UXhHt|hF|Yjy)NYUbdPTJ z`oX{!#7tMv={77GV`i{Dac=yE<3)p%=r4K)6bg>NeV(SnGBr5y(K)en`1gI?uHaFX zNR=OU4;po;*wK)cLpqyg+E}hXc2amY zvi_0d(c1Tn4LYSCjsO0#lqafKPn$&6x?bThcxa7qhOXEpfO5hSyN&BPBz9G$1=;?& zNn4oiKhs#OpMZurhp!{%b<9p5?K2wd9&0q~{XUwaz^nAv{epp(QkN9w;X=;a+RI>K->V*) z@b4)QuTj2sF)hJkXZw5WB`5P}eQUP-QWhX89u;573)RIFbUU@cHk7;RzfhE4qDsXT zJ!Zm9Vk5J{>Gys$0*;!K7T)xqGr+bbuA}STuURGY@^^DAEM+Zd{ z5>h`iQ$5}sK{I@}N49+bCkIPgF|Q0^4w2WXBTa`w*G}*_C(pgLHTLw4w$R$%B+}I& zQYqtRW^~=`^~O}y_NsK2@(E4Hhz09^un^a^5t}pIJ?t5Rs@}+sANbFqptED#W~fv_ zZJo)NF6yk^yv)s;jwO%a}X z!e%B-uUnhkabmCqDz?^4Y-Q=WlAhUrVGQxDxAhEX1*K0Vi;1cH1~?wD}Jr!SVl%NU&uwb+wclIsEQVsna+o&}GPb`^Q6S*)c;Pm;fFW z&E4byLN2g5GqBkvxxUoFDe9_}D-b8rpZ@)osYXIHU!|FV$<=}srw5s)9ED-0@gmvZ zXj7H=bXNy_nK1~9{ZcC(BpjtKch2(FWYn#4^EHS5y%TI({{OIr+CGQyTVeO7)6N7- zW-S-+DNlW&($KNdX|2#12s>`97tMLNay)F}?8_wMX9~ZATTX5|y7zw}1VM3fR|l-- zw<=+k_*-9SOp(}?tjr*hA)w)WImsH($Y>JRD}mskDFe&h7RjpZ_tM{+@g9F3cy{Zx zry1M)V&qd|79y!W)~e(@m3!D;*LC)?TmNkjowL4=sUn+%n?5mC>zpT&DBQPsJ4&et zNe^VD^WR`?_2K`~x|{0$n+Z?(Df1shnez4Kf3^pubp2odo_!SWtq1=TG)!SUCHT*o zPoaq4vEBX8c|q~m*889Q^*;xI)&KL?&y!(Uu9+yWtq384u8$|QM%MN%@V^)W+|>XXHId-XAWjRy>_GMHyJ62_fS;v>#>5G7mSmLcqI>@R zO^GtHC=5gnAAZrI1e1k0Ogm^|{8SxXqY1BuXs}P?FccFh*)Vp35ETg%wC`o$$8eYGp+-%qy*<8Vw8a!y<=mSNtf#e(drCZahGvFfB$B_TG3ci%%fB3 z-}rAWBFU8)8c48iB2d}y@$=;WY42QvqAa60zK*3crH+jE4A59imP7_cNI)_TT~tsm zi*nUw0FfM$MiI?{oOF!xwt*!U$PCDBSVoZ$1Z5&o)&abLj#8lJLJOjaf>~br9d`ZT z*JheNw4ZU;eV_Mv&- ziHf!kX!yAS%flt?;rXelsovo>=HJki7kW_h7FiR^wym<4xpXD?!kkFd%4;wsl3gTM z_DOy~f%Dn&Fpi8HfnEf6NGZsYQS#Klu9F{@qz^ZJV`^EU#%ELlBVal2t14>1js(hV z#Z~SFFR1n1eiOuW?Ty;c40^ig7~}>vFFvH-<2x?`3^Q%lF87i?+Q-NU-HotckaQy_ zxh#~QkgL7eChkA;^=d!AD}dE4r&FF^*0J|`1fWNMEhhQQ z);uP8DMp(Ui2{Vjl@^NNMun`D6!j%z^?LTlujnE&!ZsMo9S^P=Z@~&Gto@{E5oBI6 zK$T}C$I8xUbJHWUKC~Rk#$GLd3J!_L$w7 zEr&G72=OTEnX9eLJd0F^hSviv`0%(PGhTQ9l{%qLAQ_6}%Cxt;y7D2Yy1^7@zI1WJ zhYb@(7z$SODvW|9K;mc^z;h4^oAc!P;jNrF6Y#gEdDt(ge>^`P2m@)OO*7cZc6msX!VfIg`7fXZH(yDI<(gn510dKyEZ zBpi3IT|7=jJ2y%2W<<`zs|-#=T!rxqavoSL>+TX$`(EKwY}#X z8djikl31!d69@`8JH3onqU^2|LscOzNl-zZuoTxq zpg{<~MU^3c z*`XtdkHScb+AL9nW;8F_RsO zTZ=-ML@t~5HO@_u4&%@_p&bV9l!car9{q3!e8_L(M#RR(##2HSkrnN8zVnOCR-UW5 ze3c#vVoni63cJ*C@HlJF&C78C1=ja&>LIL(YAiM+Zj7v}^p`Z6FY>(^h2?Sfeh2pN z&*7CG#D;05ccbU)xzRB(a_hXd4z#2W89J6Zx`9L1rN-HtK8IG}@3`jOu($UhzU;EH zn@zt+XbnE(tEJt{xAV+35Ji*A`AYXfxCr` Date: Mon, 26 Jun 2017 12:28:42 +0200 Subject: [PATCH 52/72] fix image links --- DNS.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/DNS.md b/DNS.md index 49eedac..5d3c935 100644 --- a/DNS.md +++ b/DNS.md @@ -4,7 +4,7 @@ DNS services are provided by OTC since months. Now the complete stack is reworked so we can take a closer look on API service. -![OTC Dashboard]|pictures/otc-dns.png +![OTC Dashboard](/pictures/otc-dns.png) The service is located on the dashboard in the network services area. There are 3 main features as you can see on the screen: @@ -17,7 +17,7 @@ Private Zones and PTR-Records are completly new. If you have older implementation with setup reverse zones, please update to the new one. A good thing: it's simple! -![OTC API]|pictures/otc-dns-api.png +![OTC API](/pictures/otc-dns-api.png) Docmentation can you found at https://docs.otc.t-systems.com/en-us/dns_dld/index.html From 47509a0dd8811d11954154ef9470f99f58733d75 Mon Sep 17 00:00:00 2001 From: Frank Kloeker Date: Mon, 26 Jun 2017 14:05:08 +0200 Subject: [PATCH 53/72] fix doc --- pictures/tenant-ini-dns.png | Bin 0 -> 111382 bytes tenant.ini | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) create mode 100644 pictures/tenant-ini-dns.png diff --git a/pictures/tenant-ini-dns.png b/pictures/tenant-ini-dns.png new file mode 100644 index 0000000000000000000000000000000000000000..1b49e24eafa54114d573b1ca9b3608643064ff9c GIT binary patch literal 111382 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 w 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 Date: Mon, 26 Jun 2017 14:09:07 +0200 Subject: [PATCH 54/72] fix ansible installation --- CONNECT.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONNECT.md b/CONNECT.md index 92ea382..22fbffd 100644 --- a/CONNECT.md +++ b/CONNECT.md @@ -7,7 +7,7 @@ Install prerequisites as root on your Ubuntu 16.04 machine: ``` apt-get update -apt-get -y install curl git ansible python-openstackclient python-pip python-jmespath python-netaddr libs3-2 jq +apt-get -y install curl git python-openstackclient python-pip python-jmespath python-netaddr libs3-2 jq pip install python-otcclient pip install ansible==2.2.0.0 ``` From f2966ad554918aee2f5bd18313089d2160ff41f8 Mon Sep 17 00:00:00 2001 From: Frank Kloeker Date: Mon, 26 Jun 2017 14:33:44 +0200 Subject: [PATCH 55/72] fix vpc id --- tenant.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tenant.ini b/tenant.ini index 14ee22f..1bd8f00 100644 --- a/tenant.ini +++ b/tenant.ini @@ -3,7 +3,7 @@ # usage ecs: # ansible-playbook -i hosts tenant_create.yml -e "ecs_name=ansible-test101" # usage dns (public and internal usage, only in selected vpc): -# ansible-playbook -i hosts dns_create.yml -e "vpc_name=ansible-vpc01" +# ansible-playbook -i hosts dns_create.yml -e "vpc_name=ansible-vpc101" # [DEFAULT] image_name=Community_Ubuntu_16.04_TSI_latest From 3d5ac3012fd81dd449cbf3d1e6732662bafa0584 Mon Sep 17 00:00:00 2001 From: Frank Kloeker Date: Mon, 26 Jun 2017 14:35:20 +0200 Subject: [PATCH 56/72] complete doc --- DNS.md | 87 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 87 insertions(+) diff --git a/DNS.md b/DNS.md index 5d3c935..72ace76 100644 --- a/DNS.md +++ b/DNS.md @@ -21,5 +21,92 @@ A good thing: it's simple! Docmentation can you found at https://docs.otc.t-systems.com/en-us/dns_dld/index.html +Let's start to implement some DNS entries via API. We will do this with Ansible. +First of all we need connection to OTC. Use the [Connect Cheat Sheet](https://github.com/eumel8/ansible-otc/blob/poc_dns_v2/CONNECT.md) + +It's a good idea to install openstack-client because ansible will use +the same os-client-config. + +``` +git clone -b poc_dns_v2 https://github.com/eumel8/ansible-otc.git +cd ansible-otc +cp secrets.yml _secrets.yml +``` +In _secrets.yml are only S3 credentials stored. You need to adjust *env.yml* +with the used profile name in clouds.yml + +Imagine we have a tenant.ini with the configuration of all resources in our tenant. +DNS configuration are also there: + +![tenant.ini](/pictures/tenant-ini-dns.png) + +Public zones are isolated on OTC. You can host your zones there but there +is no registration service to catch new domains. This means you need to +delegate your elsewhere registered domains to + +**ns1.open-telekom-cloud.com** and **ns2.open-telekom-cloud.com** + +Before you need to configure your zone in OTC (see below) because the domain +(and all sub-domain) are uniq bound to one tenant. If someone else has +configured the domain, you need the service desk to clarify. + +Private zones are only reachable in the selected VPC and with the resolver host **100.125.4.25** + +Reverse DNS (PTR records) are only provided for public ip (EIP). The +ip address must assigned to your tenant to set the PTR record + +Related playbooks are **zone_create.yml**, **zonerecord_create.yml** and **ptrrecord_create.yml** + +Lets start e virtual machine with a fix private address and a allocated EIP: + +``` +ansible-playbook -i hosts tenant_create.yml -e "ecs_name=ansible-testi101" +``` + +In this play we allocate all resources to boostrap our ECS instance, set the floating ip +address and the reverse DND + +``` +ansible-playbook -i hosts dns_create.yml -e "vpc_name=ansible-vpc01" +``` + +Here we create zones and zonerecords. API works asynchron so if job processing is slow +you need to repeat the step if the zone is not ready when zonerecords are added. + +Tests: +``` +$ host -t A ansible-test101.ansible.otc.telekomcloud2.com ns1.open-telekom-cloud.com +Using domain server: +Name: ns1.open-telekom-cloud.com +Address: 46.29.103.61#53 +Aliases: + +ansible-test101.ansible.otc.telekomcloud2.com has address 160.44.207.211 + +$ host -t A 160.44.207.211 ns1.open-telekom-cloud.com +Using domain server: +Name: ns1.open-telekom-cloud.com +Address: 46.29.103.61#53 +Aliases: + +211.207.44.160.in-addr.arpa domain name pointer ansible-test101.ansible.otc.telekomcloud2.com. + +$ host ansible-test101.ansible.internal.corp 100.125.4.25 +Using domain server: +Name: 100.125.4.25 +Address: 100.125.4.25#53 +Aliases: + +ansible-test101.ansible.internal.corp has address 192.168.0.101 + +``` + +Remove DNS reverse entry: + +``` +ansible-playbook -i hosts ptrrecord_delete.yml -e "public_ip_address=160.44.207.211" +``` + +End of PoC. Look at the other plays and roles to interact with OTC API From a2a904c24ba7d08616967dd8fce9dd3358138e5f Mon Sep 17 00:00:00 2001 From: Frank Kloeker Date: Mon, 26 Jun 2017 14:37:12 +0200 Subject: [PATCH 57/72] add link --- DNS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DNS.md b/DNS.md index 72ace76..8c374b6 100644 --- a/DNS.md +++ b/DNS.md @@ -108,5 +108,5 @@ Remove DNS reverse entry: ansible-playbook -i hosts ptrrecord_delete.yml -e "public_ip_address=160.44.207.211" ``` -End of PoC. Look at the other plays and roles to interact with OTC API +End of PoC. Look at the [other plays and roles](https://github.com/eumel8/ansible-otc) to interact with OTC API From b61fa6cba040e8b29bfc622ddcaaacddb83d5a05 Mon Sep 17 00:00:00 2001 From: Frank Kloeker Date: Mon, 26 Jun 2017 14:39:35 +0200 Subject: [PATCH 58/72] fix md --- DNS.md | 1 - 1 file changed, 1 deletion(-) diff --git a/DNS.md b/DNS.md index 8c374b6..e54ed37 100644 --- a/DNS.md +++ b/DNS.md @@ -1,5 +1,4 @@ # OTC DNS - the complete example -============================== DNS services are provided by OTC since months. Now the complete stack is reworked so we can take a closer look on API service. From 929dcfa04ad5de065d77dc81332d3dca08ae47da Mon Sep 17 00:00:00 2001 From: Frank Kloeker Date: Mon, 26 Jun 2017 16:22:55 +0200 Subject: [PATCH 59/72] fixup md --- DNS.md | 40 +++++++++++++++++++++++++++++++--------- 1 file changed, 31 insertions(+), 9 deletions(-) diff --git a/DNS.md b/DNS.md index e54ed37..e2f7fdc 100644 --- a/DNS.md +++ b/DNS.md @@ -33,16 +33,37 @@ cd ansible-otc cp secrets.yml _secrets.yml ``` In _secrets.yml are only S3 credentials stored. You need to adjust *env.yml* -with the used profile name in clouds.yml +with the used profile name in clouds.yml. Ignore the *_secrets.yml* settings + +``` +# adjust account data here or in clouds.yml +USERNAME: "" +PASSWORD: "" +DOMAIN: "OTC-EU-DE-0000000000100000XXXX" +PROJECT_NAME: "eu-de" + +EC2_ACCESS_KEY: "" +EC2_SECRET_KEY: "" +EC2_URL: "https://obs.otc.t-systems.com" + +# endpoint urls +IAM_AUTH_URL: "https://iam.{{ PROJECT_NAME }}.otc.t-systems.com/v3" +AUTH_URL_ELB: "https://elb.{{ PROJECT_NAME }}.otc.t-systems.com/v1.0" +AUTH_URL_ECS_CLOUD: "https://ecs.{{ PROJECT_NAME }}.otc.t-systems.com/v1" +AUTH_URL_RDS: "https://rds.{{ PROJECT_NAME }}.otc.t-systems.com/rds/v1" +``` + +Service endpoint for DNS is provided by IAM, so it's not necessary to setup. + Imagine we have a tenant.ini with the configuration of all resources in our tenant. DNS configuration are also there: ![tenant.ini](/pictures/tenant-ini-dns.png) -Public zones are isolated on OTC. You can host your zones there but there +**Public** zones are isolated on OTC. You can host your zones there but there is no registration service to catch new domains. This means you need to -delegate your elsewhere registered domains to +delegate your elsewhere registered domains to the public OTC server: **ns1.open-telekom-cloud.com** and **ns2.open-telekom-cloud.com** @@ -50,20 +71,21 @@ Before you need to configure your zone in OTC (see below) because the domain (and all sub-domain) are uniq bound to one tenant. If someone else has configured the domain, you need the service desk to clarify. -Private zones are only reachable in the selected VPC and with the resolver host **100.125.4.25** +**Private zones** are only reachable in the selected VPC and with the resolver host **100.125.4.25** + +**Reverse DNS** (PTR records) are only provided for public ip (EIP). The +ip address must assigned to your tenant to set the PTR record. -Reverse DNS (PTR records) are only provided for public ip (EIP). The -ip address must assigned to your tenant to set the PTR record +Related playbooks are *zone_create.yml*, *zonerecord_create.yml* and *ptrrecord_create.yml* -Related playbooks are **zone_create.yml**, **zonerecord_create.yml** and **ptrrecord_create.yml** -Lets start e virtual machine with a fix private address and a allocated EIP: +Lets start a virtual machine with a fixed private ip address and a allocated EIP: ``` ansible-playbook -i hosts tenant_create.yml -e "ecs_name=ansible-testi101" ``` -In this play we allocate all resources to boostrap our ECS instance, set the floating ip +In this play we allocate all resources to bootstrap our ECS instance, set the floating ip address and the reverse DND ``` From 9c3a84676d3db70d92707399c924a5dcc129c658 Mon Sep 17 00:00:00 2001 From: Frank Kloeker Date: Mon, 26 Jun 2017 16:23:47 +0200 Subject: [PATCH 60/72] fixup md --- DNS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DNS.md b/DNS.md index e2f7fdc..a55852e 100644 --- a/DNS.md +++ b/DNS.md @@ -18,7 +18,7 @@ A good thing: it's simple! ![OTC API](/pictures/otc-dns-api.png) -Docmentation can you found at https://docs.otc.t-systems.com/en-us/dns_dld/index.html +Documentation can you found at https://docs.otc.t-systems.com/en-us/dns_dld/index.html Let's start to implement some DNS entries via API. We will do this with Ansible. From 7132be02bacce1550818254c9f16ad8af6e9a5d4 Mon Sep 17 00:00:00 2001 From: Frank Kloeker Date: Tue, 27 Jun 2017 07:10:49 +0200 Subject: [PATCH 61/72] fix typo --- DNS.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/DNS.md b/DNS.md index a55852e..a505d91 100644 --- a/DNS.md +++ b/DNS.md @@ -79,14 +79,14 @@ ip address must assigned to your tenant to set the PTR record. Related playbooks are *zone_create.yml*, *zonerecord_create.yml* and *ptrrecord_create.yml* -Lets start a virtual machine with a fixed private ip address and a allocated EIP: +Lets start a virtual machine with a fixed private ip address and an allocated EIP: ``` ansible-playbook -i hosts tenant_create.yml -e "ecs_name=ansible-testi101" ``` In this play we allocate all resources to bootstrap our ECS instance, set the floating ip -address and the reverse DND +address and the reverse DNS ``` ansible-playbook -i hosts dns_create.yml -e "vpc_name=ansible-vpc01" From c79ec8bbac285c9e4ea81a5f0d224a37159be000 Mon Sep 17 00:00:00 2001 From: Frank Kloeker Date: Wed, 28 Jun 2017 22:37:38 +0200 Subject: [PATCH 62/72] first version of buildservice playbook --- buildservice.yml | 153 +++++++++++++++++++++++++++++++++++++++++++ buildservice_var.yml | 25 +++++++ 2 files changed, 178 insertions(+) create mode 100644 buildservice.yml create mode 100644 buildservice_var.yml diff --git a/buildservice.yml b/buildservice.yml new file mode 100644 index 0000000..c0a5080 --- /dev/null +++ b/buildservice.yml @@ -0,0 +1,153 @@ +--- + +- hosts: localhost + gather_facts: no + connection: local + vars_files: + - buildservice_var.yml + vars: + date: "{{ lookup('pipe', 'date +%Y%m%d') }}" + tasks: + - name: Download ubuntu image {{ distro }} + get_url: + url: "https://cloud-images.ubuntu.com/{{ distro}}/current/{{ distro }}-server-cloudimg-amd64-disk1.vmdk" + dest: "./{{ distro }}-server-cloudimg-amd64-{{ date }}.vmdk" + force: yes + +- hosts: localhost + gather_facts: no + connection: local + vars: + date: "{{ lookup('pipe', 'date +%Y%m%d') }}" + distro: "xenial" + bucket: "buildservice" + ecs_name: "buildserver" + object: "{{ distro }}-server-cloudimg-amd64-{{ date }}.vmdk" + vars_files: + - buildservice_var.yml + roles: + - role: s3_bucket_create + - role: s3_upload + +- hosts: localhost + gather_facts: no + connection: local + vars: + date: "{{ lookup('pipe', 'date +%Y%m%d') }}" + distro: "xenial" + bucket: "buildservice" + image_name: "{{ distro }}-server-cloudimg-amd64-{{ date }}" + image_url: "{{ bucket }}:{{ distro }}-server-cloudimg-amd64-{{ date }}.vmdk" + image_min_disk: 12 + roles: + - role: token + - role: image_create + + tasks: + - name: check image status + uri: + url: "{{ AUTH_URL_IMS }}/v2/cloudimages?name={{ image_name }}" + method: GET + return_content: yes + validate_certs: yes + HEADER_Content-Type: "application/json" + HEADER_X-Auth-Token: "{{ token['x_subject_token'] }}" + when: image_name is defined + register: ims_result + until: (ims_result.content|from_json)|json_query('images[].status|[0]') == 'active' + retries: 50 + delay: 10 + +- name: Create VPC + hosts: localhost + gather_facts: no + vars_files: + - buildservice_var.yml + roles: + - role: token + - role: lookup_name + - role: vpc_create + +- name: Create Subnet + hosts: localhost + gather_facts: no + connection: local + vars_files: + - buildservice_var.yml + roles: + - role: token + - role: lookup_name + - role: subnet_create + +- name: Create Secgroup + hosts: localhost + gather_facts: no + vars_files: + - buildservice_var.yml + roles: + - role: token + - role: lookup_name + - role: secgroup_create + +- name: Create Secgrouprules + hosts: localhost + gather_facts: no + vars_files: + - buildservice_var.yml + roles: + - role: token + - role: lookup_name + - role: secgrouprule_helper + +- name: Create Keypair + hosts: localhost + gather_facts: no + vars_files: + - buildservice_var.yml + roles: + - role: token + - role: lookup_name + - role: keypair_create + +- name: Create EIP + hosts: localhost + gather_facts: no + vars_files: + - buildservice_var.yml + roles: + - role: token + - role: lookup_name + - role: eip_apply + +- name: Create ECS + hosts: localhost + gather_facts: no + vars_files: + - buildservice_var.yml + vars: + date: "{{ lookup('pipe', 'date +%Y%m%d') }}" + distro: "xenial" + image_name: "{{ distro }}-server-cloudimg-amd64-{{ date }}" + roles: + - role: token + - role: lookup_name + - role: ecs_create + - role: job + +- name: Switch to ECS + hosts: localhost + gather_facts: no + vars_files: + - buildservice_var.yml + tasks: + - name: Wait for ssh + local_action: wait_for + args: + port: 22 + host: "{{ public_ip_address }}" + delay: 60 + + - name: Doing things on ECS + shell: apt-get -y install puppet-common; git clone https://github.com/dev-sec/puppet-os-hardening.git; sudo puppet apply --modulepath ./puppet-os-hardening -e "include os_hardening" + remote_user: ubuntu + delegate_to: "{{ public_ip_address }}" diff --git a/buildservice_var.yml b/buildservice_var.yml new file mode 100644 index 0000000..c2a3008 --- /dev/null +++ b/buildservice_var.yml @@ -0,0 +1,25 @@ +--- +ecs_name: "buildserver" +distro: "xenial" +bucket: "buildservice" +availability_zone: "eu-de-01" +vpc_name: "buildserver-vpc01" +vpc_net: "192.168.0.0/16" +subnet_name: "buildserver-subnet01" +subnet_net: "192.168.0.0/24" +subnet_gateway: "192.168.0.1" +subnet_dhcp_enable: true +subnet_primary_dns: 8.8.8.8 +subnet_secondary_dns: 8.4.4.8 +secgroup_name: "buildserver-secgroup01" +secgroup_rule1: "ingress;IPv4;tcp;22;22;0.0.0.0/0" +secgroup_rule2: "ingress;IPv4;icmp;;;0.0.0.0/0" +ecs_volumetype: "SATA" +ecs_ram: "2048" +ecs_vcpus: "2" +ecs_adminkey: "buildserver-key" +keypair_file: "~/.ssh/id_rsa.pub" +ecs_ipaddress: "192.168.0.100" +public_ip_address: "160.44.201.86" +eip_bandwidth_name: "buildserver-eip01" +eip_bandwidth_size: "500" From 17afd22dd107c3f2af41cf7f8f5536c7b6d40cb7 Mon Sep 17 00:00:00 2001 From: Frank Kloeker Date: Fri, 30 Jun 2017 09:38:27 +0200 Subject: [PATCH 63/72] more depedencies in secgrouprule_create variables --- .../secgrouprule_create/templates/request.json.j2 | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/roles/secgrouprule_create/templates/request.json.j2 b/roles/secgrouprule_create/templates/request.json.j2 index aad5af9..1102306 100644 --- a/roles/secgrouprule_create/templates/request.json.j2 +++ b/roles/secgrouprule_create/templates/request.json.j2 @@ -1,25 +1,25 @@ { "security_group_rule": { "direction": "{{ secgrouprule_direction }}", -{% if secgrouprule_ethertype is defined %} +{% if secgrouprule_ethertype is defined and secgrouprule_ethertype|length != 0 %} "ethertype": "{{ secgrouprule_ethertype }}", {% endif %} -{% if secgrouprule_ethertype is defined %} +{% if secgrouprule_ethertype is defined and secgrouprule_ethertype|length != 0 %} "ethertype": "{{ secgrouprule_ethertype }}", {% endif %} -{% if secgrouprule_protocol is defined %} +{% if secgrouprule_protocol is defined and secgrouprule_protocol|length != 0 %} "protocol": "{{ secgrouprule_protocol }}", {% endif %} -{% if secgrouprule_port_range_min is defined %} +{% if secgrouprule_port_range_min is defined and secgrouprule_port_range_min|length != 0 %} "port_range_min": {{ secgrouprule_port_range_min }}, {% endif %} -{% if secgrouprule_port_range_max is defined %} +{% if secgrouprule_port_range_max is defined and secgrouprule_port_range_max|length != 0 %} "port_range_max": {{ secgrouprule_port_range_max }}, {% endif %} -{% if secgrouprule_remote_ip_prefix is defined %} +{% if secgrouprule_remote_ip_prefix is defined and secgrouprule_remote_ip_prefix|length != 0 %} "remote_ip_prefix": "{{ secgrouprule_remote_ip_prefix }}", {% endif %} -{% if secgrouprule_remote_group_id is defined %} +{% if secgrouprule_remote_group_id is defined and secgrouprule_remote_group_id|length != 0 %} "remote_group_id": "{{ secgrouprule_remote_group_id }}", {% endif %} "security_group_id": "{{ secgroup_id }}", From 1199a243bc7247052047a3acab719f7c0f90770f Mon Sep 17 00:00:00 2001 From: Frank Kloeker Date: Fri, 30 Jun 2017 09:41:15 +0200 Subject: [PATCH 64/72] accept conflict status if secgrouprule already exists --- roles/secgrouprule_create/tasks/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/roles/secgrouprule_create/tasks/main.yml b/roles/secgrouprule_create/tasks/main.yml index 82b5f3d..8c358d3 100644 --- a/roles/secgrouprule_create/tasks/main.yml +++ b/roles/secgrouprule_create/tasks/main.yml @@ -6,7 +6,7 @@ follow_redirects: all return_content: yes validate_certs: yes - status_code: 200,201,202,203,204 + status_code: 200,201,202,203,204,409 HEADER_Content-Type: "application/json" HEADER_X-Auth-Token: "{{ token['x_subject_token'] }}" body: "{{ lookup('template', 'roles/secgrouprule_create/templates/request.json.j2')|to_json }}" From 9425103cc7c6aeba99b22d268c4f2def142572bf Mon Sep 17 00:00:00 2001 From: Frank Kloeker Date: Fri, 30 Jun 2017 13:27:48 +0200 Subject: [PATCH 65/72] full workflow for image build (works only with trusty due the missing python package in xenial cloud image --- BUILDSERVICE.md | 7 ++++++ buildservice.yml | 55 +++++++++++++++++++++++++++++++++++++++----- buildservice_var.yml | 21 +++++++++++++---- 3 files changed, 73 insertions(+), 10 deletions(-) diff --git a/BUILDSERVICE.md b/BUILDSERVICE.md index 6d8c5ef..cbb1ba4 100644 --- a/BUILDSERVICE.md +++ b/BUILDSERVICE.md @@ -6,3 +6,10 @@ Build your own images with ansible (WiP) 4. Login ssh, install, configure, doing things 5. Shutdown VM 6. Upload VM image to IMS (private, customize name) + +requirments: adjust buildservice_var.yml + +``` +ansible-playbook -i hosts buildservice.yml --vault-password-file vaultpass.txt +``` + diff --git a/buildservice.yml b/buildservice.yml index c0a5080..0460795 100644 --- a/buildservice.yml +++ b/buildservice.yml @@ -10,7 +10,8 @@ tasks: - name: Download ubuntu image {{ distro }} get_url: - url: "https://cloud-images.ubuntu.com/{{ distro}}/current/{{ distro }}-server-cloudimg-amd64-disk1.vmdk" +# url: "https://cloud-images.ubuntu.com/{{ distro}}/current/{{ distro }}-server-cloudimg-amd64-disk1.vmdk" + url: "https://cloud-images.ubuntu.com/{{ distro}}/current/{{ distro }}-server-cloudimg-amd64-disk1.img" dest: "./{{ distro }}-server-cloudimg-amd64-{{ date }}.vmdk" force: yes @@ -19,7 +20,7 @@ connection: local vars: date: "{{ lookup('pipe', 'date +%Y%m%d') }}" - distro: "xenial" + distro: "trusty" bucket: "buildservice" ecs_name: "buildserver" object: "{{ distro }}-server-cloudimg-amd64-{{ date }}.vmdk" @@ -34,7 +35,7 @@ connection: local vars: date: "{{ lookup('pipe', 'date +%Y%m%d') }}" - distro: "xenial" + distro: "trusty" bucket: "buildservice" image_name: "{{ distro }}-server-cloudimg-amd64-{{ date }}" image_url: "{{ bucket }}:{{ distro }}-server-cloudimg-amd64-{{ date }}.vmdk" @@ -97,7 +98,18 @@ roles: - role: token - role: lookup_name - - role: secgrouprule_helper + tasks: + - name: secgrouprule_create role + include_role: + name: secgrouprule_create + vars: + secgrouprule_direction: "{{ item.value.secgrouprule_direction }}" + secgrouprule_ethertype: "{{ item.value.secgrouprule_ethertype }}" + secgrouprule_protocol: "{{ item.value.secgrouprule_protocol }}" + secgrouprule_port_range_min: "{{ item.value.secgrouprule_port_range_min }}" + secgrouprule_port_range_max: "{{ item.value.secgrouprule_port_range_max }}" + secgrouprule_remote_ip_prefix: "{{ item.value.secgrouprule_remote_ip_prefix }}" + with_dict: "{{ secgroup_rules }}" - name: Create Keypair hosts: localhost @@ -126,7 +138,7 @@ - buildservice_var.yml vars: date: "{{ lookup('pipe', 'date +%Y%m%d') }}" - distro: "xenial" + distro: "trusty" image_name: "{{ distro }}-server-cloudimg-amd64-{{ date }}" roles: - role: token @@ -148,6 +160,37 @@ delay: 60 - name: Doing things on ECS - shell: apt-get -y install puppet-common; git clone https://github.com/dev-sec/puppet-os-hardening.git; sudo puppet apply --modulepath ./puppet-os-hardening -e "include os_hardening" + shell: apt-get -y install git puppet-common; rm -rf modules; mkdir modules; git clone https://github.com/dev-sec/puppet-os-hardening.git modules/os_hardening; git clone https://github.com/thias/puppet-sysctl.git modules/sysctl; git clone https://github.com/puppetlabs/puppetlabs-stdlib.git modules/stdlib; puppet apply --modulepath ./modules -e "include os_hardening" + remote_user: ubuntu + become: true + become_user: root + delegate_to: "{{ public_ip_address }}" + + - name: Stop ECS + shell: halt remote_user: ubuntu + become: true + become_user: root delegate_to: "{{ public_ip_address }}" + + - name: Wait for shutdown + local_action: wait_for + args: + host: "{{ public_ip_address }}" + port: 22 + state: "stopped" + delay: 60 + +- name: Create new image + hosts: localhost + gather_facts: no + vars: + ecs_name: "buildserver" + date: "{{ lookup('pipe', 'date +%Y%m%d') }}" + image_name: "buildserver-{{ date }}" + vars_files: + - buildservice_var.yml + roles: + - role: token + - role: lookup_name + - role: image_create diff --git a/buildservice_var.yml b/buildservice_var.yml index c2a3008..28b328f 100644 --- a/buildservice_var.yml +++ b/buildservice_var.yml @@ -1,6 +1,6 @@ --- ecs_name: "buildserver" -distro: "xenial" +distro: "trusty" bucket: "buildservice" availability_zone: "eu-de-01" vpc_name: "buildserver-vpc01" @@ -12,9 +12,22 @@ subnet_dhcp_enable: true subnet_primary_dns: 8.8.8.8 subnet_secondary_dns: 8.4.4.8 secgroup_name: "buildserver-secgroup01" -secgroup_rule1: "ingress;IPv4;tcp;22;22;0.0.0.0/0" -secgroup_rule2: "ingress;IPv4;icmp;;;0.0.0.0/0" -ecs_volumetype: "SATA" +secgroup_rules: + 1: + secgrouprule_direction: ingress + secgrouprule_ethertype: IPv4 + secgrouprule_protocol: tcp + secgrouprule_port_range_min: 22 + secgrouprule_port_range_max: 22 + secgrouprule_remote_ip_prefix: 0.0.0.0/0 + 2: + secgrouprule_direction: ingress + secgrouprule_ethertype: IPv4 + secgrouprule_protocol: icmp + secgrouprule_port_range_min: null + secgrouprule_port_range_max: null + secgrouprule_remote_ip_prefix: "0.0.0.0/0" +ecs_volumetype: "SSD" ecs_ram: "2048" ecs_vcpus: "2" ecs_adminkey: "buildserver-key" From e0e618d8e9407c5a7e9b2caf636f5e851a5fadcb Mon Sep 17 00:00:00 2001 From: Frank Kloeker Date: Sat, 1 Jul 2017 14:16:00 +0200 Subject: [PATCH 66/72] clean variables, improve image creation, delete vm --- BUILDSERVICE.md | 9 +++++-- buildservice.yml | 60 ++++++++++++++++++++++++++++++++------------ buildservice_var.yml | 2 +- 3 files changed, 52 insertions(+), 19 deletions(-) diff --git a/BUILDSERVICE.md b/BUILDSERVICE.md index cbb1ba4..64eb39a 100644 --- a/BUILDSERVICE.md +++ b/BUILDSERVICE.md @@ -7,9 +7,14 @@ Build your own images with ansible (WiP) 5. Shutdown VM 6. Upload VM image to IMS (private, customize name) -requirments: adjust buildservice_var.yml +requirements: ** adjust buildservice_var.yml ** + + +usage: ``` -ansible-playbook -i hosts buildservice.yml --vault-password-file vaultpass.txt +ansible-playbook -i hosts buildservice.yml -e "distro=trusty" --vault-password-file vaultpass.txt + +ansible-playbook -i hosts buildservice.yml -e "distro=xenial" --vault-password-file vaultpass.txt ``` diff --git a/buildservice.yml b/buildservice.yml index 0460795..36abf7a 100644 --- a/buildservice.yml +++ b/buildservice.yml @@ -10,7 +10,6 @@ tasks: - name: Download ubuntu image {{ distro }} get_url: -# url: "https://cloud-images.ubuntu.com/{{ distro}}/current/{{ distro }}-server-cloudimg-amd64-disk1.vmdk" url: "https://cloud-images.ubuntu.com/{{ distro}}/current/{{ distro }}-server-cloudimg-amd64-disk1.img" dest: "./{{ distro }}-server-cloudimg-amd64-{{ date }}.vmdk" force: yes @@ -20,7 +19,6 @@ connection: local vars: date: "{{ lookup('pipe', 'date +%Y%m%d') }}" - distro: "trusty" bucket: "buildservice" ecs_name: "buildserver" object: "{{ distro }}-server-cloudimg-amd64-{{ date }}.vmdk" @@ -35,7 +33,6 @@ connection: local vars: date: "{{ lookup('pipe', 'date +%Y%m%d') }}" - distro: "trusty" bucket: "buildservice" image_name: "{{ distro }}-server-cloudimg-amd64-{{ date }}" image_url: "{{ bucket }}:{{ distro }}-server-cloudimg-amd64-{{ date }}.vmdk" @@ -138,7 +135,6 @@ - buildservice_var.yml vars: date: "{{ lookup('pipe', 'date +%Y%m%d') }}" - distro: "trusty" image_name: "{{ distro }}-server-cloudimg-amd64-{{ date }}" roles: - role: token @@ -159,38 +155,70 @@ host: "{{ public_ip_address }}" delay: 60 + - name: install python2 first + raw: apt-get -y install python-simplejson + remote_user: ubuntu + become: true + become_method: sudo + become_user: root + delegate_to: "{{ public_ip_address }}" + - name: Doing things on ECS - shell: apt-get -y install git puppet-common; rm -rf modules; mkdir modules; git clone https://github.com/dev-sec/puppet-os-hardening.git modules/os_hardening; git clone https://github.com/thias/puppet-sysctl.git modules/sysctl; git clone https://github.com/puppetlabs/puppetlabs-stdlib.git modules/stdlib; puppet apply --modulepath ./modules -e "include os_hardening" + shell: apt-get update; apt-get -y install git puppet; rm -rf modules; mkdir modules; git clone https://github.com/dev-sec/puppet-os-hardening.git modules/os_hardening; git clone https://github.com/thias/puppet-sysctl.git modules/sysctl; git clone https://github.com/puppetlabs/puppetlabs-stdlib.git modules/stdlib; puppet apply --modulepath ./modules -e "include os_hardening" remote_user: ubuntu become: true + become_method: sudo become_user: root delegate_to: "{{ public_ip_address }}" - name: Stop ECS - shell: halt + shell: /sbin/shutdown -H +1 & remote_user: ubuntu become: true become_user: root + become_method: sudo + ignore_errors: yes delegate_to: "{{ public_ip_address }}" - - name: Wait for shutdown - local_action: wait_for - args: - host: "{{ public_ip_address }}" - port: 22 - state: "stopped" - delay: 60 - -- name: Create new image +- name: Check ECS Status + hosts: localhost + gather_facts: no + vars: + ecs_name: "buildserver" + ecs_body: "{\"server\": { \"name\": \"{{ ecs_name }}\" }}" + roles: + - role: token + - role: lookup_name + tasks: + - name: Check API if ECS is stopped + uri: + url: "{{ AUTH_URL_ECS }}/servers/{{ ecs_id }}" + method: PUT + body_format: raw + follow_redirects: all + return_content: yes + validate_certs: yes + HEADER_Content-Type: "application/json" + HEADER_X-Auth-Token: "{{ token['x_subject_token'] }}" + body: "{{ ecs_body|to_json }}" + register: ecs_status + until: (ecs_status.content|from_json)|json_query('server.status') != 'ACTIVE' + retries: 50 + delay: 10 + +- name: Create new image and delete ECS hosts: localhost gather_facts: no vars: ecs_name: "buildserver" + delete_volume: true date: "{{ lookup('pipe', 'date +%Y%m%d') }}" - image_name: "buildserver-{{ date }}" + image_name: "buildservice-{{ distro }}-{{ date }}" vars_files: - buildservice_var.yml roles: - role: token - role: lookup_name - role: image_create + - role: ecs_delete + diff --git a/buildservice_var.yml b/buildservice_var.yml index 28b328f..013dddf 100644 --- a/buildservice_var.yml +++ b/buildservice_var.yml @@ -1,6 +1,6 @@ --- ecs_name: "buildserver" -distro: "trusty" +# distro: "xenial" bucket: "buildservice" availability_zone: "eu-de-01" vpc_name: "buildserver-vpc01" From 4962dfb2b3b6007c0d7a84e5f3115f2c43686743 Mon Sep 17 00:00:00 2001 From: Frank Kloeker Date: Sat, 1 Jul 2017 16:54:02 +0200 Subject: [PATCH 67/72] fix typo --- buildservice.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/buildservice.yml b/buildservice.yml index 36abf7a..e2a6be5 100644 --- a/buildservice.yml +++ b/buildservice.yml @@ -42,7 +42,7 @@ - role: image_create tasks: - - name: check image status + - name: Check image status uri: url: "{{ AUTH_URL_IMS }}/v2/cloudimages?name={{ image_name }}" method: GET @@ -155,7 +155,7 @@ host: "{{ public_ip_address }}" delay: 60 - - name: install python2 first + - name: Install python2 first raw: apt-get -y install python-simplejson remote_user: ubuntu become: true @@ -211,7 +211,7 @@ gather_facts: no vars: ecs_name: "buildserver" - delete_volume: true +# delete_volume: true date: "{{ lookup('pipe', 'date +%Y%m%d') }}" image_name: "buildservice-{{ distro }}-{{ date }}" vars_files: From 9bc87666c71e514842877366efdca10784d5233f Mon Sep 17 00:00:00 2001 From: Frank Kloeker Date: Sat, 1 Jul 2017 19:54:57 +0200 Subject: [PATCH 68/72] cleanup documentation --- BUILDSERVICE.md | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/BUILDSERVICE.md b/BUILDSERVICE.md index 64eb39a..4758560 100644 --- a/BUILDSERVICE.md +++ b/BUILDSERVICE.md @@ -1,4 +1,5 @@ -Build your own images with ansible (WiP) +Build your own images with ansible +================================== 1. Download Ubuntu Cloud Image 2. Upload to IMS (private, generic name) @@ -7,10 +8,19 @@ Build your own images with ansible (WiP) 5. Shutdown VM 6. Upload VM image to IMS (private, customize name) -requirements: ** adjust buildservice_var.yml ** +Supported OS: +============= +* Ubuntu 14.04 +* Ubuntu 16.04 -usage: +Requirements: +============= + +** adjust buildservice_var.yml ** + +Usage: +====== ``` ansible-playbook -i hosts buildservice.yml -e "distro=trusty" --vault-password-file vaultpass.txt @@ -18,3 +28,4 @@ ansible-playbook -i hosts buildservice.yml -e "distro=trusty" --vault-password- ansible-playbook -i hosts buildservice.yml -e "distro=xenial" --vault-password-file vaultpass.txt ``` +Easy to adapt for other operating systems From dde71a7902b26953a5aebbbab4f23ff76fd3cebe Mon Sep 17 00:00:00 2001 From: Frank Kloeker Date: Sat, 1 Jul 2017 19:56:55 +0200 Subject: [PATCH 69/72] cleanup documentation --- BUILDSERVICE.md | 2 ++ buildservice.yml | 45 +++++++++++++++++++++++++++++++++++---------- 2 files changed, 37 insertions(+), 10 deletions(-) diff --git a/BUILDSERVICE.md b/BUILDSERVICE.md index 4758560..d835df9 100644 --- a/BUILDSERVICE.md +++ b/BUILDSERVICE.md @@ -19,6 +19,8 @@ Requirements: ** adjust buildservice_var.yml ** +S3 credentials in _secrets.yml + Usage: ====== diff --git a/buildservice.yml b/buildservice.yml index e2a6be5..50e805e 100644 --- a/buildservice.yml +++ b/buildservice.yml @@ -8,11 +8,11 @@ vars: date: "{{ lookup('pipe', 'date +%Y%m%d') }}" tasks: - - name: Download ubuntu image {{ distro }} - get_url: - url: "https://cloud-images.ubuntu.com/{{ distro}}/current/{{ distro }}-server-cloudimg-amd64-disk1.img" - dest: "./{{ distro }}-server-cloudimg-amd64-{{ date }}.vmdk" - force: yes +# - name: Download ubuntu image {{ distro }} +# get_url: +# url: "https://cloud-images.ubuntu.com/{{ distro}}/current/{{ distro }}-server-cloudimg-amd64-disk1.img" +# dest: "./{{ distro }}-server-cloudimg-amd64-{{ date }}.vmdk" +# force: yes - hosts: localhost gather_facts: no @@ -26,7 +26,7 @@ - buildservice_var.yml roles: - role: s3_bucket_create - - role: s3_upload +# - role: s3_upload - hosts: localhost gather_facts: no @@ -39,7 +39,7 @@ image_min_disk: 12 roles: - role: token - - role: image_create +# - role: image_create tasks: - name: Check image status @@ -206,19 +206,44 @@ retries: 50 delay: 10 -- name: Create new image and delete ECS +- name: Create new image hosts: localhost gather_facts: no vars: ecs_name: "buildserver" -# delete_volume: true date: "{{ lookup('pipe', 'date +%Y%m%d') }}" image_name: "buildservice-{{ distro }}-{{ date }}" + image_job_id: "{{ (image_create.content|from_json)|json_query('job_id') }}" vars_files: - buildservice_var.yml roles: - role: token - role: lookup_name - role: image_create - - role: ecs_delete + tasks: + - name: Request job status from API + uri: + url: "{{ AUTH_URL_ECS_CLOUD }}/{{ PROJECT_ID }}/jobs/{{ image_job_id }}" + method: GET + return_content: yes + validate_certs: yes + HEADER_Content-Type: "application/json" + HEADER_X-Auth-Token: "{{ token['x_subject_token'] }}" + when: image_job_id is defined + register: jobstatus + until: (jobstatus.content|from_json)|json_query('status') == 'SUCCESS' + retries: 50 + delay: 10 +- name: Delete ECS + hosts: localhost + gather_facts: no + vars: + ecs_name: "buildserver" + delete_volume: true + vars_files: + - buildservice_var.yml + roles: + - role: token + - role: lookup_name + - role: ecs_delete From 729a19ede8d5c9b68e0ee22b944fb9808883b481 Mon Sep 17 00:00:00 2001 From: Frank Kloeker Date: Sat, 1 Jul 2017 19:58:05 +0200 Subject: [PATCH 70/72] final version. dependencies added between ecs create/delete and image create/delete --- buildservice.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/buildservice.yml b/buildservice.yml index 50e805e..4b1da6c 100644 --- a/buildservice.yml +++ b/buildservice.yml @@ -8,11 +8,11 @@ vars: date: "{{ lookup('pipe', 'date +%Y%m%d') }}" tasks: -# - name: Download ubuntu image {{ distro }} -# get_url: -# url: "https://cloud-images.ubuntu.com/{{ distro}}/current/{{ distro }}-server-cloudimg-amd64-disk1.img" -# dest: "./{{ distro }}-server-cloudimg-amd64-{{ date }}.vmdk" -# force: yes + - name: Download ubuntu image {{ distro }} + get_url: + url: "https://cloud-images.ubuntu.com/{{ distro}}/current/{{ distro }}-server-cloudimg-amd64-disk1.img" + dest: "./{{ distro }}-server-cloudimg-amd64-{{ date }}.vmdk" + force: yes - hosts: localhost gather_facts: no @@ -26,7 +26,7 @@ - buildservice_var.yml roles: - role: s3_bucket_create -# - role: s3_upload + - role: s3_upload - hosts: localhost gather_facts: no @@ -39,7 +39,7 @@ image_min_disk: 12 roles: - role: token -# - role: image_create + - role: image_create tasks: - name: Check image status From a2fbfc78f99474cbc74b61466e19bd11ace72193 Mon Sep 17 00:00:00 2001 From: Frank Kloeker Date: Sun, 2 Jul 2017 01:19:49 +0200 Subject: [PATCH 71/72] more examples of usage in tenant.ini --- tenant.ini | 119 ++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 103 insertions(+), 16 deletions(-) diff --git a/tenant.ini b/tenant.ini index 1bd8f00..a52e292 100644 --- a/tenant.ini +++ b/tenant.ini @@ -1,28 +1,35 @@ # ini file for tenant configuration # each block for each vm # usage ecs: -# ansible-playbook -i hosts tenant_create.yml -e "ecs_name=ansible-test101" -# usage dns (public and internal usage, only in selected vpc): -# ansible-playbook -i hosts dns_create.yml -e "vpc_name=ansible-vpc101" +# ansible-playbook -i hosts tenant_create.yml -e "ecs_name=ansible-test01" +# ansible-playbook -i hosts tenant_delete.yml -e "ecs_name=ansible-test01" +# usage evs: +# ansible-playbook -i hosts evs_create.yml -e "evs_name=ansible-evs01" +# usage dns (public zones): +# ansible-playbook -i hosts dns_create.yml +# usage dns (internal usage, only in selected vpc): +# ansible-playbook -i hosts dns_create.yml -e "vpc_name=ansible-vpc01" +# usage elb (listener, healthcheck, backendmembers) +# ansible-playbook -i hosts tenant_create.yml -e "elb_name=ansible-elb01" -e "listener_name=ansible-listener01" # [DEFAULT] image_name=Community_Ubuntu_16.04_TSI_latest availability_zone=eu-de-01 evs_availability_zone=eu-de-01 -vpc_name=ansible-vpc101 +vpc_name=ansible-vpc01 vpc_net=192.168.0.0/16 -subnet_name=ansible-subnet101 +subnet_name=ansible-subnet01 subnet_net=192.168.0.0/24 subnet_gateway=192.168.0.1 subnet_dhcp_enable=true -subnet_primary_dns=100.125.4.25 -secgroup_name=ansible-secgroup101 +subnet_primary_dns=8.8.8.8 +subnet_secondary_dns=8.4.4.8 +secgroup_name=ansible-secgroup01 secgroup_rule1=ingress;IPv4;tcp;22;22;0.0.0.0/0 -secgroup_rule2=ingress;IPv4;icmp;;;0.0.0.0/0 ecs_volumetype=SATA ecs_ram=2048 ecs_vcpus=2 -ecs_adminkey=ansible-key-101 +ecs_adminkey=ansible-key keypair_file=~/.ssh/id_rsa.pub [dnszones] # name; description; type (public/private); email-address; ttl (in sec) @@ -30,12 +37,92 @@ zone1=ansible.internal.corp.;Core Zone internal services;private;cloud-operation zone2=ansible.otc.telekomcloud2.com.;Core Zone public OTC services;public;cloud-operations@telekom.de;86400 [dnszonerecords] # domain; description; name; type; ttl; value -zonerecord1=ansible.internal.corp.;;ansible-test101.ansible.internal.corp.;A;300;192.168.0.101 -zonerecord2=ansible.otc.telekomcloud2.com.;;ansible-test101.ansible.otc.telekomcloud2.com.;A;300;160.44.207.211 -[ansible-test101] +zonerecord1=ansible.internal.corp.;;ansible-test01.ansible.internal.corp.;A;300;192.168.0.101 +zonerecord2=ansible.otc.telekomcloud2.com.;;ansible-test01.ansible.otc.telekomcloud2.com.;A;300;160.44.201.86 +[ansible-test01] +secgroup_name=ansible-secgroup01 +secgroup_rule1=ingress;IPv4;tcp;22;22;0.0.0.0/0 +secgroup_rule2=ingress;IPv4;tcp;80;80;0.0.0.0/0 +secgroup_rule3=egress;IPv4;tcp;80;80;0.0.0.0/0 +secgroup_rule4=ingress;IPv4;icmp;;;0.0.0.0/0 ecs_ipaddress=192.168.0.101 -ecs_publicip=160.44.207.211 -ecs_publicfqdn=ansible-test101.ansible.otc.telekomcloud2.com. +ecs_publicip=160.44.201.86 +ecs_publicfqdn=ansible-test01.ansible.otc.telekomcloud2.com. ecs_publicttl=300 -eip_bandwidth_name=ansible-eip101 -eip_bandwidth_size=300 +eip_bandwidth_name=ansible-eip01 +eip_bandwidth_size=100 +[ansible-test02] +image_name=Community_Ubuntu_14.04_TSI_latest +ecs_volumetype=SATA +ecs_ram=2048 +ecs_vcpus=4 +ecs_ipaddress=192.168.0.102 +[ansible-test03] +ecs_volumetype=SSD +ecs_ipaddress=192.168.0.103 +# ecs_publicip=0.0.0.0 +# eip_bandwidth_name=ansible-eip1 +# eip_bandwidth_size=100 +[console] +image_name=Community_Ubuntu_16.04_TSI_latest +ecs_volumetype=SATA +ecs_ram=2048 +ecs_vcpus=2 +vpc_name=cloudcamp-vpc01 +secgroup_name=cloudcamp-secgroup01 +secgroup_rule1=ingress;IPv4;tcp;22;22;0.0.0.0/0 +secgroup_rule2=ingress;IPv4;tcp;80;80;0.0.0.0/0 +secgroup_rule3=ingress;IPv4;tcp;443;443;0.0.0.0/0 +secgroup_rule4=egress;IPv4;tcp;22;22;0.0.0.0/0 +secgroup_rule5=egress;IPv4;tcp;80;80;0.0.0.0/0 +secgroup_rule6=egress;IPv4;tcp;443;443;0.0.0.0/0 +secgroup_rule7=ingress;IPv4;icmp;;;0.0.0.0/0 +vpc_net=192.168.0.0/16 +subnet_name=cloudcamp-subnet01 +subnet_net=192.168.0.0/24 +subnet_gateway=192.168.0.1 +subnet_dhcp_enable=true +subnet_primary_dns=8.8.8.8 +subnet_secondary_dns=8.4.4.8 +availability_zone=eu-de-01 +ecs_ipaddress=192.168.0.87 +ecs_publicip=160.44.204.87 +eip_bandwidth_name=cloudcamp-eip1 +eip_bandwidth_size=100 +ecs_adminkey=eumel-key +keypair_file=~/.ssh/id_rsa.pub +[ansible-evs01] +evs_volume_type=SATA +evs_size=20 +# evs_multiattach=true +# evs_scsi=true +[ansible-elb01] +elb_type=External +elb_bandwidth=100 +admin_state_up=true +elb_availability_zone=eu_de-01 +elb_secgroup_name=ansible-secgroup02 +secgroup_rule1=ingress;IPv4;tcp;22;22;0.0.0.0/0 +secgroup_rule1=ingress;IPv4;tcp;80;80;0.0.0.0/0 +elb_subnet_name=ansible-subnet01 +[ansible-listener01] +# HTTP, HTTPS, TCP +listener_protocol=TCP +listener_port=22 +listener_backend_protocol=TCP +listener_backend_port=22 +# source, roundrobin, leastconn +listener_lb_algorithm=source +#listener_certificate_name=ansible-cert +#listener_tcp_timeout= +#listener_cookie_timeout= +#listener_sticky_session_type=insert +#listener_session_sticky= +healthcheck_connect_port=22 +healthcheck_interval=5 +# HTTP, TCP +healthcheck_protocol=TCP +healthcheck_timeout=10 +#healthcheck_uri="/" +unhealthy_threshold=3 +backend_members=ansible-test01,ansible-test02 From d90d68264d34336669cff86d06fd5151594d7e70 Mon Sep 17 00:00:00 2001 From: Frank Kloeker Date: Sun, 2 Jul 2017 01:20:17 +0200 Subject: [PATCH 72/72] add playbook to delete tenant --- tenant_delete.yml | 93 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 93 insertions(+) create mode 100644 tenant_delete.yml diff --git a/tenant_delete.yml b/tenant_delete.yml new file mode 100644 index 0000000..6239380 --- /dev/null +++ b/tenant_delete.yml @@ -0,0 +1,93 @@ +--- +- name: Delete DNS PTR record + hosts: localhost + gather_facts: no + connection: local + vars_files: + - tenant_var.yml + roles: + - role: token + - role: lookup_name + - role: ptrrecord_delete + ignore_errors: yes + +- name: Delete ECS + hosts: localhost + gather_facts: no + connection: local + vars: + delete_volume: true +# delete_publicip: true + ecs_job_id: "{{ (ecs.content|from_json)|json_query('job_id') }}" + roles: + - role: token + - role: lookup_name + - role: ecs_delete + tasks: + - name: Request job status from API + uri: + url: "{{ AUTH_URL_ECS_CLOUD }}/{{ PROJECT_ID }}/jobs/{{ ecs_job_id }}" + method: GET + return_content: yes + validate_certs: yes + HEADER_Content-Type: "application/json" + HEADER_X-Auth-Token: "{{ token['x_subject_token'] }}" + when: ecs_job_id is defined + register: jobstatus + until: (jobstatus.content|from_json)|json_query('status') == 'SUCCESS' + retries: 50 + delay: 10 + + +- name: Delete keypair + hosts: localhost + gather_facts: no + connection: local + vars_files: + - tenant_var_default.yml + - tenant_var.yml + roles: + - role: token + - role: lookup_name + - role: keypair_delete + ignore_errors: yes + +- name: Delete Secgroup + hosts: localhost + gather_facts: no + connection: local + vars_files: + - tenant_var_default.yml + - tenant_var.yml + roles: + - role: token + - role: lookup_name + - role: secgroup_delete + ignore_errors: yes + +- name: Delete Subnet + hosts: localhost + gather_facts: no + connection: local + vars_files: + - tenant_var_default.yml + - tenant_var.yml + roles: + - role: token + - role: lookup_name + - role: subnet_delete + ignore_errors: yes + +- name: Delete VPC + hosts: localhost + gather_facts: no + connection: local + vars_files: + - tenant_var_default.yml + - tenant_var.yml + roles: + - role: token + - role: lookup_name + - role: vpc_delete + ignore_errors: yes +