From eaaa35de252101f3cb1c57b127e6147d2bed7f32 Mon Sep 17 00:00:00 2001 From: Marty Oehme Date: Wed, 2 Apr 2025 20:49:41 +0200 Subject: [PATCH] Split incus role from playbook --- ansible/playbook.yaml | 335 +----------------- ansible/roles/arr/tasks/main.yaml | 11 + ansible/roles/arr/templates/arr.yml | 171 +++++++++ .../roles/incus-install/tasks/add-repo.yaml | 56 +++ .../roles/incus-install/tasks/bootstrap.yaml | 220 ++++++++++++ ansible/roles/incus-install/tasks/main.yaml | 7 + .../system}/files/incus.servers.tpl | 0 .../system}/files/incus.sources.tpl | 0 .../{ => roles/system}/files/zabbly-key.asc | 0 ansible/roles/system/handlers/main.yaml | 11 + ansible/roles/system/tasks/main.yaml | 38 ++ 11 files changed, 531 insertions(+), 318 deletions(-) create mode 100644 ansible/roles/arr/tasks/main.yaml create mode 100644 ansible/roles/arr/templates/arr.yml create mode 100644 ansible/roles/incus-install/tasks/add-repo.yaml create mode 100644 ansible/roles/incus-install/tasks/bootstrap.yaml create mode 100644 ansible/roles/incus-install/tasks/main.yaml rename ansible/{ => roles/system}/files/incus.servers.tpl (100%) rename ansible/{ => roles/system}/files/incus.sources.tpl (100%) rename ansible/{ => roles/system}/files/zabbly-key.asc (100%) create mode 100644 ansible/roles/system/handlers/main.yaml create mode 100644 ansible/roles/system/tasks/main.yaml diff --git a/ansible/playbook.yaml b/ansible/playbook.yaml index 1dd7d4c..47d5fc5 100644 --- a/ansible/playbook.yaml +++ b/ansible/playbook.yaml @@ -1,324 +1,23 @@ --- -- name: Update system - hosts: all +- name: Prepare incus server host + hosts: incus_server tasks: - - name: Ensure aptitude installed - apt: - name: "aptitude" - state: present - tags: - - apt - become: true + - name: Prepare system + ansible.builtin.import_role: + name: system + tags: system - - name: Ensure OS upgraded - apt: - upgrade: dist - tags: - - apt - - update - - os - become: true + # FIXME: Role needs much fixup before it can run + # - name: Prepare incus + # ansible.builtin.import_role: + # name: incus-install + # tags: incus - - name: Check if reboot is necessary - register: reboot_required_file - stat: - path: /var/run/reboot-required - get_checksum: false - tags: - - os - - reboot - notify: reboot host - - name: All packages updated - apt: - name: "*" - state: latest # noqa 403 - tags: - - apt - - update - - packages - become: true - - handlers: - - name: Reboot host - reboot: - msg: "Reboot initiated by Ansible" - connect_timeout: 5 - reboot_timeout: 600 - pre_reboot_delay: 0 - post_reboot_delay: 30 - test_command: whoami - become: true - when: reboot_required_file.stat.exists - -- name: Incus - Add package repository (apt) - hosts: all - gather_facts: true - gather_subset: - - "distribution_release" - vars: - task_release: "{{ incus_release | default('stable') }}" - task_roles: "{{ incus_roles | default(['standalone', 'ui']) }}" - any_errors_fatal: true +- name: Prepare all docker hosted containers + hosts: docker_instance tasks: - - name: Check if distribution is supported - meta: end_play - when: 'ansible_distribution not in ("Ubuntu", "Debian")' - - - name: Create apt keyring path - file: - path: /etc/apt/keyrings/ - mode: 0755 - state: directory - when: 'task_roles|length > 0 and task_release != "distro"' - - - name: Add Zabbly repository key - copy: - src: files/zabbly-key.asc - dest: /etc/apt/keyrings/ansible-zabbly.asc - notify: Update apt - become: true - when: 'task_roles|length > 0 and task_release != "distro"' - - - name: Get DPKG architecture - shell: dpkg --print-architecture - register: dpkg_architecture - changed_when: false - check_mode: no - when: 'task_roles|length > 0 and task_release != "distro"' - - - name: Add Zabbly package source - template: - src: files/incus.sources.tpl - dest: /etc/apt/sources.list.d/ansible-zabbly-incus-{{ task_release }}.sources - notify: Update apt - become: true - when: 'task_roles|length > 0 and task_release != "distro"' - - handlers: - - name: Update apt - apt: - force_apt_get: yes - update_cache: yes - cache_valid_time: 0 - become: true - -- name: Incus - Install packages and bootstrap - hosts: all - gather_facts: true - gather_subset: - - "default_ipv4" - - "default_ipv6" - - "distribution_release" - vars: - task_init: "{{ incus_init | default('{}') }}" - task_ip_address: "{{ incus_ip_address | default(ansible_default_ipv6['address'] | default(ansible_default_ipv4['address'])) }}" - task_name: "{{ incus_name | default('') }}" - task_roles: "{{ incus_roles | default(['ui', 'standalone']) }}" - - task_ovn_northbound: "{{ lookup('template', '../files/ovn/ovn-central.servers.tpl') | from_yaml | map('regex_replace', '^(.*)$', 'ssl:[\\1]:6641') | join(',') }}" - task_servers: "{{ lookup('template', 'files/incus.servers.tpl') | from_yaml | sort }}" - any_errors_fatal: true - become: true - tasks: - - name: Install the Incus package (deb) - apt: - name: - - incus - install_recommends: no - state: present - register: install_deb - when: 'ansible_distribution in ("Debian", "Ubuntu") and task_roles | length > 0' - - - name: Install the Incus package (rpm) - ansible.builtin.package: - name: - - incus - state: present - register: install_rpm - when: 'ansible_distribution == "CentOS" and task_roles | length > 0' - - - name: Install the Incus UI package (deb) - apt: - name: - - incus-ui-canonical - install_recommends: no - state: present - when: 'ansible_distribution in ("Debian", "Ubuntu") and "ui" in task_roles' - - # - name: Install btrfs tools - # ansible.builtin.package: - # name: - # - btrfs-progs - # state: present - # when: "task_roles | length > 0 and 'btrfs' in task_init['storage'] | dict2items | json_query('[].value.driver')" - # - # - name: Install ceph tools - # ansible.builtin.package: - # name: - # - ceph-common - # state: present - # when: "task_roles | length > 0 and 'ceph' in task_init['storage'] | dict2items | json_query('[].value.driver')" - # - # - name: Install LVM tools - # ansible.builtin.package: - # name: - # - lvm2 - # state: present - # when: "task_roles | length > 0 and 'lvm' in task_init['storage'] | dict2items | json_query('[].value.driver')" - # - # - name: Install ZFS dependencies - # ansible.builtin.package: - # name: - # - zfs-dkms - # state: present - # when: "task_roles | length > 0 and 'zfs' in task_init['storage'] | dict2items | json_query('[].value.driver') and ansible_distribution == 'Debian'" - # - # - name: Install ZFS tools - # ansible.builtin.package: - # name: - # - zfsutils-linux - # state: present - # when: "task_roles | length > 0 and 'zfs' in task_init['storage'] | dict2items | json_query('[].value.driver')" - - - name: Set uid allocation - shell: - cmd: "usermod root --add-subuids 10000000-1009999999" - when: '(install_deb.changed or install_rpm.changed) and ansible_distribution == "CentOS"' - - - name: Set gid allocation - shell: - cmd: "usermod root --add-subgids 10000000-1009999999" - when: '(install_deb.changed or install_rpm.changed) and ansible_distribution == "CentOS"' - - - name: Enable incus socket unit - systemd: - enabled: true - name: incus.socket - state: started - when: 'install_deb.changed or install_rpm.changed' - - - name: Enable incus service unit - systemd: - enabled: true - name: incus.service - state: started - when: 'install_deb.changed or install_rpm.changed' - - - name: Enable incus startup unit - systemd: - enabled: true - name: incus-startup.service - state: started - when: 'install_deb.changed or install_rpm.changed' - - - name: Set client listen address - shell: - cmd: "incus --force-local config set core.https_address {{ task_ip_address }}" - when: '(install_deb.changed or install_rpm.changed) and ("standalone" in task_roles or ("cluster" in task_roles and task_servers[0] == inventory_hostname))' - - - name: Set cluster listen address - shell: - cmd: "incus --force-local config set cluster.https_address {{ task_ip_address }}" - when: '(install_deb.changed or install_rpm.changed) and "cluster" in task_roles and task_servers[0] == inventory_hostname' - - # - name: Set OVN NorthBound database - # shell: - # cmd: "incus --force-local config set network.ovn.northbound_connection={{ task_ovn_northbound }} network.ovn.client_cert=\"{{ lookup('file', '../data/ovn/'+ovn_name+'/'+inventory_hostname+'.crt') }}\" network.ovn.client_key=\"{{ lookup('file', '../data/ovn/'+ovn_name+'/'+inventory_hostname+'.key') }}\" network.ovn.ca_cert=\"{{ lookup('file', '../data/ovn/'+ovn_name+'/ca.crt') }}\"" - # notify: Restart Incus - # when: '(install_deb.changed or install_rpm.changed) and task_ovn_northbound and ("standalone" in task_roles or ("cluster" in task_roles and task_servers[0] == inventory_hostname))' - - - name: Add networks - shell: - cmd: "incus network create {{ item.key }} --type={{ item.value.type }}{% for k in item.value.local_config | default([]) %} {{ k }}={{ item.value.local_config[k] }}{% endfor %}{% for k in item.value.config | default([]) %} {{ k }}={{ item.value.config[k] }}{% endfor %}" - loop: "{{ task_init['network'] | dict2items }}" - when: '(install_deb.changed or install_rpm.changed) and ("standalone" in task_roles or ("cluster" in task_roles and task_servers[0] == inventory_hostname))' - - - name: Set network description - shell: - cmd: "incus network set --property {{ item.key }} description=\"{{ item.value.description }}\"" - loop: "{{ task_init['network'] | dict2items }}" - when: '(install_deb.changed or install_rpm.changed) and ("standalone" in task_roles or ("cluster" in task_roles and task_servers[0] == inventory_hostname)) and item.value.description | default(None)' - - - name: Add storage pools - shell: - cmd: "incus storage create {{ item.key }} {{ item.value.driver }}{% for k in item.value.local_config | default([]) %} {{ k }}={{ item.value.local_config[k] }}{% endfor %}{% for k in item.value.config | default([]) %} {{ k }}={{ item.value.config[k] }}{% endfor %}" - loop: "{{ task_init['storage'] | dict2items }}" - when: '(install_deb.changed or install_rpm.changed) and ("standalone" in task_roles or ("cluster" in task_roles and task_servers[0] == inventory_hostname))' - - - name: Set storage pool description - shell: - cmd: "incus storage set --property {{ item.key }} description=\"{{ item.value.description }}\"" - loop: "{{ task_init['storage'] | dict2items }}" - when: '(install_deb.changed or install_rpm.changed) and ("standalone" in task_roles or ("cluster" in task_roles and task_servers[0] == inventory_hostname)) and item.value.description | default(None)' - - - name: Add storage pool to default profile - shell: - cmd: "incus profile device add default root disk path=/ pool={{ item }}" - loop: "{{ task_init['storage'] | dict2items | json_query('[?value.default].key') }}" - when: '(install_deb.changed or install_rpm.changed) and ("standalone" in task_roles or ("cluster" in task_roles and task_servers[0] == inventory_hostname))' - - - name: Add network to default profile - shell: - cmd: "incus profile device add default eth0 nic network={{ item }} name=eth0" - loop: "{{ task_init['network'] | dict2items | json_query('[?value.default].key') }}" - when: '(install_deb.changed or install_rpm.changed) and ("standalone" in task_roles or ("cluster" in task_roles and task_servers[0] == inventory_hostname))' - - - name: Bootstrap the cluster - shell: - cmd: "incus --force-local cluster enable {{ inventory_hostname }}" - when: '(install_deb.changed or install_rpm.changed) and "cluster" in task_roles and task_servers[0] == inventory_hostname' - - - name: Create join tokens - delegate_to: "{{ task_servers[0] }}" - shell: - cmd: "incus --force-local --quiet cluster add {{ inventory_hostname }}" - register: cluster_add - when: '(install_deb.changed or install_rpm.changed) and "cluster" in task_roles and task_servers[0] != inventory_hostname' - - - name: Wait 5s to avoid token use before valid - ansible.builtin.wait_for: - timeout: 5 - delegate_to: localhost - when: 'cluster_add.changed' - - - name: Join the cluster - throttle: 1 - shell: - cmd: "incus --force-local admin init --preseed" - stdin: |- - cluster: - enabled: true - cluster_address: "{{ task_ip_address }}" - cluster_token: "{{ cluster_add.stdout }}" - server_address: "{{ task_ip_address }}" - member_config: {% for pool in task_init.storage %}{% for key in task_init.storage[pool].local_config | default([]) %} - - - entity: storage-pool - name: {{ pool }} - key: {{ key }} - value: {{ task_init.storage[pool].local_config[key] }}{% endfor %}{% endfor %}{% for network in task_init.network %}{% for key in task_init.network[network].local_config | default([]) %} - - - entity: network - name: {{ network }} - key: {{ key }} - value: {{ task_init.network[network].local_config[key] }}{% endfor %}{% endfor %} - when: 'cluster_add.changed' - - - name: Apply additional configuration - shell: - cmd: "incus config set {{ item.key }}=\"{{ item.value }}\"" - loop: "{{ task_init['config'] | default({}) | dict2items }}" - when: '(install_deb.changed or install_rpm.changed) and ("standalone" in task_roles or ("cluster" in task_roles and task_servers[0] == inventory_hostname))' - - - name: Load client certificates - shell: - cmd: "incus config trust add-certificate --name \"{{ item.key }}\" --type={{ item.value.type | default('client') }} -" - stdin: "{{ item.value.certificate }}" - loop: "{{ task_init['clients'] | default({}) | dict2items }}" - when: '(install_deb.changed or install_rpm.changed) and ("standalone" in task_roles or ("cluster" in task_roles and task_servers[0] == inventory_hostname))' - handlers: - - name: Restart Incus - systemd: - name: incus.service - state: restarted + - name: Set up Arr stack + ansible.builtin.import_role: + name: arr + tags: arr diff --git a/ansible/roles/arr/tasks/main.yaml b/ansible/roles/arr/tasks/main.yaml new file mode 100644 index 0000000..5e303d9 --- /dev/null +++ b/ansible/roles/arr/tasks/main.yaml @@ -0,0 +1,11 @@ +--- +- name: Deploy wallabag to swarm + community.general.docker_stack: + name: arr + state: present + prune: true + compose: + - "{{ lookup('template', 'docker-stack.yml.j2') | from_yaml }}" + become: true + tags: + - docker-swarm diff --git a/ansible/roles/arr/templates/arr.yml b/ansible/roles/arr/templates/arr.yml new file mode 100644 index 0000000..9d765e1 --- /dev/null +++ b/ansible/roles/arr/templates/arr.yml @@ -0,0 +1,171 @@ +services: + sonarr: + container_name: sonarr + image: lscr.io/linuxserver/sonarr:latest + ports: + - 8989:8989 + environment: + - PUID=${PUID} + - PGID=${PGID} + - UMASK_SET=022 + - TZ=${TZ} + volumes: + - "./config/sonarr:/config" + - "CHANGE_TO_COMPOSE_DATA_PATH:/data" + restart: unless-stopped + radarr: + container_name: radarr + image: lscr.io/linuxserver/radarr:latest + ports: + - 7878:7878 + environment: + - PUID=${PUID} + - PGID=${PGID} + - UMASK_SET=022 + - TZ=${TZ} + volumes: + - "./config/radarr:/config" + - "CHANGE_TO_COMPOSE_DATA_PATH:/data" + restart: unless-stopped + lidarr: + container_name: lidarr + image: lscr.io/linuxserver/lidarr:latest + ports: + - 8686:8686 + environment: + - PUID=${PUID} + - PGID=${PGID} + - UMASK_SET=022 + - TZ=${TZ} + - DOCKER_MODS=linuxserver/mods:universal-docker + volumes: + - "./config/lidarr:/config" + - "CHANGE_TO_COMPOSE_DATA_PATH:/data" + - "/var/run/docker.sock:/var/run/docker.sock:ro" + restart: unless-stopped + readarr: + container_name: readarr + image: lscr.io/linuxserver/readarr:develop + ports: + - 8787:8787 + environment: + - PUID=${PUID} + - PGID=${PGID} + - UMASK_SET=022 + - TZ=${TZ} + volumes: + - "./config/readarr:/config" + - "CHANGE_TO_COMPOSE_DATA_PATH:/data" + restart: unless-stopped + prowlarr: + container_name: prowlarr + image: lscr.io/linuxserver/prowlarr:develop + environment: + - PUID=${PUID} + - PGID=${PGID} + - UMASK_SET=022 + - TZ=${TZ} + volumes: + - "./config/prowlarr:/config" + ports: + - 9696:9696 + restart: unless-stopped + sabnzbd: + container_name: sabnzbd + image: lscr.io/linuxserver/sabnzbd:latest + environment: + - PUID=${PUID} + - PGID=${PGID} + - TZ=${TZ} + volumes: + - "./config/sabnzbd:/config" + - "CHANGE_TO_COMPOSE_DATA_PATH/usenet:/data/usenet:rw" + ports: + - 8080:8080 + restart: unless-stopped + pia-qbittorrent: + image: j4ym0/pia-qbittorrent + container_name: pia-qbittorrent + cap_add: + - NET_ADMIN + environment: + - UID=${PUID} + - GID=${PGID} + - TZ=${TZ} + - REGION=Netherlands + - USER=${PIA_USER} + - PASSWORD=${PIA_PASS} + volumes: + - "./config/piaqbit:/config" + - "CHANGE_TO_COMPOSE_DATA_PATH/torrent:/downloads:rw" + ports: + - "8888:8888" + restart: unless-stopped + jellyfin: + image: lscr.io/linuxserver/jellyfin:latest + container_name: jellyfin + environment: + - PUID={$PUID} + - PGID={$PGID} + - TZ=${TZ} + #- JELLYFIN_PublishedServerUrl=192.168.0.5 #optional + volumes: + - ".config/jellyfin:/config" + - "CHANGE_TO_COMPOSE_DATA_PATH/media:/data" + ports: + - 8096:8096 + - 7359:7359/udp #optional - network discovery + - 1900:1900/udp #optional - dlna discovery + restart: unless-stopped + audiobookshelf: + container_name: audiobookshelf + image: ghcr.io/advplyr/audiobookshelf:latest + environment: + - PUID=${PUID} + - PGID=${PGID} + - UMASK_SET=022 + - TZ=${TZ} + ports: + - 13378:80 + volumes: + - "CHANGE_TO_COMPOSE_DATA_PATH/media/audio/books:/audiobooks" + - "CHANGE_TO_COMPOSE_DATA_PATH/media/audio/podcasts:/podcasts" + - ".config/audiobookshelf:/config" + - ".metadata/audiobookshelf:/metadata" + restart: unless-stopped + jellyseerr: + image: fallenbagel/jellyseerr:latest + container_name: jellyseerr + environment: + - TZ=${TZ} + ports: + - 5055:5055 + volumes: + - "./config/jellyseerr:/app/config" + restart: unless-stopped + beets: + image: lscr.io/linuxserver/beets:latest + container_name: beets + environment: + - PUID=${PUID} + - PGID=${PGID} + - TZ=${TZ} + volumes: + - "./config/beets:/config" + - "CHANGE_TO_COMPOSE_DATA_PATH/media/audio/music:/music" + - "CHANGE_TO_COMPOSE_DATA_PATH/media/audio/music-unsorted:/downloads" + - "CHANGE_TO_COMPOSE_DATA_PATH:/data" + ports: + - 8337:8337 + restart: unless-stopped + homarr: + image: ghcr.io/ajnart/homarr:latest + container_name: homarr + volumes: + - /var/run/docker.sock:/var/run/docker.sock # Optional, only if you want docker integration + - ./config/homarr/configs:/app/data/configs + - ./config/homarr/icons:/app/public/icons + - ./config/homarr/data:/data + ports: + - '80:7575' + restart: unless-stopped diff --git a/ansible/roles/incus-install/tasks/add-repo.yaml b/ansible/roles/incus-install/tasks/add-repo.yaml new file mode 100644 index 0000000..edb19c9 --- /dev/null +++ b/ansible/roles/incus-install/tasks/add-repo.yaml @@ -0,0 +1,56 @@ +--- +- name: Incus - Add package repository (apt) + hosts: all + gather_facts: true + gather_subset: + - "distribution_release" + vars: + task_release: "{{ incus_release | default('stable') }}" + task_roles: "{{ incus_roles | default(['standalone', 'ui']) }}" + any_errors_fatal: true + tasks: + - name: Check if distribution is supported + ansible.builtin.meta: end_play + when: 'ansible_distribution not in ("Ubuntu", "Debian")' + + - name: Create apt keyring path + ansible.builtin.file: + path: /etc/apt/keyrings/ + mode: "0755" + state: directory + when: 'task_roles | length > 0 and task_release != "distro"' + + - name: Add Zabbly repository key + ansible.builtin.copy: + src: files/zabbly-key.asc + dest: /etc/apt/keyrings/ansible-zabbly.asc + mode: "0644" + notify: Update apt + become: true + when: 'task_roles | length > 0 and task_release != "distro"' + + - name: Get DPKG architecture + ansible.builtin.shell: dpkg --print-architecture + register: dpkg_architecture + changed_when: false + check_mode: false + when: 'task_roles | length > 0 and task_release != "distro"' + + - name: Add Zabbly package source + ansible.builtin.template: + src: files/incus.sources.tpl + dest: /etc/apt/sources.list.d/ansible-zabbly-incus-{{ task_release }}.sources + notify: Update apt + become: true + when: 'task_roles|length > 0 and task_release != "distro"' + + - name: Handle apt by flushing handlers + meta: flush_handlers + + handlers: + - name: Update apt + ansible.builtin.apt: + force_apt_get: true + update_cache: true + cache_valid_time: 0 + become: true diff --git a/ansible/roles/incus-install/tasks/bootstrap.yaml b/ansible/roles/incus-install/tasks/bootstrap.yaml new file mode 100644 index 0000000..c6e1dfa --- /dev/null +++ b/ansible/roles/incus-install/tasks/bootstrap.yaml @@ -0,0 +1,220 @@ +--- +- name: Incus - Install packages and bootstrap + hosts: all + gather_facts: true + gather_subset: + - "default_ipv4" + - "default_ipv6" + - "distribution_release" + vars: + task_init: "{{ incus_init | default('{}') }}" + task_ip_address: "{{ incus_ip_address | default(ansible_default_ipv6['address'] | default(ansible_default_ipv4['address'])) }}" + task_name: "{{ incus_name | default('') }}" + task_roles: "{{ incus_roles | default(['ui', 'standalone']) }}" + + task_ovn_northbound: "{{ lookup('template', '../files/ovn/ovn-central.servers.tpl') | from_yaml | map('regex_replace', '^(.*)$', 'ssl:[\\1]:6641') | join(',') }}" + task_servers: "{{ lookup('template', 'files/incus.servers.tpl') | from_yaml | sort }}" + any_errors_fatal: true + become: true + tasks: + - name: Install the Incus package (deb) + ansible.builtin.apt: + name: + - incus + install_recommends: no + state: present + register: install_deb + when: 'ansible_distribution in ("Debian", "Ubuntu") and task_roles | length > 0' + + - name: Install the Incus package (rpm) + ansible.builtin.package: + name: + - incus + state: present + register: install_rpm + when: 'ansible_distribution == "CentOS" and task_roles | length > 0' + + - name: Install the Incus UI package (deb) + ansible.builtin.apt: + name: + - incus-ui-canonical + install_recommends: no + state: present + when: 'ansible_distribution in ("Debian", "Ubuntu") and "ui" in task_roles' + + # - name: Install btrfs tools + # ansible.builtin.package: + # name: + # - btrfs-progs + # state: present + # when: "task_roles | length > 0 and 'btrfs' in task_init['storage'] | dict2items | json_query('[].value.driver')" + # + # - name: Install ceph tools + # ansible.builtin.package: + # name: + # - ceph-common + # state: present + # when: "task_roles | length > 0 and 'ceph' in task_init['storage'] | dict2items | json_query('[].value.driver')" + # + # - name: Install LVM tools + # ansible.builtin.package: + # name: + # - lvm2 + # state: present + # when: "task_roles | length > 0 and 'lvm' in task_init['storage'] | dict2items | json_query('[].value.driver')" + # + # - name: Install ZFS dependencies + # ansible.builtin.package: + # name: + # - zfs-dkms + # state: present + # when: "task_roles | length > 0 and 'zfs' in task_init['storage'] | dict2items | json_query('[].value.driver') and ansible_distribution == 'Debian'" + # + # - name: Install ZFS tools + # ansible.builtin.package: + # name: + # - zfsutils-linux + # state: present + # when: "task_roles | length > 0 and 'zfs' in task_init['storage'] | dict2items | json_query('[].value.driver')" + + - name: Set uid allocation + ansible.builtin.shell: + cmd: "usermod root --add-subuids 10000000-1009999999" + when: '(install_deb.changed or install_rpm.changed) and ansible_distribution == "CentOS"' + + - name: Set gid allocation + ansible.builtin.shell: + cmd: "usermod root --add-subgids 10000000-1009999999" + when: '(install_deb.changed or install_rpm.changed) and ansible_distribution == "CentOS"' + + - name: Enable incus socket unit + ansible.builtin.systemd: + enabled: true + name: incus.socket + state: started + when: "install_deb.changed or install_rpm.changed" + + - name: Enable incus service unit + ansible.builtin.systemd: + enabled: true + name: incus.service + state: started + when: "install_deb.changed or install_rpm.changed" + + - name: Enable incus startup unit + ansible.builtin.systemd: + enabled: true + name: incus-startup.service + state: started + when: "install_deb.changed or install_rpm.changed" + + - name: Set client listen address + ansible.builtin.shell: + cmd: "incus --force-local config set core.https_address {{ task_ip_address }}" + when: '(install_deb.changed or install_rpm.changed) and ("standalone" in task_roles or ("cluster" in task_roles and task_servers[0] == inventory_hostname))' + + - name: Set cluster listen address + ansible.builtin.shell: + cmd: "incus --force-local config set cluster.https_address {{ task_ip_address }}" + when: '(install_deb.changed or install_rpm.changed) and "cluster" in task_roles and task_servers[0] == inventory_hostname' + + # - name: Set OVN NorthBound database + # shell: + # cmd: "incus --force-local config set network.ovn.northbound_connection={{ task_ovn_northbound }} network.ovn.client_cert=\"{{ lookup('file', '../data/ovn/'+ovn_name+'/'+inventory_hostname+'.crt') }}\" network.ovn.client_key=\"{{ lookup('file', '../data/ovn/'+ovn_name+'/'+inventory_hostname+'.key') }}\" network.ovn.ca_cert=\"{{ lookup('file', '../data/ovn/'+ovn_name+'/ca.crt') }}\"" + # notify: Restart Incus + # when: '(install_deb.changed or install_rpm.changed) and task_ovn_northbound and ("standalone" in task_roles or ("cluster" in task_roles and task_servers[0] == inventory_hostname))' + + - name: Add networks + ansible.builtin.shell: + cmd: "incus network create {{ item.key }} --type={{ item.value.type }}{% for k in item.value.local_config | default([]) %} {{ k }}={{ item.value.local_config[k] }}{% endfor %}{% for k in item.value.config | default([]) %} {{ k }}={{ item.value.config[k] }}{% endfor %}" + loop: "{{ task_init['network'] | dict2items }}" + when: '(install_deb.changed or install_rpm.changed) and ("standalone" in task_roles or ("cluster" in task_roles and task_servers[0] == inventory_hostname))' + + - name: Set network description + ansible.builtin.shell: + cmd: 'incus network set --property {{ item.key }} description="{{ item.value.description }}"' + loop: "{{ task_init['network'] | dict2items }}" + when: '(install_deb.changed or install_rpm.changed) and ("standalone" in task_roles or ("cluster" in task_roles and task_servers[0] == inventory_hostname)) and item.value.description | default(None)' + + - name: Add storage pools + ansible.builtin.shell: + cmd: "incus storage create {{ item.key }} {{ item.value.driver }}{% for k in item.value.local_config | default([]) %} {{ k }}={{ item.value.local_config[k] }}{% endfor %}{% for k in item.value.config | default([]) %} {{ k }}={{ item.value.config[k] }}{% endfor %}" + loop: "{{ task_init['storage'] | dict2items }}" + when: '(install_deb.changed or install_rpm.changed) and ("standalone" in task_roles or ("cluster" in task_roles and task_servers[0] == inventory_hostname))' + + - name: Set storage pool description + ansible.builtin.shell: + cmd: 'incus storage set --property {{ item.key }} description="{{ item.value.description }}"' + loop: "{{ task_init['storage'] | dict2items }}" + when: '(install_deb.changed or install_rpm.changed) and ("standalone" in task_roles or ("cluster" in task_roles and task_servers[0] == inventory_hostname)) and item.value.description | default(None)' + + - name: Add storage pool to default profile + ansible.builtin.shell: + cmd: "incus profile device add default root disk path=/ pool={{ item }}" + loop: "{{ task_init['storage'] | dict2items | json_query('[?value.default].key') }}" + when: '(install_deb.changed or install_rpm.changed) and ("standalone" in task_roles or ("cluster" in task_roles and task_servers[0] == inventory_hostname))' + + - name: Add network to default profile + ansible.builtin.shell: + cmd: "incus profile device add default eth0 nic network={{ item }} name=eth0" + loop: "{{ task_init['network'] | dict2items | json_query('[?value.default].key') }}" + when: '(install_deb.changed or install_rpm.changed) and ("standalone" in task_roles or ("cluster" in task_roles and task_servers[0] == inventory_hostname))' + + - name: Bootstrap the cluster + ansible.builtin.shell: + cmd: "incus --force-local cluster enable {{ inventory_hostname }}" + when: '(install_deb.changed or install_rpm.changed) and "cluster" in task_roles and task_servers[0] == inventory_hostname' + + - name: Create join tokens + delegate_to: "{{ task_servers[0] }}" + ansible.builtin.shell: + cmd: "incus --force-local --quiet cluster add {{ inventory_hostname }}" + register: cluster_add + when: '(install_deb.changed or install_rpm.changed) and "cluster" in task_roles and task_servers[0] != inventory_hostname' + + - name: Wait 5s to avoid token use before valid + ansible.builtin.wait_for: + timeout: 5 + delegate_to: localhost + when: "cluster_add.changed" + + - name: Join the cluster + throttle: 1 + ansible.builtin.shell: + cmd: "incus --force-local admin init --preseed" + stdin: |- + cluster: + enabled: true + cluster_address: "{{ task_ip_address }}" + cluster_token: "{{ cluster_add.stdout }}" + server_address: "{{ task_ip_address }}" + member_config: {% for pool in task_init.storage %}{% for key in task_init.storage[pool].local_config | default([]) %} + + - entity: storage-pool + name: {{ pool }} + key: {{ key }} + value: {{ task_init.storage[pool].local_config[key] }}{% endfor %}{% endfor %}{% for network in task_init.network %}{% for key in task_init.network[network].local_config | default([]) %} + + - entity: network + name: {{ network }} + key: {{ key }} + value: {{ task_init.network[network].local_config[key] }}{% endfor %}{% endfor %} + when: "cluster_add.changed" + + - name: Apply additional configuration + ansible.builtin.shell: + cmd: 'incus config set {{ item.key }}="{{ item.value }}"' + loop: "{{ task_init['config'] | default({}) | dict2items }}" + when: '(install_deb.changed or install_rpm.changed) and ("standalone" in task_roles or ("cluster" in task_roles and task_servers[0] == inventory_hostname))' + + - name: Load client certificates + ansible.builtin.shell: + cmd: 'incus config trust add-certificate --name "{{ item.key }}" --type={{ item.value.type | default(''client'') }} -' + stdin: "{{ item.value.certificate }}" + loop: "{{ task_init['clients'] | default({}) | dict2items }}" + when: '(install_deb.changed or install_rpm.changed) and ("standalone" in task_roles or ("cluster" in task_roles and task_servers[0] == inventory_hostname))' + handlers: + - name: Restart Incus + ansible.builtin.systemd: + name: incus.service + state: restarted diff --git a/ansible/roles/incus-install/tasks/main.yaml b/ansible/roles/incus-install/tasks/main.yaml new file mode 100644 index 0000000..c465a5f --- /dev/null +++ b/ansible/roles/incus-install/tasks/main.yaml @@ -0,0 +1,7 @@ +--- +- name: "Add incus repository to system" + ansible.builtin.include_tasks: add-repo.yaml + + # TODO: Should presumably be split +- name: "Install and bootstrap incus" + ansible.builtin.include_tasks: bootstrap.yaml diff --git a/ansible/files/incus.servers.tpl b/ansible/roles/system/files/incus.servers.tpl similarity index 100% rename from ansible/files/incus.servers.tpl rename to ansible/roles/system/files/incus.servers.tpl diff --git a/ansible/files/incus.sources.tpl b/ansible/roles/system/files/incus.sources.tpl similarity index 100% rename from ansible/files/incus.sources.tpl rename to ansible/roles/system/files/incus.sources.tpl diff --git a/ansible/files/zabbly-key.asc b/ansible/roles/system/files/zabbly-key.asc similarity index 100% rename from ansible/files/zabbly-key.asc rename to ansible/roles/system/files/zabbly-key.asc diff --git a/ansible/roles/system/handlers/main.yaml b/ansible/roles/system/handlers/main.yaml new file mode 100644 index 0000000..e1aa98b --- /dev/null +++ b/ansible/roles/system/handlers/main.yaml @@ -0,0 +1,11 @@ +--- +- name: Reboot host + ansible.builtin.reboot: + msg: "Reboot initiated by Ansible" + connect_timeout: 5 + reboot_timeout: 600 + pre_reboot_delay: 0 + post_reboot_delay: 30 + test_command: whoami + become: true + when: reboot_required_file.stat.exists diff --git a/ansible/roles/system/tasks/main.yaml b/ansible/roles/system/tasks/main.yaml new file mode 100644 index 0000000..068c2e0 --- /dev/null +++ b/ansible/roles/system/tasks/main.yaml @@ -0,0 +1,38 @@ +--- +- name: Ensure aptitude installed + ansible.builtin.apt: + name: "aptitude" + state: present + tags: + - apt + become: true + +- name: Ensure OS upgraded + ansible.builtin.apt: + upgrade: dist + tags: + - apt + - update + - os + become: true + +- name: Check if reboot is necessary + register: reboot_required_file + ansible.builtin.stat: + path: /var/run/reboot-required + get_checksum: false + tags: + - os + - reboot + notify: Reboot host + +- name: All system packages updated + ansible.builtin.apt: + name: "*" + state: latest # noqa package-latest + tags: + - apt + - update + - packages + become: true +