diff --git a/backup.yaml b/backup.yaml index d33c704..d9542b9 100644 --- a/backup.yaml +++ b/backup.yaml @@ -1,5 +1,5 @@ --- -- hosts: maas_postgres_primary +- hosts: maas_postgres tasks: - name: Backup Postgres ansible.builtin.include_role: @@ -8,10 +8,10 @@ become: true gather_facts: true tags: - - maas_postgres_primary + - maas_postgres - hosts: - - maas_postgres_primary + - maas_postgres - maas_region_controller - maas_rack_controller tasks: @@ -23,7 +23,7 @@ gather_facts: true - hosts: - - maas_postgres_primary + - maas_postgres tasks: - name: Remove DB dump ansible.builtin.file: diff --git a/createadmin.yaml b/createadmin.yaml index ecde6b5..3f7ef72 100644 --- a/createadmin.yaml +++ b/createadmin.yaml @@ -11,7 +11,7 @@ "(?i)Password: ": "{{ user_pwd }}" "(?i)Again: ": "{{ user_pwd }}" "(?i)Email:": "{{ user_email }}" - "(?i)Import SSH keys": "{{ user_ssh if user_ssh is defined else '' }}" + "(?i)Import SSH keys": "{{ user_ssh | default('') | quote }}" become: true no_log: true gather_facts: true diff --git a/group_vars/all/20-database b/group_vars/all/20-database index a840ec6..e17a012 100644 --- a/group_vars/all/20-database +++ b/group_vars/all/20-database @@ -25,9 +25,10 @@ maas_postgres_version_number: "{{ 14 if ansible_distribution_major_version | flo maas_postgres_replication_user: "replicator" maas_postgres_backup_dir: "/tmp/maas_backup/" maas_postgres_backup_path: "{{ maas_postgres_backup_dir }}dump.sql.gz" +maas_snap_backup_path: "{{ '' if maas_install_deb | bool else '/var/snap/maas/snap_backup/' }}" maas_config_backup_path: "{{ '/etc/maas/' if maas_install_deb | bool else '' }}" maas_runtime_backup_path: "{{ '/var/lib/maas/' if maas_install_deb | bool else '' }}" maas_exclude_backup_path: "{{ '/var/lib/maas/boot-resources/' if maas_install_deb | bool else '' }}" -maas_backup_dest_path: "/tmp/{{ inventory_hostname }}_maas_backup_{{ ansible_date_time.iso8601 }}.tgz" +maas_backup_dest_path: "/tmp/{{ inventory_hostname | replace('.','_') }}_maas_backup_{{ ansible_date_time.iso8601 }}.tgz" maas_restore_config_path: "{{ '/etc/maas' if maas_install_deb | bool else '' }}" maas_restore_runtime_path: "{{ '/var/lib/maas' if maas_install_deb | bool else '' }}" diff --git a/restore.yaml b/restore.yaml index f923438..82b4b1f 100644 --- a/restore.yaml +++ b/restore.yaml @@ -1,6 +1,16 @@ --- +- hosts: all + tasks: + - name: Check backup file exists for host + stat: + path: "{{ maas_backup_download_path }}{{ maas_backup_file }}" + delegate_to: localhost + become: false + register: backup_file + timeout: 3600 + - hosts: - - maas_postgres_primary + - maas_postgres - maas_region_controller - maas_rack_controller tasks: @@ -8,20 +18,31 @@ ansible.builtin.include_role: name: common tasks_from: restore + when: backup_file.stat.exists become: true gather_facts: true -- hosts: maas_postgres_primary +- hosts: maas_postgres tasks: - name: Restore from database dump ansible.builtin.include_role: name: maas_postgres tasks_from: restore + when: backup_file.stat.exists + become: true + gather_facts: true + +- hosts: maas_region_controller + tasks: + - name: Reset database triggers + ansible.builtin.command: maas-region dbupgrade + when: ('maas_region_controller' in group_names) and (maas_install_deb | bool) + run_once: true become: true gather_facts: true - hosts: - - maas_postgres_primary + - maas_postgres - maas_region_controller - maas_rack_controller tasks: @@ -29,5 +50,6 @@ ansible.builtin.file: path: /tmp/maas_backup state: absent + when: backup_file.stat.exists become: true gather_facts: true diff --git a/roles/common/tasks/backup.yaml b/roles/common/tasks/backup.yaml index 0cfd883..16091be 100644 --- a/roles/common/tasks/backup.yaml +++ b/roles/common/tasks/backup.yaml @@ -1,11 +1,48 @@ --- +- name: Stop MAAS snap + ansible.builtin.command: snap stop maas + when: (('maas_region_controller' in group_names) or ('maas_rack_controller' in group_names)) and (not maas_install_deb | bool) + +- name: Backup MAAS snap + ansible.builtin.shell: "\ + set -o pipefail && \ + snap save maas | awk 'NR==2' | awk '{print $1}'" + args: + executable: /bin/bash + register: snap_id + when: (('maas_region_controller' in group_names) or ('maas_rack_controller' in group_names)) and (not maas_install_deb | bool) + +- name: Verify snap backup valid + ansible.builtin.command: "snap check-snapshot {{ snap_id.stdout }}" + when: (('maas_region_controller' in group_names) or ('maas_rack_controller' in group_names)) and (not maas_install_deb | bool) + +- name: Create Temporary Unpack Directory + ansible.builtin.file: + path: /tmp/ + owner: root + group: root + mode: '0755' + state: directory + +- name: Make snap backup directory + ansible.builtin.file: + path: "{{ maas_snap_backup_path }}" + mode: 0777 + state: directory + when: (('maas_region_controller' in group_names) or ('maas_rack_controller' in group_names)) and (not maas_install_deb | bool) + +- name: Export snap backup + ansible.builtin.command: "snap export-snapshot {{ snap_id.stdout }} {{ maas_snap_backup_path }}backup.zip" + when: (('maas_region_controller' in group_names) or ('maas_rack_controller' in group_names)) and (not maas_install_deb | bool) + - name: Generate List of Archiveable Directories ansible.builtin.set_fact: archive_list: "{{ archive_list | default([]) + [item] }}" loop: - - "{{ ('maas_postgres_primary' in group_names) | ternary('{{ maas_postgres_backup_dir }}', None) }}" - - "{{ (('maas_region_controller' in group_names) or ('maas_rack_controller' in group_names)) | ternary('{{ maas_config_backup_path }}', None) }}" - - "{{ (('maas_region_controller' in group_names) or ('maas_rack_controller' in group_names)) | ternary('{{ maas_runtime_backup_path }}', None) }}" + - "{{ maas_postgres_backup_dir if 'maas_postgres' in group_names else None }}" + - "{{ maas_snap_backup_path if (('maas_region_controller' in group_names) or ('maas_rack_controller' in group_names)) else None }}" + - "{{ maas_config_backup_path if (('maas_region_controller' in group_names) or ('maas_rack_controller' in group_names)) else None }}" + - "{{ maas_runtime_backup_path if (('maas_region_controller' in group_names) or ('maas_rack_controller' in group_names)) else None }}" when: item - name: Bundle Backup Assets @@ -15,15 +52,29 @@ exclude_path: - "{{ maas_exclude_backup_path }}" dest: "{{ maas_backup_dest_path }}" + when: archive_list is defined + timeout: 3600 -# ansible.builtin.fetch has the ability to be oom killed on large files, so we're using scp instead - name: Download Backup - ansible.builtin.command: scp {{ ansible_user }}@{{ inventory_hostname }}:{{ maas_backup_dest_path }} {{ maas_backup_download_path }} - delegate_to: localhost - become: false # don't need sudo locally - changed_when: false + ansible.builtin.fetch: + src: "{{ maas_backup_dest_path }}" + dest: "{{ maas_backup_download_path }}{{ maas_backup_file }}" + when: archive_list is defined + timeout: 3600 + become: false - name: Remove Backup from Remote Host ansible.builtin.file: path: "{{ maas_backup_dest_path }}" state: absent + when: archive_list is defined + +- name: Remove Snap Backup Directory + ansible.builtin.file: + path: "{{ maas_snap_backup_path }}" + state: absent + when: (('maas_region_controller' in group_names) or ('maas_rack_controller' in group_names)) and (not maas_install_deb | bool) + +- name: Start MAAS snap + ansible.builtin.command: snap start maas + when: (('maas_region_controller' in group_names) or ('maas_rack_controller' in group_names)) and (not maas_install_deb | bool) diff --git a/roles/common/tasks/restore.yaml b/roles/common/tasks/restore.yaml index 42781e6..92a32ab 100644 --- a/roles/common/tasks/restore.yaml +++ b/roles/common/tasks/restore.yaml @@ -9,8 +9,9 @@ - name: Unpack Backup Archive ansible.builtin.unarchive: - src: "{{ maas_backup_file }}" + src: "{{ maas_backup_download_path }}{{ maas_backup_file }}" dest: /tmp/maas_backup/ + timeout: 3600 - name: Stop Region Controller ansible.builtin.systemd: @@ -28,13 +29,38 @@ ansible.builtin.command: snap stop maas when: (('maas_region_controller' in group_names) or ('maas_rack_controller' in group_names)) and (not maas_install_deb | bool) +- name: Locate snap backup file + ansible.builtin.find: + paths: /tmp/maas_backup/ + recurse: false + file_type: file + patterns: '*.zip' + register: snap_zip + +- name: Import MAAS snap backup + ansible.builtin.shell: "\ + set -o pipefail && \ + snap import-snapshot {{ snap_zip.files[0].path }} | awk 'NR==3' | awk '{print $1}'" + args: + executable: /bin/bash + register: snap_id + when: > + (('maas_region_controller' in group_names) or ('maas_rack_controller' in group_names)) and + ( not maas_install_deb | bool ) and ( snap_zip.files | length > 0 ) + +- name: Restore Snap data + ansible.builtin.command: "snap restore {{ snap_id.stdout }}" + when: > + (('maas_region_controller' in group_names) or ('maas_rack_controller' in group_names)) and + (not maas_install_deb | bool) and ( snap_zip.files | length > 0 ) + - name: Restore Config - ansible.builtin.command: "mv /tmp/maas_backup/etc/maas {{ maas_restore_config_path }}" - when: ('maas_region_controller' in group_names) or ('maas_rack_controller' in group_names) + ansible.builtin.command: "mv /tmp/maas_backup{{ maas_restore_config_path }} {{ maas_restore_config_path }}" + when: (('maas_region_controller' in group_names) or ('maas_rack_controller' in group_names)) and (maas_install_deb | bool) - name: Restore Runtime Data - ansible.builtin.command: "mv /tmp/maas_backup/var/lib/maas {{ maas_restore_runtime_path }}" - when: ('maas_region_controller' in group_names) or ('maas_rack_controller' in group_names) + ansible.builtin.command: "mv /tmp/maas_backup{{ maas_restore_runtime_path }} {{ maas_restore_runtime_path }}" + when: (('maas_region_controller' in group_names) or ('maas_rack_controller' in group_names)) and (maas_install_deb | bool) - name: Start Region Controller ansible.builtin.systemd: diff --git a/roles/maas_postgres/tasks/backup.yaml b/roles/maas_postgres/tasks/backup.yaml new file mode 100644 index 0000000..d217518 --- /dev/null +++ b/roles/maas_postgres/tasks/backup.yaml @@ -0,0 +1,14 @@ +--- +- name: "Create a backup directory" + ansible.builtin.file: + path: "{{ maas_postgres_backup_dir }}" + mode: 0777 + state: directory + +- name: Dump database to a file + community.postgresql.postgresql_db: + name: "{{ maas_postgres_database }}" + state: dump + target: "{{ maas_postgres_backup_path }}" + become: true + become_user: postgres diff --git a/roles/maas_postgres/tasks/restore.yaml b/roles/maas_postgres/tasks/restore.yaml new file mode 100644 index 0000000..30b8b3f --- /dev/null +++ b/roles/maas_postgres/tasks/restore.yaml @@ -0,0 +1,8 @@ +--- +- name: Restore database from file + community.postgresql.postgresql_db: + name: "{{ maas_postgres_database }}" + state: restore + target: "{{ maas_postgres_backup_path }}" + become: true + become_user: postgres diff --git a/roles/maas_rack_controller/tasks/main.yaml b/roles/maas_rack_controller/tasks/main.yaml index aa9359a..7788044 100644 --- a/roles/maas_rack_controller/tasks/main.yaml +++ b/roles/maas_rack_controller/tasks/main.yaml @@ -14,7 +14,6 @@ ansible.builtin.package_facts: manager: "auto" - - name: Check installed snaps ansible.builtin.shell: "\ set -o pipefail && \ diff --git a/roles/maas_region_controller/tasks/install_maas.yaml b/roles/maas_region_controller/tasks/install_maas.yaml index e2e79bc..931e296 100644 --- a/roles/maas_region_controller/tasks/install_maas.yaml +++ b/roles/maas_region_controller/tasks/install_maas.yaml @@ -79,6 +79,7 @@ command: > maas init --rbac-url={{ maas_rbac_url | default('') | quote }} --candid-agent-file={{ maas_candid_auth_file | default('') | quote }} + --admin-ssh-import={{ admin_id | default('') | quote }} responses: "(?i)Username: ": "{{ admin_username }}" "(?i)Password: ": "{{ admin_password }}" @@ -97,7 +98,7 @@ - name: Add an administrator to MAAS ansible.builtin.command: maas createadmin \ --username={{ admin_username }} --password={{ admin_password }} \ - --email={{ admin_email }} --ssh-import={{ admin_id if admin_id is defined else '' }} + --email={{ admin_email }} --ssh-import={{ admin_id | default('') | quote }} when: not maas_region_new_installation or maas_installation_type | lower == 'snap' run_once: true