From e72e0e70d26a081d9d9081fdd835fe7af32d05ef Mon Sep 17 00:00:00 2001 From: Cian Hatton Date: Fri, 2 Sep 2022 16:03:35 +0100 Subject: [PATCH] import roles --- Makefile | 4 +- playbooks/backup-docker-volumes.yml | 29 +++- playbooks/restore-docker-volumes.yml | 12 +- requirements.yml | 3 + .../defaults/main.yml | 2 - .../handlers/main.yml | 2 - .../meta/main.yml | 52 ------- .../tasks/main.yml | 2 - .../vars/main.yml | 2 - roles/docker_restore_container/tasks/main.yml | 4 +- roles/docker_s3_backup/defaults/main.yml | 3 - roles/docker_s3_backup/handlers/main.yml | 2 - roles/docker_s3_backup/meta/main.yml | 52 ------- roles/docker_s3_backup/tasks/main.yml | 54 ------- roles/docker_s3_backup/vars/main.yml | 2 - .../defaults/main.yml | 8 -- .../handlers/main.yml | 2 - roles/docker_s3_volume_restore/meta/main.yml | 52 ------- roles/docker_s3_volume_restore/tasks/main.yml | 58 -------- roles/docker_s3_volume_restore/vars/main.yml | 2 - roles/setup_hosted_services/tasks/main.yml | 43 +++--- vault_vars/qnap-vault.yml | 136 ++++++++++-------- 22 files changed, 129 insertions(+), 397 deletions(-) delete mode 100644 roles/docker_archive_volume_restore/defaults/main.yml delete mode 100644 roles/docker_archive_volume_restore/handlers/main.yml delete mode 100644 roles/docker_archive_volume_restore/meta/main.yml delete mode 100644 roles/docker_archive_volume_restore/tasks/main.yml delete mode 100644 roles/docker_archive_volume_restore/vars/main.yml delete mode 100644 roles/docker_s3_backup/defaults/main.yml delete mode 100644 roles/docker_s3_backup/handlers/main.yml delete mode 100644 roles/docker_s3_backup/meta/main.yml delete mode 100644 roles/docker_s3_backup/tasks/main.yml delete mode 100644 roles/docker_s3_backup/vars/main.yml delete mode 100644 roles/docker_s3_volume_restore/defaults/main.yml delete mode 100644 roles/docker_s3_volume_restore/handlers/main.yml delete mode 100644 roles/docker_s3_volume_restore/meta/main.yml delete mode 100644 roles/docker_s3_volume_restore/tasks/main.yml delete mode 100644 roles/docker_s3_volume_restore/vars/main.yml diff --git a/Makefile b/Makefile index b59b93e..6276d61 100644 --- a/Makefile +++ b/Makefile @@ -28,8 +28,8 @@ lint: ansible-lint roles ansible-lint playbooks -backup: +backup: deps ansible-playbook playbooks/backup-docker-volumes.yml -restore: +restore: deps ansible-playbook playbooks/restore-docker-volumes.yml diff --git a/playbooks/backup-docker-volumes.yml b/playbooks/backup-docker-volumes.yml index 01a5ffd..48f6ef1 100644 --- a/playbooks/backup-docker-volumes.yml +++ b/playbooks/backup-docker-volumes.yml @@ -5,7 +5,28 @@ - name: Include vault variables. include_vars: '../{{vault_file}}' tags: [always] - roles: - - role: docker_backup - vars: - container_backup: linkding + + tasks: + - name: Find Containers With Backup Label + register: docker_info + docker_host_info: + containers: true + containers_filters: + label: + - ie.cianhatton.backup.enabled=true + register: filter_output + - debug: msg="{{filter_output}}" + + - name: Get Container Names + set_fact: container_names="{{ filter_output.containers | map(attribute="Names") | flatten }}" + + - debug: msg="{{container_names}}" + + - name: Backup Containers with backup label + include_role: + name: chatton.docker_backup.docker_s3_backup + vars: + container_backup: "{{ container_item | regex_replace('^\\/', '') }}" + with_items: "{{ container_names }}" + loop_control: + loop_var: container_item diff --git a/playbooks/restore-docker-volumes.yml b/playbooks/restore-docker-volumes.yml index 188078e..497711d 100644 --- a/playbooks/restore-docker-volumes.yml +++ b/playbooks/restore-docker-volumes.yml @@ -6,8 +6,10 @@ include_vars: '../{{vault_file}}' tags: [always] roles: - - role: docker_s3_volume_restore - -# - role: docker_restore_container -# vars: -# container_restore: linkding + - role: chatton.docker_backup.docker_s3_volume_restore + vars: + docker_s3_volume_restore_force: true + docker_s3_volume_restore_latest_s3_key: true + docker_volume_s3_restores: + - volume_name: "linkding_data" +# s3_key: "linkding_data/linkding_data-2022-09-01T21:32:54Z.tar.gz" diff --git a/requirements.yml b/requirements.yml index cc76756..c1a9a96 100644 --- a/requirements.yml +++ b/requirements.yml @@ -6,3 +6,6 @@ collections: - name: https://github.com/chatton/ansible-portainer.git type: git version: master + - name: https://github.com/chatton/ansible-docker-backup.git,dev + type: git + version: master diff --git a/roles/docker_archive_volume_restore/defaults/main.yml b/roles/docker_archive_volume_restore/defaults/main.yml deleted file mode 100644 index a4f6355..0000000 --- a/roles/docker_archive_volume_restore/defaults/main.yml +++ /dev/null @@ -1,2 +0,0 @@ ---- -# defaults file for docker_archive_volume_restore diff --git a/roles/docker_archive_volume_restore/handlers/main.yml b/roles/docker_archive_volume_restore/handlers/main.yml deleted file mode 100644 index f03414b..0000000 --- a/roles/docker_archive_volume_restore/handlers/main.yml +++ /dev/null @@ -1,2 +0,0 @@ ---- -# handlers file for docker_archive_volume_restore diff --git a/roles/docker_archive_volume_restore/meta/main.yml b/roles/docker_archive_volume_restore/meta/main.yml deleted file mode 100644 index c572acc..0000000 --- a/roles/docker_archive_volume_restore/meta/main.yml +++ /dev/null @@ -1,52 +0,0 @@ -galaxy_info: - author: your name - description: your role description - company: your company (optional) - - # If the issue tracker for your role is not on github, uncomment the - # next line and provide a value - # issue_tracker_url: http://example.com/issue/tracker - - # Choose a valid license ID from https://spdx.org - some suggested licenses: - # - BSD-3-Clause (default) - # - MIT - # - GPL-2.0-or-later - # - GPL-3.0-only - # - Apache-2.0 - # - CC-BY-4.0 - license: license (GPL-2.0-or-later, MIT, etc) - - min_ansible_version: 2.1 - - # If this a Container Enabled role, provide the minimum Ansible Container version. - # min_ansible_container_version: - - # - # Provide a list of supported platforms, and for each platform a list of versions. - # If you don't wish to enumerate all versions for a particular platform, use 'all'. - # To view available platforms and versions (or releases), visit: - # https://galaxy.ansible.com/api/v1/platforms/ - # - # platforms: - # - name: Fedora - # versions: - # - all - # - 25 - # - name: SomePlatform - # versions: - # - all - # - 1.0 - # - 7 - # - 99.99 - - galaxy_tags: [] - # List tags for your role here, one per line. A tag is a keyword that describes - # and categorizes the role. Users find roles by searching for tags. Be sure to - # remove the '[]' above, if you add tags to this list. - # - # NOTE: A tag is limited to a single word comprised of alphanumeric characters. - # Maximum 20 tags per role. - -dependencies: [] - # List your role dependencies here, one per line. Be sure to remove the '[]' above, - # if you add dependencies to this list. diff --git a/roles/docker_archive_volume_restore/tasks/main.yml b/roles/docker_archive_volume_restore/tasks/main.yml deleted file mode 100644 index 6b2d162..0000000 --- a/roles/docker_archive_volume_restore/tasks/main.yml +++ /dev/null @@ -1,2 +0,0 @@ ---- -# tasks file for docker_archive_volume_restore diff --git a/roles/docker_archive_volume_restore/vars/main.yml b/roles/docker_archive_volume_restore/vars/main.yml deleted file mode 100644 index 57ef4d7..0000000 --- a/roles/docker_archive_volume_restore/vars/main.yml +++ /dev/null @@ -1,2 +0,0 @@ ---- -# vars file for docker_archive_volume_restore diff --git a/roles/docker_restore_container/tasks/main.yml b/roles/docker_restore_container/tasks/main.yml index 8c20bf7..26a00db 100644 --- a/roles/docker_restore_container/tasks/main.yml +++ b/roles/docker_restore_container/tasks/main.yml @@ -32,7 +32,7 @@ register: s3_list_output with_items: "{{ volume_mounts }}" -- debug: msg="{ {s3_list_output }}" +- debug: msg="{{ s3_list_output }}" - name: Extract s3 keys for container set_fact: container_s3_keys="{{ container_s3_keys | default([]) + [item.s3_keys | last] }}" @@ -66,7 +66,7 @@ volume_details: "{{ volume_details |default([]) + [ {'mount': item.0, 's3_key': item.1} ] }}" with_together: - "{{ volume_mounts }}" - - "{{ container_s3_keys }}" + - "{{ container_s3_keys }}"` - debug: msg="{{ volume_details }}" diff --git a/roles/docker_s3_backup/defaults/main.yml b/roles/docker_s3_backup/defaults/main.yml deleted file mode 100644 index fce0eef..0000000 --- a/roles/docker_s3_backup/defaults/main.yml +++ /dev/null @@ -1,3 +0,0 @@ ---- -# defaults file for chatton.docker_backup -docker_host_backups_directory: /mnt/mergerfs/backups diff --git a/roles/docker_s3_backup/handlers/main.yml b/roles/docker_s3_backup/handlers/main.yml deleted file mode 100644 index 7bcee9b..0000000 --- a/roles/docker_s3_backup/handlers/main.yml +++ /dev/null @@ -1,2 +0,0 @@ ---- -# handlers file for chatton.docker_backup diff --git a/roles/docker_s3_backup/meta/main.yml b/roles/docker_s3_backup/meta/main.yml deleted file mode 100644 index c572acc..0000000 --- a/roles/docker_s3_backup/meta/main.yml +++ /dev/null @@ -1,52 +0,0 @@ -galaxy_info: - author: your name - description: your role description - company: your company (optional) - - # If the issue tracker for your role is not on github, uncomment the - # next line and provide a value - # issue_tracker_url: http://example.com/issue/tracker - - # Choose a valid license ID from https://spdx.org - some suggested licenses: - # - BSD-3-Clause (default) - # - MIT - # - GPL-2.0-or-later - # - GPL-3.0-only - # - Apache-2.0 - # - CC-BY-4.0 - license: license (GPL-2.0-or-later, MIT, etc) - - min_ansible_version: 2.1 - - # If this a Container Enabled role, provide the minimum Ansible Container version. - # min_ansible_container_version: - - # - # Provide a list of supported platforms, and for each platform a list of versions. - # If you don't wish to enumerate all versions for a particular platform, use 'all'. - # To view available platforms and versions (or releases), visit: - # https://galaxy.ansible.com/api/v1/platforms/ - # - # platforms: - # - name: Fedora - # versions: - # - all - # - 25 - # - name: SomePlatform - # versions: - # - all - # - 1.0 - # - 7 - # - 99.99 - - galaxy_tags: [] - # List tags for your role here, one per line. A tag is a keyword that describes - # and categorizes the role. Users find roles by searching for tags. Be sure to - # remove the '[]' above, if you add tags to this list. - # - # NOTE: A tag is limited to a single word comprised of alphanumeric characters. - # Maximum 20 tags per role. - -dependencies: [] - # List your role dependencies here, one per line. Be sure to remove the '[]' above, - # if you add dependencies to this list. diff --git a/roles/docker_s3_backup/tasks/main.yml b/roles/docker_s3_backup/tasks/main.yml deleted file mode 100644 index d27fdc0..0000000 --- a/roles/docker_s3_backup/tasks/main.yml +++ /dev/null @@ -1,54 +0,0 @@ ---- -# tasks file for chatton.docker_backup -# https://docs.ansible.com/ansible/latest/collections/community/docker/docker_container_module.html#ansible-collections-community-docker-docker-container-module -# https://docs.docker.com/storage/volumes/#backup-restore-or-migrate-data-volumes - - -- name: Determine backup timestamp. - set_fact: backup_time="{{ ansible_date_time.iso8601 }}" - -- name: Stop a container - community.docker.docker_container: - name: "{{ container_backup }}" - state: stopped - -- name: Get container details - docker_container_info: - name: "{{ container_backup }}" - register: result - -- name: Extract only the volume mounts (not bind mounts) - set_fact: volume_mounts="{{ result.container.Mounts | selectattr("Type", "equalto", "volume")}}" - -- name: Create Backup of Container Volumes - community.docker.docker_container: - name: "backup-container-{{ item.Name }}-{{ 10 | random }}" - image: ubuntu - command: "tar -czvf /backups/{{ item.Name }}-{{ backup_time }}.tar.gz /data" - auto_remove: true - detach: false # block until this container exists. - state: started - volumes: - - "{{ item.Name }}:/data" - - "{{ docker_host_backups_directory }}":/backups - with_items: "{{ volume_mounts }}" - -- name: Start the container - community.docker.docker_container: - name: "{{ container_backup }}" - state: started - -- name: Upload backups to S3 - register: upload_result - amazon.aws.aws_s3: - s3_url: "https://{{aws_s3.s3_url}}" - bucket: "{{ aws_s3.bucket }}" - object: "{{ item.Name }}/{{ item.Name }}-{{ backup_time }}.tar.gz" - src: {{ docker_host_backups_directory }}/{{ item.Name }}-{{ backup_time }}.tar.gz - aws_access_key: "{{ aws_s3.aws_access_key }}" - aws_secret_key: "{{ aws_s3.aws_secret_key }}" - region: "{{ aws_s3.region }}" - mode: put - # empty permissions makes it work with IAM vs ACL, see: https://github.com/ansible/ansible/issues/48050 - permission: [] - with_items: "{{ volume_mounts }}" diff --git a/roles/docker_s3_backup/vars/main.yml b/roles/docker_s3_backup/vars/main.yml deleted file mode 100644 index 045ab9b..0000000 --- a/roles/docker_s3_backup/vars/main.yml +++ /dev/null @@ -1,2 +0,0 @@ ---- -# vars file for chatton.docker_backup diff --git a/roles/docker_s3_volume_restore/defaults/main.yml b/roles/docker_s3_volume_restore/defaults/main.yml deleted file mode 100644 index 0c3595f..0000000 --- a/roles/docker_s3_volume_restore/defaults/main.yml +++ /dev/null @@ -1,8 +0,0 @@ ---- -# defaults file for docker_s3_volume_restore - -# forces a revert to the volume. -docker_volume_s3_force: false -docker_volume_s3_restores: - - volume_name: "linkding_data" - s3_key: "linkding_data/linkding_data-2022-09-01T21:32:54Z.tar.gz" diff --git a/roles/docker_s3_volume_restore/handlers/main.yml b/roles/docker_s3_volume_restore/handlers/main.yml deleted file mode 100644 index 738fbd7..0000000 --- a/roles/docker_s3_volume_restore/handlers/main.yml +++ /dev/null @@ -1,2 +0,0 @@ ---- -# handlers file for docker_s3_volume_restore diff --git a/roles/docker_s3_volume_restore/meta/main.yml b/roles/docker_s3_volume_restore/meta/main.yml deleted file mode 100644 index c572acc..0000000 --- a/roles/docker_s3_volume_restore/meta/main.yml +++ /dev/null @@ -1,52 +0,0 @@ -galaxy_info: - author: your name - description: your role description - company: your company (optional) - - # If the issue tracker for your role is not on github, uncomment the - # next line and provide a value - # issue_tracker_url: http://example.com/issue/tracker - - # Choose a valid license ID from https://spdx.org - some suggested licenses: - # - BSD-3-Clause (default) - # - MIT - # - GPL-2.0-or-later - # - GPL-3.0-only - # - Apache-2.0 - # - CC-BY-4.0 - license: license (GPL-2.0-or-later, MIT, etc) - - min_ansible_version: 2.1 - - # If this a Container Enabled role, provide the minimum Ansible Container version. - # min_ansible_container_version: - - # - # Provide a list of supported platforms, and for each platform a list of versions. - # If you don't wish to enumerate all versions for a particular platform, use 'all'. - # To view available platforms and versions (or releases), visit: - # https://galaxy.ansible.com/api/v1/platforms/ - # - # platforms: - # - name: Fedora - # versions: - # - all - # - 25 - # - name: SomePlatform - # versions: - # - all - # - 1.0 - # - 7 - # - 99.99 - - galaxy_tags: [] - # List tags for your role here, one per line. A tag is a keyword that describes - # and categorizes the role. Users find roles by searching for tags. Be sure to - # remove the '[]' above, if you add tags to this list. - # - # NOTE: A tag is limited to a single word comprised of alphanumeric characters. - # Maximum 20 tags per role. - -dependencies: [] - # List your role dependencies here, one per line. Be sure to remove the '[]' above, - # if you add dependencies to this list. diff --git a/roles/docker_s3_volume_restore/tasks/main.yml b/roles/docker_s3_volume_restore/tasks/main.yml deleted file mode 100644 index f2f1d6e..0000000 --- a/roles/docker_s3_volume_restore/tasks/main.yml +++ /dev/null @@ -1,58 +0,0 @@ ---- -# https://docs.ansible.com/ansible/latest/collections/community/docker/docker_container_module.html#ansible-collections-community-docker-docker-container-module -# https://docs.docker.com/storage/volumes/#backup-restore-or-migrate-data-volumes - -- name: Ensure Volume. - docker_volume: - name: "{{ item.volume_name }}" - state: present - register: volume_out - with_items: "{{ docker_volume_s3_restores }}" - -- name: Determine if backup is needed. - set_fact: should_perform_backup="{{ docker_volume_s3_force == true or volume_out.changed == true }}" - -- name: End play as no backup is needed. - meta: end_play - when: not should_perform_backup - -- name: Download archive from S3 - amazon.aws.aws_s3: - bucket: "{{ aws_s3.bucket }}" - object: "{{ item.s3_key }}" - aws_access_key: "{{ aws_s3.aws_access_key }}" - aws_secret_key: "{{ aws_s3.aws_secret_key }}" - region: "{{ aws_s3.region }}" - s3_url: "https://{{ aws_s3.s3_url }}" - mode: get - dest: "/tmp/{{ item.s3_key }}" - register: get_out - with_items: "{{ docker_volume_s3_restores }}" - -- name: Remove contents of volumes - community.docker.docker_container: - name: "restore-container-{{ item.volume_name }}-{{ 10 | random }}" - image: ubuntu - command: "rm -rf ./*" - auto_remove: true - detach: false # block until this container exists. - state: started - # start inside the directory we want to wipe - working_dir: "/data" - volumes: - - "{{ item.volume_name }}:/data" - with_items: "{{ docker_volume_s3_restores }}" - -- name: Restore contents of volumes - community.docker.docker_container: - name: "restore-container-{{ item.volume_name }}-{{ 10 | random }}" - image: ubuntu - # extract the tar into the volume. - command: "tar xvf /tmp/{{ item.s3_key }} -C /data --strip-components 1" - auto_remove: true - detach: false # block until this container exists. - state: started - volumes: - - "{{ item.volume_name }}:/data" - - /tmp:/tmp - with_items: "{{ docker_volume_s3_restores }}" diff --git a/roles/docker_s3_volume_restore/vars/main.yml b/roles/docker_s3_volume_restore/vars/main.yml deleted file mode 100644 index a2212b8..0000000 --- a/roles/docker_s3_volume_restore/vars/main.yml +++ /dev/null @@ -1,2 +0,0 @@ ---- -# vars file for docker_s3_volume_restore diff --git a/roles/setup_hosted_services/tasks/main.yml b/roles/setup_hosted_services/tasks/main.yml index 5da99af..8910373 100644 --- a/roles/setup_hosted_services/tasks/main.yml +++ b/roles/setup_hosted_services/tasks/main.yml @@ -57,8 +57,7 @@ name: requests - name: Docker | Find docker volumes - shell: docker volume ls -f name={{item.name}} --format '{{ '{{' }} .Name {{ '}}' - }}' + shell: docker volume ls -f name={{item.name}} --format '{{ '{{' }} .Name {{ '}}'}}' with_items: '{{services}}' register: find_volumes changed_when: false @@ -68,36 +67,28 @@ - name: Docker | Find volumes that need to be restored script: scripts/find-volumes-to-restore.py environment: - EXISTING_VOLUMES: "{{ find_volumes.results | map(attribute='stdout_lines') | list\ - \ | flatten }}" + EXISTING_VOLUMES: "{{ find_volumes.results | map(attribute='stdout_lines') | list | flatten }}" SERVICES: '{{ services }}' - DOCKER_COMPOSE_DIR: '{{directories.docker_compose_directory}}' + DOCKER_COMPOSE_DIR: '{{ directories.docker_compose_directory }}' args: executable: python3 register: python_output changed_when: false -- debug: msg="{{python_output.stdout_lines | list }}" - -- name: Docker Volume Backup | Restore any missing backups from S3 - when: restore_from_s3 - docker_container: - command: restore-volume --s3 --volume {{item}} - image: ghcr.io/chatton/docker-volume-backup:v0.3.0 - name: s3-restore-{{item}} - cleanup: true # delete container after it's done. - state: started # container should execute. - detach: no # task fails if container exits. - volumes: - - /var/run/docker.sock:/var/run/docker.sock - - /tmp:/tmp # temp s3 archive goes here - env: - AWS_ACCESS_KEY_ID: '{{aws_s3.aws_access_key}}' - AWS_SECRET_ACCESS_KEY: '{{aws_s3.aws_secret_key}}' - AWS_DEFAULT_REGION: '{{aws_s3.region}}' - AWS_BUCKET: '{{aws_s3.bucket}}' - AWS_ENDPOINT: '{{aws_s3.s3_url}}' - with_items: '{{ python_output.stdout_lines }}' +- debug: msg="{{ python_output.stdout_lines | list }}" + +- set_fact: + restore_volumes: "{{ restore_volumes | default([]) + [{ 'volume_name': item}] }}" + with_items: "{{ python_output.stdout_lines | list }}" + +- name: Restore any missing volumes from S3 + include_role: + name: chatton.docker_backup.docker_s3_volume_restore + when: restore_volumes is defined + vars: + docker_s3_volume_restore_force: false + docker_s3_volume_restore_latest_s3_key: true + docker_volume_s3_restores: "{{ restore_volumes }}" - name: Docker | Create required docker networks docker_network: diff --git a/vault_vars/qnap-vault.yml b/vault_vars/qnap-vault.yml index 68f83d8..3bc80c7 100644 --- a/vault_vars/qnap-vault.yml +++ b/vault_vars/qnap-vault.yml @@ -1,64 +1,74 @@ $ANSIBLE_VAULT;1.1;AES256 -66353636383061643861393632363630643262383863653036313663623862616663386534306262 -3466306661356164653730653062393863363163613064660a663732333931323037373834386336 -31383464333930363163646637663631393736386139333430313463356661303033353462313339 -6232336539366234360a663662636662396138636334383737376439346239386466666239313563 -62663864316661306466316561343336613938386239383663656564336166323839383765363761 -38383733323037353961303937623365623064383163373161633733616539373662353439383531 -64393637343035613630356165626666306531353634363966333832353335646262663365313039 -63656331626330366263386466646562393033646165386432386233316235663636643736363237 -66343362663736343136336563663664323030313033326236663132316539346661323532653162 -63336135373835613232656639656161313464663461303034313632303033393233396432613537 -35633239326662333463383735633835616462396239373334373632663361393230373635343366 -33346466613237303137646263626437323964653630323236303964663364626365323162656438 -65323330626536323365613035653332376336343139323235323663623430393534316230346135 -38616333656162393837643564366463626538386633323730313564373561386334643831303063 -31306237333663346130623535636532336234613766316234383135303863653565633939313934 -34656639343763313363653739336163376634376264393133376634373062666137343338663732 -37623463313839393432366334613839663366656633663464656631656334616663636562323536 -65653865646333326631386166333263343031356461666365623532333439326430623632646433 -62373733616162386662393566633333353635373337666234623062626639656536623161343163 -32626164376539626634636561383764306539353666643132363536303361663664346163336266 -31616263663865623036656436326130636337323136633836313037356236633764383934626466 -62613265383261663962356439646135333566356335303139346430353434646661393335353138 -35373037336563326363643339356262366462343463366661386638623530613364663936326239 -64313662653139376164356333303365376366303236643862336465643465383266373335653337 -61623638626633626434343730373930343236653737396161366136373232333034366637336463 -30396436373063666233356636626537626336303163306333343837653163373932663061376566 -61336239306666333764613462663461336138363736383030663534376566303237306639353234 -35353439633830333538363136396336613536396661333337626630326132653736303836613864 -64613531623833343166376238303238633732316438633161383761653664666236633162383531 -37323761376334633535313234376237633938353332353239303863383265393430333264663662 -31613030616139613062326431366666346233383736376161643033356531316130666366333638 -35616336643964613163386264356237383034333634343266303238333864383439383236353766 -63346436393330616639623839363232376236623565666433613137336331353436376165613964 -61306130306130313336303761633037633765386562633464336636613961356430343830383237 -38626164383137613733326162633231346138306338663466396237386631313662353639396461 -62323030386336326662643766663135633231646364343561373134396230373332313561373936 -61303134636565643237633461663261613630363264363439343065653537633961653634346137 -36613766346539653332613537626138636430666432313734363831656666646364656634353163 -66616234366664636466333433383231616638353466333861333430303134646538626466353664 -66613566306135336163376137663462383432656165626663383963306336613034633131393866 -34613132306135613137303033623936393532653231373435363239373830346133643230366264 -66663866613534313831636165376434643031373535393430393863626439633732626338343931 -34376638393962353638663331633434313661643231303866333937633365613137653838646431 -37363865633934356366663431376466656337623466646338386364323761373464616238646337 -36643263383933303361313839643532613135323334623662653432353439316639323238353632 -36353465626462326332386466373062656137383763633430366635653630323031303563333434 -62663533383464363237336661356639393634613531613238343463643934316633656662353137 -31383334373032663936616664646233646164313366396339613937366434613265393162363238 -63646162326465376564623634323062323762343235336138666530333133393462616466636431 -37353736616635323663343832636438306339613563396237326366346563383666303131643264 -34383766623132316139323639626530653033666463323166663734353638393433303739313434 -64643539653631323933373232653737653466346566313930356138363139383465663861636535 -33333063636431623131363237633339303763643931663334656366316137396234643030373735 -64663338316434313731626435353033313333323133386664636531646165383065373937333335 -32336262323764653835663038363934633835313639613130363661333234666535363630326232 -35626366636631386264656162326233376332633333643832333261636634633138353266636264 -37653737303439383564363437393839323364623563376462353531653533396264653730323533 -62656562626263336439626133626663353564646236396461336662623736653037346564653262 -61356233646431646565613532376431363034343830353534386434636638646337303165313739 -62343734326361646637656534636132643438303233313562656366316432346664303364626133 -39663664613236666435646331313365363264316232373831623836323439383632643731303865 -39623766396463643939633666643730386634623230613665653432623662363137663931323363 -656162386137646562313733343631643036 +31643662633861333962303536636166333533363233366265626661636532383530633336633633 +3137656636386638643461653165666130303132313232640a336262333830663666353762656138 +30386564666435333532616665303131623964386434396363653039353761636364313865323063 +3133306161646262610a303331353633643733633164613264393665633236636164613936336632 +62383332383165303063663331646236653433643862383139326662666234373065616163636532 +34623633633134333036366635386263333538376636666131393330313439396432366264326636 +31383731633365636335346231346337313634633861636437356632653764343532356637343131 +36333737643236363530663537633765646164313937303233376464323136373061393430376664 +38396237333363616461316433346632346639613236376363386536373832613634356536656261 +30663235616439383761393239343763383938396431653964643961363961623432333033363130 +63653631346366326639353138393765336635363339383230383363633233323034356631383238 +64643133663537336331343064653263656366383761633032343636383666386263356437396630 +36316439363331626536303630626361666636633465383634363138643939623533653963376264 +35363439663238623961383061356634373931613439623765663737323136386162616237326463 +64326638333630323861613534353736323362303733333337643136613665616539303064326430 +38643731333139386138323361383931376332653737656334636530333334313030633865613563 +65663964623766636165313937336662306439626466313062646333323736656238373962333934 +37333936653835336562363432396636633932356336313030623036393437623065653863343364 +65383961336237653334643365313439326630616637366666663761356532393033633835323337 +37396432303166316433353062336464396633363762656532393364303762353465393531376639 +66346466333533376530343565306133323632383665376233656431396562623637316261653063 +37613765353035313162313539386337656534353439363930303362383862636562346432323261 +30653335633363373863306538353562626163356265343039313563633161663832343934633665 +63633539353331656564623134303061313434633336313664643831383737393237653230666635 +33316337636538316461666531393231353833633461336263643831373032343735353034306230 +66303563396466383765393938366534313633633562643535636332336237396338316239333836 +61653437333263353834336664646430303839633463613036613137316438343162666530353863 +37326161353362636163303131393738393763626134623631353832316161333633623261613164 +30393461613336376634313831343633353836646432643138306637313066643161346530653434 +35353037313162366465653632666531656461636532343236376564393131666165616134346161 +65386539613238343164643535333330666432646432313730646530356537333733383564613930 +65646231633737346364613665666563383237623765313064303863393264653435626363616365 +30363237383236353436396531653133623830353566666439633038326533363464636639346264 +32653963336666396562383032353830613436653339386439373763326531623130303034653939 +38663064333430633435396164633437343034396230623437616539376666396438636430393463 +65376638653134393232353136623762636564626562306562366465376464363363383630656538 +63323364383534353135396535303266636530386664633931613366343537613033306665656363 +32376532666165633238633761333165356563333636333431653863343530383130623032653333 +63623937633134623030616461613433333766373262623165343961313534666235656531353537 +31366161303565643564646162353466306436646132373965326535383930303366363336653633 +33613233333364616135326165336164356532613531376532623037663830633736306464666533 +65646536356466356164336330653463303831323835356336646335333837616163316264336130 +61663066303134373439336139376665356463633337666638313463646535663063343863373162 +64313761353435333562373166336163636438353938663239326466396663613739636334666566 +35643735323565323064346136633037383038633530363831636164306238656438636236366362 +30343431303935316164343239626639666130663734643166363330306264616363333439353838 +30313466343562643730393230303565646137616465336439636264653862323231363633623234 +34333863376638313036353731393832313833346531313431386534656262613439646137666230 +31613365363236656430323135643463636531663062376438626635626536653461343931636364 +32316233376635616639623737636338346365323039343332613962653734346266623163333061 +30323565653065346333353637653061353033313339636435336166623833376237656236336561 +34376161303338663532616530343535323964623665303931366462303832396532653635346463 +66333632326261373732313233396561316562613638643063663065393838376436313635316230 +37643832623562386331393461663535353937636430386437663931363534373163373166366231 +62313162643831643965303163306563626564353635343465663136396161623561666263363364 +36336532633664633739346166303132393733373832636565663463353163343565653462643163 +37373566323630656437643137646465626336623239656661373661663363343432663065373237 +34306535323738343139626663323339363538376262333038373634313130623838363535616131 +31393166653262656539643466633331316463306536323634353136663530386666313765326336 +39666266613836363235613164363661626534393837313239663538613734353338636533626266 +38366131326634666561613662313638616433636431633930303634633862316263653562643865 +30326336346165643930383632646461303131336163343137346134663531633064663932303937 +36616162353436623336336361343631326136303938316266383263303034633762303231666439 +36336332663237643437326265623131653463326237373166623364356633336234316336366137 +63363834336566646365316434326433336632373638633366616633336138323962366231383066 +33663235363532626433323639323938386333646339376632333064343861613130666365333665 +61373136396663363538363963656263633636663639633163373463346431303631366439656333 +61393137363338363133336463306132396433646532663130386233626633363239336561633335 +33343332316531633461326265623663653631663765343164363139306362633531643964653432 +66353866376130363132343436386237326639326236386539383064646230636264376432663564 +37313636353935616666333438653835646632333930323630393464636239363534636637343033 +62363830366165323836313761643061316137353463393132306431316539333133393939613331 +6465