From d325edcbeb0251c9bf4cb5cf692bbc249d85157c Mon Sep 17 00:00:00 2001
From: Chris Hines <chris.hines@monash.edu>
Date: Tue, 15 Dec 2015 12:01:58 +1100
Subject: [PATCH] updates

---
 roles/fastest_mirror/tasks/main.yml           |   7 +
 .../gluster_server/files/glusterfs-epel.repo  |   6 +-
 roles/link_usr_local/tasks/main.yml           |   6 +
 roles/move_homedir/tasks/main.yml             |  37 +-
 roles/provision_homedir/tasks/main.yml        |  25 +
 roles/provision_homedir/tasks/main.yml~       |  25 +
 .../templates/provision_homedir.py.j2         |  73 ++
 .../templates/provision_homedir.py.j2~        |  73 ++
 roles/provision_homedir/vars/main.yml         |   5 +
 roles/provision_homedir/vars/main.yml~        |   5 +
 roles/provision_slurm/tasks/main.yml          |  25 +
 roles/provision_slurm/tasks/main.yml~         |  25 +
 .../templates/provision_homedir.py.j2~        |  73 ++
 .../templates/provision_slurm.py.j2           |  76 ++
 .../templates/provision_slurm.py.j2~          |  76 ++
 roles/provision_slurm/vars/main.yml           |   4 +
 roles/provision_slurm/vars/main.yml~          |   5 +
 roles/slurm-common/tasks/main.yml             |  18 +-
 .../templates/generic_slurm_config.json.j2    |  14 +-
 roles/vncserver/tasks/main.yml                |  16 +
 roles/vncserver/templates/vncserver.centos    | 772 ++++++++++++++++++
 roles/vncserver/templates/vncserver.ubuntu    | 772 ++++++++++++++++++
 roles/vncserver/vars/CentOS_7_x86_64.yml      |  12 +-
 roles/vncserver/vars/Ubuntu_x86_64.yml        |   5 +-
 24 files changed, 2103 insertions(+), 52 deletions(-)
 create mode 100644 roles/provision_homedir/tasks/main.yml
 create mode 100644 roles/provision_homedir/tasks/main.yml~
 create mode 100644 roles/provision_homedir/templates/provision_homedir.py.j2
 create mode 100644 roles/provision_homedir/templates/provision_homedir.py.j2~
 create mode 100644 roles/provision_homedir/vars/main.yml
 create mode 100644 roles/provision_homedir/vars/main.yml~
 create mode 100644 roles/provision_slurm/tasks/main.yml
 create mode 100644 roles/provision_slurm/tasks/main.yml~
 create mode 100644 roles/provision_slurm/templates/provision_homedir.py.j2~
 create mode 100644 roles/provision_slurm/templates/provision_slurm.py.j2
 create mode 100644 roles/provision_slurm/templates/provision_slurm.py.j2~
 create mode 100644 roles/provision_slurm/vars/main.yml
 create mode 100644 roles/provision_slurm/vars/main.yml~
 create mode 100755 roles/vncserver/templates/vncserver.centos
 create mode 100755 roles/vncserver/templates/vncserver.ubuntu

diff --git a/roles/fastest_mirror/tasks/main.yml b/roles/fastest_mirror/tasks/main.yml
index b5f720a8..7b18f945 100644
--- a/roles/fastest_mirror/tasks/main.yml
+++ b/roles/fastest_mirror/tasks/main.yml
@@ -11,3 +11,10 @@
   command: yum makecache
   sudo: true
   when: ansible_os_family == 'RedHat'
+
+# For some reason ed went missing from the NeCTAR official CentOS 7 image
+# This meant that fail2ban could ban you, but could never unban you
+- name: "make sure ed is installed"
+  yum: name=ed state=installed
+  sudo: true
+  when: ansible_os_family == 'RedHat'
diff --git a/roles/gluster_server/files/glusterfs-epel.repo b/roles/gluster_server/files/glusterfs-epel.repo
index 843b4bae..6cac832a 100644
--- a/roles/gluster_server/files/glusterfs-epel.repo
+++ b/roles/gluster_server/files/glusterfs-epel.repo
@@ -2,21 +2,21 @@
 
 [glusterfs-epel]
 name=GlusterFS is a clustered file-system capable of scaling to several petabytes.
-baseurl=http://download.gluster.org/pub/gluster/glusterfs/3.6/LATEST/EPEL.repo/epel-$releasever/$basearch/
+baseurl=http://download.gluster.org/pub/gluster/glusterfs/3.7/LATEST/EPEL.repo/epel-$releasever/$basearch/
 enabled=1
 skip_if_unavailable=1
 gpgcheck=0
 
 [glusterfs-noarch-epel]
 name=GlusterFS is a clustered file-system capable of scaling to several petabytes.
-baseurl=http://download.gluster.org/pub/gluster/glusterfs/3.6/LATEST/EPEL.repo/epel-$releasever/noarch
+baseurl=http://download.gluster.org/pub/gluster/glusterfs/3.7/LATEST/EPEL.repo/epel-$releasever/noarch
 enabled=1
 skip_if_unavailable=1
 gpgcheck=0
 
 [glusterfs-source-epel]
 name=GlusterFS is a clustered file-system capable of scaling to several petabytes. - Source
-baseurl=http://download.gluster.org/pub/gluster/glusterfs/3.6/LATEST/EPEL.repo/epel-$releasever/SRPMS
+baseurl=http://download.gluster.org/pub/gluster/glusterfs/3.7/LATEST/EPEL.repo/epel-$releasever/SRPMS
 enabled=0
 skip_if_unavailable=1
 gpgcheck=0
diff --git a/roles/link_usr_local/tasks/main.yml b/roles/link_usr_local/tasks/main.yml
index 72847692..091d49c2 100644
--- a/roles/link_usr_local/tasks/main.yml
+++ b/roles/link_usr_local/tasks/main.yml
@@ -3,6 +3,10 @@
   stat: path={{ dest }}
   register: stat_r
 
+
+- name: debug1
+  debug: var=stat_r
+
 - name: mv
   command: mv "{{ dest }}" "{{ dest }}_old"
   when: stat_r.stat.exists and stat_r.stat.isdir
@@ -12,6 +16,8 @@
   stat: path={{ dest }}
   register: stat_r
 
+- name: debug2
+  debug: var=stat_r
 
 - name: link
   file: src="{{ src }}" dest="{{ dest }}" state=link
diff --git a/roles/move_homedir/tasks/main.yml b/roles/move_homedir/tasks/main.yml
index d81c8fe3..9ed97c5d 100644
--- a/roles/move_homedir/tasks/main.yml
+++ b/roles/move_homedir/tasks/main.yml
@@ -13,43 +13,12 @@
   lineinfile:
   args:
     dest: /etc/passwd
-    regexp: "{{ ansible_ssh_user }}:x:1001:1001::/home/{{ ansible_ssh_user }}:.*"
-    line: "{{ ansible_ssh_user }}:x:1001:1001::/local_home/{{ ansible_ssh_user }}:/bin/bash"
+    regexp: '{{ ansible_ssh_user }}:x:(.*):(.*):(.*):/home/{{ ansible_ssh_user }}:(.*)'
+    line: '{{ ansible_ssh_user }}:x:\1:\2:\3:/local_home/{{ ansible_ssh_user }}:\4'
     backrefs: yes
   sudo: true
-  register: edit1
+  register: edit
 
 
-- name: edit passwd file
-  lineinfile:
-  args:
-    dest: /etc/passwd
-    regexp: "{{ ansible_ssh_user }}:x:500:500::/home/{{ ansible_ssh_user }}:.*"
-    line: "{{ ansible_ssh_user }}:x:500:500::/local_home/{{ ansible_ssh_user }}:/bin/bash"
-    backrefs: yes
-  sudo: true
-  register: edit2
-
-
-
-- name: edit passwd file
-  lineinfile:
-  args:
-    dest: /etc/passwd
-    regexp: "{{ ansible_ssh_user }}:x:1000:1000::/home/{{ ansible_ssh_user }}:.*"
-    line: "{{ ansible_ssh_user }}:x:1000:1000::/local_home/{{ ansible_ssh_user }}:/bin/bash"
-    backrefs: yes
-  sudo: true
-  register: edit3
 
-# ubuntu:x:1000:1000:Ubuntu:/home/ubuntu:/bin/bash
-- name: edit passwd file for ubuntu 14
-  lineinfile:
-  args:
-    dest: /etc/passwd
-    regexp: "{{ ansible_ssh_user }}:x:1000:1000:Ubuntu:/home/{{ ansible_ssh_user }}:.*"
-    line: "{{ ansible_ssh_user }}:x:1000:1000:Ubuntu:/local_home/{{ ansible_ssh_user }}:/bin/bash"
-    backrefs: yes
-  sudo: true
-  register: edit4
 
