From 293bf6d004f3ce1f24f4aa9e48a7663619600e26 Mon Sep 17 00:00:00 2001 From: dima Date: Wed, 13 Nov 2024 23:18:44 +0300 Subject: [PATCH 1/3] ver 3 --- Jenkinsfile | 5 +- README.md | 103 +++++++++++++---------- ansible.cfg | 1 + roles/postgresql/handlers/main.yml | 2 +- roles/postgresql/tasks/backup.yml | 14 +-- roles/postgresql/tasks/configure.yml | 4 - roles/postgresql/tasks/databases.yml | 13 +-- roles/postgresql/tasks/initialize.yml | 6 +- roles/postgresql/tasks/insert_data.yml | 11 +++ roles/postgresql/tasks/main.yml | 26 +++++- roles/postgresql/tasks/open_firewall.yml | 6 +- roles/postgresql/tasks/setup.yml | 4 - roles/postgresql/tasks/users.yml | 4 +- roles/postgresql/vars/main.yml | 5 +- 14 files changed, 115 insertions(+), 89 deletions(-) create mode 100644 roles/postgresql/tasks/insert_data.yml diff --git a/Jenkinsfile b/Jenkinsfile index eb554fd..9844218 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -1,5 +1,8 @@ pipeline { agent any + options { + ansiColor('xterm') + } tools { ansible 'Ansible' } @@ -11,7 +14,7 @@ pipeline { string(name: 'DB_USER', defaultValue: 'postgres', description: 'Имя пользователя базы данных') string(name: 'DB_NAME', defaultValue: 'mydb', description: 'Имя базы данных') string(name: 'BACKUP_DIR', defaultValue: '/var/backups/postgresql', description: 'Директория для бэкапа') - string(name: 'TASKS', defaultValue: '', description: 'Список тегов задач для выполнения (setup,firewall,init,configure,database,user,backup)') + string(name: 'TASKS', defaultValue: '', description: 'Список тегов задач для выполнения (setup,firewall,init,configure,database,insert,user,backup)') } stages { stage('Clone repository') { diff --git a/README.md b/README.md index 5a756cd..facef0c 100644 --- a/README.md +++ b/README.md @@ -1,61 +1,74 @@ -# Проект по автоматизации настройки PostgreSQL +# Проект по Автоматизации Настройки PostgreSQL -Этот проект предназначен для автоматизации установки, настройки и управления базой данных PostgreSQL с использованием **Ansible** и **Jenkins**. Он включает пайплайн Jenkins и Ansible плейбук с ролью для управления PostgreSQL. +Этот проект представляет собой систему для автоматизированной установки, настройки и управления базой данных PostgreSQL с использованием **Ansible** и **Jenkins**. Включает в себя Ansible playbook, роли для управления конфигурацией PostgreSQL и Jenkins pipeline. -## Структура проекта +--- -- **Jenkinsfile** — скрипт для автоматизации пайплайна Jenkins, который позволяет клонировать репозиторий, расшифровывать SSH-ключи, запускать Ansible плейбук и выполнять определенные задачи роли PostgreSQL. -- **ansible.cfg** — конфигурационный файл Ansible, где указаны настройки по умолчанию для выполнения плейбуков. -- **inventory.yml** — файл инвентаря Ansible, который определяет хосты, на которых будут выполняться плейбуки. -- **playbooks/install_postgresql.yml** — основной Ansible плейбук для установки и настройки PostgreSQL. -- **roles/postgresql** — Ansible роль для управления PostgreSQL, которая содержит: - - **tasks/** — каталог с задачами: - - `setup.yml` — установка необходимых пакетов PostgreSQL. - - `open_firewall.yml` — настройка правил брандмауэра для доступа к базе данных. - - `initialize.yml` — инициализация новой базы данных. - - `configure.yml` — настройка параметров конфигурации PostgreSQL. - - `databases.yml` — создание и управление базами данных. - - `users.yml` — создание и управление пользователями базы данных. - - `backup.yml` — резервное копирование базы данных. - - **templates/** — шаблоны конфигурационных файлов для PostgreSQL: - - `pg_hba.conf.j2` — шаблон файла для управления доступом к базе данных. - - `postgresql.conf.j2` — шаблон основного конфигурационного файла PostgreSQL. - - **vars/** — переменные, используемые в роли PostgreSQL. +## Описание Функционала -## Запуск проекта +Проект использует: -### Предварительные требования +- **Jenkins** — для запуска CI/CD pipeline, обеспечивая контроль версий, автоматизацию тестов и упрощение процесса деплоя. +- **Роли и Playbook Ansible** — для настройки и поддержания PostgreSQL, включая управление пользователями, доступом, резервным копированием и настройками безопасности. -- **Jenkins** с установленным Ansible плагином -- Доступ к хостам, указанным в `inventory.yml` +--- -### Пайплайн Jenkins +## Структура Проекта -Файл **Jenkinsfile** определяет этапы пайплайна для автоматизированного развертывания PostgreSQL: +- **`Jenkinsfile`** — Определяет pipeline для Jenkins. Этот файл задает шаги для клонирования репозитория, расшифровки SSH-ключей, выполнения Ansible playbook и запуска задач по настройке и тестированию PostgreSQL. +- **`ansible.cfg`** — Конфигурационный файл Ansible, определяющий параметры по умолчанию для выполнения плейбуков. +- **`inventory.yml`** — Указывает IP-адреса или доменные имена серверов, на которых будет развернут PostgreSQL. +- **`playbooks/install_postgresql.yml`** — Ansible playbook для установки и начальной настройки PostgreSQL. +- **`roles/postgresql/`** — Роль Ansible с задачами, обработчиками, переменными и шаблонами для управления PostgreSQL. -1. **Клонирование репозитория** — загрузка кода проекта. -2. **Расшифровка SSH-ключа** — декодирование зашифрованного ключа с использованием Ansible Vault. -3. **Запуск плейбука PostgreSQL** — выполнение Ansible плейбука для развертывания и настройки PostgreSQL на целевых хостах. +--- -### Параметры запуска +## Детали Роли PostgreSQL -Пайплайн поддерживает следующие параметры: +### Файлы Задач (`tasks`) -- **DB_USER** — имя пользователя PostgreSQL. -- **DB_NAME** — название базы данных. -- **BACKUP_DIR** — директория для хранения резервных копий. -- **TASKS** — список задач для выполнения, указанный через запятую (например, `setup,backup`). +- **`setup.yml`** — начальная установка и настройка PostgreSQL. +- **`initialize.yml`** — инициализация PostgreSQL (создание каталогов и базовая настройка). +- **`configure.yml`** — настройки PostgreSQL, такие как лимиты на подключение и параметры памяти. +- **`users.yml`** — управление пользователями и привилегиями. +- **`databases.yml`** — создание и настройка баз данных. +- **`insert_data.yml`** — заполнение базы начальными данными. +- **`open_firewall.yml`** — настройка брандмауэра для PostgreSQL. +- **`backup.yml`** — настройка и выполнение резервного копирования базы данных. -### Порядок задач +### Шаблоны (`templates`) -По умолчанию задачи выполняются в следующем порядке: +- **`pg_hba.conf.j2`** и **`postgresql.conf.j2`** — Jinja2-шаблоны конфигурационных файлов PostgreSQL. Позволяют динамически генерировать конфигурацию на основе параметров безопасности и производительности. -1. `setup` — установка необходимых пакетов. -2. `firewall` — настройка брандмауэра. -3. `init` — инициализация базы данных. -4. `configure` — конфигурация базы данных. -5. `database` — создание базы данных. -6. `user` — управление пользователями. -7. `backup` — резервное копирование данных. +### Переменные (`vars`) -При указании параметра `TASKS`, можно выбрать конкретные задачи, и они будут выполнены в этом порядке. +- **`vars/main.yml`** — определяет переменные PostgreSQL, включая порты, параметры подключения и пути для резервного копирования. + +--- + +## Использование Проекта + +### Предварительные Требования + +1. **Jenkins**: Jenkins настроен с установленным плагином для Ansible и доступом к репозиторию проекта. +2. **SSH ключи**: Приватный SSH-ключ (`id_ed25519_vault`) загружен в Jenkins для доступа к целевым серверам. + +### Настройка + +1. Склонируйте репозиторий и откройте **inventory.yml** для указания IP-адресов серверов. +2. Настройте переменные в `vars/main.yml` в соответствии с вашими требованиями (например, `postgres_port`, `postgres_db` и т.д.). + +### Запуск + +1. Запустите pipeline в Jenkins, указав необходимые параметры (`DB_USER`, `DB_NAME`, `BACKUP_DIR`). +2. Jenkins выполнит следующие шаги: + - Инициализация и подготовка сервера. + - Настройка безопасности и конфигурации PostgreSQL. + - Создание и настройка пользователей, баз данных и резервного копирования. + +--- + +## Управление и Поддержка + +- **Рестарт PostgreSQL**: При изменении конфигурации роль включает обработчик `Restart PostgreSQL`, который перезапускает базу данных для применения изменений. +- **Резервное Копирование**: Задача `backup.yml` автоматически выполняет резервное копирование базы данных в указанный каталог (`backup_dir`). \ No newline at end of file diff --git a/ansible.cfg b/ansible.cfg index 6db58e7..4ae0c8c 100644 --- a/ansible.cfg +++ b/ansible.cfg @@ -1,3 +1,4 @@ [defaults] inventory = inventory.yml roles_path = ./roles +force_color = true diff --git a/roles/postgresql/handlers/main.yml b/roles/postgresql/handlers/main.yml index ef04727..79cf716 100644 --- a/roles/postgresql/handlers/main.yml +++ b/roles/postgresql/handlers/main.yml @@ -1,4 +1,4 @@ - name: Restart PostgreSQL service: name: postgresql - state: restarted + state: restarted \ No newline at end of file diff --git a/roles/postgresql/tasks/backup.yml b/roles/postgresql/tasks/backup.yml index 84a6fc6..f85bba0 100644 --- a/roles/postgresql/tasks/backup.yml +++ b/roles/postgresql/tasks/backup.yml @@ -5,8 +5,6 @@ owner: postgres group: postgres mode: '0755' - tags: - - backup - name: Perform database backup command: > @@ -14,28 +12,22 @@ become_user: postgres environment: PGPASSWORD: '{{ postgres_password }}' - tags: - - backup - name: Daily cron full backup cron: - name: 'PostgreSQL daily full backup' + name: 'PostgreSQL daily full backup {{ postgres_user }}' user: postgres minute: '0' hour: '1' job: "pg_dump -U {{ postgres_user }} -F c {{ postgres_db }} > {{ backup_dir }}/full_db_backup_{{ postgres_db }}_$(date +\\%F-\\%H-%M).sql" environment: PGPASSWORD: '{{ postgres_password }}' - tags: - - backup - name: Hourly cron incremental backup cron: - name: 'PostgreSQL hourly incremental backup' + name: 'PostgreSQL hourly incremental backup {{ postgres_user }}' user: postgres minute: '0' job: "pg_dump -U {{ postgres_user }} -F c --data-only --file=\"{{ backup_dir }}/incremental_db_backup_{{ postgres_db }}_$(date +\\%F-\\%H-%M).sql\" {{ postgres_db }}" environment: - PGPASSWORD: '{{ postgres_password }}' - tags: - - backup \ No newline at end of file + PGPASSWORD: '{{ postgres_password }}' \ No newline at end of file diff --git a/roles/postgresql/tasks/configure.yml b/roles/postgresql/tasks/configure.yml index 8cc7565..c015e8d 100644 --- a/roles/postgresql/tasks/configure.yml +++ b/roles/postgresql/tasks/configure.yml @@ -6,8 +6,6 @@ group: postgres mode: '0644' notify: Restart PostgreSQL - tags: - - configure - name: Configure pg_hba.conf with template template: @@ -17,5 +15,3 @@ group: postgres mode: '0644' notify: Restart PostgreSQL - tags: - - configure diff --git a/roles/postgresql/tasks/databases.yml b/roles/postgresql/tasks/databases.yml index 999b203..66dabdd 100644 --- a/roles/postgresql/tasks/databases.yml +++ b/roles/postgresql/tasks/databases.yml @@ -4,14 +4,15 @@ owner: '{{ postgres_user }}' encoding: UTF8 state: present - tags: - - database - name: Create contacts table in PostgreSQL community.postgresql.postgresql_query: db: '{{ postgres_db }}' - query: 'CREATE TABLE IF NOT EXISTS contacts (id SERIAL PRIMARY KEY, name VARCHAR(100), phone_number VARCHAR(15));' + query: | + CREATE TABLE IF NOT EXISTS contacts ( + id SERIAL PRIMARY KEY, + name VARCHAR(100), + phone_number VARCHAR(15) UNIQUE + ); login_user: '{{ postgres_user }}' - login_password: '{{ postgres_password }}' - tags: - - database + login_password: '{{ postgres_password }}' \ No newline at end of file diff --git a/roles/postgresql/tasks/initialize.yml b/roles/postgresql/tasks/initialize.yml index c9de0f8..8d5b1be 100644 --- a/roles/postgresql/tasks/initialize.yml +++ b/roles/postgresql/tasks/initialize.yml @@ -2,13 +2,9 @@ command: sudo -u postgres initdb -D /var/lib/pgsql/data args: creates: /var/lib/pgsql/data/PG_VERSION - tags: - - init - + - name: Systemctl start and enable PostgreSQL service: name: postgresql state: started enabled: true - tags: - - init diff --git a/roles/postgresql/tasks/insert_data.yml b/roles/postgresql/tasks/insert_data.yml new file mode 100644 index 0000000..151f687 --- /dev/null +++ b/roles/postgresql/tasks/insert_data.yml @@ -0,0 +1,11 @@ +- name: Insert data into PostgreSQL database idempotently + community.postgresql.postgresql_query: + db: '{{ postgres_db }}' + query: | + INSERT INTO contacts (name, phone_number) VALUES + ('dima', '123123123'), + ('vlad', '8800525252'), + ('denis', '77777777') + ON CONFLICT (phone_number) DO NOTHING; + login_user: '{{ postgres_user }}' + login_password: '{{ postgres_password }}' diff --git a/roles/postgresql/tasks/main.yml b/roles/postgresql/tasks/main.yml index ee7f31a..204231a 100644 --- a/roles/postgresql/tasks/main.yml +++ b/roles/postgresql/tasks/main.yml @@ -1,7 +1,31 @@ - import_tasks: setup.yml + tags: + - setup + - import_tasks: initialize.yml + tags: + - init + - import_tasks: configure.yml + tags: + - configure + - import_tasks: users.yml + tags: + - users + - import_tasks: databases.yml + tags: + - database + +- import_tasks: insert_data.yml + tags: + - insert + - import_tasks: open_firewall.yml -- import_tasks: backup.yml \ No newline at end of file + tags: + - firewall + +- import_tasks: backup.yml + tags: + - backup diff --git a/roles/postgresql/tasks/open_firewall.yml b/roles/postgresql/tasks/open_firewall.yml index 1d984a2..99ff9ed 100644 --- a/roles/postgresql/tasks/open_firewall.yml +++ b/roles/postgresql/tasks/open_firewall.yml @@ -4,11 +4,7 @@ permanent: true state: enabled become: true - tags: - - firewall - + - name: Reload firewall using command command: firewall-cmd --reload become: true - tags: - - firewall diff --git a/roles/postgresql/tasks/setup.yml b/roles/postgresql/tasks/setup.yml index 1a378e6..0271a1b 100644 --- a/roles/postgresql/tasks/setup.yml +++ b/roles/postgresql/tasks/setup.yml @@ -4,12 +4,8 @@ - postgresql-server - postgresql-contrib state: present - tags: - - setup - name: Install python3-psycopg2 zypper: name: python3-psycopg2 state: present - tags: - - setup diff --git a/roles/postgresql/tasks/users.yml b/roles/postgresql/tasks/users.yml index efa300d..22aa2c1 100644 --- a/roles/postgresql/tasks/users.yml +++ b/roles/postgresql/tasks/users.yml @@ -2,6 +2,4 @@ community.postgresql.postgresql_user: name: '{{ postgres_user }}' password: '{{ postgres_password }}' - state: present - tags: - - users + state: present \ No newline at end of file diff --git a/roles/postgresql/vars/main.yml b/roles/postgresql/vars/main.yml index d86ab40..2dd04d2 100644 --- a/roles/postgresql/vars/main.yml +++ b/roles/postgresql/vars/main.yml @@ -5,7 +5,6 @@ postgres_hba_entries: - { type: 'host', database: 'all', user: 'all', address: '0.0.0.0/0', method: 'md5' } - { type: 'local', database: 'all', user: 'all', address: '', method: 'trust' } -backup_dir: "/var/lib/pgsql/backups" +backup_dir: "/var/backups/postgresql" postgres_user: "postgres" -postgres_password: "your_password" -postgres_db: "your_database" +postgres_db: "mydb" From 3bf99fc8f0d47a6e49d6433b4db49755c0b25123 Mon Sep 17 00:00:00 2001 From: dima Date: Mon, 18 Nov 2024 23:52:42 +0300 Subject: [PATCH 2/3] ver 4 --- ansible.cfg | 1 + inventory.yml | 8 +++----- roles/postgresql/tasks/configure.yml | 4 ++-- roles/postgresql/tasks/databases.yml | 2 +- roles/postgresql/tasks/insert_data.yml | 2 +- roles/postgresql/tasks/open_firewall.yml | 8 +++++--- roles/postgresql/templates/postgresql.conf.j2 | 19 +++++++++---------- roles/postgresql/vars/main.yml | 8 ++++++++ 8 files changed, 30 insertions(+), 22 deletions(-) diff --git a/ansible.cfg b/ansible.cfg index 4ae0c8c..ea0bfe0 100644 --- a/ansible.cfg +++ b/ansible.cfg @@ -2,3 +2,4 @@ inventory = inventory.yml roles_path = ./roles force_color = true +interpreter_python = /usr/bin/python3 diff --git a/inventory.yml b/inventory.yml index 215250f..590be8f 100644 --- a/inventory.yml +++ b/inventory.yml @@ -1,6 +1,4 @@ all: - children: - postgres_servers: - hosts: - 192.168.0.71: - ansible_user: ansible \ No newline at end of file + hosts: + 192.168.0.71: + ansible_user: ansible \ No newline at end of file diff --git a/roles/postgresql/tasks/configure.yml b/roles/postgresql/tasks/configure.yml index c015e8d..9cd61ac 100644 --- a/roles/postgresql/tasks/configure.yml +++ b/roles/postgresql/tasks/configure.yml @@ -1,4 +1,4 @@ -- name: Configure postgresql.conf with template +- name: Configure postgresql.conf template: src: postgresql.conf.j2 dest: /var/lib/pgsql/data/postgresql.conf @@ -7,7 +7,7 @@ mode: '0644' notify: Restart PostgreSQL -- name: Configure pg_hba.conf with template +- name: Configure pg_hba.conf template: src: pg_hba.conf.j2 dest: /var/lib/pgsql/data/pg_hba.conf diff --git a/roles/postgresql/tasks/databases.yml b/roles/postgresql/tasks/databases.yml index 66dabdd..ac5cf48 100644 --- a/roles/postgresql/tasks/databases.yml +++ b/roles/postgresql/tasks/databases.yml @@ -5,7 +5,7 @@ encoding: UTF8 state: present -- name: Create contacts table in PostgreSQL +- name: Create contacts table community.postgresql.postgresql_query: db: '{{ postgres_db }}' query: | diff --git a/roles/postgresql/tasks/insert_data.yml b/roles/postgresql/tasks/insert_data.yml index 151f687..4f7dcd5 100644 --- a/roles/postgresql/tasks/insert_data.yml +++ b/roles/postgresql/tasks/insert_data.yml @@ -1,4 +1,4 @@ -- name: Insert data into PostgreSQL database idempotently +- name: Insert data into PostgreSQL database community.postgresql.postgresql_query: db: '{{ postgres_db }}' query: | diff --git a/roles/postgresql/tasks/open_firewall.yml b/roles/postgresql/tasks/open_firewall.yml index 99ff9ed..2c084af 100644 --- a/roles/postgresql/tasks/open_firewall.yml +++ b/roles/postgresql/tasks/open_firewall.yml @@ -4,7 +4,9 @@ permanent: true state: enabled become: true - -- name: Reload firewall using command - command: firewall-cmd --reload + +- name: Reload firewalld + systemd: + name: firewalld + state: reloaded become: true diff --git a/roles/postgresql/templates/postgresql.conf.j2 b/roles/postgresql/templates/postgresql.conf.j2 index 7013fde..7bff891 100644 --- a/roles/postgresql/templates/postgresql.conf.j2 +++ b/roles/postgresql/templates/postgresql.conf.j2 @@ -1,12 +1,11 @@ -listen_addresses = '{{ postgres_listen_addresses | default("*") }}' +listen_addresses = '{{ postgres_listen_addresses }}' -port = {{ postgres_port | default(5432) }} +port = {{ postgres_port }} - -max_connections = {{ postgres_max_connections | default(100) }} -shared_buffers = {{ postgres_shared_buffers | default("128MB") }} -effective_cache_size = {{ postgres_effective_cache_size | default("4GB") }} -maintenance_work_mem = {{ postgres_maintenance_work_mem | default("64MB") }} -checkpoint_completion_target = {{ postgres_checkpoint_completion_target | default(0.7) }} -wal_buffers = {{ postgres_wal_buffers | default("16MB") }} -default_statistics_target = {{ postgres_default_statistics_target | default(100) }} \ No newline at end of file +max_connections = {{ postgres_max_connections }} +shared_buffers = {{ postgres_shared_buffers }} +effective_cache_size = {{ postgres_effective_cache_size }} +maintenance_work_mem = {{ postgres_maintenance_work_mem }} +checkpoint_completion_target = {{ postgres_checkpoint_completion_target }} +wal_buffers = {{ postgres_wal_buffers }} +default_statistics_target = {{ postgres_default_statistics_target }} diff --git a/roles/postgresql/vars/main.yml b/roles/postgresql/vars/main.yml index 2dd04d2..c76a487 100644 --- a/roles/postgresql/vars/main.yml +++ b/roles/postgresql/vars/main.yml @@ -8,3 +8,11 @@ postgres_hba_entries: backup_dir: "/var/backups/postgresql" postgres_user: "postgres" postgres_db: "mydb" + +postgres_max_connections: 100 +postgres_shared_buffers: '128MB' +postgres_effective_cache_size: '4GB' +postgres_maintenance_work_mem: '64MB' +postgres_checkpoint_completion_target: 0.7 +postgres_wal_buffers: '16MB' +postgres_default_statistics_target: 100 \ No newline at end of file From 54f9f8df11a2ed581f3b578d343d95bf356da6c0 Mon Sep 17 00:00:00 2001 From: dima Date: Mon, 18 Nov 2024 23:53:53 +0300 Subject: [PATCH 3/3] ver 4 --- id_ed25519_vault | 50 ++++++++++++++++++++++++------------------------ 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/id_ed25519_vault b/id_ed25519_vault index c88bd0b..486a3db 100644 --- a/id_ed25519_vault +++ b/id_ed25519_vault @@ -1,26 +1,26 @@ $ANSIBLE_VAULT;1.1;AES256 -65316433646437363338333233313530386265396432326633303334363130396438623632373733 -6234643334313336376439343564616333366632393666320a623066663130336665663763626337 -36353135383933386431643036336561653438356537363262333530663363333138663966336231 -3631333939653033370a643061336136313031336163346431393034653237646265653665316466 -63333966383038666635636462393361313731666239356139653466663761383531653063343733 -64396533303131666139323333653838323961396437326438353733653262393164343263643738 -37626464353762656532373739376363363935383065336637333161356331303230356163626533 -66363331616263383366303534376235663564313031343031323466333564646233393238336665 -62383138623137323761656163336631393861386436626338666662313739353338373563626335 -31663831366135396437643562373463613566333433666162313833653230396439353461633437 -31663937343437643363323137313331373839313032333830316135303734376264396539396339 -37613031376235316439303363326134613136616137623133353738313236383436386631636432 -64373861346631386234316234663134316231336666356230373862396237346565393434383039 -64323462653532636161333339623138663564396261363832626630393533323139616165363065 -66376166306131333531323966633036623762323037616261643930343733383165333939326537 -34636534343436313132383532633631363631356563336365393437616337333062323862336164 -66656463643761366335663331633733383065316530653935613134653837666332653262326266 -39396237616235383163386662363637346633366231373236323734383934383035623739333263 -65316364306462376134373165393661316561383837383438306365666437373365366461663439 -61336463636462333363313766363465313163373063323864613136303564396137333536373235 -33656135393732653230373031613663633866386537643164623138623663626663303331656631 -37666562663135643832386335373132643738393233656361663931306563386666613135303033 -32663633373439646564663036626561336338313239316634623838633534306530633739363831 -39306631356662363430633866653538623837303537343331363066326466646430346638623162 -6438 +32383831316464663266383334396166313235356563376262623739323138343738363066346162 +3330333861363833363435323762306236386461323363610a363965333935326132363031346564 +62666163383837333362363532326661343337323633643063303132363031613436633336313831 +6562353233353663320a336638653239633130353934393838333063653862633732626166356635 +33363138663432373965666532633634383234633232306631613664336134663030336363626439 +33336236633431393139393834313939646362663164653161326634613736373530346438636565 +64666335626431333635643632303963393039343763353238633737623331383365363761326431 +30356535653336366238373039356137353132643034363638356166666566646331306330363535 +64376234636664646164633763636165326161333237326666613365393665623138623061386130 +31366664656461623938356234633265643962396138316536373963353663346138616233633837 +37633730666362326433353834313035306461633034303466653536323938616633343531326234 +66363132346632306166626462323634336562623063613735663862643738663565363062643861 +37633065383037306236363865363639633334653938393733663830313161316337343039303966 +35313532383761383838613539653132643165643536663439306232353961633835383865363434 +65336339653930353934313063343538333833376561386230643161366230636232323565333562 +30383231666131393562393166323230643937316531636332333236373462663863646333663836 +66356365303730366563353936633436333962626338663430656534306666316632633030343131 +63383031333537383762326366633535316136323430626666343736623561333264333933626166 +35313861323763393334393632336330316436346635356164313466643163393138626263356432 +31393662336462303131366236366461336231613239383663396531313039656431326435323634 +30613564653232666237306332663266393433306432366438323030346334643464386633306665 +61333236626339346336633862613964303061383236353864323039343932636637396663336161 +30333132633330303539396236326661323963623363623039383735326138613665346333653666 +34653939336562386361636366663863363062363362643261653866373836343264316664363831 +6137 \ No newline at end of file