summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMartin Schwenke <martin@meltin.net>2019-02-06 14:53:10 +1100
committerMartin Schwenke <martin@meltin.net>2019-03-25 16:52:25 +1100
commit51ff83de30db6934e243226ce05c6394b8986a12 (patch)
treef8dbbe3ceabc398a4596c968285a7245b3c70e01
parent7003df8ad2ec9eaa119439f21976e7117b1771e5 (diff)
downloadautocluster-51ff83de30db6934e243226ce05c6394b8986a12.tar.gz
autocluster-51ff83de30db6934e243226ce05c6394b8986a12.tar.xz
autocluster-51ff83de30db6934e243226ce05c6394b8986a12.zip
Add Ansible playbook for node configuration
This will replace all of the existing node provisioning/configuration. CentOS 7 nodes are currently supported. Signed-off-by: Martin Schwenke <martin@meltin.net>
-rw-r--r--ansible/node/ad.yml7
-rw-r--r--ansible/node/base.yml6
-rw-r--r--ansible/node/build.yml8
-rw-r--r--ansible/node/cbuild.yml8
-rw-r--r--ansible/node/nas.yml11
-rw-r--r--ansible/node/roles/README33
-rw-r--r--ansible/node/roles/ad/tasks/generic/configure_ad.yml65
-rw-r--r--ansible/node/roles/ad/tasks/main.yml12
-rw-r--r--ansible/node/roles/ad/tasks/redhat/packages.yml22
-rw-r--r--ansible/node/roles/build/tasks/main.yml7
-rw-r--r--ansible/node/roles/build/tasks/redhat/packages.yml24
-rw-r--r--ansible/node/roles/build/tasks/redhat/packaging_setup.yml8
-rw-r--r--ansible/node/roles/clusterfs/files/autocluster-gpfs.profile2
-rw-r--r--ansible/node/roles/clusterfs/tasks/main.yml7
-rw-r--r--ansible/node/roles/clusterfs/tasks/redhat/clusterfs-gpfs.yml20
-rw-r--r--ansible/node/roles/clusterfs/tasks/redhat/repo.yml12
-rw-r--r--ansible/node/roles/common/files/rsyslog.conf14
-rw-r--r--ansible/node/roles/common/files/ssh_config2
-rw-r--r--ansible/node/roles/common/handlers/main.yml12
-rw-r--r--ansible/node/roles/common/tasks/generic/autocluster.yml5
-rw-r--r--ansible/node/roles/common/tasks/generic/hosts.yml5
-rw-r--r--ansible/node/roles/common/tasks/generic/mount_home.yml12
-rw-r--r--ansible/node/roles/common/tasks/generic/resolv_conf.yml5
-rw-r--r--ansible/node/roles/common/tasks/generic/rsyslog.yml7
-rw-r--r--ansible/node/roles/common/tasks/generic/selinux.yml4
-rw-r--r--ansible/node/roles/common/tasks/generic/ssh.yml5
-rw-r--r--ansible/node/roles/common/tasks/generic/timezone.yml12
-rw-r--r--ansible/node/roles/common/tasks/main.yml25
-rw-r--r--ansible/node/roles/common/tasks/redhat/firewall.yml6
-rw-r--r--ansible/node/roles/common/tasks/redhat/ntp.yml18
-rw-r--r--ansible/node/roles/common/tasks/redhat/packages.yml53
-rw-r--r--ansible/node/roles/common/templates/chrony.conf.j241
-rw-r--r--ansible/node/roles/common/templates/hosts.j210
-rw-r--r--ansible/node/roles/common/templates/resolv.conf.j23
-rw-r--r--ansible/node/roles/ctdb/tasks/generic/ctdb.yml5
-rw-r--r--ansible/node/roles/ctdb/tasks/main.yml12
-rw-r--r--ansible/node/roles/ctdb/tasks/redhat/packages.yml13
-rw-r--r--ansible/node/roles/ctdb/templates/ctdb_nodes.j25
-rw-r--r--ansible/node/roles/nas/files/rpc-rquotad.sysconfig1
-rw-r--r--ansible/node/roles/nas/files/smb.conf4
-rw-r--r--ansible/node/roles/nas/tasks/generic/ctdb-once.yml3
-rw-r--r--ansible/node/roles/nas/tasks/generic/ctdb-start.yml12
-rw-r--r--ansible/node/roles/nas/tasks/generic/ctdb-stop.yml5
-rw-r--r--ansible/node/roles/nas/tasks/generic/ctdb-with-samba-nfs.yml41
-rw-r--r--ansible/node/roles/nas/tasks/generic/ctdb.yml37
-rw-r--r--ansible/node/roles/nas/tasks/generic/nfs.yml5
-rw-r--r--ansible/node/roles/nas/tasks/generic/samba-gpfs-once.yml16
-rw-r--r--ansible/node/roles/nas/tasks/generic/samba-once.yml12
-rw-r--r--ansible/node/roles/nas/tasks/generic/samba.yml10
-rw-r--r--ansible/node/roles/nas/tasks/generic/shares.yml9
-rw-r--r--ansible/node/roles/nas/tasks/main.yml17
-rw-r--r--ansible/node/roles/nas/tasks/redhat/nfs.yml31
-rw-r--r--ansible/node/roles/nas/tasks/redhat/samba.yml48
-rw-r--r--ansible/node/roles/nas/templates/ctdb_conf.j26
-rw-r--r--ansible/node/roles/nas/templates/ctdb_public_addresses.j255
-rw-r--r--ansible/node/roles/nas/templates/ctdb_sysconfig.j223
-rw-r--r--ansible/node/roles/nas/templates/nfs_exports.j26
-rw-r--r--ansible/node/roles/nas/templates/nfs_sysconfig.j214
-rw-r--r--ansible/node/roles/nas/templates/samba_registry.j233
-rw-r--r--ansible/node/roles/nasrepos/tasks/main.yml6
-rw-r--r--ansible/node/roles/nasrepos/tasks/redhat/repo.yml12
-rw-r--r--ansible/node/roles/storage/tasks/generic/clusterfs-gpfs-once.yml135
-rw-r--r--ansible/node/roles/storage/tasks/generic/clusterfs-gpfs.yml20
-rw-r--r--ansible/node/roles/storage/tasks/main.yml6
-rw-r--r--ansible/node/roles/storage/templates/gpfs_nodes.j233
-rw-r--r--ansible/node/roles/storage/templates/gpfs_primary_secondary.j219
-rw-r--r--ansible/node/site.yml8
-rw-r--r--ansible/node/storage.yml8
-rw-r--r--ansible/node/test.yml8
69 files changed, 1169 insertions, 0 deletions
diff --git a/ansible/node/ad.yml b/ansible/node/ad.yml
new file mode 100644
index 0000000..79e2cc0
--- /dev/null
+++ b/ansible/node/ad.yml
@@ -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
index 0000000..f5e9d5e
--- /dev/null
+++ b/ansible/node/base.yml
@@ -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
index 0000000..fe4023d
--- /dev/null
+++ b/ansible/node/build.yml
@@ -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
index 0000000..1a3d474
--- /dev/null
+++ b/ansible/node/cbuild.yml
@@ -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
index 0000000..664f78d
--- /dev/null
+++ b/ansible/node/nas.yml
@@ -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
index 0000000..84e9a86
--- /dev/null
+++ b/ansible/node/roles/README
@@ -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
index 0000000..c56d1e4
--- /dev/null
+++ b/ansible/node/roles/ad/tasks/generic/configure_ad.yml
@@ -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
index 0000000..6b0e811
--- /dev/null
+++ b/ansible/node/roles/ad/tasks/main.yml
@@ -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
index 0000000..49d1eaf
--- /dev/null
+++ b/ansible/node/roles/ad/tasks/redhat/packages.yml
@@ -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
index 0000000..5e6cd27
--- /dev/null
+++ b/ansible/node/roles/build/tasks/main.yml
@@ -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
index 0000000..1b0d485
--- /dev/null
+++ b/ansible/node/roles/build/tasks/redhat/packages.yml
@@ -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
index 0000000..7415960
--- /dev/null
+++ b/ansible/node/roles/build/tasks/redhat/packaging_setup.yml
@@ -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
index 0000000..71c610d
--- /dev/null
+++ b/ansible/node/roles/clusterfs/files/autocluster-gpfs.profile
@@ -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
index 0000000..6e3ec1a
--- /dev/null
+++ b/ansible/node/roles/clusterfs/tasks/main.yml
@@ -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
index 0000000..c77e380
--- /dev/null
+++ b/ansible/node/roles/clusterfs/tasks/redhat/clusterfs-gpfs.yml
@@ -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
index 0000000..0000ccd
--- /dev/null
+++ b/ansible/node/roles/clusterfs/tasks/redhat/repo.yml
@@ -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
index 0000000..6478b45
--- /dev/null
+++ b/ansible/node/roles/common/files/rsyslog.conf
@@ -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
index 0000000..de7ff06
--- /dev/null
+++ b/ansible/node/roles/common/files/ssh_config
@@ -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
index 0000000..1b033cb
--- /dev/null
+++ b/ansible/node/roles/common/handlers/main.yml
@@ -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
index 0000000..753b225
--- /dev/null
+++ b/ansible/node/roles/common/tasks/generic/autocluster.yml
@@ -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
index 0000000..6983826
--- /dev/null
+++ b/ansible/node/roles/common/tasks/generic/hosts.yml
@@ -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
index 0000000..8a49816
--- /dev/null
+++ b/ansible/node/roles/common/tasks/generic/mount_home.yml
@@ -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
index 0000000..b6704ee
--- /dev/null
+++ b/ansible/node/roles/common/tasks/generic/resolv_conf.yml
@@ -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
index 0000000..88535af
--- /dev/null
+++ b/ansible/node/roles/common/tasks/generic/rsyslog.yml
@@ -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
index 0000000..b7e9c2f
--- /dev/null
+++ b/ansible/node/roles/common/tasks/generic/selinux.yml
@@ -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
index 0000000..c3bff9f
--- /dev/null
+++ b/ansible/node/roles/common/tasks/generic/ssh.yml
@@ -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
index 0000000..87b0ba4
--- /dev/null
+++ b/ansible/node/roles/common/tasks/generic/timezone.yml
@@ -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
index 0000000..104d9f5
--- /dev/null
+++ b/ansible/node/roles/common/tasks/main.yml
@@ -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
index 0000000..bf5eeb4
--- /dev/null
+++ b/ansible/node/roles/common/tasks/redhat/firewall.yml
@@ -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
index 0000000..3495457
--- /dev/null
+++ b/ansible/node/roles/common/tasks/redhat/ntp.yml
@@ -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
index 0000000..b2430e5
--- /dev/null
+++ b/ansible/node/roles/common/tasks/redhat/packages.yml
@@ -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
index 0000000..2a4f259
--- /dev/null
+++ b/ansible/node/roles/common/templates/chrony.conf.j2
@@ -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
index 0000000..c575ea2
--- /dev/null
+++ b/ansible/node/roles/common/templates/hosts.j2
@@ -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
index 0000000..7ebaf95
--- /dev/null
+++ b/ansible/node/roles/common/templates/resolv.conf.j2
@@ -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
index 0000000..350aeeb
--- /dev/null
+++ b/ansible/node/roles/ctdb/tasks/generic/ctdb.yml
@@ -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
index 0000000..5259448
--- /dev/null
+++ b/ansible/node/roles/ctdb/tasks/main.yml
@@ -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
index 0000000..7bd6ca6
--- /dev/null
+++ b/ansible/node/roles/ctdb/tasks/redhat/packages.yml
@@ -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
index 0000000..7094678
--- /dev/null
+++ b/ansible/node/roles/ctdb/templates/ctdb_nodes.j2
@@ -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
index 0000000..93f7089
--- /dev/null
+++ b/ansible/node/roles/nas/files/rpc-rquotad.sysconfig
@@ -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
index 0000000..5c8ead3
--- /dev/null
+++ b/ansible/node/roles/nas/files/smb.conf
@@ -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
index 0000000..139bd32
--- /dev/null
+++ b/ansible/node/roles/nas/tasks/generic/ctdb-once.yml
@@ -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
index 0000000..8bc9dbb
--- /dev/null
+++ b/ansible/node/roles/nas/tasks/generic/ctdb-start.yml
@@ -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
index 0000000..ca624dc
--- /dev/null
+++ b/ansible/node/roles/nas/tasks/generic/ctdb-stop.yml
@@ -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
index 0000000..dea44fa
--- /dev/null
+++ b/ansible/node/roles/nas/tasks/generic/ctdb-with-samba-nfs.yml
@@ -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
index 0000000..5041db4
--- /dev/null
+++ b/ansible/node/roles/nas/tasks/generic/ctdb.yml
@@ -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
index 0000000..90c00bc
--- /dev/null
+++ b/ansible/node/roles/nas/tasks/generic/nfs.yml
@@ -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
index 0000000..101cd4b
--- /dev/null
+++ b/ansible/node/roles/nas/tasks/generic/samba-gpfs-once.yml
@@ -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
index 0000000..8a586e6
--- /dev/null
+++ b/ansible/node/roles/nas/tasks/generic/samba-once.yml
@@ -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
index 0000000..c1e58b8
--- /dev/null
+++ b/ansible/node/roles/nas/tasks/generic/samba.yml
@@ -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
index 0000000..db5d58e
--- /dev/null
+++ b/ansible/node/roles/nas/tasks/generic/shares.yml
@@ -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
index 0000000..6d56084
--- /dev/null
+++ b/ansible/node/roles/nas/tasks/main.yml
@@ -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
index 0000000..4dc1d7c
--- /dev/null
+++ b/ansible/node/roles/nas/tasks/redhat/nfs.yml
@@ -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
index 0000000..ebcd7fb
--- /dev/null
+++ b/ansible/node/roles/nas/tasks/redhat/samba.yml
@@ -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
index 0000000..fbfaead
--- /dev/null
+++ b/ansible/node/roles/nas/templates/ctdb_conf.j2
@@ -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
index 0000000..77f95b7
--- /dev/null
+++ b/ansible/node/roles/nas/templates/ctdb_public_addresses.j2
@@ -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
index 0000000..9992023
--- /dev/null
+++ b/ansible/node/roles/nas/templates/ctdb_sysconfig.j2
@@ -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
index 0000000..00bd867
--- /dev/null
+++ b/ansible/node/roles/nas/templates/nfs_exports.j2
@@ -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
index 0000000..c103fc7
--- /dev/null
+++ b/ansible/node/roles/nas/templates/nfs_sysconfig.j2
@@ -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
index 0000000..f6200af
--- /dev/null
+++ b/ansible/node/roles/nas/templates/samba_registry.j2
@@ -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
index 0000000..69a1776
--- /dev/null
+++ b/ansible/node/roles/nasrepos/tasks/main.yml
@@ -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
index 0000000..d330d08
--- /dev/null
+++ b/ansible/node/roles/nasrepos/tasks/redhat/repo.yml
@@ -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
index 0000000..5ed26eb
--- /dev/null
+++ b/ansible/node/roles/storage/tasks/generic/clusterfs-gpfs-once.yml
@@ -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
index 0000000..c498443
--- /dev/null
+++ b/ansible/node/roles/storage/tasks/generic/clusterfs-gpfs.yml
@@ -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
index 0000000..07a5ad1
--- /dev/null
+++ b/ansible/node/roles/storage/tasks/main.yml
@@ -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
index 0000000..5a9ecd7
--- /dev/null
+++ b/ansible/node/roles/storage/templates/gpfs_nodes.j2
@@ -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
index 0000000..aec7a37
--- /dev/null
+++ b/ansible/node/roles/storage/templates/gpfs_primary_secondary.j2
@@ -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
index 0000000..cfbc3f0
--- /dev/null
+++ b/ansible/node/site.yml
@@ -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
index 0000000..94032ec
--- /dev/null
+++ b/ansible/node/storage.yml
@@ -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
index 0000000..a28f56d
--- /dev/null
+++ b/ansible/node/test.yml
@@ -0,0 +1,8 @@
+---
+- hosts: test-nodes
+ remote_user: root
+
+ roles:
+ - common
+ - nasrepos
+ - ctdb