diff --git a/roles/provision_homedir/tasks/main.yml b/roles/provision_homedir/tasks/main.yml
new file mode 100644
index 00000000..a057f44f
--- /dev/null
+++ b/roles/provision_homedir/tasks/main.yml
@@ -0,0 +1,25 @@
+---
+- name: make  dir
+  file: path="{{ provision_homedir | dirname }}" state=directory mode=755 owner=root
+  sudo: true
+  run_once: true
+
+- name: install python packages
+  yum: name=python-ldap state=installed
+  sudo: true
+  when: ansible_os_family == 'RedHat'
+
+- name: install python packages
+  apt: name=python-ldap state=installed
+  sudo: true
+  when: ansible_os_family == 'Debian'
+
+- name: copy provision_homedir template 
+  template: src=provision_homedir.py.j2 dest={{ provision_homedir }} mode=700 owner=root
+  sudo: true
+  run_once: true
+
+# the lockfile for makeing home directories should be located on the shared directory where the home directories will be created. Otherwise it will be racey
+- name: provision_homedir cron job 
+  cron: name=provision_homedir job="/usr/bin/flock -x -n /home/provision.lck -c {{ provision_homedir }}" user=root minute=*/30 state=present
+  sudo: true
diff --git a/roles/provision_homedir/tasks/main.yml~ b/roles/provision_homedir/tasks/main.yml~
new file mode 100644
index 00000000..af61122d
--- /dev/null
+++ b/roles/provision_homedir/tasks/main.yml~
@@ -0,0 +1,25 @@
+---
+- name: make  dir
+  file: path="{{ provision_homedir | dirname }}" state=directory mode=755 owner=root
+  sudo: true
+  run_once: true
+
+- name: install python packages
+  yum: name=python-ldap, state=installed
+  sudo: true
+  when: ansible_os_family == 'RedHat'
+
+- name: install python packages
+  apt: name=python-ldap, state=installed
+  sudo: true
+  when: ansible_os_family == 'Debian'
+
+- name: copy provision_homedir template 
+  template: src=provision_homedir.py.j2 dest={{ provision_homedir }} mode=700 owner=root
+  sudo: true
+  run_once: true
+
+# the lockfile for makeing home directories should be located on the shared directory where the home directories will be created. Otherwise it will be racey
+- name: provision_homedir cron job 
+  cron: name=provision_homedir job="/usr/bin/flock -x -n /home/provision.lck -c {{ provision_homedir }}" user=root minute=*/30 state=present
+  sudo: true
diff --git a/roles/provision_homedir/templates/provision_homedir.py.j2 b/roles/provision_homedir/templates/provision_homedir.py.j2
new file mode 100644
index 00000000..b601f099
--- /dev/null
+++ b/roles/provision_homedir/templates/provision_homedir.py.j2
@@ -0,0 +1,73 @@
+#!/usr/bin/python
+import ldap
+import traceback
+import os
+import stat
+
+class ldapSearchConfig:
+    def __init__(self):
+        self.ldapserver=""
+        self.binddn=""
+        self.bindpw=""
+        self.baseDN=""
+        self.searchFilter=""
+        self.cacertfile=''
+
+class genericUser:
+    def __init__(self):
+        self.dn=""
+        self.cn=""
+        self.entry=""
+	self.uid=""
+
+
+def get_users(server):
+#    ldap.set_option(ldap.OPT_X_TLS_CACERTFILE,server.cacertfile)
+    ldap.set_option( ldap.OPT_X_TLS_REQUIRE_CERT, ldap.OPT_X_TLS_NEVER )
+    l=ldap.initialize(server.ldapserver)
+    l.simple_bind_s(server.binddn,server.bindpw)
+    retrieveAttributes = ["*"]
+    searchScope=ldap.SCOPE_SUBTREE
+    try:
+        ldap_result_id = l.search(server.baseDN,searchScope,server.searchFilter,retrieveAttributes)
+    except ldap.LDAPError, e:
+        pass
+    rtype,rdata = l.result(ldap_result_id,1)
+    allusers={}
+    for user in rdata:
+        dn=user[0]
+        attrs=user[1]
+        allusers[dn]=genericUser()
+        allusers[dn].dn=dn
+        allusers[dn].entry=attrs
+    return allusers
+
+
+def mk_homedir(path,uidNumber,gidNumber):
+	try:
+		statinfo = os.stat(path)
+	except OSError as e:
+		if 'No such file or directory' in e:
+			os.mkdir(path,0700)
+	statinfo = os.stat(path)
+	if stat.S_ISDIR(statinfo.st_mode):
+		if statinfo.st_gid != gidNumber or statinfo.st_uid!=uidNumber:
+			os.chown(path,uidNumber,gidNumber)
+	else:
+		raise Exception("users homedirectory is not a directory %s"%path)
+
+s=ldapSearchConfig()
+s.ldapserver="{{ ldapURI }}"
+s.binddn="{{ ldapBindDN }}"
+s.bindpw="{{ ldapBindDNPassword }}"
+s.baseDN="{{ ldapBase }}"
+s.searchFilter = "{{ search_filter }}"
+homeDirEntry= "{{ homeDirEntry }}"
+
+users=get_users(s)
+for user in users:
+	try:
+		mk_homedir(users[user].entry[homeDirEntry][0],int(users[user].entry['uidNumber'][0]),int(users[user].entry['gidNumber'][0]))
+	except:
+		print traceback.format_exc()
+		pass
diff --git a/roles/provision_homedir/templates/provision_homedir.py.j2~ b/roles/provision_homedir/templates/provision_homedir.py.j2~
new file mode 100644
index 00000000..cbd50387
--- /dev/null
+++ b/roles/provision_homedir/templates/provision_homedir.py.j2~
@@ -0,0 +1,73 @@
+#!/usr/bin/python
+import ldap
+import traceback
+import os
+import stat
+
+class ldapSearchConfig:
+    def __init__(self):
+        self.ldapserver=""
+        self.binddn=""
+        self.bindpw=""
+        self.baseDN=""
+        self.searchFilter=""
+        self.cacertfile=''
+
+class genericUser:
+    def __init__(self):
+        self.dn=""
+        self.cn=""
+        self.entry=""
+	self.uid=""
+
+
+def get_users(server):
+#    ldap.set_option(ldap.OPT_X_TLS_CACERTFILE,server.cacertfile)
+    ldap.set_option( ldap.OPT_X_TLS_REQUIRE_CERT, ldap.OPT_X_TLS_NEVER )
+    l=ldap.initialize(server.ldapserver)
+    l.simple_bind_s(server.binddn,server.bindpw)
+    retrieveAttributes = ["*"]
+    searchScope=ldap.SCOPE_SUBTREE
+    try:
+        ldap_result_id = l.search(server.baseDN,searchScope,server.searchFilter,retrieveAttributes)
+    except ldap.LDAPError, e:
+        pass
+    rtype,rdata = l.result(ldap_result_id,1)
+    allusers={}
+    for user in rdata:
+        dn=user[0]
+        attrs=user[1]
+        allusers[dn]=genericUser()
+        allusers[dn].dn=dn
+        allusers[dn].entry=attrs
+    return allusers
+
+
+def mk_homedir(path,uidNumber,gidNumber):
+	try:
+		statinfo = os.stat(path)
+	except OSError as e:
+		if 'No such file or directory' in e:
+			os.mkdir(path,0700)
+	statinfo = os.stat(path)
+	if stat.S_ISDIR(statinfo.st_mode):
+		if statinfo.st_gid != gidNumber or statinfo.st_uid!=uidNumber:
+			os.chown(path,uidNumber,gidNumber)
+	else:
+		raise Exception("users homedirectory is not a directory %s"%path)
+
+s=ldapSearchConfig()
+s.ldapserver="{{ ldapURI }}"
+s.binddn="{{ ldapBindDN }}"
+s.bindpw="{{ ldapBindDNPassword }}"
+s.baseDN="{{ ldapBase }}"
+s.searchFilter = "{{ search_filter }}"
+homeDirEntry= "{{ homeDirEntry }}"
+
+users=get_users(s)
+for user in users:
+	try:
+		mk_homedir(users[user].entry['homeDirectory'][0],int(users[user].entry['uidNumber'][0]),int(users[user].entry['gidNumber'][0]))
+	except:
+		print traceback.format_exc()
+		pass
diff --git a/roles/provision_homedir/vars/main.yml b/roles/provision_homedir/vars/main.yml
new file mode 100644
index 00000000..f1b8ef6c
--- /dev/null
+++ b/roles/provision_homedir/vars/main.yml
@@ -0,0 +1,5 @@
+---
+use_active_directory: False
+provision_homedir: /usr/local/sbin/provision_homedir.py
+homeDirEntry: "{% if use_active_directory %}unixHomeDirectory{% else %}homeDirectory {% endif %}"
+search_filter: "{% if use_active_directory %}(unixHomeDirectory=*){% else %} (objectClass=posixAccount) {% endif %}"
diff --git a/roles/provision_homedir/vars/main.yml~ b/roles/provision_homedir/vars/main.yml~
new file mode 100644
index 00000000..e890945a
--- /dev/null
+++ b/roles/provision_homedir/vars/main.yml~
@@ -0,0 +1,5 @@
+---
+use_active_directory: False
+provision_homedir: /usr/local/sbin/provision_homedir.py
+homeDirEntry: "{% if use_active_directory %} unixHomeDirectory {% else %}homeDirectory {% endif %}"
+search_filter: "{% if use_active_directory %} (unixHomeDirectory=*) {% else %} (objectClass=posixAccount) {% endif %}"
diff --git a/roles/provision_slurm/tasks/main.yml b/roles/provision_slurm/tasks/main.yml
new file mode 100644
index 00000000..6d4e6aa9
--- /dev/null
+++ b/roles/provision_slurm/tasks/main.yml
@@ -0,0 +1,25 @@
+---
+- name: make  dir
+  file: path="{{ provision_slurm | dirname }}" state=directory mode=755 owner=root
+  sudo: true
+  run_once: true
+
+- name: install python packages
+  yum: name=python-ldap state=installed
+  sudo: true
+  when: ansible_os_family == 'RedHat'
+
+- name: install python packages
+  apt: name=python-ldap state=installed
+  sudo: true
+  when: ansible_os_family == 'Debian'
+
+- name: copy provision_slurm template 
+  template: src=provision_slurm.py.j2 dest={{ provision_slurm }} mode=700 owner=root
+  sudo: true
+  run_once: true
+
+# the lockfile for makeing home directories should be located on the shared directory where the home directories will be created. Otherwise it will be racey
+- name: provision_slurm cron job 
+  cron: name=provision_slurm job="/usr/bin/flock -x -n /home/provision.lck -c {{ provision_slurm }}" user=root minute=*/30 state=present
+  sudo: true
diff --git a/roles/provision_slurm/tasks/main.yml~ b/roles/provision_slurm/tasks/main.yml~
new file mode 100644
index 00000000..a057f44f
--- /dev/null
+++ b/roles/provision_slurm/tasks/main.yml~
@@ -0,0 +1,25 @@
+---
+- name: make  dir
+  file: path="{{ provision_homedir | dirname }}" state=directory mode=755 owner=root
+  sudo: true
+  run_once: true
+
+- name: install python packages
+  yum: name=python-ldap state=installed
+  sudo: true
+  when: ansible_os_family == 'RedHat'
+
+- name: install python packages
+  apt: name=python-ldap state=installed
+  sudo: true
+  when: ansible_os_family == 'Debian'
+
+- name: copy provision_homedir template 
+  template: src=provision_homedir.py.j2 dest={{ provision_homedir }} mode=700 owner=root
+  sudo: true
+  run_once: true
+
+# the lockfile for makeing home directories should be located on the shared directory where the home directories will be created. Otherwise it will be racey
+- name: provision_homedir cron job 
+  cron: name=provision_homedir job="/usr/bin/flock -x -n /home/provision.lck -c {{ provision_homedir }}" user=root minute=*/30 state=present
+  sudo: true
diff --git a/roles/provision_slurm/templates/provision_homedir.py.j2~ b/roles/provision_slurm/templates/provision_homedir.py.j2~
new file mode 100644
index 00000000..cbd50387
--- /dev/null
+++ b/roles/provision_slurm/templates/provision_homedir.py.j2~
@@ -0,0 +1,73 @@
+#!/usr/bin/python
+import ldap
+import traceback
+import os
+import stat
+
+class ldapSearchConfig:
+    def __init__(self):
+        self.ldapserver=""
+        self.binddn=""
+        self.bindpw=""
+        self.baseDN=""
+        self.searchFilter=""
+        self.cacertfile=''
+
+class genericUser:
+    def __init__(self):
+        self.dn=""
+        self.cn=""
+        self.entry=""
+	self.uid=""
+
+
+def get_users(server):
+#    ldap.set_option(ldap.OPT_X_TLS_CACERTFILE,server.cacertfile)
+    ldap.set_option( ldap.OPT_X_TLS_REQUIRE_CERT, ldap.OPT_X_TLS_NEVER )
+    l=ldap.initialize(server.ldapserver)
+    l.simple_bind_s(server.binddn,server.bindpw)
+    retrieveAttributes = ["*"]
+    searchScope=ldap.SCOPE_SUBTREE
+    try:
+        ldap_result_id = l.search(server.baseDN,searchScope,server.searchFilter,retrieveAttributes)
+    except ldap.LDAPError, e:
+        pass
+    rtype,rdata = l.result(ldap_result_id,1)
+    allusers={}
+    for user in rdata:
+        dn=user[0]
+        attrs=user[1]
+        allusers[dn]=genericUser()
+        allusers[dn].dn=dn
+        allusers[dn].entry=attrs
+    return allusers
+
+
+def mk_homedir(path,uidNumber,gidNumber):
+	try:
+		statinfo = os.stat(path)
+	except OSError as e:
+		if 'No such file or directory' in e:
+			os.mkdir(path,0700)
+	statinfo = os.stat(path)
+	if stat.S_ISDIR(statinfo.st_mode):
+		if statinfo.st_gid != gidNumber or statinfo.st_uid!=uidNumber:
+			os.chown(path,uidNumber,gidNumber)
+	else:
+		raise Exception("users homedirectory is not a directory %s"%path)
+
+s=ldapSearchConfig()
+s.ldapserver="{{ ldapURI }}"
+s.binddn="{{ ldapBindDN }}"
+s.bindpw="{{ ldapBindDNPassword }}"
+s.baseDN="{{ ldapBase }}"
+s.searchFilter = "{{ search_filter }}"
+homeDirEntry= "{{ homeDirEntry }}"
+
+users=get_users(s)
+for user in users:
+	try:
+		mk_homedir(users[user].entry['homeDirectory'][0],int(users[user].entry['uidNumber'][0]),int(users[user].entry['gidNumber'][0]))
+	except:
+		print traceback.format_exc()
+		pass
diff --git a/roles/provision_slurm/templates/provision_slurm.py.j2 b/roles/provision_slurm/templates/provision_slurm.py.j2
new file mode 100644
index 00000000..a8a6bca3
--- /dev/null
+++ b/roles/provision_slurm/templates/provision_slurm.py.j2
@@ -0,0 +1,76 @@
+#!/usr/bin/python
+import ldap
+import traceback
+import os
+import stat
+import subprocess
+
+class ldapSearchConfig:
+    def __init__(self):
+        self.ldapserver=""
+        self.binddn=""
+        self.bindpw=""
+        self.baseDN=""
+        self.searchFilter=""
+        self.cacertfile=''
+
+class genericUser:
+    def __init__(self):
+        self.dn=""
+        self.cn=""
+        self.entry=""
+	self.uid=""
+
+
+def get_users(server):
+#    ldap.set_option(ldap.OPT_X_TLS_CACERTFILE,server.cacertfile)
+    ldap.set_option( ldap.OPT_X_TLS_REQUIRE_CERT, ldap.OPT_X_TLS_NEVER )
+    l=ldap.initialize(server.ldapserver)
+    l.simple_bind_s(server.binddn,server.bindpw)
+    retrieveAttributes = ["*"]
+    searchScope=ldap.SCOPE_SUBTREE
+    try:
+        ldap_result_id = l.search(server.baseDN,searchScope,server.searchFilter,retrieveAttributes)
+    except ldap.LDAPError, e:
+        pass
+    rtype,rdata = l.result(ldap_result_id,1)
+    allusers={}
+    for user in rdata:
+        dn=user[0]
+        attrs=user[1]
+        allusers[dn]=genericUser()
+        allusers[dn].dn=dn
+        allusers[dn].entry=attrs
+    return allusers
+
+
+def mk_slurmaccount(acct):
+    output=subprocess.check_output(["{{ slurm_dir }}/bin/sacctmgr","--noheader","list","account",acct])
+    if acct in output:
+        return
+    else:
+        subprocess.call(["{{ slurm_dir }}/bin/sacctmgr","-i","create","account",acct])
+
+def mk_slurmuser(user,acct):
+    output=subprocess.check_output(["{{ slurm_dir }}/bin/sacctmgr","--noheader","list","Association","user=%s"%user,"format=account"])
+    if acct in output.splitlines():
+        return
+    else:
+        subprocess.call(["{{ slurm_dir }}/bin/sacctmgr","-i","create","user",user,"account=%s"%acct,"DefaultAccount=%s"%acct])
+
+
+s=ldapSearchConfig()
+s.ldapserver="{{ ldapURI }}"
+s.binddn="{{ ldapBindDN }}"
+s.bindpw="{{ ldapBindDNPassword }}"
+s.baseDN="{{ ldapBase }}"
+s.searchFilter = "{{ search_filter }}"
+
+users=get_users(s)
+mk_slurmaccount("default")
+for user in users:
+	try:
+                mk_slurmuser(users[user].entry['uid'][0],"default")
+	except:
+		print traceback.format_exc()
+		pass
diff --git a/roles/provision_slurm/templates/provision_slurm.py.j2~ b/roles/provision_slurm/templates/provision_slurm.py.j2~
new file mode 100644
index 00000000..3513e0dc
--- /dev/null
+++ b/roles/provision_slurm/templates/provision_slurm.py.j2~
@@ -0,0 +1,76 @@
+#!/usr/bin/python
+import ldap
+import traceback
+import os
+import stat
+import subprocess
+
+class ldapSearchConfig:
+    def __init__(self):
+        self.ldapserver=""
+        self.binddn=""
+        self.bindpw=""
+        self.baseDN=""
+        self.searchFilter=""
+        self.cacertfile=''
+
+class genericUser:
+    def __init__(self):
+        self.dn=""
+        self.cn=""
+        self.entry=""
+	self.uid=""
+
+
+def get_users(server):
+#    ldap.set_option(ldap.OPT_X_TLS_CACERTFILE,server.cacertfile)
+    ldap.set_option( ldap.OPT_X_TLS_REQUIRE_CERT, ldap.OPT_X_TLS_NEVER )
+    l=ldap.initialize(server.ldapserver)
+    l.simple_bind_s(server.binddn,server.bindpw)
+    retrieveAttributes = ["*"]
+    searchScope=ldap.SCOPE_SUBTREE
+    try:
+        ldap_result_id = l.search(server.baseDN,searchScope,server.searchFilter,retrieveAttributes)
+    except ldap.LDAPError, e:
+        pass
+    rtype,rdata = l.result(ldap_result_id,1)
+    allusers={}
+    for user in rdata:
+        dn=user[0]
+        attrs=user[1]
+        allusers[dn]=genericUser()
+        allusers[dn].dn=dn
+        allusers[dn].entry=attrs
+    return allusers
+
+
+def mk_slurmaccount(acct):
+    output=subprocess.check_output(["{{ slurm_dir }}/bin/sacctmgr","--noheader","list","account",acct])
+    if acct in output:
+        return
+    else:
+        subprocess.call(["{{ slurm_dir }}/bin/sacctmgr","-i","create","account",acct])
+
+def mk_slurmuser(user,acct):
+    output=subprocess.check_output(["{{ slurm_dir }}/bin/sacctmgr","--noheader","list","Association","user=%s"%user,"format=account"])
+    if acct in output.splitlines():
+        return
+    else:
+        subprocess.call(["{{ slurm_dir }}/bin/sacctmgr","-i","create","user",user,"account=%s"%acct])
+
+
+s=ldapSearchConfig()
+s.ldapserver="{{ ldapURI }}"
+s.binddn="{{ ldapBindDN }}"
+s.bindpw="{{ ldapBindDNPassword }}"
+s.baseDN="{{ ldapBase }}"
+s.searchFilter = "{{ search_filter }}"
+
+users=get_users(s)
+mk_slurmaccount("default")
+for user in users:
+	try:
+                mk_slurmuser(users[user].entry['uid'][0],"default")
+	except:
+		print traceback.format_exc()
+		pass
diff --git a/roles/provision_slurm/vars/main.yml b/roles/provision_slurm/vars/main.yml
new file mode 100644
index 00000000..2254544f
--- /dev/null
+++ b/roles/provision_slurm/vars/main.yml
@@ -0,0 +1,4 @@
+---
+use_active_directory: False
+provision_slurm: /usr/local/sbin/provision_slurm.py
+search_filter: "{% if use_active_directory %}(unixHomeDirectory=*){% else %} (objectClass=posixAccount) {% endif %}"
diff --git a/roles/provision_slurm/vars/main.yml~ b/roles/provision_slurm/vars/main.yml~
new file mode 100644
index 00000000..f1b8ef6c
--- /dev/null
+++ b/roles/provision_slurm/vars/main.yml~
@@ -0,0 +1,5 @@
+---
+use_active_directory: False
+provision_homedir: /usr/local/sbin/provision_homedir.py
+homeDirEntry: "{% if use_active_directory %}unixHomeDirectory{% else %}homeDirectory {% endif %}"
+search_filter: "{% if use_active_directory %}(unixHomeDirectory=*){% else %} (objectClass=posixAccount) {% endif %}"
diff --git a/roles/slurm-common/tasks/main.yml b/roles/slurm-common/tasks/main.yml
index 763702d6..57e71511 100644
--- a/roles/slurm-common/tasks/main.yml
+++ b/roles/slurm-common/tasks/main.yml
@@ -38,11 +38,21 @@
   sudo: true
   when: slurmpiddir is defined and not runstat.stat.exists
 
