diff --git a/.gitignore b/.gitignore index 32ebbc98..4981a5c8 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,7 @@ +# Backup files +*~ +\#*\# + # Unit test / coverage reports .coverage .tox diff --git a/roles/configure-swap/README.rst b/roles/configure-swap/README.rst new file mode 100644 index 00000000..eaba5cf5 --- /dev/null +++ b/roles/configure-swap/README.rst @@ -0,0 +1,11 @@ +Configure a swap partition + +Creates a swap partition on the ephemeral block device (the rest of which +will be mounted on /opt). + +**Role Variables** + +.. zuul:rolevar:: configure_swap_size + :default: 8192 + + The size of the swap partition, in MiB. diff --git a/roles/configure-swap/defaults/main.yaml b/roles/configure-swap/defaults/main.yaml new file mode 100644 index 00000000..6d2d2f71 --- /dev/null +++ b/roles/configure-swap/defaults/main.yaml @@ -0,0 +1,2 @@ +# Default swap partition/file size, in MiB +configure_swap_size: 8192 \ No newline at end of file diff --git a/roles/configure-swap/tasks/ephemeral.yaml b/roles/configure-swap/tasks/ephemeral.yaml new file mode 100644 index 00000000..7ec125a9 --- /dev/null +++ b/roles/configure-swap/tasks/ephemeral.yaml @@ -0,0 +1,118 @@ +--- + +# Configure attached ephemeral devices for storage and swap + +- assert: + that: + - "ephemeral_device is defined" + +- name: Set partition names + set_fact: + swap_partition: "{{ ephemeral_device}}1" + opt_partition: "{{ ephemeral_device}}2" + +- name: Ensure ephemeral device is unmounted + become: yes + mount: + name: "{{ ephemeral_device }}" + state: unmounted + +- name: Get existing partitions + become: yes + parted: + device: "{{ ephemeral_device }}" + unit: MiB + register: ephemeral_partitions + +- name: Remove any existing partitions + become: yes + parted: + device: "{{ ephemeral_device }}" + number: "{{ item.num }}" + state: absent + with_items: + - "{{ ephemeral_partitions.partitions }}" + +- name: Create new disk label + become: yes + parted: + label: msdos + device: "{{ ephemeral_device }}" + +- name: Create swap partition + become: yes + parted: + device: "{{ ephemeral_device }}" + number: 1 + state: present + part_start: '0%' + part_end: "{{ configure_swap_size }}MiB" + +- name: Create opt partition + become: yes + parted: + device: "{{ ephemeral_device }}" + number: 2 + state: present + part_start: "{{ configure_swap_size }}MiB" + part_end: "100%" + +- name: Make swap on partition + become: yes + command: "mkswap {{ swap_partition }}" + +- name: Write swap to fstab + become: yes + mount: + path: none + src: "{{ swap_partition }}" + fstype: swap + opts: sw + passno: 0 + dump: 0 + state: present + +# XXX: does "parted" plugin ensure the partition is available +# before moving on? No udev settles here ... + +- name: Add all swap + become: yes + command: swapon -a + +- name: Create /opt filesystem + become: yes + filesystem: + fstype: ext4 + # The default ratio is 16384 bytes per inode or so. Reduce that to 8192 + # bytes per inode so that we get roughly twice the number of inodes as + # by default. This should still be well above the block size of 4096. + # We do this because we have found in at least a couple locations that + # more inodes is useful and is painful to fix after the fact. + opts: -i 8192 + dev: "{{ opt_partition }}" + +# Rackspace at least does not have enough room for two devstack +# installs on the primary partition. We copy in the existing /opt to +# the new partition on the ephemeral device, and then overmount /opt +# to there for the test runs. +# +# NOTE(ianw): the existing "mount" touches fstab. There is currently (Sep2017) +# work in [1] to split mount & fstab into separate parts, but for now we bundle +# it into an atomic shell command +# [1] https://github.com/ansible/ansible/pull/27174 +- name: Copy old /opt + become: yes + shell: | + mount {{ opt_partition }} /mnt + find /opt/ -mindepth 1 -maxdepth 1 -exec mv {} /mnt/ \; + umount /mnt + +# This overmounts any existing /opt +- name: Add opt to fstab and mount + become: yes + mount: + path: /opt + src: "{{ opt_partition }}" + fstype: ext4 + opts: noatime + state: mounted diff --git a/roles/configure-swap/tasks/main.yaml b/roles/configure-swap/tasks/main.yaml new file mode 100644 index 00000000..e5878b67 --- /dev/null +++ b/roles/configure-swap/tasks/main.yaml @@ -0,0 +1,65 @@ +--- + +# On RAX hosts, we have a small root partition and a large, +# unallocated ephemeral device attached at /dev/xvde +- name: Set ephemeral device if /dev/xvde exists + when: ansible_devices["xvde"] is defined + set_fact: + ephemeral_device: "/dev/xvde" + +# On other providers, we have a device called "ephemeral0". +# +# NOTE(ianw): Once [1] is in our ansible (2.4 era?), we can figure +# this out more directly by walking the device labels in the facts +# +# [1] https://github.com/ansible/ansible/commit/d46dd99f47c0ee5081d15bc5b741e9096d8bfd3e +- name: Set ephemeral device by label + when: ephemeral_device is undefined + block: + - name: Get ephemeral0 device node + command: /sbin/blkid -L ephemeral0 + register: ephemeral0 + # If this doesn't exist, returns !0 + ignore_errors: yes + changed_when: False + + - name: Set ephemeral device if LABEL exists + when: "ephemeral0.rc == 0" + set_fact: + ephemeral_device: "{{ ephemeral0.stdout }}" + +# If we have ephemeral storage and we don't appear to have setup swap, +# we will create a swap and move /opt to a large data partition there. +- include: ephemeral.yaml + static: no + when: + - ephemeral_device is defined + - ansible_memory_mb['swap']['total'] | int + 10 <= configure_swap_size + +# If no ephemeral device and no swap, then we will setup some swap +# space on the root device to ensure all hosts a consistent memory +# environment. +- include: root.yaml + static: no + when: + - ephemeral_device is undefined + - ansible_memory_mb['swap']['total'] | int + 10 <= configure_swap_size + +# ensure a standard level of swappiness. Some platforms +# (rax+centos7) come with swappiness of 0 (presumably because the +# vm doesn't come with swap setup ... but we just did that above), +# which depending on the kernel version can lead to the OOM killer +# kicking in on some processes despite swap being available; +# particularly things like mysql which have very high ratio of +# anonymous-memory to file-backed mappings. +# +# This sets swappiness low; we really don't want to be relying on +# cloud I/O based swap during our runs if we can help it +- name: Set swappiness + become: yes + sysctl: + name: vm.swappiness + value: 30 + state: present + +- debug: var=ephemeral_device diff --git a/roles/configure-swap/tasks/root.yaml b/roles/configure-swap/tasks/root.yaml new file mode 100644 index 00000000..fbe4e92d --- /dev/null +++ b/roles/configure-swap/tasks/root.yaml @@ -0,0 +1,65 @@ +--- + +# If no ephemeral devices are available, use root filesystem + +- name: Calculate required swap + set_fact: + swap_required: "{{ configure_swap_size - ansible_memory_mb['swap']['total'] | int }}" + +- block: + - name: Get root filesystem + shell: df --output='fstype' /root | tail -1 + register: root_fs + + - name: Save root filesystem + set_fact: + root_filesystem: "{{ root_fs.stdout }}" + + - debug: var=root_filesystem + +# Note, we don't use a sparse device to avoid wedging when disk space +# and memory are both unavailable. + +# Cannot fallocate on filesystems like XFS, so use slower dd +- name: Create swap backing file for non-EXT fs + when: '"ext" not in root_filesystem' + become: yes + command: dd if=/dev/zero of=/root/swapfile bs=1M count={{ swap_required }} + args: + creates: /root/swapfile + +- name: Create sparse swap backing file for EXT fs + when: '"ext" in root_filesystem' + become: yes + command: fallocate -l {{ swap_required }}M /root/swapfile + args: + creates: /root/swapfile + +- name: Ensure swapfile perms + become: yes + file: + path: /root/swapfile + owner: root + group: root + mode: 0600 + +- name: Make swapfile + become: yes + command: mkswap /root/swapfile + +- name: Write swap to fstab + become: yes + mount: + path: none + src: /root/swapfile + fstype: swap + opts: sw + passno: 0 + dump: 0 + state: present + +- name: Add all swap + become: yes + command: swapon -a + +- debug: var=swap_required