Add Ansible playbook for node configuration
authorMartin Schwenke <martin@meltin.net>
Wed, 6 Feb 2019 03:53:10 +0000 (14:53 +1100)
committerMartin Schwenke <martin@meltin.net>
Mon, 25 Mar 2019 05:52:25 +0000 (16:52 +1100)
This will replace all of the existing node provisioning/configuration.
CentOS 7 nodes are currently supported.

Signed-off-by: Martin Schwenke <martin@meltin.net>
69 files changed:
ansible/node/ad.yml [new file with mode: 0644]
ansible/node/base.yml [new file with mode: 0644]
ansible/node/build.yml [new file with mode: 0644]
ansible/node/cbuild.yml [new file with mode: 0644]
ansible/node/nas.yml [new file with mode: 0644]
ansible/node/roles/README [new file with mode: 0644]
ansible/node/roles/ad/tasks/generic/configure_ad.yml [new file with mode: 0644]
ansible/node/roles/ad/tasks/main.yml [new file with mode: 0644]
ansible/node/roles/ad/tasks/redhat/packages.yml [new file with mode: 0644]
ansible/node/roles/build/tasks/main.yml [new file with mode: 0644]
ansible/node/roles/build/tasks/redhat/packages.yml [new file with mode: 0644]
ansible/node/roles/build/tasks/redhat/packaging_setup.yml [new file with mode: 0644]
ansible/node/roles/clusterfs/files/autocluster-gpfs.profile [new file with mode: 0644]
ansible/node/roles/clusterfs/tasks/main.yml [new file with mode: 0644]
ansible/node/roles/clusterfs/tasks/redhat/clusterfs-gpfs.yml [new file with mode: 0644]
ansible/node/roles/clusterfs/tasks/redhat/repo.yml [new file with mode: 0644]
ansible/node/roles/common/files/rsyslog.conf [new file with mode: 0644]
ansible/node/roles/common/files/ssh_config [new file with mode: 0644]
ansible/node/roles/common/handlers/main.yml [new file with mode: 0644]
ansible/node/roles/common/tasks/generic/autocluster.yml [new file with mode: 0644]
ansible/node/roles/common/tasks/generic/hosts.yml [new file with mode: 0644]
ansible/node/roles/common/tasks/generic/mount_home.yml [new file with mode: 0644]
ansible/node/roles/common/tasks/generic/resolv_conf.yml [new file with mode: 0644]
ansible/node/roles/common/tasks/generic/rsyslog.yml [new file with mode: 0644]
ansible/node/roles/common/tasks/generic/selinux.yml [new file with mode: 0644]
ansible/node/roles/common/tasks/generic/ssh.yml [new file with mode: 0644]
ansible/node/roles/common/tasks/generic/timezone.yml [new file with mode: 0644]
ansible/node/roles/common/tasks/main.yml [new file with mode: 0644]
ansible/node/roles/common/tasks/redhat/firewall.yml [new file with mode: 0644]
ansible/node/roles/common/tasks/redhat/ntp.yml [new file with mode: 0644]
ansible/node/roles/common/tasks/redhat/packages.yml [new file with mode: 0644]
ansible/node/roles/common/templates/chrony.conf.j2 [new file with mode: 0644]
ansible/node/roles/common/templates/hosts.j2 [new file with mode: 0644]
ansible/node/roles/common/templates/resolv.conf.j2 [new file with mode: 0644]
ansible/node/roles/ctdb/tasks/generic/ctdb.yml [new file with mode: 0644]
ansible/node/roles/ctdb/tasks/main.yml [new file with mode: 0644]
ansible/node/roles/ctdb/tasks/redhat/packages.yml [new file with mode: 0644]
ansible/node/roles/ctdb/templates/ctdb_nodes.j2 [new file with mode: 0644]
ansible/node/roles/nas/files/rpc-rquotad.sysconfig [new file with mode: 0644]
ansible/node/roles/nas/files/smb.conf [new file with mode: 0644]
ansible/node/roles/nas/tasks/generic/ctdb-once.yml [new file with mode: 0644]
ansible/node/roles/nas/tasks/generic/ctdb-start.yml [new file with mode: 0644]
ansible/node/roles/nas/tasks/generic/ctdb-stop.yml [new file with mode: 0644]
ansible/node/roles/nas/tasks/generic/ctdb-with-samba-nfs.yml [new file with mode: 0644]
ansible/node/roles/nas/tasks/generic/ctdb.yml [new file with mode: 0644]
ansible/node/roles/nas/tasks/generic/nfs.yml [new file with mode: 0644]
ansible/node/roles/nas/tasks/generic/samba-gpfs-once.yml [new file with mode: 0644]
ansible/node/roles/nas/tasks/generic/samba-once.yml [new file with mode: 0644]
ansible/node/roles/nas/tasks/generic/samba.yml [new file with mode: 0644]
ansible/node/roles/nas/tasks/generic/shares.yml [new file with mode: 0644]
ansible/node/roles/nas/tasks/main.yml [new file with mode: 0644]
ansible/node/roles/nas/tasks/redhat/nfs.yml [new file with mode: 0644]
ansible/node/roles/nas/tasks/redhat/samba.yml [new file with mode: 0644]
ansible/node/roles/nas/templates/ctdb_conf.j2 [new file with mode: 0644]
ansible/node/roles/nas/templates/ctdb_public_addresses.j2 [new file with mode: 0644]
ansible/node/roles/nas/templates/ctdb_sysconfig.j2 [new file with mode: 0644]
ansible/node/roles/nas/templates/nfs_exports.j2 [new file with mode: 0644]
ansible/node/roles/nas/templates/nfs_sysconfig.j2 [new file with mode: 0644]
ansible/node/roles/nas/templates/samba_registry.j2 [new file with mode: 0644]
ansible/node/roles/nasrepos/tasks/main.yml [new file with mode: 0644]
ansible/node/roles/nasrepos/tasks/redhat/repo.yml [new file with mode: 0644]
ansible/node/roles/storage/tasks/generic/clusterfs-gpfs-once.yml [new file with mode: 0644]
ansible/node/roles/storage/tasks/generic/clusterfs-gpfs.yml [new file with mode: 0644]
ansible/node/roles/storage/tasks/main.yml [new file with mode: 0644]
ansible/node/roles/storage/templates/gpfs_nodes.j2 [new file with mode: 0644]
ansible/node/roles/storage/templates/gpfs_primary_secondary.j2 [new file with mode: 0644]
ansible/node/site.yml [new file with mode: 0644]
ansible/node/storage.yml [new file with mode: 0644]
ansible/node/test.yml [new file with mode: 0644]