+- name: create shared state directory
+  file: path={{slurmsharedstatedir }} state=directory owner=slurm group=slurm mode=750
+  sudo: true
+  run_once: true
+  when: usesharedstatedir
+
+- name: symlink shared state dir
+  file: path={{ slurmstatedir }} src={{ slurmsharedstatedir }} state=link
+  sudo: true
+  when: usesharedstatedir
+
 - name: create state directory
   file: path={{ slurmstatedir }} state=directory owner=slurm group=slurm mode=750
-
   sudo: true
-  when: slurmstatedir is defined 
+  when: slurmstatedir is defined  and not usesharedstatedir
 
 - name: create log directory
   file: path={{ slurmlogdir }} state=directory owner=slurm group=slurm mode=750
@@ -129,11 +139,11 @@
   when: slurm_gres_list is defined
 
 - name: install slurm prolog
-  template: src=slurm.prolog.j2 dest={{ slurm_dir }}/bin/slurm.prolog
+  template: src=slurm.prolog.j2 dest={{ slurm_dir }}/bin/slurm.prolog mode=755
   sudo: true
 
 - name: install slurm epilog
-  template: src=slurm.epilog.j2 dest={{ slurm_dir }}/bin/slurm.epilog
+  template: src=slurm.epilog.j2 dest={{ slurm_dir }}/bin/slurm.epilog mode=755
   sudo: true
 
 - name: install slurm.conf
diff --git a/roles/strudel_config/templates/generic_slurm_config.json.j2 b/roles/strudel_config/templates/generic_slurm_config.json.j2
index 17ba6262..976557af 100644
--- a/roles/strudel_config/templates/generic_slurm_config.json.j2
+++ b/roles/strudel_config/templates/generic_slurm_config.json.j2
@@ -1,9 +1,12 @@
 [
     [
-        "GenericDesktops"
+{% for partition in slurmqueues %}
+        "{{ partition.name }}"{% if not loop.last %},{% endif %}
+{% endfor %}
     ],
     {
-        "GenericDesktops": {
+{% for partition in slurmqueues %}
+        "{{ partition.name }}": {
             "__class__": "siteConfig",
             "__module__": "siteConfig",
             "agent": {
@@ -268,7 +271,7 @@
                 "__class__": "cmdRegEx",
                 "__module__": "siteConfig",
                 "async": false,
-                "cmd": "\"mkdir ~/.vnc ; rm -f ~/.vnc/clearpass ; touch ~/.vnc/clearpass ; chmod 600 ~/.vnc/clearpass ; passwd=\"'$'\"( dd if=/dev/urandom bs=1 count=8 2>/dev/null | md5sum | cut -b 1-8 ) ; echo \"'$'\"passwd > ~/.vnc/clearpass ; cat ~/.vnc/clearpass | vncpasswd -f > ~/.vnc/passwd ; chmod 600 ~/.vnc/passwd ; echo -e '#!/bin/bash\\nvncserver ; sleep 36000000 ' |  {{slurm_dir}}/bin/sbatch -p batch -N {nodes} -n {ppn} --time={hours}:00:00 -J desktop_{username} -o .vnc/slurm-%j.out \"",
+                "cmd": "\"mkdir ~/.vnc ; rm -f ~/.vnc/clearpass ; touch ~/.vnc/clearpass ; chmod 600 ~/.vnc/clearpass ; passwd=\"'$'\"( dd if=/dev/urandom bs=1 count=8 2>/dev/null | md5sum | cut -b 1-8 ) ; echo \"'$'\"passwd > ~/.vnc/clearpass ; cat ~/.vnc/clearpass | vncpasswd -f > ~/.vnc/passwd ; chmod 600 ~/.vnc/passwd ; echo -e '#!/bin/bash\\nexport PATH=\"'$'\"PATH:/bin ; vncserver ; sleep 36000000 ' |  {{slurm_dir}}/bin/sbatch -p {{ partition.name }} -N {nodes} -n {ppn} --time={hours}:00:00 -J desktop_{username} -o .vnc/slurm-%j.out \"",
                 "failFatal": true,
                 "formatFatal": false,
                 "host": "login",
@@ -345,7 +348,7 @@
                 "host": "exec",
                 "loop": false,
                 "regex": [
-                    "^.*?New 'X' desktop is \\S+(?P<vncDisplay>:[0-9]+)\\s*$"
+                    "^.*?New .* desktop is \\S+(?P<vncDisplay>:[0-9]+)\\s*$"
                 ],
                 "requireMatch": true
             },
@@ -447,6 +450,7 @@
                 ],
                 "requireMatch": true
             }
-        }
+        }{% if not loop.last %},{% endif %}
+{% endfor %}
     }
 ]
