--- # The 500 GB data disk. # # knots-box does this by hand: /mnt/knots_data holds ~875 GB of blockchain and # appears nowhere in Ansible, so a rebuild of that host would come up with the # chain silently missing. This role does it in code instead. - name: Check the Monero data device ansible.builtin.stat: path: "{{ monero_data_device }}" register: monero_device - name: Find the device backing the root filesystem ansible.builtin.command: findmnt -no SOURCE / register: monero_root_source changed_when: false check_mode: false # A guard, not ceremony. mkfs on the wrong device is unrecoverable, and # /dev/sdX names are assigned by the kernel at boot - the whole reason # everything below this point works by UUID. - name: Assert the data device is a real block device and is not the root disk ansible.builtin.assert: that: - monero_device.stat.exists - monero_device.stat.isblk | default(false) - not monero_root_source.stdout.startswith(monero_data_device) quiet: true fail_msg: >- {{ monero_data_device }} is not a usable data device, or it is the disk the root filesystem lives on ({{ monero_root_source.stdout }}). Refusing to touch it. Check `lsblk` on the host. - name: Read any existing filesystem on the data device ansible.builtin.command: "blkid -o value -s TYPE {{ monero_data_device }}" register: monero_existing_fs changed_when: false failed_when: false check_mode: false # community.general.filesystem is idempotent and will not rebuild a filesystem # that is already the requested type, but it would happily create one on a disk # holding something it does not recognise. Say so out loud instead. - name: Refuse to reformat a disk that already carries a different filesystem ansible.builtin.assert: that: - monero_existing_fs.stdout | trim in ['', monero_data_fstype] quiet: true fail_msg: >- {{ monero_data_device }} already holds a '{{ monero_existing_fs.stdout | trim }}' filesystem, not '{{ monero_data_fstype }}'. Refusing to overwrite it. - name: Create the filesystem on the data device community.general.filesystem: fstype: "{{ monero_data_fstype }}" dev: "{{ monero_data_device }}" opts: "{{ monero_data_mkfs_opts }}" - name: Read the filesystem UUID ansible.builtin.command: "blkid -o value -s UUID {{ monero_data_device }}" register: monero_fs_uuid changed_when: false failed_when: false check_mode: false # Everything past this point mounts by UUID, so there is nothing sensible to do # without one. On a --check run against a disk that has no filesystem yet this # is expected and unavoidable: check mode did not create the filesystem, so # there is no UUID to read. Say that, rather than leaving a bare blkid rc=2. - name: Assert the UUID looks like a UUID ansible.builtin.assert: that: - monero_fs_uuid.stdout | trim | length == 36 quiet: true fail_msg: >- No filesystem UUID on {{ monero_data_device }}. {% if ansible_check_mode %}This is expected under --check when the disk has not been formatted yet - check mode cannot create the filesystem, so it cannot dry-run the mount either. Run without --check once, then --check is meaningful from then on.{% else %}mkfs appears to have failed; check `lsblk` and `blkid` on the host.{% endif %} # Same owner and mode as the "Own the mounted data directory" task below, and # that is deliberate rather than duplication. Before the disk is mounted this # creates the mount point; afterwards it targets the mounted filesystem and is a # no-op. Giving it root:root 0755 here - the obvious thing to write - made the # two tasks disagree, and each run flipped the directory back and forth. - name: Create the Monero data directory as a mount point ansible.builtin.file: path: "{{ monero_data_dir }}" state: directory owner: "{{ monero_user }}" group: "{{ monero_group }}" mode: '0750' # By UUID, never by /dev/sdb. nofail so a missing disk does not drop a headless # VM into an emergency shell - monerod is stopped from starting by # RequiresMountsFor= in its unit instead, which the systemd monitor then reports. # noatime because LMDB touches a lot of pages and none of it wants an atime write. - name: Mount the data disk by UUID ansible.posix.mount: path: "{{ monero_data_dir }}" src: "UUID={{ monero_fs_uuid.stdout | trim }}" fstype: "{{ monero_data_fstype }}" opts: defaults,noatime,nofail state: mounted # AFTER the mount, never before: chowning the mount point first sets permissions # on the directory the mount then hides, and the blockchain lands on a root-owned # filesystem that monerod cannot write to. - name: Own the mounted data directory ansible.builtin.file: path: "{{ monero_data_dir }}" state: directory owner: "{{ monero_user }}" group: "{{ monero_group }}" mode: '0750' - name: Confirm the data directory is a mount point and not the root filesystem ansible.builtin.command: "findmnt -no SOURCE --target {{ monero_data_dir }}" register: monero_mounted_source changed_when: false check_mode: false - name: Assert the blockchain will land on the data disk ansible.builtin.assert: that: - monero_mounted_source.stdout | trim != monero_root_source.stdout | trim quiet: true fail_msg: >- {{ monero_data_dir }} resolves to {{ monero_mounted_source.stdout | trim }}, which is the root filesystem. The 500 GB disk is not mounted and a full sync would fill the 10 GB root disk.