diff --git a/ansible/node/ad.yml b/ansible/node/ad.yml
new file mode 100644 (file)
index 0000000..79e2cc0
--- /dev/null
@@ -0,0 +1,7 @@
+---
+- hosts: ad-nodes
+  remote_user: root
+
+  roles:
+    - common
+    - ad
diff --git a/ansible/node/base.yml b/ansible/node/base.yml
new file mode 100644 (file)
index 0000000..f5e9d5e
--- /dev/null
@@ -0,0 +1,6 @@
+---
+- hosts: base-nodes
+  remote_user: root
+
+  roles:
+    - common
diff --git a/ansible/node/build.yml b/ansible/node/build.yml
new file mode 100644 (file)
index 0000000..fe4023d
--- /dev/null
@@ -0,0 +1,8 @@
+---
+- hosts: build-nodes
+  remote_user: root
+
+  roles:
+    - common
+    - nasrepos
+    - build
diff --git a/ansible/node/cbuild.yml b/ansible/node/cbuild.yml
new file mode 100644 (file)
index 0000000..1a3d474
--- /dev/null
@@ -0,0 +1,8 @@
+---
+- hosts: cbuild-nodes
+  remote_user: root
+
+  roles:
+    - common
+    - clusterfs
+    - build
diff --git a/ansible/node/nas.yml b/ansible/node/nas.yml
new file mode 100644 (file)
index 0000000..664f78d
--- /dev/null
@@ -0,0 +1,11 @@
+---
+- hosts: nas-nodes
+  remote_user: root
+
+  roles:
+    - common
+    - clusterfs
+    - nasrepos
+    - ctdb
+    - storage
+    - nas
diff --git a/ansible/node/roles/README b/ansible/node/roles/README
new file mode 100644 (file)
index 0000000..84e9a86
--- /dev/null
@@ -0,0 +1,33 @@
+Roles defined:
+
+common:                The basis for all node types
+
+               Includes:
+               * Firewall disabled
+               * NTP setup
+               * NetworkManager disabled
+               * Repositories set up
+               * /etc/{hosts,resolv.conf} set yp
+               * Passwordless ssh enabled between cluster nodes
+               * Timezone set as configured
+               * Syslog configured to log with high resolution timestamps
+
+ad:            Samba AD installed and configured
+
+build:         Development and packaging tools installed
+
+clusterfs:     Cluster filesystem is installed
+
+ctdb:          CTDB packages installed, nodes file created
+
+               Depends: nasrepos
+
+nas:           CTDB, Samba, NFS installed and configured
+
+               Depends: clusterfs, nasrepos, ctdb
+
+nasrepos:      Samba/CTDB package repositories configured
+
+storage:       Cluster filesystem configured and accessible
+
+               Depends: clusterfs
diff --git a/ansible/node/roles/ad/tasks/generic/configure_ad.yml b/ansible/node/roles/ad/tasks/generic/configure_ad.yml
new file mode 100644 (file)
index 0000000..c56d1e4
--- /dev/null
@@ -0,0 +1,65 @@
+---
+- name: check if AD server active flag file exists
+  stat:
+    path: /root/.autocluster/ad_active
+  register: ad_active
+
+- name: remove smb.conf
+  file:
+    path: /etc/samba/smb.conf
+    state: absent
+  when: not ad_active.stat.exists
+
+- name: provision domain
+  command: >
+    samba-tool domain provision
+      --server-role="dc"
+      --use-rfc2307
+      --dns-backend="SAMBA_INTERNAL"
+      --realm="{{ resolv_conf.domain }}"
+      --domain="{{ samba.workgroup }}"
+      --adminpass="{{ ad.admin_password }}"
+      --host-ip={{ nodes[ansible_hostname].ips[0] }}
+      --option="dns forwarder = {{ ad.dns_forwarder }}"
+  when: not ad_active.stat.exists
+
+- name: add users and groups
+  command: samba-tool {{ p }}
+  with_list:
+    - domain passwordsettings set --min-pwd-length=3
+    - domain passwordsettings set --complexity=off
+    - user setexpiry --noexpiry Administrator
+    - user setpassword administrator --newpassword="{{ ad.admin_password }}"
+    - group add group1
+    - group add group2
+    - user add user1 "{{ ad.admin_password }}"
+    - group addmembers group1 user1
+    - user setexpiry --noexpiry user1
+    - user add user2 "{{ ad.admin_password }}"
+    - group addmembers group2 user2
+    - user setexpiry --noexpiry user2
+  loop_control:
+    loop_var: p
+  when: not ad_active.stat.exists
+
+# This is created from a template in common/.  It might be good not to
+# update this twice but we probably want a working configuration under
+# the DC is started below.
+- name: update /etc/resolv.conf
+  lineinfile:
+    path: /etc/resolv.conf
+    regexp: "^nameserver.*"
+    line: "nameserver {{ nodes[ansible_hostname].ips[0] }}"
+  when: not ad_active.stat.exists
+
+- name: ensure domain controller is enabled and running
+  service:
+    name: samba
+    state: started
+    enabled: yes
+  when: not ad_active.stat.exists
+
+- name: flag AD server as active
+  file:
+    path: /root/.autocluster/ad_active
+    state: touch
diff --git a/ansible/node/roles/ad/tasks/main.yml b/ansible/node/roles/ad/tasks/main.yml
new file mode 100644 (file)
index 0000000..6b0e811
--- /dev/null
@@ -0,0 +1,12 @@
+---
+- include_tasks: "{{ ansible_os_family | lower }}/{{ task }}.yml"
+  with_list:
+  - packages
+  loop_control:
+    loop_var: task
+
+- include_tasks: generic/{{ task }}.yml
+  with_list:
+  - configure_ad
+  loop_control:
+    loop_var: task
diff --git a/ansible/node/roles/ad/tasks/redhat/packages.yml b/ansible/node/roles/ad/tasks/redhat/packages.yml
new file mode 100644 (file)
index 0000000..49d1eaf
--- /dev/null
@@ -0,0 +1,22 @@
+---
+- name: Add Samba AD repos
+  yum_repository:
+    name: "autocluster-{{ repo.name }}"
+    description: "{{ repo.name }}"
+    baseurl: "{{ repo.baseurl | default(repository_baseurl) }}/{{ repo.path }}"
+    gpgcheck: no
+    proxy: _none_
+  when: repo.type == "ad"
+  with_list: "{{ repositories }}"
+  loop_control:
+    loop_var: repo
+
+- name: FIXME - create /run/samba directory
+  file:
+    path: /run/samba
+    state: directory
+
+- name: install packages for Active Directory node
+  package:
+    name: samba-dc
+    state: present
diff --git a/ansible/node/roles/build/tasks/main.yml b/ansible/node/roles/build/tasks/main.yml
new file mode 100644 (file)
index 0000000..5e6cd27
--- /dev/null
@@ -0,0 +1,7 @@
+---
+- include_tasks: "{{ ansible_os_family | lower }}/{{ task }}.yml"
+  with_list:
+  - packages
+  - packaging_setup
+  loop_control:
+    loop_var: task
diff --git a/ansible/node/roles/build/tasks/redhat/packages.yml b/ansible/node/roles/build/tasks/redhat/packages.yml
new file mode 100644 (file)
index 0000000..1b0d485
--- /dev/null
@@ -0,0 +1,24 @@
+---
+- name: install packages for build node
+  package:
+    name:
+    - git
+    # RPM development
+    - rpmdevtools
+    # Performance co-pilot to allow build of CTDB pmda code
+    - pcp-libs
+    - pcp-libs-devel
+    # Building Samba 
+    - readline-devel
+    - libacl-devel
+    - e2fsprogs-devel
+    - libxslt
+    - docbook-utils
+    - docbook-style-xsl
+    - dbus-devel
+    - libaio-devel
+    - libcap-devel
+    - quota-devel
+    - perl-Parse-Yapp
+    - perl-ExtUtils-MakeMaker
+    state: present
diff --git a/ansible/node/roles/build/tasks/redhat/packaging_setup.yml b/ansible/node/roles/build/tasks/redhat/packaging_setup.yml
new file mode 100644 (file)
index 0000000..7415960
--- /dev/null
@@ -0,0 +1,8 @@
+---
+- name: Setup the RPM directory tree
+  command: rpmdev-setuptree
+
+- name: Remove .rpmmacros
+  file:
+    path: /root/.rpmmacros
+    state: absent
diff --git a/ansible/node/roles/clusterfs/files/autocluster-gpfs.profile b/ansible/node/roles/clusterfs/files/autocluster-gpfs.profile
new file mode 100644 (file)
index 0000000..71c610d
--- /dev/null
@@ -0,0 +1,2 @@
+# Added by autocluster
+pathmunge /usr/lpp/mmfs/bin
diff --git a/ansible/node/roles/clusterfs/tasks/main.yml b/ansible/node/roles/clusterfs/tasks/main.yml
new file mode 100644 (file)
index 0000000..6e3ec1a
--- /dev/null
@@ -0,0 +1,7 @@
+---
+- include_tasks: "{{ ansible_os_family | lower }}/{{ task }}.yml"
+  with_list:
+  - repo
+  - clusterfs-{{ clusterfs.type }}
+  loop_control:
+    loop_var: task
diff --git a/ansible/node/roles/clusterfs/tasks/redhat/clusterfs-gpfs.yml b/ansible/node/roles/clusterfs/tasks/redhat/clusterfs-gpfs.yml
new file mode 100644 (file)
index 0000000..c77e380
--- /dev/null
@@ -0,0 +1,20 @@
+---
+- name: install GPFS packages
+  package:
+    name:
+    - gpfs.base
+    - gpfs.docs
+    - gpfs.gpl
+    - gpfs.msg.en_US
+    - gpfs.gskit
+    state: present
+
+- name: install GPFS /etc/profile.d/ snippet
+  copy:
+    src: autocluster-gpfs.profile
+    dest: /etc/profile.d/autocluster-gpfs.sh
+
+- name: build GPFS modules
+  shell: LINUX_DISTRIBUTION=REDHAT_AS_LINUX /usr/lpp/mmfs/bin/mmbuildgpl
+  args:
+    chdir: /usr/lpp/mmfs/src
diff --git a/ansible/node/roles/clusterfs/tasks/redhat/repo.yml b/ansible/node/roles/clusterfs/tasks/redhat/repo.yml
new file mode 100644 (file)
index 0000000..0000ccd
--- /dev/null
@@ -0,0 +1,12 @@
+---
+- name: Add cluster filesystem repos
+  yum_repository:
+    name: "autocluster-{{ repo.name }}"
+    description: "{{ repo.name }}"
+    baseurl: "{{ repo.baseurl | default(repository_baseurl) }}/{{ repo.path }}"
+    gpgcheck: no
+    proxy: _none_
+  when: repo.type == "clusterfs"
+  with_list: "{{ repositories }}"
+  loop_control:
+    loop_var: repo
diff --git a/ansible/node/roles/common/files/rsyslog.conf b/ansible/node/roles/common/files/rsyslog.conf
new file mode 100644 (file)
index 0000000..6478b45
--- /dev/null
@@ -0,0 +1,14 @@
+# Select a high precision time format.  This allows accurate merging
+# of logs from multiple cluster nodes for easier CTDB debugging.
+$ActionFileDefaultTemplate   RSYSLOG_FileFormat
+
+# Turn off rate-limiting.  Why would we want to lose messages by
+# default?
+$SystemLogRateLimitInterval 0
+$SystemLogRateLimitBurst 0
+
+# Turn on UDP listener to be able to take advantage of CTDB's new
+# direct-to-syslog-on-UDP feature.
+$ModLoad imudp
+$UDPServerAddress 127.0.0.1
+$UDPServerRun 514
diff --git a/ansible/node/roles/common/files/ssh_config b/ansible/node/roles/common/files/ssh_config
new file mode 100644 (file)
index 0000000..de7ff06
--- /dev/null
@@ -0,0 +1,2 @@
+StrictHostKeyChecking no
+IdentityFile ~/.ssh/id_autocluster
diff --git a/ansible/node/roles/common/handlers/main.yml b/ansible/node/roles/common/handlers/main.yml
new file mode 100644 (file)
index 0000000..1b033cb
--- /dev/null
@@ -0,0 +1,12 @@
+---
+# Including handlers and conditional handlers seem broken :-(
+
+- name: restart NTP server redhat
+  service:
+    name: chronyd
+    state: restarted
+
+- name: restart rsyslog
+  service:
+    name: rsyslog
+    state: restarted
diff --git a/ansible/node/roles/common/tasks/generic/autocluster.yml b/ansible/node/roles/common/tasks/generic/autocluster.yml
new file mode 100644 (file)
index 0000000..753b225
--- /dev/null
@@ -0,0 +1,5 @@
+---
+- name: create autocluster state directory
+  file:
+    path: /root/.autocluster
+    state: directory
diff --git a/ansible/node/roles/common/tasks/generic/hosts.yml b/ansible/node/roles/common/tasks/generic/hosts.yml
new file mode 100644 (file)
index 0000000..6983826
--- /dev/null
@@ -0,0 +1,5 @@
+---
+- name: create /etc/hosts
+  template:
+    src: hosts.j2
+    dest: /etc/hosts
diff --git a/ansible/node/roles/common/tasks/generic/mount_home.yml b/ansible/node/roles/common/tasks/generic/mount_home.yml
new file mode 100644 (file)
index 0000000..8a49816
--- /dev/null
@@ -0,0 +1,12 @@
+---
+- name: ensure that an fstab entry exists to NFS mount /home
+  lineinfile:
+    path: /etc/fstab
+    regexp: '^.*:/home /home nfs.*'
+    # Do not use locking, since this starts/needs rpc.statd, which is
+    # stopped/started by CTDB
+    line: '{{ virthost }}:/home /home nfs nfsvers=3,intr,nolock 0 0'
+
+- name: ensure that /home is mounted
+  shell: >
+    findmnt -n /home || mount /home
diff --git a/ansible/node/roles/common/tasks/generic/resolv_conf.yml b/ansible/node/roles/common/tasks/generic/resolv_conf.yml
new file mode 100644 (file)
index 0000000..b6704ee
--- /dev/null
@@ -0,0 +1,5 @@
+---
+- name: configure resolver
+  template:
+    src: resolv.conf.j2
+    dest: /etc/resolv.conf
diff --git a/ansible/node/roles/common/tasks/generic/rsyslog.yml b/ansible/node/roles/common/tasks/generic/rsyslog.yml
new file mode 100644 (file)
index 0000000..88535af
--- /dev/null
@@ -0,0 +1,7 @@
+---
+- name: add autocluster-specific rsyslog configuration
+  copy:
+    src: rsyslog.conf
+    dest: /etc/rsyslog.d/autocluster.conf
+  notify:
+    - restart rsyslog
diff --git a/ansible/node/roles/common/tasks/generic/selinux.yml b/ansible/node/roles/common/tasks/generic/selinux.yml
new file mode 100644 (file)
index 0000000..b7e9c2f
--- /dev/null
@@ -0,0 +1,4 @@
+---
+- selinux:
+    policy: targeted
+    state: permissive
diff --git a/ansible/node/roles/common/tasks/generic/ssh.yml b/ansible/node/roles/common/tasks/generic/ssh.yml
new file mode 100644 (file)
index 0000000..c3bff9f
--- /dev/null
@@ -0,0 +1,5 @@
+---
+- name: configure passwordless SSH
+  copy:
+    src: ssh_config
+    dest: /root/.ssh/config
diff --git a/ansible/node/roles/common/tasks/generic/timezone.yml b/ansible/node/roles/common/tasks/generic/timezone.yml
new file mode 100644 (file)
index 0000000..87b0ba4
--- /dev/null
@@ -0,0 +1,12 @@
+---
+- name: configure node timezone
+  timezone:
+    hwclock: UTC
+    name: "{{timezone}}"
+
+- name: hand hack timezone to avoid reboot
+  file:
+    src: /usr/share/zoneinfo/{{timezone}}
+    path: /etc/localtime
+    state: link
+    force: yes
diff --git a/ansible/node/roles/common/tasks/main.yml b/ansible/node/roles/common/tasks/main.yml
new file mode 100644 (file)
index 0000000..104d9f5
--- /dev/null
@@ -0,0 +1,25 @@
+---
+- include_tasks: "{{ ansible_os_family | lower }}/{{ task }}.yml"
+  with_list:
+  - packages
+  - firewall
+  - ntp
+  loop_control:
+    loop_var: task
+
+- meta: flush_handlers
+
+- include_tasks: generic/{{ task }}.yml
+  with_list:
+  - selinux
+  - autocluster
+  - hosts
+  - resolv_conf
+  - ssh
+  - timezone
+  - rsyslog
+  - mount_home
+  loop_control:
+    loop_var: task
+
+- meta: flush_handlers
diff --git a/ansible/node/roles/common/tasks/redhat/firewall.yml b/ansible/node/roles/common/tasks/redhat/firewall.yml
new file mode 100644 (file)
index 0000000..bf5eeb4
--- /dev/null
@@ -0,0 +1,6 @@
+---
+- name: disable firewall
+  service:
+    name: firewalld
+    enabled: no
+    state: stopped
diff --git a/ansible/node/roles/common/tasks/redhat/ntp.yml b/ansible/node/roles/common/tasks/redhat/ntp.yml
new file mode 100644 (file)
index 0000000..3495457
--- /dev/null
@@ -0,0 +1,18 @@
+---
+- name: ensure NTP server is installed
+  package:
+    name: chrony 
+    state: installed
+
+- name: ensure NTP server is configured
+  template:
+    src: chrony.conf.j2
+    dest: /etc/chrony.conf
+  notify:
+    - restart NTP server redhat
+
+- name: ensure NTP server is running and enabled
+  service:
+    name: chronyd
+    state: started
+    enabled: yes
diff --git a/ansible/node/roles/common/tasks/redhat/packages.yml b/ansible/node/roles/common/tasks/redhat/packages.yml
new file mode 100644 (file)
index 0000000..b2430e5
--- /dev/null
@@ -0,0 +1,53 @@
+---
+- name: disable Network Manager on next boot
+  service:
+    name: NetworkManager
+    enabled: no
+    # Note that this only works because the interfaces of interest
+    # have been marked in Vagrant as: nm_controlled: "no" - otherwise
+    # NetworkManager would stop and take the interfaces down with it!
+    state: stopped
+
+- name: disable EPEL to speed things up
+  package:
+    name: epel-release
+    state: absent
+
+- name: find non-autocluster YUM repo files
+  find:
+    paths: /etc/yum.repos.d/
+    patterns: '(?!autocluster-)^.*\.repo$'
+    use_regex: yes
+  register: find_results
+  when: repositories_delete_existing
+
+- name: Remove non-autocluster repo files
+  file:
+    path: "{{ f['path'] }}"
+    state: absent
+  with_list: "{{ find_results['files'] }}"
+  loop_control:
+    loop_var: f
+  when: repositories_delete_existing
+
+- name: Add local distro repos
+  yum_repository:
+    name: "autocluster-{{ repo.name }}"
+    description: "{{ repo.name }}"
+    baseurl: "{{ repo.baseurl | default(repository_baseurl) }}/{{ repo.path }}"
+    gpgcheck: "{{ repo.gpgcheck | default('yes') }}"
+    proxy: _none_
+  when: repo.type == "distro"
+  with_list: "{{ repositories }}"
+  loop_control:
+    loop_var: repo
+
+- name: ensure optional dependencies for Ansible template handling
+  package:
+    name: libselinux-python
+    state: present
+
+- name: ensure NFS client tools are installed
+  package:
+    name: nfs-utils
+    state: present
diff --git a/ansible/node/roles/common/templates/chrony.conf.j2 b/ansible/node/roles/common/templates/chrony.conf.j2
new file mode 100644 (file)
index 0000000..2a4f259
--- /dev/null
@@ -0,0 +1,41 @@
+server {{virthost}} iburst
+
+# Ignore stratum in source selection.
+stratumweight 0
+
+# Record the rate at which the system clock gains/losses time.
+driftfile /var/lib/chrony/drift
+
+# Enable kernel RTC synchronization.
+rtcsync
+
+# In first three updates step the system clock instead of slew
+# if the adjustment is larger than 10 seconds.
+makestep 10 3
+
+# Allow NTP client access from local network.
+#allow 192.168/16
+
+# Listen for commands only on localhost.
+bindcmdaddress 127.0.0.1
+bindcmdaddress ::1
+
+# Serve time even if not synchronized to any NTP server.
+#local stratum 10
+
+keyfile /etc/chrony.keys
+
+# Specify the key used as password for chronyc.
+commandkey 1
+
+# Generate command key if missing.
+generatecommandkey
+
+# Disable logging of client accesses.
+noclientlog
+
+# Send a message to syslog if a clock adjustment is larger than 0.5 seconds.
+logchange 0.5
+
+logdir /var/log/chrony
+#log measurements statistics tracking
diff --git a/ansible/node/roles/common/templates/hosts.j2 b/ansible/node/roles/common/templates/hosts.j2
new file mode 100644 (file)
index 0000000..c575ea2
--- /dev/null
@@ -0,0 +1,10 @@
+# Generated by autocluster
+127.0.0.1      localhost localhost.localdomain localhost4 localhost4.localdomain4
+::1            localhost localhost.localdomain localhost6 localhost6.localdomain6
+
+{{ virthost }} kvmhost
+
+# autocluster {{ cluster }}
+{% for hostname, n in nodes | dictsort %}
+{{ n.ips[0] }} {{ hostname }}.{{ resolv_conf.domain | lower }} {{ hostname }}
+{% endfor                              %}
diff --git a/ansible/node/roles/common/templates/resolv.conf.j2 b/ansible/node/roles/common/templates/resolv.conf.j2
new file mode 100644 (file)
index 0000000..7ebaf95
--- /dev/null
@@ -0,0 +1,3 @@
+domain {{resolv_conf.domain}}
+search {{resolv_conf.search}}
+nameserver {{resolv_conf.nameserver}}
diff --git a/ansible/node/roles/ctdb/tasks/generic/ctdb.yml b/ansible/node/roles/ctdb/tasks/generic/ctdb.yml
new file mode 100644 (file)
index 0000000..350aeeb
--- /dev/null
@@ -0,0 +1,5 @@
+---
+- name: generate CTDB nodes file
+  template:
+    src: ctdb_nodes.j2
+    dest: /etc/ctdb/nodes
diff --git a/ansible/node/roles/ctdb/tasks/main.yml b/ansible/node/roles/ctdb/tasks/main.yml
new file mode 100644 (file)
index 0000000..5259448
--- /dev/null
@@ -0,0 +1,12 @@
+---
+- include_tasks: "{{ ansible_os_family | lower }}/{{ task }}.yml"
+  with_list:
+  - packages
+  loop_control:
+    loop_var: task
+
+- include_tasks: generic/{{ task }}.yml
+  with_list:
+  - ctdb
+  loop_control:
+    loop_var: task
diff --git a/ansible/node/roles/ctdb/tasks/redhat/packages.yml b/ansible/node/roles/ctdb/tasks/redhat/packages.yml
new file mode 100644 (file)
index 0000000..7bd6ca6
--- /dev/null
@@ -0,0 +1,13 @@
+---
+- name: install CTDB packages
+  package:
+    name:
+    - ctdb
+    - ctdb-debuginfo
+    - ctdb-tests
+    state: present
+
+- name: install tcpdump
+  package:
+    name: tcpdump
+    state: present
diff --git a/ansible/node/roles/ctdb/templates/ctdb_nodes.j2 b/ansible/node/roles/ctdb/templates/ctdb_nodes.j2
new file mode 100644 (file)
index 0000000..7094678
--- /dev/null
@@ -0,0 +1,5 @@
+{% for hostname, n in nodes | dictsort %}
+{%   if n.is_ctdb_node                 %}
+{{ n.ips[0] }}
+{%   endif                             %}
+{% endfor                              %}
diff --git a/ansible/node/roles/nas/files/rpc-rquotad.sysconfig b/ansible/node/roles/nas/files/rpc-rquotad.sysconfig
new file mode 100644 (file)
index 0000000..93f7089
--- /dev/null
@@ -0,0 +1 @@
+RPCRQUOTADOPTS="-p 32768"
diff --git a/ansible/node/roles/nas/files/smb.conf b/ansible/node/roles/nas/files/smb.conf
new file mode 100644 (file)
index 0000000..5c8ead3
--- /dev/null
@@ -0,0 +1,4 @@
+[global]
+       clustering=yes
+       ctdb:registry.tdb=yes
+       include=registry
diff --git a/ansible/node/roles/nas/tasks/generic/ctdb-once.yml b/ansible/node/roles/nas/tasks/generic/ctdb-once.yml
new file mode 100644 (file)
index 0000000..139bd32
--- /dev/null
@@ -0,0 +1,3 @@
+---
+- name: set security context for CTDB recovery lock directory
+  command: chcon -t ctdbd_var_t {{ clusterfs.mountpoint }}/.ctdb
diff --git a/ansible/node/roles/nas/tasks/generic/ctdb-start.yml b/ansible/node/roles/nas/tasks/generic/ctdb-start.yml
new file mode 100644 (file)
index 0000000..8bc9dbb
--- /dev/null
@@ -0,0 +1,12 @@
+---
+- name: start CTDB
+  service:
+    name: ctdb
+    state: started
+
+- name: wait until CTDB is healthy
+  command: ctdb nodestatus all
+  register: result
+  until: result.rc == 0
+  retries: 24
+  delay: 5
diff --git a/ansible/node/roles/nas/tasks/generic/ctdb-stop.yml b/ansible/node/roles/nas/tasks/generic/ctdb-stop.yml
new file mode 100644 (file)
index 0000000..ca624dc
--- /dev/null
@@ -0,0 +1,5 @@
+---
+- name: stop CTDB
+  service:
+    name: ctdb
+    state: stopped
diff --git a/ansible/node/roles/nas/tasks/generic/ctdb-with-samba-nfs.yml b/ansible/node/roles/nas/tasks/generic/ctdb-with-samba-nfs.yml
new file mode 100644 (file)
index 0000000..dea44fa
--- /dev/null
@@ -0,0 +1,41 @@
+---
+# Should be running already but this won't hurt
+- import_tasks: ctdb-start.yml
+
+- name: join active directory domain
+  shell: |
+    net ads testjoin || \
+      timeout 10 net ads join -U "administrator%{{ ad.admin_password }}"
+  register: result
+  until: result.rc == 0
+  retries: 5
+  delay: 1
+  run_once: true
+  when: auth_method == 'winbind'
+
+# FIXME: This will be useful to allow version checking to enable
+# services/event scripts in different ways
+
+# New in Ansible 2.5
+#- name: get package facts
+#  package_facts:
+#    manager: "auto"
+
+#- name: show them
+#  debug: var=ansible_facts.packages
+
+- import_tasks: ctdb-stop.yml
+
+- name: configure CTDB to manage smbd and NFS
+  command: ctdb event script enable legacy {{ s }}
+  with_list:
+    - 50.samba
+    - 60.nfs
+  loop_control:
+    loop_var: s
+
+- name: configure CTDB to manage winbindd
+  command: ctdb event script enable legacy 49.winbind
+  when: auth_method == 'winbind'
+
+- import_tasks: ctdb-start.yml
diff --git a/ansible/node/roles/nas/tasks/generic/ctdb.yml b/ansible/node/roles/nas/tasks/generic/ctdb.yml
new file mode 100644 (file)
index 0000000..5041db4
--- /dev/null
@@ -0,0 +1,37 @@
+---
+- name: generate CTDB configuration file
+  template:
+    src: ctdb_conf.j2
+    dest: /etc/ctdb/ctdb.conf
+
+- name: generate CTDB public addresses file
+  template:
+    src: ctdb_public_addresses.j2
+    dest: /etc/ctdb/public_addresses
+
+- name: create directory for CTDB recovery lock
+  file:
+    path: "{{ clusterfs.mountpoint }}/.ctdb"
+    state: directory
+
+- import_tasks: ctdb-once.yml
+  run_once: true
+
+- name: ensure CTDB is enabled
+  service:
+    name: ctdb
+    enabled: yes
+
+# This stops things failing if the domain has not been joined or similar
+- name: ensure that CTDB is not managing smbd, winbind and NFS
+  command: ctdb event script disable legacy {{ s }}
+  with_list:
+    - 49.winbind
+    - 50.samba
+    - 60.nfs
+  loop_control:
+    loop_var: s
+
+# Restart just in case ctdbd was running but unhealthy
+- import_tasks: ctdb-stop.yml
+- import_tasks: ctdb-start.yml
diff --git a/ansible/node/roles/nas/tasks/generic/nfs.yml b/ansible/node/roles/nas/tasks/generic/nfs.yml
new file mode 100644 (file)
index 0000000..90c00bc
--- /dev/null
@@ -0,0 +1,5 @@
+---
+- name: generate NFS exports file
+  template:
+    src: nfs_exports.j2
+    dest: /etc/exports
diff --git a/ansible/node/roles/nas/tasks/generic/samba-gpfs-once.yml b/ansible/node/roles/nas/tasks/generic/samba-gpfs-once.yml
new file mode 100644 (file)
index 0000000..101cd4b
--- /dev/null
@@ -0,0 +1,16 @@
+---
+- name: Tweak Samba config for GPFS cluster filesystem
+  command: net conf setparm global "{{ p.param }}" "{{ p.value }}"
+  with_list:
+    - param: vfs objects
+      value: gpfs fileid
+    - param: fileid:mapping
+      value: fsname
+    - param: nfs4:chown
+      value: "yes"
+    - param: nfs4:acedup
+      value: merge
+    - param: force unknown acl user
+      value: "yes"
+  loop_control:
+    loop_var: p
diff --git a/ansible/node/roles/nas/tasks/generic/samba-once.yml b/ansible/node/roles/nas/tasks/generic/samba-once.yml
new file mode 100644 (file)
index 0000000..8a586e6
--- /dev/null
@@ -0,0 +1,12 @@
+---
+- name: generate initial Samba registry configuration
+  template:
+    src: samba_registry.j2
+    dest: /root/.autocluster/samba-registry.conf
+
+# Need to start at least ctdbd... maybe smbd?
+
+- name: initialise Samba registry configuration
+  command: net conf import /root/.autocluster/samba-registry.conf
+
+- import_tasks: samba-{{ clusterfs.type }}-once.yml
diff --git a/ansible/node/roles/nas/tasks/generic/samba.yml b/ansible/node/roles/nas/tasks/generic/samba.yml
new file mode 100644 (file)
index 0000000..c1e58b8
--- /dev/null
@@ -0,0 +1,10 @@
+---
+- name: add smb.conf
+  copy:
+    src: smb.conf
+    dest: /etc/samba/smb.conf
+
+# TODO: Enable 50.samba and 60.nfs event scripts
+
+- import_tasks: samba-once.yml
+  run_once: true
diff --git a/ansible/node/roles/nas/tasks/generic/shares.yml b/ansible/node/roles/nas/tasks/generic/shares.yml
new file mode 100644 (file)
index 0000000..db5d58e
--- /dev/null
@@ -0,0 +1,9 @@
+---
+- name: create share directories
+  file:
+    path: "{{s.directory}}"
+    mode: "{{s.mode}}"
+    state: directory
+  with_list: "{{shares}}"
+  loop_control:
+    loop_var: s
diff --git a/ansible/node/roles/nas/tasks/main.yml b/ansible/node/roles/nas/tasks/main.yml
new file mode 100644 (file)
index 0000000..6d56084
--- /dev/null
@@ -0,0 +1,17 @@
+---
+- include_tasks: "{{ ansible_os_family | lower }}/{{ task }}.yml"
+  with_list:
+  - samba
+  - nfs
+  loop_control:
+    loop_var: task
+
+- include_tasks: generic/{{ task }}.yml
+  with_list:
+  - shares
+  - ctdb
+  - samba
+  - nfs
+  - ctdb-with-samba-nfs
+  loop_control:
+    loop_var: task
diff --git a/ansible/node/roles/nas/tasks/redhat/nfs.yml b/ansible/node/roles/nas/tasks/redhat/nfs.yml
new file mode 100644 (file)
index 0000000..4dc1d7c
--- /dev/null
@@ -0,0 +1,31 @@
+---
+- name: install NFS packages
+  package:
+    name: nfs-utils
+    state: present
+
+- name: ensure NFS does not autostart
+  service:
+    name: "{{ s }}"
+    enabled: no
+  with_list:
+    - nfs
+    - nfslock
+  loop_control:
+    loop_var: s
+
+- name: generate NFS configuration
+  template:
+    src: nfs_sysconfig.j2
+    dest: /etc/sysconfig/nfs
+
+- name: check if /etc/sysconfig/rpc-rquotad exists
+  stat:
+    path: /etc/sysconfig/rpc-rquotad
+  register: sysconfig_rpc_rquotad
+
+- name: generate quota configuration file
+  file:
+    src: rpc-rquotad.sysconfig
+    path: /etc/sysconfig/rpc-rquotad
+  when: sysconfig_rpc_rquotad.stat.exists
diff --git a/ansible/node/roles/nas/tasks/redhat/samba.yml b/ansible/node/roles/nas/tasks/redhat/samba.yml
new file mode 100644 (file)
index 0000000..ebcd7fb
--- /dev/null
@@ -0,0 +1,48 @@
+---
+
+- name: install Samba packages
+  package:
+    name:
+    - tdb-tools
+    - samba
+    - samba-client
+    - samba-doc
+    - samba-winbind
+    - samba-winbind-clients
+    state: present
+
+- name: install Samba packages for GPFS
+  package:
+    name: samba-vfs-gpfs
+    state: present
+  when: clusterfs.type == 'gpfs'
+
+- name: ensure Samba does not autostart
+  service:
+    name: "{{ service }}"
+    enabled: no
+  with_list:
+    - smb
+    - nmb
+    - winbind
+  loop_control:
+    loop_var: service
+
+- name: Set up NSS, PAM, ...
+  command: >
+    authconfig --update --nostart
+      --disablewinbindauth --disablewinbind --disablekrb5
+  when: auth_method == 'files'
+
+- name: Set up NSS, PAM, KRB5, ...
+  command: >
+    authconfig --update --nostart
+      --enablewinbindauth --enablewinbind --enablekrb5
+      --krb5kdc={{ kdc }}.{{ resolv_conf.domain }}
+      --krb5realm={{ resolv_conf.domain }}
+  when: auth_method == 'winbind'
+
+- name: Set up NSS, PAM, KRB5, ...
+  fail:
+    msg: "Invalid auth_method: {{ auth_method }}"
+  when: auth_method != 'files' and auth_method != 'winbind'
diff --git a/ansible/node/roles/nas/templates/ctdb_conf.j2 b/ansible/node/roles/nas/templates/ctdb_conf.j2
new file mode 100644 (file)
index 0000000..fbfaead
--- /dev/null
@@ -0,0 +1,6 @@
+[logging]
+       location = syslog
+       log level = NOTICE
+
+[cluster]
+       recovery lock = {{ clusterfs.mountpoint }}/.ctdb/recovery.lock
diff --git a/ansible/node/roles/nas/templates/ctdb_public_addresses.j2 b/ansible/node/roles/nas/templates/ctdb_public_addresses.j2
new file mode 100644 (file)
index 0000000..77f95b7
--- /dev/null
@@ -0,0 +1,55 @@
+{#                                                                 #}
+{# How many static public addresses/interfaces per node?           #}
+{#                                                                 #}
+{% set num_static = (nodes[ansible_hostname].ips | length) - 1     %}
+{#                                                                 #}
+{# Gather all static addresses, sublist per interface              #}
+{#                                                                 #}
+{% set static_addrs = []                                           %}
+{% for i in range(1, num_static + 1)                              -%}
+{{   static_addrs.append([])                                       }}
+{%- endfor                                                         %}
+{% for hostname, n in nodes | dictsort                             %}
+{%   if n.is_ctdb_node                                             %}
+{%     for i in range(1, num_static + 1)                          -%}
+{{       static_addrs[i - 1].append(n.ips[i])                      }}
+{%-    endfor                                                      %}
+{%   endif                                                         %}
+{% endfor                                                          %}
+{#                                                                 #}
+{# For each list of static IPs, find interface, print with each IP #}
+{#                                                                 #}
+{% set h = ansible_hostname                                        %}
+{% for ips in static_addrs                                         %}
+{%   for iface in ansible_interfaces                               %}
+{%     set ai = 'ansible_%s'|format(iface)                         %}
+{%     if hostvars[h][ai]['ipv4'] is defined                       %}
+{%       set ip4 = hostvars[h][ai]['ipv4']                         %}
+{%       if ip4['address'] is defined                              %}
+{%         set aip = ip4['address']                                %}
+{%         set netmask = ip4['netmask']                            %}
+{%         set prefix = (aip + '/' + netmask) | ipv4('prefix')     %}
+{%         if aip in ips                                           %}
+{%           for ip in ips                                         %}
+{%             set ip_int = ip | ipaddr('int')                     %}
+{{ (ip_int + 100) | ipaddr('address') }}/{{ prefix }}  {{ iface }}
+{%           endfor                                                %}
+{%         endif                                                   %}
+{%       endif                                                     %}
+{%     endif                                                       %}
+{%     if hostvars[h][ai]['ipv6'] is defined                       %}
+{%       for ip6 in hostvars[h][ai]['ipv6']                        %}
+{%         if ip6['address'] is defined                            %}
+{%           set aip = ip6['address']                              %}
+{%           set prefix = ip6['prefix']                            %}
+{%           if aip in ips                                         %}
+{%             for ip in ips                                       %}
+{%               set ip_int = ip | ipaddr('int')                   %}
+{{ (ip_int + 100) | ipaddr('address') }}/{{ prefix }}  {{ iface }}
+{%             endfor                                              %}
+{%           endif                                                 %}
+{%         endif                                                   %}
+{%       endfor                                                    %}
+{%     endif                                                       %}
+{%   endfor                                                        %}
+{% endfor                                                          %}
diff --git a/ansible/node/roles/nas/templates/ctdb_sysconfig.j2 b/ansible/node/roles/nas/templates/ctdb_sysconfig.j2
new file mode 100644 (file)
index 0000000..9992023
--- /dev/null
@@ -0,0 +1,23 @@
+# Core
+CTDB_PUBLIC_ADDRESSES=/etc/ctdb/public_addresses
+CTDB_RECOVERY_LOCK={{ clusterfs.mountpoint }}/.ctdb/recovery.lock
+
+# Services managed
+CTDB_MANAGES_SAMBA=yes
+{% if auth_method == 'winbind' %}
+CTDB_MANAGES_WINBIND=yes
+{% else %}
+CTDB_MANAGES_WINBIND=no
+{% endif %}
+
+CTDB_MANAGES_NFS=yes
+CTDB_MANAGES_HTTPD=yes
+CTDB_MANAGES_VSFTPD=yes
+
+# System
+ulimit -n 1048576
+ulimit -c unlimited
+
+# Logging
+CTDB_LOGGING="syslog"
+CTDB_DEBUGLEVEL=NOTICE
diff --git a/ansible/node/roles/nas/templates/nfs_exports.j2 b/ansible/node/roles/nas/templates/nfs_exports.j2
new file mode 100644 (file)
index 0000000..00bd867
--- /dev/null
@@ -0,0 +1,6 @@
+# NFS exports file generated by autocluster
+{% set fsid = 834258092 %}
+{% for s in shares %}
+"{{ s.directory }}" *(rw,no_root_squash,subtree_check,fsid={{ fsid }})
+  {% set fsid = fsid + 1 %}
+{% endfor %}
diff --git a/ansible/node/roles/nas/templates/nfs_sysconfig.j2 b/ansible/node/roles/nas/templates/nfs_sysconfig.j2
new file mode 100644 (file)
index 0000000..c103fc7
--- /dev/null
@@ -0,0 +1,14 @@
+NFS_HOSTNAME="{{ cluster }}"
+
+STATD_PORT=32765
+STATD_OUTGOING_PORT=32766
+MOUNTD_PORT=32767
+RQUOTAD_PORT=32768
+LOCKD_UDPPORT=32769
+LOCKD_TCPPORT=32769
+
+STATDARG="-n ${NFS_HOSTNAME}"
+STATD_HA_CALLOUT="/etc/ctdb/statd-callout"
+
+RPCNFSDARGS="-N 4"
+RPCNFSDCOUNT=8
diff --git a/ansible/node/roles/nas/templates/samba_registry.j2 b/ansible/node/roles/nas/templates/samba_registry.j2
new file mode 100644 (file)
index 0000000..f6200af
--- /dev/null
@@ -0,0 +1,33 @@
+[global]
+{% if auth_method == 'winbind' %}
+       security = ADS
+{% elif auth_method == 'files' %}
+       security = USER
+{% else %}
+       security = BROKEN
+{% endif %}
+
+       logging = syslog
+       log level = 1
+
+       netbios name = {{ cluster }}
+       workgroup = {{ samba.workgroup }}
+       realm = {{ resolv_conf.domain }}
+
+       disable netbios = yes
+       disable spoolss = yes
+
+       idmap config * : backend = autorid
+       idmap config * : range = 1000000-1999999
+
+       kernel oplocks = yes
+
+       read only = no
+
+{% for s in shares %}
+[{{ s.name }}]
+       path = {{ s.directory }}
+       comment = Example share {{ s.name }}
+       guest ok = yes
+       browseable = yes
+{% endfor %}
diff --git a/ansible/node/roles/nasrepos/tasks/main.yml b/ansible/node/roles/nasrepos/tasks/main.yml
new file mode 100644 (file)
index 0000000..69a1776
--- /dev/null
@@ -0,0 +1,6 @@
+---
+- include_tasks: "{{ ansible_os_family | lower }}/{{ task }}.yml"
+  with_list:
+  - repo
+  loop_control:
+    loop_var: task
diff --git a/ansible/node/roles/nasrepos/tasks/redhat/repo.yml b/ansible/node/roles/nasrepos/tasks/redhat/repo.yml
new file mode 100644 (file)
index 0000000..d330d08
--- /dev/null
@@ -0,0 +1,12 @@
+---
+- name: Add NAS (Samba/CTDB) repos
+  yum_repository:
+    name: "autocluster-{{ repo.name }}"
+    description: "{{ repo.name }}"
+    baseurl: "{{ repo.baseurl | default(repository_baseurl) }}/{{ repo.path }}"
+    gpgcheck: no
+    proxy: _none_
+  when: repo.type == "nas"
+  with_list: "{{ repositories }}"
+  loop_control:
+    loop_var: repo
diff --git a/ansible/node/roles/storage/tasks/generic/clusterfs-gpfs-once.yml b/ansible/node/roles/storage/tasks/generic/clusterfs-gpfs-once.yml
new file mode 100644 (file)
index 0000000..5ed26eb
--- /dev/null
@@ -0,0 +1,135 @@
+---
+- name: generate GPFS nodes file
+  template:
+    src: gpfs_nodes.j2
+    dest: /root/.autocluster/gpfs_nodes
+
+- name: generate file containing GPFS primary and secondary nodes
+  template:
+    src: gpfs_primary_secondary.j2
+    dest: /root/.autocluster/gpfs_primary_secondary
+
+- name: check if GPFS active flag file exists
+  stat:
+    path: /root/.autocluster/gpfs_active
+  register: gpfs_active
+
+- name: create GPFS cluster
+  shell: |
+    mmlscluster || {
+      read primary secondary </root/.autocluster/gpfs_primary_secondary && \
+      mmcrcluster -N /root/.autocluster/gpfs_nodes \
+        -p "$primary" -s "$secondary" \
+        -r /usr/bin/ssh -R /usr/bin/scp \
+        -C "{{ cluster }}.{{ resolv_conf.domain | lower }}"
+    }
+  when: not gpfs_active.stat.exists
+
+- name: set GPFS server license mode
+  command: mmchlicense server --accept -N all
+  when: not gpfs_active.stat.exists
+
+- name: set GPFS admin mode to allToAll
+  command: mmchconfig adminMode=allToAll
+  when: not gpfs_active.stat.exists
+
+- name : generate GPFS auth key
+  # Without the commit, this can't be run more than once
+  shell: mmauth genkey new && mmauth genkey commit
+  when: not gpfs_active.stat.exists
+
+- name: set GPFS config options
+  # Can not be run if GPFS is active
+  command: mmchconfig autoload=yes,leaseRecoveryWait=3,maxFilesToCache=20000,failureDetectionTime=10,maxMBpS=500,unmountOnDiskFail=yes,pagepool=64M,allowSambaCaseInsensitiveLookup=no
+  when: not gpfs_active.stat.exists
+
+- name: set GPFS cipher list option
+  # Can not be set with the above, can not be run if GPFS is active
+  command: mmchconfig cipherList=AUTHONLY
+  when: not gpfs_active.stat.exists
+
+- name: start GPFS
+  command: mmstartup -a
+  when: not gpfs_active.stat.exists
+
+- name: wait until GPFS is active on all nodes
+  # The field-separator passed to awk must be protected from YAML
+  shell: |
+    mmgetstate -a -Y | awk -F':' 'NR > 1 && $9 != "active" { exit(1) }'
+  register: result
+  until: result.rc == 0
+  retries: 12
+  delay: 5
+  when: not gpfs_active.stat.exists
+
+- name: flag GPFS as active
+  file:
+    path: /root/.autocluster/gpfs_active
+    state: touch
+
+- name: generate NSD file
+  shell: >
+    ls /dev/disk/by-id/virtio-AUTO-* |
+      xargs -n 1 realpath |
+      awk '{printf "%nsd:\n  device=%s\n  usage=dataAndMetadata\n  failureGroup=1\n\n", $1}' |
+      tee gpfs_nsds
+  args:
+    chdir: /root/.autocluster/
+    creates: gpfs_nsds
+
+- name: check if GPFS NSDs created file exists
+  stat:
+    path: /root/.autocluster/gpfs_nsds_created
+  register: gpfs_nsds_created
+
+- name: create GPFS NSDs
+  command: mmcrnsd -F gpfs_nsds
+  args:
+    chdir: /root/.autocluster/ 
+  when: not gpfs_nsds_created.stat.exists
+
+- name: flag GPFS NSDs as created
+  file:
+    path: /root/.autocluster/gpfs_nsds_created
+    state: touch
+
+- name: check if GPFS filesystem created file exists
+  stat:
+    path: /root/.autocluster/gpfs_fs_created
+  register: gpfs_fs_created
+
+- name: create GPFS filesystem
+  command: >
+    mmcrfs gpfs0 -F gpfs_nsds
+      -A yes -Q yes -D nfs4 -B 64k -k nfs4 -n 32 -E yes -S no
+      -T {{ clusterfs.mountpoint}} -i 512
+  args:
+    chdir: /root/.autocluster/ 
+  when: not gpfs_fs_created.stat.exists
+
+- name: flag GPFS filesystem as created
+  file:
+    path: /root/.autocluster/gpfs_fs_created
+    state: touch
+
+- name: check if GPFS filesystem mounted file exists
+  stat:
+    path: /root/.autocluster/gpfs_fs_mounted
+  register: gpfs_fs_mounted
+
+- name: mount GPFS filesystem
+  command: mmmount gpfs0 -a
+  when: not gpfs_fs_mounted.stat.exists
+
+- name: wait until GPFS filesystem is mounted
+  command: findmnt {{ clusterfs.mountpoint }}
+  register: result
+  until: result.rc == 0
+  retries: 12
+  delay: 5
+  when: not gpfs_fs_mounted.stat.exists
+
+- name: flag GPFS filesystem as mounted
+  file:
+    path: /root/.autocluster/gpfs_fs_mounted
+    state: touch
diff --git a/ansible/node/roles/storage/tasks/generic/clusterfs-gpfs.yml b/ansible/node/roles/storage/tasks/generic/clusterfs-gpfs.yml
new file mode 100644 (file)
index 0000000..c498443
--- /dev/null
@@ -0,0 +1,20 @@
+---
+- name: create cluster filesystem mountpoint
+  file:
+    path: "{{clusterfs.mountpoint}}"
+    state: directory
+
+- name: create cluster filesystem automount directory
+  file:
+    path: "{{clusterfs.mountpoint}}/automount"
+    state: directory
+
+- name: make cluster filesystem mountpoint immutable
+  shell: >
+    if ! findmnt "{{clusterfs.mountpoint}}"; then
+      chattr +i "{{clusterfs.mountpoint}}"
+    fi
+
+- import_tasks: clusterfs-gpfs-once.yml
+  run_once: true
+  when: nodes[ansible_hostname].has_shared_storage
diff --git a/ansible/node/roles/storage/tasks/main.yml b/ansible/node/roles/storage/tasks/main.yml
new file mode 100644 (file)
index 0000000..07a5ad1
--- /dev/null
@@ -0,0 +1,6 @@
+---
+- include_tasks: generic/{{ task }}.yml
+  with_list:
+  - clusterfs-{{ clusterfs.type }}
+  loop_control:
+    loop_var: task
diff --git a/ansible/node/roles/storage/templates/gpfs_nodes.j2 b/ansible/node/roles/storage/templates/gpfs_nodes.j2
new file mode 100644 (file)
index 0000000..5a9ecd7
--- /dev/null
@@ -0,0 +1,33 @@
+# GPFS nodes file generated by autocluster
+{#                                                      #}
+{# Count dedicated storage nodes, find first CTDB node  #}
+{#                                                      #}
+{% set num_storage_nodes = 0                            %}
+{% set first_ctdb_node = ""                             %}
+{% for hostname, n in nodes | dictsort                  %}
+{%   if n.has_shared_storage                            %}
+{%     if n.is_ctdb_node                                %}
+{%       if not first_ctdb_node                         %}
+{%         set first_ctdb_node = hostname               %}
+{%       endif                                          %}
+{%     else                                             %}
+{%       set num_storage_nodes = num_storage_nodes + 1  %}
+{%     endif                                            %}
+{%   endif                                              %}
+{% endfor                                               %}
+{#                                                      #}
+{# Generate GPFS nodes file lines                       #}
+{#                                                      #}
+{% for hostname, n in nodes | dictsort                  %}
+{%   if n.is_ctdb_node                                  %}
+{%     if hostname == first_ctdb_node                   %}
+{{ hostname }}:manager-quorum:
+{%     elif num_storage_nodes > 0                       %}
+{{ hostname }}:manager:
+{%     else                                             %}
+{{ hostname }}:manager-quorum:
+{%     endif                                            %}
+{%   elif n.has_shared_storage                          %}
+{{ hostname }}:manager-quorum:
+{%   endif                                              %}
+{% endfor                                               %}
diff --git a/ansible/node/roles/storage/templates/gpfs_primary_secondary.j2 b/ansible/node/roles/storage/templates/gpfs_primary_secondary.j2
new file mode 100644 (file)
index 0000000..aec7a37
--- /dev/null
@@ -0,0 +1,19 @@
+{#                                                      #}
+{# Count dedicated storage nodes                        #}
+{#                                                      #}
+{% set num_storage_nodes = 0                            %}
+{% for hostname, n in nodes | dictsort                  %}
+{%   if n.has_shared_storage and not n.is_ctdb_node     %}
+{%     set num_storage_nodes = num_storage_nodes + 1    %}
+{%   endif                                              %}
+{% endfor                                               %}
+{#                                                      #}
+{# Write a single line containing "primary secondary"   #}
+{#                                                      #}
+{% if num_storage_nodes >= 2                            %}
+{{ groups['storage-nodes'][0] }} {{ groups['storage-nodes'][1] }}
+{% elif num_storage_nodes == 1                          %}
+{{ groups['storage-nodes'][0] }} {{ groups['nas-nodes'][0] }}
+{% else                                                 %}
+{{ groups['nas-nodes'][0] }} {{ groups['nas-nodes'][1] }}
+{% endif %}
diff --git a/ansible/node/site.yml b/ansible/node/site.yml
new file mode 100644 (file)
index 0000000..cfbc3f0
--- /dev/null
@@ -0,0 +1,8 @@
+---
+- import_playbook: ad.yml
+- import_playbook: base.yml
+- import_playbook: build.yml
+- import_playbook: cbuild.yml
+- import_playbook: storage.yml
+- import_playbook: test.yml
+- import_playbook: nas.yml
diff --git a/ansible/node/storage.yml b/ansible/node/storage.yml
new file mode 100644 (file)
index 0000000..94032ec
--- /dev/null
@@ -0,0 +1,8 @@
+---
+- hosts: storage-nodes
+  remote_user: root
+
+  roles:
+    - common
+    - clusterfs
+    - storage
diff --git a/ansible/node/test.yml b/ansible/node/test.yml
new file mode 100644 (file)
index 0000000..a28f56d
--- /dev/null
@@ -0,0 +1,8 @@
+---
+- hosts: test-nodes
+  remote_user: root
+
+  roles:
+    - common
+    - nasrepos
+    - ctdb