diff --git a/roles/vncserver/tasks/main.yml b/roles/vncserver/tasks/main.yml
index 59b754ea..e3ee008c 100644
--- a/roles/vncserver/tasks/main.yml
+++ b/roles/vncserver/tasks/main.yml
@@ -35,8 +35,24 @@
   with_items: system_packages
   when: ansible_os_family == 'Debian'
 
+- name: yum group installs
+  shell: yum -y group install {{ item }}
+  sudo: true
+  with_items: system_group_packages
+  when: system_group_packages is defined
+
 - name: install system packages yum
   yum: name={{ item }} state=installed update_cache=yes
   sudo: true
   with_items: system_packages
   when: ansible_os_family == 'RedHat'
+
+- name: force the use of mate desktop
+  template: src=vncserver.centos dest=/bin/vncserver
+  sudo: true
+  when: ansible_os_family == 'RedHat'
+
+- name: force the use of mate desktop
+  template: src=vncserver.ubuntu dest=/usr/bin/vncserver
+  sudo: true
+  when: ansible_os_family == 'Debian'
diff --git a/roles/vncserver/templates/vncserver.centos b/roles/vncserver/templates/vncserver.centos
new file mode 100755
index 00000000..e8d71f85
--- /dev/null
+++ b/roles/vncserver/templates/vncserver.centos
@@ -0,0 +1,772 @@
+#!/usr/bin/perl
+#
+#  Copyright (C) 2009-2010 D. R. Commander.  All Rights Reserved.
+#  Copyright (C) 2005-2006 Sun Microsystems, Inc.  All Rights Reserved.
+#  Copyright (C) 2002-2003 Constantin Kaplinsky.  All Rights Reserved.
+#  Copyright (C) 2002-2005 RealVNC Ltd.
+#  Copyright (C) 1999 AT&T Laboratories Cambridge.  All Rights Reserved.
+#
+#  This is free software; you can redistribute it and/or modify
+#  it under the terms of the GNU General Public License as published by
+#  the Free Software Foundation; either version 2 of the License, or
+#  (at your option) any later version.
+#
+#  This software is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+#
+#  You should have received a copy of the GNU General Public License
+#  along with this software; if not, write to the Free Software
+#  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+#  USA.
+#
+
+#
+# vncserver - wrapper script to start an X VNC server.
+#
+
+#
+# First make sure we're operating in a sane environment.
+#
+
+$exedir = "";
+$slashndx = rindex($0, "/");
+if($slashndx>=0) {
+    $exedir = substr($0, 0, $slashndx+1);
+}
+
+$vncClasses = "";
+
+$xauth = "xauth";
+
+&SanityCheck();
+
+#
+# Global variables.  You may want to configure some of these for your site.
+#
+
+$geometry = "1024x768";
+#$depth = 16;
+$vncJavaFiles = (((-d "$vncClasses") && "$vncClasses") ||
+                 ((-d "/usr/share/vnc/classes") && "/usr/share/vnc/classes") ||
+                 ((-d "/usr/local/vnc/classes") && "/usr/local/vnc/classes"));
+                 
+$vncUserDir = "$ENV{HOME}/.vnc";
+$xauthorityFile = "$ENV{XAUTHORITY}" || "$ENV{HOME}/.Xauthority";
+
+$defaultXStartup
+    = ("#!/bin/sh\n\n".
+       "unset SESSION_MANAGER\n".
+       "unset DBUS_SESSION_BUS_ADDRESS\n".
+       "exec mate-session\n");
+
+chop($host = `uname -n`);
+
+if (-d "/etc/X11/fontpath.d") {
+    $fontPath = "catalogue:/etc/X11/fontpath.d";
+}
+
+@fontpaths = ('/usr/share/X11/fonts', '/usr/share/fonts', '/usr/share/fonts/X11/');
+if (! -l "/usr/lib/X11") {push(@fontpaths, '/usr/lib/X11/fonts');}
+if (! -l "/usr/X11") {push(@fontpaths, '/usr/X11/lib/X11/fonts');}
+if (! -l "/usr/X11R6") {push(@fontpaths, '/usr/X11R6/lib/X11/fonts');}
+push(@fontpaths, '/usr/share/fonts/default');
+
+@fonttypes = ('misc',
+             '75dpi',
+             '100dpi',
+             'Speedo',
+             'Type1');
+
+foreach $_fpath (@fontpaths) {
+    foreach $_ftype (@fonttypes) {
+        if (-f "$_fpath/$_ftype/fonts.dir") {
+            if (! -l "$_fpath/$_ftype") {
+                $defFontPath .= "$_fpath/$_ftype,";
+            }
+        }
+    }
+}
+
+if ($defFontPath) {
+    if (substr($defFontPath, -1, 1) == ',') {
+        chop $defFontPath;
+    }
+}
+
+if ($fontPath eq "") {
+    $fontPath = $defFontPath;
+}
+
+# Check command line options
+
+&ParseOptions("-geometry",1,"-depth",1,"-pixelformat",1,"-name",1,"-kill",1,
+	      "-help",0,"-h",0,"--help",0,"-fp",1,"-list",0,"-fg",0,"-autokill",0);
+
+&Usage() if ($opt{'-help'} || $opt{'-h'} || $opt{'--help'});
+
+&Kill() if ($opt{'-kill'});
+
+&List() if ($opt{'-list'});
+
+# Uncomment this line if you want default geometry, depth and pixelformat
+# to match the current X display:
+# &GetXDisplayDefaults();
+
+if ($opt{'-geometry'}) {
+    $geometry = $opt{'-geometry'};
+}
+if ($opt{'-depth'}) {
+    $depth = $opt{'-depth'};
+    $pixelformat = "";
+}
+if ($opt{'-pixelformat'}) {
+    $pixelformat = $opt{'-pixelformat'};
+}
+if ($opt{'-fp'}) {
+    $fontPath = $opt{'-fp'};
+    $fpArgSpecified = 1;
+}
+
+&CheckGeometryAndDepth();
+
+
+# Create the user's vnc directory if necessary.
+
+if (!(-e $vncUserDir)) {
+    if (!mkdir($vncUserDir,0755)) {
+	die "$prog: Could not create $vncUserDir.\n";
+    }
+}
+    
+# Check whether VNC authentication is enabled, and if so, prompt the user to
+# create a VNC password if they don't already have one.
+
+$securityTypeArgSpecified = 0;
+$vncAuthEnabled = 0;
+$passwordArgSpecified = 0;
+
+for ($i = 0; $i < @ARGV; ++$i) {
+    # -SecurityTypes can be followed by a space or "="
+    my @splitargs = split('=', $ARGV[$i]);
+    if (@splitargs <= 1 && $i < @ARGV - 1) {
+        push(@splitargs, $ARGV[$i + 1]);
+    }
+    if (lc(@splitargs[0]) eq "-securitytypes") {
+        if (@splitargs > 1) {
+            $securityTypeArgSpecified = 1;
+        }
+        foreach $arg2 (split(',', @splitargs[1])) {
+            if (lc($arg2) eq "vncauth" || lc($arg2) eq "tlsvnc"
+             || lc($arg2) eq "x509vnc") {
+                $vncAuthEnabled = 1;
+            }
+        }
+    }
+    if ((lc(@splitargs[0]) eq "-password")
+     || (lc(@splitargs[0]) eq "-passwordfile"
+     || (lc(@splitargs[0]) eq "-rfbauth"))) {
+        $passwordArgSpecified = 1;
+    }
+}
+
+if ((!$securityTypeArgSpecified || $vncAuthEnabled) && !$passwordArgSpecified) {
+    ($z,$z,$mode) = stat("$vncUserDir/passwd");
+    if (!(-e "$vncUserDir/passwd") || ($mode & 077)) {
+        warn "\nYou will require a password to access your desktops.\n\n";
+        system($exedir."vncpasswd -q $vncUserDir/passwd");
+        if (($? >> 8) != 0) {
+            exit 1;
+        }
+    }
+}
+ 
+# Find display number.
+
+if ((@ARGV > 0) && ($ARGV[0] =~ /^:(\d+)$/)) {
+    $displayNumber = $1;
+    shift(@ARGV);
+    if (!&CheckDisplayNumber($displayNumber)) {
+	die "A VNC server is already running as :$displayNumber\n";
+    }
+} elsif ((@ARGV > 0) && ($ARGV[0] !~ /^-/) && ($ARGV[0] !~ /^\+/)) {
+    &Usage();
+} else {
+    $displayNumber = &GetDisplayNumber();
+}
+
+$vncPort = 5900 + $displayNumber;
+
+$desktopLog = "$vncUserDir/$host:$displayNumber.log";
+unlink($desktopLog);
+
+# Make an X server cookie - use mcookie
+$cookie = `/usr/bin/mcookie`;
+open (XAUTH, "|xauth -f $xauthorityFile source -");
+print XAUTH "add $host:$displayNumber . $cookie\n";
+print XAUTH "add $host/unix:$displayNumber . $cookie\n";
+close XAUTH;
+
+if ($opt{'-name'}) {
+    $desktopName = $opt{'-name'};
+} else {
+    $desktopName = "$host:$displayNumber ($ENV{USER})";
+}
+
+# Now start the X VNC Server
+
+$cmd = $exedir."Xvnc :$displayNumber";
+$cmd .= " -desktop " . &quotedString($desktopName);
+$cmd .= " -httpd $vncJavaFiles" if ($vncJavaFiles);
+$cmd .= " -auth $xauthorityFile";
+$cmd .= " -geometry $geometry" if ($geometry);
+$cmd .= " -depth $depth" if ($depth);
+$cmd .= " -pixelformat $pixelformat" if ($pixelformat);
+$cmd .= " -rfbwait 30000";
+$cmd .= " -rfbauth $vncUserDir/passwd";
+$cmd .= " -rfbport $vncPort";
+$cmd .= " -fp $fontPath" if ($fontPath);
+$cmd .= " -pn";
+
+# Add color database stuff here, e.g.:
+#
+# $cmd .= " -co /usr/lib/X11/rgb";
+#
+
+foreach $arg (@ARGV) {
+    $cmd .= " " . &quotedString($arg);
+}
+$cmd .= " >> " . &quotedString($desktopLog) . " 2>&1";
+
+# Run $cmd and record the process ID.
+
+$pidFile = "$vncUserDir/$host:$displayNumber.pid";
+system("$cmd & echo \$! >$pidFile");
+
+# Give Xvnc a chance to start up
+
+sleep(3); 
+if ($fontPath ne $defFontPath) {
+    unless (kill 0, `cat $pidFile`) {
+        if ($fpArgSpecified) {
+	    warn "\nWARNING: The first attempt to start Xvnc failed, probably because the font\n";
+	    warn "path you specified using the -fp argument is incorrect.  Attempting to\n";
+	    warn "determine an appropriate font path for this system and restart Xvnc using\n";
+	    warn "that font path ...\n";
+        } else {
+	    warn "\nWARNING: The first attempt to start Xvnc failed, possibly because the font\n";
+	    warn "catalog is not properly configured.  Attempting to determine an appropriate\n";
+	    warn "font path for this system and restart Xvnc using that font path ...\n";
+        }
+	$cmd =~ s@-fp [^ ]+@@;
+	$cmd .= " -fp $defFontPath" if ($defFontPath);
+	system("$cmd & echo \$! >$pidFile");
+	sleep(3);
+    }
+}
+unless (kill 0, `cat $pidFile`) {
+    warn "Could not start Xvnc.\n\n";
+    open(LOG, "<$desktopLog");
+    while (<LOG>) { print; }
+    close(LOG);
+    die "\n";
+}
+
+warn "\nNew '$desktopName' desktop is $host:$displayNumber\n\n";
+
+# Create the user's xstartup script if necessary.
+
+if (!(-e "$vncUserDir/xstartup")) {
+    warn "Creating default startup script $vncUserDir/xstartup\n";
+    open(XSTARTUP, ">$vncUserDir/xstartup");
+    print XSTARTUP $defaultXStartup;
+    close(XSTARTUP);
+    chmod 0755, "$vncUserDir/xstartup";
+}
+
+# Run the X startup script.
+
+warn "Starting applications specified in $vncUserDir/xstartup\n";
+warn "Log file is $desktopLog\n\n";
+
+# If the unix domain socket exists then use that (DISPLAY=:n) otherwise use
+# TCP (DISPLAY=host:n)
+
+if (-e "/tmp/.X11-unix/X$displayNumber" ||
+    -e "/usr/spool/sockets/X11/$displayNumber")
+{
+    $ENV{DISPLAY}= ":$displayNumber";
+} else {
+    $ENV{DISPLAY}= "$host:$displayNumber";
+}
+$ENV{VNCDESKTOP}= $desktopName;
+
+system($exedir."vncconfig -iconic >> " . &quotedString($desktopLog) . " 2>&1 &");
+
+if ($opt{'-fg'}) {
+    system("$vncUserDir/xstartup >> " . &quotedString($desktopLog) . " 2>&1");
+    if (kill 0, `cat $pidFile`) {
+        $opt{'-kill'} = ':'.$displayNumber;
+        &Kill();
+    }
+} else {
+    if ($opt{'-autokill'}) {
+        system("($vncUserDir/xstartup; $0 -kill :$displayNumber) >> "
+            . &quotedString($desktopLog) . " 2>&1 &");
+    } else {
+        system("$vncUserDir/xstartup >> " . &quotedString($desktopLog)
+            . " 2>&1 &");
+    }
+}
+
+exit;
+
+
+###############################################################################
+#
+# CheckGeometryAndDepth simply makes sure that the geometry and depth values
+# are sensible.
+#
+
+sub CheckGeometryAndDepth
+{
+    if ($geometry =~ /^(\d+)x(\d+)$/) {
+	$width = $1; $height = $2;
+
+	if (($width<1) || ($height<1)) {
+	    die "$prog: geometry $geometry is invalid\n";
+	}
+
+	$geometry = "${width}x$height";
+    } else {
+	die "$prog: geometry $geometry is invalid\n";
+    }
+
+    if ($depth && (($depth < 8) || ($depth > 32))) {
+	die "Depth must be between 8 and 32\n";
+    }
+}
+
+
+#
+# GetDisplayNumber gets the lowest available display number.  A display number
+# n is taken if something is listening on the VNC server port (5900+n) or the
+# X server port (6000+n).
+#
+
+sub GetDisplayNumber
+{
+    foreach $n (1..99) {
+	if (&CheckDisplayNumber($n)) {
+	    return $n+0; # Bruce Mah's workaround for bug in perl 5.005_02
+	}
+    }
+    
+    die "$prog: no free display number on $host.\n";
+}
+
+
+#
+# CheckDisplayNumber checks if the given display number is available.  A
+# display number n is taken if something is listening on the VNC server port
+# (5900+n) or the X server port (6000+n).
+#
+
+sub CheckDisplayNumber
+{
+    local ($n) = @_;
+
+    socket(S, $AF_INET, $SOCK_STREAM, 0) || die "$prog: socket failed: $!\n";
+    eval 'setsockopt(S, &SOL_SOCKET, &SO_REUSEADDR, pack("l", 1))';
+    if (!bind(S, pack('S n x12', $AF_INET, 6000 + $n))) {
+	close(S);
+	return 0;
+    }
+    close(S);
+
+    socket(S, $AF_INET, $SOCK_STREAM, 0) || die "$prog: socket failed: $!\n";
+    eval 'setsockopt(S, &SOL_SOCKET, &SO_REUSEADDR, pack("l", 1))';
+    if (!bind(S, pack('S n x12', $AF_INET, 5900 + $n))) {
+	close(S);
+	return 0;
+    }
+    close(S);
+
+    if (-e "/tmp/.X$n-lock") {
+	warn "\nWarning: $host:$n is taken because of /tmp/.X$n-lock\n";
+	warn "Remove this file if there is no X server $host:$n\n";
+	return 0;
+    }
+
+    if (-e "/tmp/.X11-unix/X$n") {
+	warn "\nWarning: $host:$n is taken because of /tmp/.X11-unix/X$n\n";
+	warn "Remove this file if there is no X server $host:$n\n";
+	return 0;
+    }
+
+    if (-e "/usr/spool/sockets/X11/$n") {
+	warn("\nWarning: $host:$n is taken because of ".
+             "/usr/spool/sockets/X11/$n\n");
+	warn "Remove this file if there is no X server $host:$n\n";
+	return 0;
+    }
+
+    return 1;
+}
+
+
+#
+# GetXDisplayDefaults uses xdpyinfo to find out the geometry, depth and pixel
+# format of the current X display being used.  If successful, it sets the
+# options as appropriate so that the X VNC server will use the same settings
+# (minus an allowance for window manager decorations on the geometry).  Using
+# the same depth and pixel format means that the VNC server won't have to
+# translate pixels when the desktop is being viewed on this X display (for
+# TrueColor displays anyway).
+#
+
+sub GetXDisplayDefaults
+{
+    local (@lines, @matchlines, $width, $height, $defaultVisualId, $i,
+	   $red, $green, $blue);
+
+    $wmDecorationWidth = 4;	# a guess at typical size for window manager
+    $wmDecorationHeight = 24;	# decoration size
+
+    return if (!defined($ENV{DISPLAY}));
+
+    @lines = `xdpyinfo 2>/dev/null`;
+
+    return if ($? != 0);
+
+    @matchlines = grep(/dimensions/, @lines);
+    if (@matchlines) {
+	($width, $height) = ($matchlines[0] =~ /(\d+)x(\d+) pixels/);
+
+	$width -= $wmDecorationWidth;
+	$height -= $wmDecorationHeight;
+
+	$geometry = "${width}x$height";
+    }
+
+    @matchlines = grep(/default visual id/, @lines);
+    if (@matchlines) {
+	($defaultVisualId) = ($matchlines[0] =~ /id:\s+(\S+)/);
+
+	for ($i = 0; $i < @lines; $i++) {
+	    if ($lines[$i] =~ /^\s*visual id:\s+$defaultVisualId$/) {
+		if (($lines[$i+1] !~ /TrueColor/) ||
+		    ($lines[$i+2] !~ /depth/) ||
+		    ($lines[$i+4] !~ /red, green, blue masks/))
+		{
+		    return;
+		}
+		last;
+	    }
+	}
+
+	return if ($i >= @lines);
+
+	($depth) = ($lines[$i+2] =~ /depth:\s+(\d+)/);
+	($red,$green,$blue)
+	    = ($lines[$i+4]
+	       =~ /masks:\s+0x([0-9a-f]+), 0x([0-9a-f]+), 0x([0-9a-f]+)/);
+
+	$red = hex($red);
+	$green = hex($green);
+	$blue = hex($blue);
+
+	if ($red > $blue) {
+	    $red = int(log($red) / log(2)) - int(log($green) / log(2));
+	    $green = int(log($green) / log(2)) - int(log($blue) / log(2));
+	    $blue = int(log($blue) / log(2)) + 1;
+	    $pixelformat = "rgb$red$green$blue";
+	} else {
+	    $blue = int(log($blue) / log(2)) - int(log($green) / log(2));
+	    $green = int(log($green) / log(2)) - int(log($red) / log(2));
+	    $red = int(log($red) / log(2)) + 1;
+	    $pixelformat = "bgr$blue$green$red";
+	}
+    }
+}
+
+
+#
+# quotedString returns a string which yields the original string when parsed
+# by a shell.
+#
+
+sub quotedString
+{
+    local ($in) = @_;
+
+    $in =~ s/\'/\'\"\'\"\'/g;
+
+    return "'$in'";
+}
+
+
+#
+# removeSlashes turns slashes into underscores for use as a file name.
+#
+
+sub removeSlashes
+{
+    local ($in) = @_;
+
+    $in =~ s|/|_|g;
+
+    return "$in";
+}
+
+
+#
+# Usage
+#
+
+sub Usage
+{
+    die("\nusage: $prog [:<number>] [-name <desktop-name>] [-depth <depth>]\n".
+	"                 [-geometry <width>x<height>]\n".
+	"                 [-pixelformat rgbNNN|bgrNNN]\n".
+	"                 [-fp <font-path>]\n".
+	"                 [-cc <visual>]\n".
+	"                 [-fg]\n".
+	"                 [-autokill]\n".
+	"                 <Xvnc-options>...\n\n".
+	"       $prog -kill <X-display>\n\n".
+	"       $prog -list\n\n");
+}
+
+
+#
+# List
+#
+
+sub List
+{
+    opendir(dir, $vncUserDir);
+    my @filelist = readdir(dir);
+    closedir(dir);
+    print "\nTigerVNC server sessions:\n\n";
+    print "X DISPLAY #\tPROCESS ID\n";
+    foreach my $file (@filelist) {
+	if ($file =~ /$host:(\d+)$\.pid/) {
+	    print ":".$1."\t\t".`cat $vncUserDir/$file`;
+	}
+    }
+    exit 1;
+}
+
+
+#
+# Kill
+#
+
+sub Kill
+{
+    $opt{'-kill'} =~ s/(:\d+)\.\d+$/$1/; # e.g. turn :1.0 into :1
+
+    if ($opt{'-kill'} =~ /^:\d+$/) {
+	$pidFile = "$vncUserDir/$host$opt{'-kill'}.pid";
+    } else {
+	if ($opt{'-kill'} !~ /^$host:/) {
+	    die "\nCan't tell if $opt{'-kill'} is on $host\n".
+		"Use -kill :<number> instead\n\n";
+	}
+	$pidFile = "$vncUserDir/$opt{'-kill'}.pid";
+    }
+
+    if (! -r $pidFile) {
+	die "\nCan't find file $pidFile\n".
+	    "You'll have to kill the Xvnc process manually\n\n";
+    }
+
+    $SIG{'HUP'} = 'IGNORE';
+    chop($pid = `cat $pidFile`);
+    warn "Killing Xvnc process ID $pid\n";
+
+    if (kill 0, $pid) {
+	system("kill $pid");
+	sleep(1);
+	if (kill 0, $pid) {
+	    print "Xvnc seems to be deadlocked.  Kill the process manually and then re-run\n";
+	    print "    ".$0." -kill ".$opt{'-kill'}."\n";
+	    print "to clean up the socket files.\n";
+	    exit
+	}
+
+    } else {
+	warn "Xvnc process ID $pid already killed\n";
+	$opt{'-kill'} =~ s/://;
+    
+	if (-e "/tmp/.X11-unix/X$opt{'-kill'}") {
+	    print "Xvnc did not appear to shut down cleanly.";
+	    print " Removing /tmp/.X11-unix/X$opt{'-kill'}\n";
+	    unlink "/tmp/.X11-unix/X$opt{'-kill'}";
+	}
+	if (-e "/tmp/.X$opt{'-kill'}-lock") {
+	    print "Xvnc did not appear to shut down cleanly.";
+	    print " Removing /tmp/.X$opt{'-kill'}-lock\n";
+	    unlink "/tmp/.X$opt{'-kill'}-lock";
+	}
+    }
+
+    unlink $pidFile;
+    exit;
+}
+
+
+#
+# ParseOptions takes a list of possible options and a boolean indicating
+# whether the option has a value following, and sets up an associative array
+# %opt of the values of the options given on the command line. It removes all
+# the arguments it uses from @ARGV and returns them in @optArgs.
+#
+
+sub ParseOptions
+{
+    local (@optval) = @_;
+    local ($opt, @opts, %valFollows, @newargs);
+
+    while (@optval) {
+	$opt = shift(@optval);
+	push(@opts,$opt);
+	$valFollows{$opt} = shift(@optval);
+    }
+
+    @optArgs = ();
+    %opt = ();
+
+    arg: while (defined($arg = shift(@ARGV))) {
+	foreach $opt (@opts) {
+	    if ($arg eq $opt) {
+		push(@optArgs, $arg);
+		if ($valFollows{$opt}) {
+		    if (@ARGV == 0) {
+			&Usage();
+		    }
+		    $opt{$opt} = shift(@ARGV);
+		    push(@optArgs, $opt{$opt});
+		} else {
+		    $opt{$opt} = 1;
+		}
+		next arg;
+	    }
+	}
+	push(@newargs,$arg);
+    }
+
+    @ARGV = @newargs;
+}
+
+
+#
+# Routine to make sure we're operating in a sane environment.
+#
+
+sub SanityCheck
+{
+    local ($cmd);
+
+    #
+    # Get the program name
+    #
+
+    ($prog) = ($0 =~ m|([^/]+)$|);
+
+    #
+    # Check we have all the commands we'll need on the path.
+    #
+
+ cmd:
+    foreach $cmd ("uname") {
+	for (split(/:/,$ENV{PATH})) {
+	    if (-x "$_/$cmd") {
+		next cmd;
+	    }
+	}
+	die "$prog: couldn't find \"$cmd\" on your PATH.\n";
+    }
+    if (-x "/usr/X11R6/bin/xauth") {
+	$xauth = "/usr/X11R6/bin/xauth";
+    }
+    else {
+      cmd1:
+	foreach $cmd ("xauth") {
+	    for (split(/:/,$ENV{PATH})) {
+		if (-x "$_/$cmd") {
+		    next cmd1;
+		}
+	    }
+	    die "$prog: couldn't find \"$cmd\" on your PATH.\n";
+	}
+    }
+
+    if($exedir eq "") {
+      cmd2:
+	foreach $cmd ("Xvnc","vncpasswd") {
+	    for (split(/:/,$ENV{PATH})) {
+		if (-x "$_/$cmd") {
+		    $vncClasses = "$_/../vnc/classes";
+		    next cmd2;
+		}
+	    }
+	    die "$prog: couldn't find \"$cmd\" on your PATH.\n";
+	}
+    }
+    else {
+      cmd3:
+	foreach $cmd ($exedir."Xvnc",$exedir."vncpasswd") {
+	    for (split(/:/,$ENV{PATH})) {
+		if (-x "$cmd") {
+		    $vncClasses = $exedir."../vnc/classes";
+		    next cmd3;
+		}
+	    }
+	    die "$prog: couldn't find \"$cmd\".\n";
+	}
+    }
+
+    #
+    # Check the HOME environment variable is set
+    #
+
+    if (!defined($ENV{HOME})) {
+	die "$prog: The HOME environment variable is not set.\n";
+    }
+#    chdir($ENV{HOME});
+
+    #
+    # Find socket constants. 'use Socket' is a perl5-ism, so we wrap it in an
+    # eval, and if it fails we try 'require "sys/socket.ph"'.  If this fails,
+    # we just guess at the values.  If you find perl moaning here, just
+    # hard-code the values of AF_INET and SOCK_STREAM.  You can find these out
+    # for your platform by looking in /usr/include/sys/socket.h and related
+    # files.
+    #
+
+    chop($os = `uname`);
+    chop($osrev = `uname -r`);
+
+    eval 'use Socket';
+    if ($@) {
+	eval 'require "sys/socket.ph"';
+	if ($@) {
+	    if (($os eq "SunOS") && ($osrev !~ /^4/)) {
+		$AF_INET = 2;
+		$SOCK_STREAM = 2;
+	    } else {
+		$AF_INET = 2;
+		$SOCK_STREAM = 1;
+	    }
+	} else {
+	    $AF_INET = &AF_INET;
+	    $SOCK_STREAM = &SOCK_STREAM;
+	}
+    } else {
+	$AF_INET = &AF_INET;
+	$SOCK_STREAM = &SOCK_STREAM;
+    }
+}
diff --git a/roles/vncserver/templates/vncserver.ubuntu b/roles/vncserver/templates/vncserver.ubuntu
new file mode 100755
index 00000000..4f814a59
--- /dev/null
+++ b/roles/vncserver/templates/vncserver.ubuntu
@@ -0,0 +1,772 @@
+#!/usr/bin/perl
+#
+#  Copyright (C) 2004-2006,2008-2009 Ola Lundqvist <opal@debian.org>
+#  Copyright (C) 2002-2003 Constantin Kaplinsky.  All Rights Reserved.
+#  Copyright (C) 1999 AT&T Laboratories Cambridge.  All Rights Reserved.
+#
+#  This is free software; you can redistribute it and/or modify
+#  it under the terms of the GNU General Public License as published by
+#  the Free Software Foundation; either version 2 of the License, or
+#  (at your option) any later version.
+#
+#  This software is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+#
+#  You should have received a copy of the GNU General Public License
+#  along with this software; if not, write to the Free Software
+#  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+#  USA.
+#
+
+#
+# vncserver - wrapper script to start an X VNC server.
+#
+
+# This file was heavily edited by
+#              Ola Lundqvist <opal@debian.org>
+#               Marcus Brinkmann <Marcus.Brinkmann@ruhr-uni-bochum.de>
+# Clean option by Dirk Eddelbuettel <edd@debian.org>
+
+# Please report all errors to Debian and not to ORL.
+
+#
+# First make sure we're operating in a sane environment.
+#
+
+&SanityCheck();
+
+#
+# Global variables.  You may want to configure some of these for your site.
+#
+
+$geometry = "1024x768";
+$depth = 24;
+$desktopName = "X";
+if (-d "/usr/share/tightvnc-java") {
+    $vncClasses = "/usr/share/tightvnc-java";
+}
+$vncUserDir = "$ENV{HOME}/.vnc";
+#$fontPath = "unix/:7100";
+$authType = "-rfbauth $vncUserDir/passwd";
+
+# Here is another example of setting the font path:
+# $fontPath = "/usr/lib/X11/fonts/misc/,/usr/lib/X11/fonts/75dpi/";
+
+# X colors database path is optional, uncomment and edit to use:
+# $colorPath = "/usr/lib/X11/rgb";
+
+# You might wish to make your vnc directory under /tmp, to make sure
+# passwords are always kept on the local filesystem. To do that, just
+# uncomment the line below. Note that in this case Xtightvnc's .Xauthority
+# file will be searched in the same $vncUserDir directory by default,
+# and $ENV{HOME}/.vncstartup will be executed instead of usual
+# $vncUserDir/xstartup.
+#
+# $vncUserDir = "/tmp/$ENV{USER}-vnc";
+
+$defaultXStartup
+    = ("#!/bin/sh\n\n".
+       "xrdb \$HOME/.Xresources\n".
+       "xsetroot -solid grey\n".
+       "#x-terminal-emulator -geometry 80x24+10+10 -ls -title \"\$VNCDESKTOP Desktop\" &\n".
+       "#x-window-manager &\n".
+       "# Fix to make GNOME work\n".
+       "export XKL_XMODMAP_DISABLE=1\n".
+       "exec mate-session\n");
+
+$xauthorityFile = "$ENV{XAUTHORITY}";
+
+######## Adding configuration possibility ################
+$Config_file = "/etc/vnc.conf";
+&ReadConfigFile();
+$Config_file = "$ENV{HOME}/.vncrc";
+&ReadConfigFile();
+
+if (!$XFConfigPath) {
+  foreach ("/etc/X11/xorg.conf", "/etc/X11/XF86Config-4", "/etc/X11/XF86Config" ){
+      $XFConfigPath = $_;
+      last if ( -e $XFConfigPath );
+  }
+}
+if (!$fontPath) {
+  &ReadXFConfigFont;
+}
+if (!$fontPath) {
+  $fontPath = "/usr/share/fonts/X11/misc/,".
+              "/usr/share/fonts/X11/Type1/,".
+              "/usr/share/fonts/X11/75dpi/,".
+              "/usr/share/fonts/X11/100dpi/"
+}
+if (!$colorPath) {
+  &ReadXFConfigColor;
+}
+if (!$colorPath) {
+  foreach ("/etc/X11/rgb", "/usr/share/X11/rgb", "/usr/X11R6/lib/X11/rgb"){
+      $colorPath = $_;
+      last if ( -e "${colorPath}.txt" );
+  }
+}
+##########################################################
+
+$vncUserDirUnderTmp = ($vncUserDir =~ m|^/tmp/.+|) ? 1 : 0;
+$xstartup = ($vncUserDirUnderTmp) ?
+  "$ENV{HOME}/.vncstartup" : "$vncUserDir/xstartup";
+$xstartup = $vncStartup if ($vncStartup);
+unless ($xauthorityFile) {
+    if ($vncUserDirUnderTmp) {
+        $xauthorityFile = "$vncUserDir/.Xauthority";
+    } else {
+        $xauthorityFile = "$ENV{HOME}/.Xauthority";
+    }
+}
+
+chop($host = `uname -n`);
+
+
+# Check command line options
+
+&ParseOptions("-geometry",1,"-depth",1,"-pixelformat",1,"-name",1,"-kill",1,
+	      "-help",0,"-h",0,"--help",0,
+	      "-clean",0, "-fp",1,
+              "-alwaysshared",0, "-nevershared",0,
+              "-httpport",1,"-basehttpport",1);
+
+&Usage() if ($opt{'-help'} || $opt{'-h'} || $opt{'--help'});
+
+&Kill() if ($opt{'-kill'});
+
+$useClasses = 0;
+if (defined $vncClasses) {
+  if(! -d $vncClasses) {
+    die "VNC class files can not be found at $vncClasses.";
+  }
+  $useClasses = 1;
+}
+
+# Uncomment this line if you want default geometry, depth and pixelformat
+# to match the current X display:
+# &GetXDisplayDefaults();
+
+if ($opt{'-geometry'}) {
+    $geometry = $opt{'-geometry'};
+}
+if ($opt{'-depth'}) {
+    $depth = $opt{'-depth'};
+    $pixelformat = "";
+}
+if ($opt{'-pixelformat'}) {
+    $pixelformat = $opt{'-pixelformat'};
+}
+
+if ($opt{'-fp'}) {
+    @fontPathElements = split(/\s*,\s*/, "$opt{'-fp'}");
+
+    $fontPath = '';
+
+    foreach $i (0.."$#fontPathElements") {
+        $tempFontPath = $fontPathElements[$i];
+        if ($tempFontPath !~ m!^[^/]*/[^/]*:\d+$!) {
+            $tempFontPath =~ s/:unscaled$//;
+            if (-r "$tempFontPath/fonts.dir") {
+                $fontPath .= "$fontPathElements[$i],";
+            }
+        } else {
+            $fontPath .= "$fontPathElements[$i],";
+        }
+    }
+    chop $fontPath;
+}
+
+&CheckGeometryAndDepth();
+
+if ($opt{'-name'}) {
+    $desktopName = $opt{'-name'};
+}
+
+# Create the user's vnc directory if necessary.
+
+unless (-e $vncUserDir) {
+    unless (mkdir($vncUserDir, 0700)) {
+        die "$prog: Could not create $vncUserDir.\n";
+    }
+}
+($z,$z,$mode) = stat("$vncUserDir");
+if (!-d _ || !-o _ || ($vncUserDirUnderTmp && ($mode & 0777) != 0700)) {
+    die "$prog: Wrong type or access mode of $vncUserDir.\n";
+}
+
+# Make sure the user has a password.
+
+($z,$z,$mode) = stat("$vncUserDir/passwd");
+if (-e _ && (!-f _ || !-o _)) {
+    die "$prog: Wrong type or ownership on $vncUserDir/passwd.\n";
+}
+if (!-e _ || ($mode & 077) != 0) {
+    warn "\nYou will require a password to access your desktops.\n\n";
+    system("vncpasswd $vncUserDir/passwd");
+    if (($? & 0xFF00) != 0) {
+        exit 1;
+    }
+}
+
+# Find display number.
+
+if ((@ARGV > 0) && ($ARGV[0] =~ /^:(\d+)$/)) {
+    $displayNumber = $1;
+    shift(@ARGV);
+    unless (&CheckDisplayNumber($displayNumber)) {
+	die "A VNC server is already running as :$displayNumber\n";
+    }
+} elsif ((@ARGV > 0) && ($ARGV[0] !~ /^-/)) {
+    &Usage();
+} else {
+    $displayNumber = &GetDisplayNumber();
+}
+
+$vncPort = 5900 + $displayNumber;
+
+$desktopLog = "$vncUserDir/$host:$displayNumber.log";
+unlink($desktopLog);
+
+# Make an X server cookie - use as the seed the sum of the current time, our
+# PID and part of the encrypted form of the password.  Ideally we'd use
+# /dev/urandom, but that's only available on Linux.
+
+srand(time+$$+unpack("L",`cat $vncUserDir/passwd`));
+$cookie = "";
+for (1..16) {
+    $cookie .= sprintf("%02x", int(rand(256)));
+}
+
+system("xauth -f $xauthorityFile add $host:$displayNumber . $cookie");
+system("xauth -f $xauthorityFile add $host/unix:$displayNumber . $cookie"); 
+
+# Now start the X VNC Server
+
+$cmd = "Xtightvnc :$displayNumber";
+$cmd .= " -desktop " . &quotedString($desktopName);
+if ($useClasses) {
+  $cmd .= " -httpd $vncClasses";
+  print ("Found $vncClasses for http connections.\n");
+  if ($opt{'-httpport'}) {
+    $cmd .= " -httpport $opt{'-httpport'}";
+    print ("Listening to $opt{'-httpport'} for http connections.\n");
+  }
+  elsif ($opt{'-basehttpport'}) {
+    my $v = $opt{'-basehttpport'} + $displayNumber;
+    print ("Listening to $v for http connections.\n");
+    $cmd .= " -httpport $v";
+  }
+}
+$cmd .= " -auth $xauthorityFile";
+$cmd .= " -geometry $geometry" if ($geometry);
+$cmd .= " -depth $depth" if ($depth);
+$cmd .= " -pixelformat $pixelformat" if ($pixelformat);
+$cmd .= " -rfbwait 120000";
+$cmd .= " $authType";
+$cmd .= " -rfbport $vncPort";
+$cmd .= " -fp $fontPath" if ($fontPath);
+$cmd .= " -co $colorPath" if ($colorPath);
+$cmd .= " -alwaysshared" if ($opt{'-alwaysshared'});
+$cmd .= " -nevershared" if ($opt{'-nevershared'});
+
+foreach $arg (@ARGV) {
+    $cmd .= " " . &quotedString($arg);
+}
+$cmd .= " >> " . &quotedString($desktopLog) . " 2>&1";
+
+# Run $cmd and record the process ID.
+
+$pidFile = "$vncUserDir/$host:$displayNumber.pid";
+system("$cmd & echo \$! >$pidFile");
+
+# Give Xtightvnc a chance to start up
+
+sleep(1);
+unless (kill 0, `cat $pidFile`) {
+    warn "Couldn't start Xtightvnc; trying default font path.\n";
+    warn "Please set correct fontPath in the $prog script.\n";
+    $cmd =~ s@-fp [^ ]+@@;
+    system("$cmd & echo \$! >$pidFile");
+    sleep(1);
+}
+unless (kill 0, `cat $pidFile`) {
+    warn "Couldn't start Xtightvnc process.\n\n";
+    open(LOG, "<$desktopLog");
+    while (<LOG>) { print; }
+    close(LOG);
+    die "\n";
+}
+
+warn "\nNew '$desktopName' desktop is $host:$displayNumber\n\n";
+
+# Create the user's xstartup script if necessary.
+
+unless (-e "$xstartup") {
+    warn "Creating default startup script $xstartup\n";
+    open(XSTARTUP, ">$xstartup");
+    print XSTARTUP $defaultXStartup;
+    close(XSTARTUP);
+    chmod 0755, "$xstartup";
+}
+
+# Run the X startup script.
+
+warn "Starting applications specified in $xstartup\n";
+warn "Log file is $desktopLog\n\n";
+
+# If the unix domain socket exists then use that (DISPLAY=:n) otherwise use
+# TCP (DISPLAY=host:n)
+
+if (-e "/tmp/.X11-unix/X$displayNumber") {
+    $ENV{DISPLAY}= ":$displayNumber";
+} else {
+    $ENV{DISPLAY}= "$host:$displayNumber";
+}
+$ENV{VNCDESKTOP}= $desktopName;
+
+system("$xstartup >> " . &quotedString($desktopLog) . " 2>&1 &");
+
+exit;
+
+############################ Debian functions #################################
+# I thank Manoj for the code below.
+#
+# ReadConfigFile reads in a config file and sets variables according to it.
+#
+
+sub ReadConfigFile
+{
+  open(CONFIG, "$Config_file") || return;
+  my $lineno = 0;
+  while (<CONFIG>) {
+      chomp;
+      $lineno++;
+      s/\#.*//og;
+      next if /^\s*$/og;
+      $_ .= ";" unless /;\s*$/;
+      if (/^\s*([^=]+)\s*=\s*(\S.*)$/o) {
+          my $ret = eval "$1=$2";
+          if ($@) {
+              print STDERR "Error parsing config file $Config_file!\n";
+              print STDERR "$lineno:$_\n";
+          }
+      }
+  }
+}
+
+sub ReadXFConfigFont
+{
+  open(CONFIG, "$XFConfigPath") || return;
+  my $lineno = 0;
+  while (<CONFIG>) {
+      chomp;
+      $lineno++;
+      s/\#.*//og;
+      next if /^\s*$/og;
+      if (/^\s*FontPath\s*"(\S.*)"\s*$/o) {
+          $fontPath .= "," if $fontPath;
+          $fontPath .= $1;
+      }
+  }
+}
+
+sub ReadXFConfigColor
+{
+  open(CONFIG, "$XFConfigPath") || return;
+  my $lineno = 0;
+  while (<CONFIG> && !$colorPath) {
+      chomp;
+      $lineno++;
+      s/\#.*//og;
+      next if /^\s*$/og;
+      if (/^\s*RgbPath\s*"(\S.*)"\s*$/o) {
+          $colorPath = $1;
+      }
+  }
+}
+
+
+########## End of debian functions ###########
+
+###############################################################################
+#
+# CheckGeometryAndDepth simply makes sure that the geometry and depth values
+# are sensible.
+#
+
+sub CheckGeometryAndDepth
+{
+    if ($geometry =~ /^(\d+)x(\d+)$/) {
+	$width = $1; $height = $2;
+
+	if (($width<1) || ($height<1)) {
+	    die "$prog: geometry $geometry is invalid\n";
+	}
+
+	while (($width % 4)!=0) {
+	    $width = $width + 1;
+	}
+
+	while (($height % 2)!=0) {
+	    $height = $height + 1;
+	}
+
+	$geometry = "${width}x$height";
+    } else {
+	die "$prog: geometry $geometry is invalid\n";
+    }
+
+    if (($depth < 8) || ($depth > 32)) {
+	die "Depth must be between 8 and 32\n";
+    }
+}
+
+
+#
+# GetDisplayNumber gets the lowest available display number.  A display number
+# n is taken if something is listening on the VNC server port (5900+n) or the
+# X server port (6000+n).
+#
+
+sub GetDisplayNumber
+{
+    foreach $n (1..99) {
+	if (&CheckDisplayNumber($n)) {
+	    return $n+0; # Bruce Mah's workaround for bug in perl 5.005_02
+	}
+    }
+    
+    die "$prog: no free display number on $host.\n";
+}
+
+
+#
+# CheckDisplayNumber checks if the given display number is available.  A
+# display number n is taken if something is listening on the VNC server port
+# (5900+n) or the X server port (6000+n).
+#
+
+sub CheckDisplayNumber
+{
+    local ($n) = @_;
+
+    socket(S, $AF_INET, $SOCK_STREAM, 0) || die "$prog: socket failed: $!\n";
+    eval 'setsockopt(S, &SOL_SOCKET, &SO_REUSEADDR, pack("l", 1))';
+    #unless (bind(S, pack('S n x12', $AF_INET, 6000 + $n))) {
+    unless (bind(S, sockaddr_in(6000 + $n, &INADDR_ANY))) {
+	close(S);
+	return 0;
+    }
+    close(S);
+
+    socket(S, $AF_INET, $SOCK_STREAM, 0) || die "$prog: socket failed: $!\n";
+    eval 'setsockopt(S, &SOL_SOCKET, &SO_REUSEADDR, pack("l", 1))';
+    #unless (bind(S, pack('S n x12', $AF_INET, 5900 + $n))) {
+    unless (bind(S, sockaddr_in(5900 + $n, &INADDR_ANY))) {
+	close(S);
+	return 0;
+    }
+    close(S);
+
+    if (-e "/tmp/.X$n-lock") {
+	warn "\nWarning: $host:$n is taken because of /tmp/.X$n-lock\n";
+	warn "Remove this file if there is no X server $host:$n\n";
+	return 0;
+    }
+
+    if (-e "/tmp/.X11-unix/X$n") {
+	warn "\nWarning: $host:$n is taken because of /tmp/.X11-unix/X$n\n";
+	warn "Remove this file if there is no X server $host:$n\n";
+	return 0;
+    }
+
+    return 1;
+}
+
+
+#
+# GetXDisplayDefaults uses xdpyinfo to find out the geometry, depth and pixel
+# format of the current X display being used.  If successful, it sets the
+# options as appropriate so that the X VNC server will use the same settings
+# (minus an allowance for window manager decorations on the geometry).  Using
+# the same depth and pixel format means that the VNC server won't have to
+# translate pixels when the desktop is being viewed on this X display (for
+# TrueColor displays anyway).
+#
+
+sub GetXDisplayDefaults
+{
+    local (@lines, @matchlines, $width, $height, $defaultVisualId, $i,
+	   $red, $green, $blue);
+
+    $wmDecorationWidth = 4;	# a guess at typical size for window manager
+    $wmDecorationHeight = 24;	# decoration size
+
+    return unless (defined($ENV{DISPLAY}));
+
+    @lines = `xdpyinfo 2>/dev/null`;
+
+    return if ($? != 0);
+
+    @matchlines = grep(/dimensions/, @lines);
+    if (@matchlines) {
+	($width, $height) = ($matchlines[0] =~ /(\d+)x(\d+) pixels/);
+
+	$width -= $wmDecorationWidth;
+	$height -= $wmDecorationHeight;
+
+	$geometry = "${width}x$height";
+    }
+
+    @matchlines = grep(/default visual id/, @lines);
+    if (@matchlines) {
+	($defaultVisualId) = ($matchlines[0] =~ /id:\s+(\S+)/);
+
+	for ($i = 0; $i < @lines; $i++) {
+	    if ($lines[$i] =~ /^\s*visual id:\s+$defaultVisualId$/) {
+		if (($lines[$i+1] !~ /TrueColor/) ||
+		    ($lines[$i+2] !~ /depth/) ||
+		    ($lines[$i+4] !~ /red, green, blue masks/))
+		{
+		    return;
+		}
+		last;
+	    }
+	}
+
+	return if ($i >= @lines);
+
+	($depth) = ($lines[$i+2] =~ /depth:\s+(\d+)/);
+	($red,$green,$blue)
+	    = ($lines[$i+4]
+	       =~ /masks:\s+0x([0-9a-f]+), 0x([0-9a-f]+), 0x([0-9a-f]+)/);
+
+	$red = hex($red);
+	$green = hex($green);
+	$blue = hex($blue);
+
+	if ($red > $blue) {
+	    $red = int(log($red) / log(2)) - int(log($green) / log(2));
+	    $green = int(log($green) / log(2)) - int(log($blue) / log(2));
+	    $blue = int(log($blue) / log(2)) + 1;
+	    $pixelformat = "rgb$red$green$blue";
+	} else {
+	    $blue = int(log($blue) / log(2)) - int(log($green) / log(2));
+	    $green = int(log($green) / log(2)) - int(log($red) / log(2));
+	    $red = int(log($red) / log(2)) + 1;
+	    $pixelformat = "bgr$blue$green$red";
+	}
+    }
+}
+
+
+#
+# quotedString returns a string which yields the original string when parsed
+# by a shell.
+#
+
+sub quotedString
+{
+    local ($in) = @_;
+
+    $in =~ s/\'/\'\"\'\"\'/g;
+
+    return "'$in'";
+}
+
+
+#
+# removeSlashes turns slashes into underscores for use as a file name.
+#
+
+sub removeSlashes
+{
+    local ($in) = @_;
+
+    $in =~ s|/|_|g;
+
+    return "$in";
+}
+
+
+#
+# Usage
+#
+
+sub Usage
+{
+    die("TightVNC Server version 1.3.9\n".
+	"\n".
+	"Usage: $prog [<OPTIONS>] [:<DISPLAY#>]\n".
+	"       $prog -kill :<DISPLAY#>\n".
+	"\n".
+	"<OPTIONS> are Xtightvnc options, or:\n".
+	"\n".
+	"        -name <DESKTOP-NAME>\n".
+	"        -depth <DEPTH>\n".
+	"        -geometry <WIDTH>x<HEIGHT>\n".
+        "        -httpport number\n".
+        "        -basehttpport number\n".
+        "        -alwaysshared\n".
+        "        -nevershared\n".
+	"        -pixelformat rgb<NNN>\n".
+	"        -pixelformat bgr<NNN>\n".
+	"\n".
+	"See vncserver and Xtightvnc manual pages for more information.\n");
+}
+
+
+#
+# Kill
+#
+
+sub Kill
+{
+    $opt{'-kill'} =~ s/(:\d+)\.\d+$/$1/; # e.g. turn :1.0 into :1
+
+    if ($opt{'-kill'} =~ /^:\d+$/) {
+	$pidFile = "$vncUserDir/$host$opt{'-kill'}.pid";
+    } else {
+	if ($opt{'-kill'} !~ /^$host:/) {
+	    die "\nCan't tell if $opt{'-kill'} is on $host\n".
+		"Use -kill :<number> instead\n\n";
+	}
+	$pidFile = "$vncUserDir/$opt{'-kill'}.pid";
+    }
+
+    unless (-r $pidFile) {
+	die "\nCan't find file $pidFile\n".
+	    "You'll have to kill the Xtightvnc process manually\n\n";
+    }
+
+    $SIG{'HUP'} = 'IGNORE';
+    chop($pid = `cat $pidFile`);
+    warn "Killing Xtightvnc process ID $pid\n";
+    system("kill $pid");
+    unlink $pidFile;
+
+    ## If option -clean is given, also remove the logfile
+    unlink "$vncUserDir/$host$opt{'-kill'}.log" if ($opt{'-clean'});
+
+    exit;
+}
+
+
+#
+# ParseOptions takes a list of possible options and a boolean indicating
+# whether the option has a value following, and sets up an associative array
+# %opt of the values of the options given on the command line. It removes all
+# the arguments it uses from @ARGV and returns them in @optArgs.
+#
+
+sub ParseOptions
+{
+    local (@optval) = @_;
+    local ($opt, @opts, %valFollows, @newargs);
+
+    while (@optval) {
+	$opt = shift(@optval);
+	push(@opts,$opt);
+	$valFollows{$opt} = shift(@optval);
+    }
+
+    @optArgs = ();
+    %opt = ();
+
+    arg: while (defined($arg = shift(@ARGV))) {
+	foreach $opt (@opts) {
+	    if ($arg eq $opt) {
+		push(@optArgs, $arg);
+		if ($valFollows{$opt}) {
+		    if (@ARGV == 0) {
+			&Usage();
+		    }
+		    $opt{$opt} = shift(@ARGV);
+		    push(@optArgs, $opt{$opt});
+		} else {
+		    $opt{$opt} = 1;
+		}
+		next arg;
+	    }
+	}
+	push(@newargs,$arg);
+    }
+
+    @ARGV = @newargs;
+}
+
+
+#
+# Routine to make sure we're operating in a sane environment.
+#
+
+sub SanityCheck
+{
+    local ($cmd);
+
+    #
+    # Get the program name
+    #
+
+    ($prog) = ($0 =~ m|([^/]+)$|);
+
+    #
+    # Check we have all the commands we'll need on the path.
+    #
+
+ cmd:
+    foreach $cmd ("uname","xauth","Xtightvnc","vncpasswd") {
+	for (split(/:/,$ENV{PATH})) {
+	    if (-x "$_/$cmd") {
+		next cmd;
+	    }
+	}
+	die "$prog: couldn't find \"$cmd\" on your PATH.\n";
+    }
+
+    #
+    # Check the HOME and USER environment variables are both set.
+    #
+
+    unless (defined($ENV{HOME})) {
+	die "$prog: The HOME environment variable is not set.\n";
+    }
+    unless (defined($ENV{USER})) {
+	die "$prog: The USER environment variable is not set.\n";
+    }
+
+    #
+    # Find socket constants. 'use Socket' is a perl5-ism, so we wrap it in an
+    # eval, and if it fails we try 'require "sys/socket.ph"'.  If this fails,
+    # we just guess at the values.  If you find perl moaning here, just
+    # hard-code the values of AF_INET and SOCK_STREAM.  You can find these out
+    # for your platform by looking in /usr/include/sys/socket.h and related
+    # files.
+    #
+
+    chop($os = `uname`);
+    chop($osrev = `uname -r`);
+
+    eval 'use Socket';
+    if ($@) {
+	eval 'require "sys/socket.ph"';
+	if ($@) {
+	    if (($os eq "SunOS") && ($osrev !~ /^4/)) {
+		$AF_INET = 2;
+		$SOCK_STREAM = 2;
+	    } else {
+		$AF_INET = 2;
+		$SOCK_STREAM = 1;
+	    }
+	} else {
+	    $AF_INET = &AF_INET;
+	    $SOCK_STREAM = &SOCK_STREAM;
+	}
+    } else {
+	$AF_INET = &AF_INET;
+	$SOCK_STREAM = &SOCK_STREAM;
+    }
+}
diff --git a/roles/vncserver/vars/CentOS_7_x86_64.yml b/roles/vncserver/vars/CentOS_7_x86_64.yml
index a32cb14e..0880aa27 100644
--- a/roles/vncserver/vars/CentOS_7_x86_64.yml
+++ b/roles/vncserver/vars/CentOS_7_x86_64.yml
@@ -1,8 +1,6 @@
 ---
  system_packages:
-   - "@Desktop"
    - tigervnc-server
-   - "@xfce"
    - libXcomposite
    - libXdamage
    - mesa-libEGL
@@ -12,5 +10,11 @@
    - graphite2
    - xterm
    - libpng
-   - "@mate-desktop-environment"
-   #- "@MATE Desktop"
+   - xorg-x11-fonts-100dpi
+   - xorg-x11-fonts-75dpi
+   - xorg-x11-fonts-misc
+
+ system_group_packages:
+   - "mate-desktop-environment"
+   - "graphical-server-environment"
+
diff --git a/roles/vncserver/vars/Ubuntu_x86_64.yml b/roles/vncserver/vars/Ubuntu_x86_64.yml
index 780918f1..3b5b3eb4 100644
--- a/roles/vncserver/vars/Ubuntu_x86_64.yml
+++ b/roles/vncserver/vars/Ubuntu_x86_64.yml
@@ -1,5 +1,6 @@
 ---
  system_packages:
    - tightvncserver
-   - xfce4-session
-   - xfwm4
+   - ubuntu-mate-desktop
+#   - xfce4-session
+#   - xfwm4
-- 
GitLab