Skip to content

Instantly share code, notes, and snippets.

@keithchambers
Last active October 21, 2021 01:12
Show Gist options
  • Star 6 You must be signed in to star a gist
  • Fork 4 You must be signed in to fork a gist
  • Save keithchambers/80b60559ad83cebf1672 to your computer and use it in GitHub Desktop.
Save keithchambers/80b60559ad83cebf1672 to your computer and use it in GitHub Desktop.
Ansible role to set 'noop' i/o scheduler (CentOS 7)
---
- name: test if grub configured for noop i/o scheduler
command: egrep -q 'elevator=noop' /boot/grub2/grub.cfg
register: grub
changed_when: no
failed_when: grub_test.rc == 2
- name: configure grub for noop i/o scheduler
sudo: yes
command: grubby --update-kernel=ALL --args=elevator=noop
when: grub_test.rc == 1
- name: get list of block devices to test
shell: ls /sys/block/ | egrep '^([shv]|xv])d[a-z]$'
register: block_devs
changed_when: no
- name: test if block device using noop i/o scheduler
shell: cat /sys/block/{{ item }}/queue/scheduler | egrep -q '\[noop\]'
with_items: block_devs.stdout
register: noop_test
changed_when: noop_test.rc == 1
failed_when: noop_test.rc == 2
- name: set block device to use noop i/o scheduler
sudo: yes
shell: echo noop > /sys/block/{{ item }}/queue/scheduler
with_items: block_devs.stdout
when: noop_test.changed
@xtaran
Copy link

xtaran commented Oct 21, 2021

Why so complicated to set the current scheduler via /sys/block/*/queue/scheduler when this can also be done in a single Ansible task? At least with enabled facts gathering which already does most of the work.

I got these variants of that task and they even cope with newer releases requiring none instead of noop because it should work with more than just one release of one distro and also with NVMe SSDs:

Variant 1 (shortest, but hackish):

- name: Set noop or none as scheduler (shell hack), whichever works
  shell: "printf 'noop\nnone' > /sys/block/{{ item }}/queue/scheduler 2> /dev/null || true"
  when: "ansible_devices[item].scheduler_mode not in ['', 'none', 'noop']"
  with_items: "{{ ansible_devices }}"

Variant 2 (longer, but doesn't require suppressing expected errors):

- name: Set noop or none as scheduler (shell safe), whichever works
  shell: "( fgrep -q noop /sys/block/{{ item }}/queue/scheduler && printf 'noop' || printf 'none') > /sys/block/{{ item }}/queue/scheduler"
  when: "ansible_devices[item].scheduler_mode not in ['', 'none', 'noop']"
  with_items: "{{ ansible_devices }}"

The only downside (of these two variants) I'm currently aware of is that they say "skipped" instead of "ok" if it is actually already ok.

So I wrote variant 3, based on variant 1:

- name: Set noop or none as scheduler, whichever works
  shell: "printf 'noop\nnone' > /sys/block/{{ item }}/queue/scheduler 2> /dev/null || true"
  when: "ansible_devices[item].scheduler_mode != ''"
  changed_when: "ansible_devices[item].scheduler_mode not in ['none', 'noop']"
  with_items: "{{ ansible_devices }}"

This correctly says ok if it is ok, but it though executes the command. So let's do a variant 4 which also defines when the command actually failed (having write error: Invalid argument' in STDERR is actually a sign of success in this case):

- name: Set noop or none as scheduler, whichever works
  shell: "grep -qP '\\[no(op|ne)\\]' /sys/block/{{ item  }}/queue/scheduler || printf 'noop\nnone' > /sys/block/{{ item }}/queue/scheduler"
  register: executed_command
  when: "ansible_devices[item].scheduler_mode != ''"
  changed_when: executed_command.rc == 1
  failed_when:
    - executed_command.rc != 0
    - executed_command.rc != 1
    - "'write error: Invalid argument' not in executed_command.stderr"
  with_items: "{{ ansible_devices }}"

Since I wasn't happy with that error prone looking failed_when statement, my final variant is now this one:

- name: Set noop or none as scheduler, whichever works
  shell: "if ! grep -qP '\\[no(op|ne)\\]' /sys/block/{{ item }}/queue/scheduler; then ( fgrep -q noop /sys/block/{{ item }}/queue/scheduler && printf noop || printf none) > /sys/block/{{ item }}/queue/scheduler; else cat /sys/block/{{ item }}/queue/scheduler; fi"
  register: executed_command
  changed_when: "'[no' not in executed_command.stdout"
  failed_when: "executed_command.rc != 0 or executed_command.stderr != ''"
  when: "ansible_devices[item].scheduler_mode != ''"
  with_items: "{{ ansible_devices }}"

In you case, this simple variant would likely suffice:

- name: Set noop as scheduler
  shell: "printf 'noop' > /sys/block/{{ item }}/queue/scheduler"
  when: "ansible_devices[item].scheduler_mode not in ['', 'noop']"
  with_items: "{{ ansible_devices }}"

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment