Browse Source

Upgrade full dev simple conf

Pierre-Yves Barriat 2 years ago
parent
commit
697c7b5cb0
42 changed files with 834 additions and 249 deletions
  1. 1 0
      .gitignore
  2. 3 1
      dev/README.md
  3. 229 79
      dev/Vagrantfile
  4. 1 1
      dev/provisioning/ansible/grafana.yml
  5. 14 14
      dev/provisioning/ansible/haproxy.yml
  6. 8 8
      dev/provisioning/ansible/mariadb.yml
  7. 11 12
      dev/provisioning/ansible/nextcloud.yml
  8. 1 1
      dev/provisioning/ansible/node_exporter.yml
  9. 9 0
      dev/provisioning/ansible/playbook.yml
  10. 1 1
      dev/provisioning/ansible/prometheus.yml
  11. 10 10
      dev/provisioning/ansible/redis.yml
  12. 4 1
      dev/provisioning/ansible/roles/haproxy/defaults/main.yml
  13. 3 0
      dev/provisioning/ansible/roles/haproxy/handlers/main.yml
  14. 59 15
      dev/provisioning/ansible/roles/haproxy/tasks/main.yml
  15. 14 0
      dev/provisioning/ansible/roles/haproxy/tasks/setup/Debian.yml
  16. 19 0
      dev/provisioning/ansible/roles/haproxy/tasks/setup/RedHat.yml
  17. 13 0
      dev/provisioning/ansible/roles/haproxy/tasks/setup/Suse.yml
  18. 77 25
      dev/provisioning/ansible/roles/haproxy/templates/haproxy.cfg.j2
  19. 25 0
      dev/provisioning/ansible/roles/haproxy/templates/keepalived.conf.j2
  20. 14 0
      dev/provisioning/ansible/roles/haproxy/vars/Debian.yml
  21. 15 0
      dev/provisioning/ansible/roles/haproxy/vars/RedHat.yml
  22. 9 0
      dev/provisioning/ansible/roles/haproxy/vars/Suse.yml
  23. 6 7
      dev/provisioning/ansible/roles/mariadb/defaults/main.yml
  24. 6 7
      dev/provisioning/ansible/roles/mariadb/tasks/database/users.yml
  25. 16 12
      dev/provisioning/ansible/roles/nextcloud/defaults/main.yml
  26. 70 18
      dev/provisioning/ansible/roles/nextcloud/tasks/main.yml
  27. 17 6
      dev/provisioning/ansible/roles/nextcloud/tasks/nc_install.yml
  28. 49 0
      dev/provisioning/ansible/roles/nextcloud/tasks/nc_multiple.yml
  29. 9 11
      dev/provisioning/ansible/roles/nextcloud/tasks/nc_setup.yml
  30. 0 3
      dev/provisioning/ansible/roles/nextcloud/tasks/prep_os/CentOS.yml
  31. 0 5
      dev/provisioning/ansible/roles/nextcloud/tasks/prep_php/CentOS.yml
  32. 14 0
      dev/provisioning/ansible/roles/nextcloud/templates/nc_config.php.j2
  33. 1 1
      dev/provisioning/ansible/roles/nextcloud/templates/nextcloud_apache2.j2
  34. 3 2
      dev/provisioning/bash/Centos_7.sh
  35. 16 0
      dev/provisioning/bash/common.sh
  36. 23 0
      dev/provisioning/bash/nfs-server.sh
  37. 16 0
      dev/provisioning/bash/web-setup.sh
  38. 40 6
      report/Projet_brevet.md
  39. BIN
      report/Projet_brevet.pdf
  40. BIN
      report/assets/dia_nc_dev_improved.png
  41. 1 1
      report/compile.sh
  42. 7 2
      report/scripts/dia_nc_dev_improved.py

+ 1 - 0
.gitignore

@@ -5,3 +5,4 @@ report/old/
 sandbox/
 commands.txt
 dev/.vagrant/
+dev/provisioning/ansible/hosts

+ 3 - 1
dev/README.md

@@ -5,6 +5,8 @@
 ```bash
 sudo apt install vagrant ansible virtualbox
 
+vagrant plugin install vagrant-hostmanager
+
 ansible-galaxy collection install ansible.posix
 
 ansible-galaxy collection install community.crypto
@@ -16,4 +18,4 @@ ansible-galaxy collection install community.general
 
 ```bash
 vagrant up
-```
+```

+ 229 - 79
dev/Vagrantfile

@@ -3,112 +3,262 @@
 
 VAGRANTFILE_API_VERSION = "2" 
 
-Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
+NETWORK = "192.168.56."
+NETMASK = "255.255.255.0"
 
-  config.vm.boot_timeout = 360
-  #config.ssh.insert_key = false
-  #config.ssh.username = "vagrant"
-  #config.ssh.password = "vagrant"
+# Put the MAIN ip <-> NCDOM domain below to host /etc/hosts
+MAIN = NETWORK+"10"
+NCDOM = "nextcloud.test"
 
-  config.vm.provider "virtualbox" do |vb|
-    vb.memory = "1024"
-    vb.cpus = "1"
-  end
+# VM machines configuration
+# ip address of the vm is NETWORK plus the last part of the IP
+HOSTS = [
+     #VM_NAME              IP_ADDRESS            RAM(mb)        CPU         BOX                  GROUP
+  { :hostname => "db",     :ip => NETWORK+"11",  :ram => 1024,  :cpu => 1,  :box => "centos/7",  :group => "database_servers" },
+  { :hostname => "redis",  :ip => NETWORK+"21",  :ram =>  512,  :cpu => 1,  :box => "centos/7",  :group => "redis_servers" }, #:folder_guest => "/srv/website", :folder_host => "src/" },
+  #{ :hostname => "redis2", :ip => NETWORK+"22",  :ram =>  512,  :cpu => 1,  :box => "centos/7",  :group => "redis_servers" }, #:port_guest => 80, :port_host => 8080 },
+  { :hostname => "web",    :ip => NETWORK+"31",  :ram => 1024,  :cpu => 1,  :box => "centos/7",  :group => "web_servers" },
+  #{ :hostname => "web2",   :ip => NETWORK+"32",  :ram => 1024,  :cpu => 1,  :box => "centos/7",  :group => "web_servers" },
+  { :hostname => "lb",     :ip => NETWORK+"41",  :ram =>  512,  :cpu => 1,  :box => "ubuntu/focal64",  :group => "loadbalancer_servers" },
+  #{ :hostname => "lb2",    :ip => NETWORK+"42",  :ram =>  512,  :cpu => 1,  :box => "ubuntu/focal64",  :group => "loadbalancer_servers" },
+]
 
-  #LoadBalancer
-  config.vm.define "loadbalancer" do |loadbalancer|
-    loadbalancer.vm.box = 'ubuntu/focal64'
-    loadbalancer.vm.hostname = "loadbalancer"
-    loadbalancer.vm.network :private_network, ip: "192.168.56.10"
-    loadbalancer.vm.provision "shell", inline: "apt-get install -y haproxy"
+# Defined ansible playbook
+# If empty, will skip the ansible provisioner block
+ansible_playbook = "provisioning/ansible/playbook.yml"
+# Ansible inventory. The path supports nested directories or a single file
+ansible_inventory_path = "provisioning/ansible/hosts"
 
-    #Provision the loadbalancer with Ansible
-    loadbalancer.vm.provision "ansible" do |ansible|
-       ansible.compatibility_mode = "2.0"
-       ansible.playbook="provisioning/ansible/haproxy.yml"
-       ansible.become = true
-       ansible.extra_vars = {
-         ansible_python_interpreter: "/usr/bin/python3",
-         ssl_name: "nextcloud.test",
-         network_allowed: "192.168.56.0/24",
-         haproxy_backend_servers:
-           { name: 'web', ip: '192.168.56.14:8000' },
-       }
+Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
+
+  if Vagrant.has_plugin?("vagrant-hostmanager")
+    config.vm.box_check_update = false 
+    # To enable the hostmanager plugin
+    config.hostmanager.enabled           = true
+    # To enable add records to host /etc/hosts
+    config.hostmanager.manage_host       = false
+    # To enable add records to guest /etc/hosts
+    config.hostmanager.manage_guest      = true
+    # Not use private ip addresses for the hosts file, set to false
+    config.hostmanager.ignore_private_ip = false
+    config.hostmanager.include_offline   = false
+  end
+
+  # Create groups to be used in ansible inventory
+  groups = {"all" => []}
+  HOSTS.each do |cfg|
+    if ! groups.has_key?(cfg[:group])
+      groups[cfg[:group]] = [cfg[:hostname]]
+    else
+      #combi = cfg[:ip]+" server_name="+cfg[:hostname]
+      #groups[cfg[:group]].push(combi)
+      groups[cfg[:group]].push(cfg[:hostname])
     end
+    #combi = cfg[:ip]+" server_name="+cfg[:hostname]
+    #groups["all"].push(combi)
+    groups["all"].push(cfg[:hostname])
   end
 
-  #Redis Server 
-  config.vm.define "redis" do |redis| 
-    redis.vm.hostname = "redis"
-    redis.vm.box = "centos/7"
-    redis.vm.network "private_network", ip: "192.168.56.12"
-    redis.vm.provision "shell", inline: "yum install -y python3 dnf epel-release"
-
-    #Provision the webserver with Ansible
-    redis.vm.provision "ansible" do |ansible|
-       ansible.compatibility_mode = "2.0"
-       ansible.playbook="provisioning/ansible/redis.yml"
-       ansible.become = true
-       ansible.extra_vars = { 
-         ansible_python_interpreter: "/usr/bin/python2",
-         #redis_bind_interface: "192.168.56.14", #bug Centos
-       }
-    end 
+  # Create inventory for ansible provision
+  # The inventory will hold servers details and groups per each server.
+  if File.dirname(ansible_inventory_path) != "."
+    Dir.mkdir(File.dirname(ansible_inventory_path)) unless Dir.exist?(File.dirname(ansible_inventory_path))
+  end
+  File.open(ansible_inventory_path, 'w') do |f|
+    HOSTS.each do |cfg|
+      f.write "#{cfg[:hostname]} ansible_host=#{cfg[:ip]}\n"
+    end
+    groups.keys.each do |g|
+      f.write "\n"
+      f.write "[#{g}]\n"
+      groups[g].each do |h| 
+        f.write "#{h}\n"
+      end
+    end
   end
 
-  #Database Server 
-  config.vm.define "db" do |db| 
-    db.vm.hostname = "mariadb"
-    db.vm.box = "centos/7"
-    db.vm.network "private_network", ip: "192.168.56.13"
-    db.vm.provision "shell", inline: "yum install -y python3 dnf"
+  # VM DEFINITIONS
 
-    #Provision the webserver with Ansible
-    db.vm.provision "ansible" do |ansible|
-       ansible.compatibility_mode = "2.0"
-       ansible.playbook="provisioning/ansible/mariadb.yml"
-       ansible.become = true
-       ansible.extra_vars = { 
-         ansible_python_interpreter: "/usr/bin/python2",
-         app_bind_address: "192.168.56.14"
-       }
+  HOSTS.each_with_index do |server, index|
+    config.vm.define server[:hostname] do |conf|
+      conf.vm.box = server[:box]
+      conf.vm.hostname = server[:hostname]
+      conf.vm.boot_timeout = 360
+      # Set system options
+      cpu = server[:cpu] ? server[:cpu] : 1;
+      memory = server[:ram] ? server[:ram] : 512;
+      name = server[:hostname] ? server[:hostname] : "linux";
+      conf.vm.provider "virtualbox" do |vbox|
+        vbox.cpus   = cpu.to_s
+        vbox.memory = memory.to_s
+        vbox.name   = name
+      end
+      # Set network options
+      netmask = server[:netmask] || NETMASK
+      conf.vm.network :private_network, ip: server[:ip], netmask: netmask
+      # Set port forwarding if defined
+      if !server[:port_guest].nil? && !server[:port_host].nil?
+        conf.vm.network "forwarded_port", guest: server[:port_guest], host: server[:port_host]
+      end
+      # Set synced folders if defined
+      if !server[:folder_guest].nil? && !server[:folder_host].nil?
+        conf.vm.synced_folder server[:folder_host], server[:folder_guest]
+      end 
+      # Set common provision
+      conf.vm.provision "shell" do |s|
+        s.path = "provisioning/bash/common.sh"
+        s.args = [server[:box]]
+      end     
+      # Provision nodes with Ansible.
+      # The index used here in order to execute the provision just after all
+      # the servers are up and running.
+      #if index == HOSTS.size - 1
+      #  if ansible_playbook != ""
+      #    conf.vm.provision :ansible do |ansible|
+      #      ansible.limit = "all"
+      #      ansible.compatibility_mode = "2.0"
+      #      ansible.become = true
+      #      ansible.inventory_path = ansible_inventory_path
+      #      ansible.playbook = ansible_playbook
+      #      #ansible.verbose = "vvvv"
+      #  end
+      #end      
     end
   end
 
-  #Web Server 
-  config.vm.define "web" do |web|
-    web.vm.hostname = "nextcloud"
-    web.vm.box = "centos/7"
-    web.vm.network "private_network", ip: "192.168.56.14"
+  # VM PROVISIONING
 
-    # Creating a Shared Directory between host and guest VM 
-    #web.vm.synced_folder "/apps/shared", "/shared"
+  #Database Server 
+  config.vm.define "db" do |db|
+    # Temp NFS stuff waiting Ceph
+    db.vm.provision "shell", path: "provisioning/bash/nfs-server.sh"
+    #
+    db.vm.provision "ansible" do |ansible|
+      ansible.compatibility_mode = "2.0"
+      ansible.playbook="provisioning/ansible/mariadb.yml"
+      ansible.inventory_path = ansible_inventory_path
+      ansible.become = true
+      ansible.extra_vars = {
+        db_users: [
+          { name: 'web', password: 'secret', host: 'web' },
+          { name: 'web', password: 'secret', host: 'web2' }
+        ]
+      }
+    end    
+  end
 
-    #Provision the webserver for nextcloud role ansible
-    web.vm.provision "shell", path: "provisioning/install/Centos_7.sh"
+  #Redis Server 
+  config.vm.define "redis" do |redis|
+    redis.vm.provision :ansible do |ansible|
+      ansible.compatibility_mode = "2.0"
+      ansible.playbook="provisioning/ansible/redis.yml"
+      ansible.inventory_path = ansible_inventory_path
+      ansible.become = true
+      #ansible.extra_vars = {
+      #  #redis_bind_interface: "192.168.56.14", #bug Centos
+      #}
+    end
+  end
 
-    #Provision the webserver with Ansible
+  #  #Web Server
+  config.vm.define "web" do |web|
+    web.vm.provision "shell", path: "provisioning/bash/Centos_7.sh"
     web.vm.provision "ansible" do |ansible|
        ansible.compatibility_mode = "2.0"
        ansible.playbook = "provisioning/ansible/nextcloud.yml"
+       ansible.inventory_path = ansible_inventory_path
        ansible.become = true
-       ansible.extra_vars = {
-         ansible_python_interpreter: "/usr/bin/python2",
-         db_host: "192.168.56.13",
+       ansible.extra_vars = { 
+         ssl_name: NCDOM,
+         nc_trusted_domain: "web",
+         db_host: "db",
+         nc_db_user: "web",
+         nc_db_password: "secret",
          use_redis_server: "true",
-         redis_host: "192.168.56.12",
-         debug_speed: "false",
+         redis_host: "redis",
+         #nc_multiple: "nfs",
+         #nfs_server: "db",
        }
-       #ansible.inventory_path = "provisioning/apache.inventory"
        #ansible.verbose = "vvvv"
     end
   end
 
+  #  #LoadBalancer (master)
+  config.vm.define "lb" do |lb|
+    lb.vm.provision "ansible" do |ansible|
+      ansible.compatibility_mode = "2.0"
+      ansible.playbook="provisioning/ansible/haproxy.yml"
+      ansible.inventory_path = ansible_inventory_path
+      ansible.become = true
+      ansible.extra_vars = {
+        ssl_name: NCDOM,
+        network_allowed: NETWORK+"0/24",
+        keepalived_vip: MAIN,
+        keepalived_priority: 101,
+        keepalived_state: "MASTER",
+        haproxy_backend_servers: [
+          { name: 'web', ip: 'web:8000' },
+          #{ name: 'web2', ip: 'web2:8000' }
+        ]
+      }
+    end
+  end
+
+#  #LoadBalancer (backup)
+#  config.vm.define "lb2" do |lb2|
+#    lb2.vm.provision "shell", inline: "apt-get install -y haproxy keepalived"
+#    lb2.vm.provision "ansible" do |ansible|
+#       ansible.compatibility_mode = "2.0"
+#       ansible.playbook="provisioning/ansible/haproxy.yml"
+#       ansible.become = true
+#       ansible.extra_vars = { 
+#         ansible_python_interpreter: "/usr/bin/python3",
+#         ssl_name: NCDOM,
+#         network_allowed: NETWORK+"0/24",
+#         keepalived_vip: MAIN,
+#         keepalived_priority: 100,
+#         keepalived_state: "BACKUP",
+#         haproxy_backend_servers: [
+#           { name: 'web', ip: 'web:8000' },
+#           #{ name: 'web2', ip: 'web2:8000' }
+#         ]
+#       }
+#    end 
+#  end
+#
+#  #Web Server 2
+#  config.vm.define "web2" do |web2|
+#    web2.vm.hostname = "nextcloud"
+#    web2.vm.box = "centos/7"
+#    web2.vm.network "private_network", ip: "192.168.56.15"
+#
+#    web2.vm.provision "shell", path: "provisioning/install/Centos_7.sh"
+#    web2.vm.provision "ansible" do |ansible|
+#       ansible.compatibility_mode = "2.0"
+#       ansible.playbook = "provisioning/ansible/nextcloud.yml"
+#       ansible.become = true
+#       ansible.extra_vars = { 
+#         ansible_python_interpreter: "/usr/bin/python2",
+#         ssl_name: "nextcloud.test",
+#         nc_trusted_domain: "192.168.56.15",
+#         db_host: "192.168.56.21",
+#         nc_db_user: "web",
+#         nc_db_password: "secret",
+#         use_redis_server: "true",
+#         redis_host: "192.168.56.13",
+#         #nc_multiple: "nfs",
+#         #nfs_server: "192.168.56.21",
+#       }
+#       #ansible.inventory_path = "provisioning/apache.inventory"
+#       #ansible.verbose = "vvvv"
+#    end
+#  end 
+#
 #  #Prometheus
 #  config.vm.define "prometheus" do |prometheus|
 #    prometheus.vm.box = 'centos/7'
 #    prometheus.vm.hostname = "prometheus"
-#    prometheus.vm.network :private_network, ip: "192.168.56.11"
+#    prometheus.vm.network :private_network, ip: "192.168.56.41"
 #    prometheus.vm.provision "shell", path: "provisioning/install/Centos_7.sh"
 #
 #    #Provision prometheus-grafana with Ansible
@@ -126,7 +276,7 @@ Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
 #  config.vm.define "node" do |node|
 #    node.vm.box = 'centos/7'
 #    node.vm.hostname = "nodexporter"
-#    node.vm.network :private_network, ip: "192.168.56.15"
+#    node.vm.network :private_network, ip: "192.168.56.42"
 #    node.vm.provision "shell", path: "provisioning/install/Centos_7.sh"
 #
 #    #Provision prometheus-grafana with Ansible
@@ -144,7 +294,7 @@ Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
 #  config.vm.define "grafana" do |grafana|
 #    grafana.vm.box = 'centos/7'
 #    grafana.vm.hostname = "grafana"
-#    grafana.vm.network :private_network, ip: "192.168.56.16"
+#    grafana.vm.network :private_network, ip: "192.168.56.43"
 #    grafana.vm.provision "shell", path: "provisioning/install/Centos_7.sh"
 #
 #    #Provision prometheus-grafana with Ansible
@@ -158,5 +308,5 @@ Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
 #    end 
 #  end 
 
-  config.vm.box_check_update = false 
+  #end
 end

+ 1 - 1
dev/provisioning/ansible/grafana.yml

@@ -1,6 +1,6 @@
 ---
 - name: apply grafana role
-  hosts: all
+  hosts: grafana_servers
   #  vars:
   #    ansible_python_interpreter: /usr/bin/python3
   #    ansible_user: vagrant

+ 14 - 14
dev/provisioning/ansible/haproxy.yml

@@ -1,18 +1,18 @@
 ---
 - name: apply haproxy role
-  hosts: all
-  #  vars:
-  #    ansible_python_interpreter: /usr/bin/python3
-  #    ansible_user: vagrant
-  #    ansible_password: vagrant
-  #    ssl_name: "nextcloud.test"
-  #    network_allowed: "192.168.56.0/24"
-  #    haproxy_backend_servers:
-  #      { name: 'web', ip: '192.168.56.14:8000' }
-  #  pre_tasks:
-  #    - name: define ansible_python_interpreter group // linux distribution
-  #      set_fact:
-  #        ansible_python_interpreter: /usr/bin/python2
-  #      when: ansible_distribution == 'CentOS' and ansible_distribution_major_version == '7'
+  hosts: loadbalancer_servers
+  vars:
+    ansible_python_interpreter: /usr/bin/python3
+  #  ansible_user: vagrant
+  #  ansible_password: vagrant
+  #  ssl_name: "nextcloud.test"
+  #  network_allowed: "192.168.56.0/24"
+  #  haproxy_backend_servers:
+  #    { name: 'web', ip: '192.168.56.14:8000' }
+  pre_tasks:
+    - name: define ansible_python_interpreter group // linux distribution
+      set_fact:
+        ansible_python_interpreter: /usr/bin/python2
+      when: ansible_distribution == 'CentOS' and ansible_distribution_major_version == '7'
   roles:
     - { role: haproxy }

+ 8 - 8
dev/provisioning/ansible/mariadb.yml

@@ -1,15 +1,15 @@
 ---
 - name: apply mariadb configuration
-  hosts: all
-  #vars:
-  #  ansible_python_interpreter: /usr/bin/python3
+  hosts: database_servers
+  vars:
+    ansible_python_interpreter: /usr/bin/python3
   #  ansible_user: vagrant
   #  ansible_password: vagrant
   #  app_bind_address: 192.168.56.14
-  #pre_tasks:
-  #  - name: define ansible_python_interpreter group // linux distribution
-  #    set_fact:
-  #      ansible_python_interpreter: /usr/bin/python2
-  #    when: ansible_distribution == 'CentOS' and ansible_distribution_major_version == '7'
+  pre_tasks:
+    - name: define ansible_python_interpreter group // linux distribution
+      set_fact:
+        ansible_python_interpreter: /usr/bin/python2
+      when: ansible_distribution == 'CentOS' and ansible_distribution_major_version == '7'
   roles:
     - { role: mariadb }

+ 11 - 12
dev/provisioning/ansible/nextcloud.yml

@@ -3,17 +3,16 @@
   collections:
     - community.general
     - ansible.posix
-  hosts: all
-  #  vars:
-  #    ansible_python_interpreter: /usr/bin/python3
-  #    ansible_user: vagrant
-  #    ansible_password: vagrant
-  #    db_host: 192.168.56.13
-  #    debug_speed: false
-  #  pre_tasks:
-  #    - name: define ansible_python_interpreter group // linux distribution
-  #      set_fact:
-  #        ansible_python_interpreter: /usr/bin/python2
-  #      when: ansible_distribution == 'CentOS' and ansible_distribution_major_version == '7'
+  hosts: web_servers
+  vars:
+    ansible_python_interpreter: /usr/bin/python3
+  #  ansible_user: vagrant
+  #  ansible_password: vagrant
+  #  db_host: 192.168.56.13
+  pre_tasks:
+    - name: define ansible_python_interpreter group // linux distribution
+      set_fact:
+        ansible_python_interpreter: /usr/bin/python2
+      when: ansible_distribution == 'CentOS' and ansible_distribution_major_version == '7'
   roles:
     - { role: nextcloud }

+ 1 - 1
dev/provisioning/ansible/node_exporter.yml

@@ -1,6 +1,6 @@
 ---
 - name: apply node_exporter role
-  hosts: all
+  hosts: node_exporter_servers
   #  vars:
   #    ansible_python_interpreter: /usr/bin/python3
   #    ansible_user: vagrant

+ 9 - 0
dev/provisioning/ansible/playbook.yml

@@ -0,0 +1,9 @@
+---
+- import_playbook: mariadb.yml
+- import_playbook: redis.yml
+- import_playbook: nextcloud.yml
+- import_playbook: haproxy.yml
+
+  #- import_playbook: node_exporter.yml
+  #- import_playbook: prometheus.yml
+  #- import_playbook: grafana.yml

+ 1 - 1
dev/provisioning/ansible/prometheus.yml

@@ -1,6 +1,6 @@
 ---
 - name: apply prometheus role
-  hosts: all
+  hosts: prometheus_servers
   #  vars:
   #    ansible_python_interpreter: /usr/bin/python3
   #    ansible_user: vagrant

+ 10 - 10
dev/provisioning/ansible/redis.yml

@@ -1,14 +1,14 @@
 ---
 - name: apply redis role
-  hosts: all
-  #  vars:
-  #    ansible_python_interpreter: /usr/bin/python3
-  #    ansible_user: vagrant
-  #    ansible_password: vagrant
-  #  pre_tasks:
-  #    - name: define ansible_python_interpreter group // linux distribution
-  #      set_fact:
-  #        ansible_python_interpreter: /usr/bin/python2
-  #      when: ansible_distribution == 'CentOS' and ansible_distribution_major_version == '7'
+  hosts: redis_servers
+  vars:
+    ansible_python_interpreter: /usr/bin/python3
+  #  ansible_user: vagrant
+  #  ansible_password: vagrant
+  pre_tasks:
+    - name: define ansible_python_interpreter group // linux distribution
+      set_fact:
+        ansible_python_interpreter: /usr/bin/python2
+      when: ansible_distribution == 'CentOS' and ansible_distribution_major_version == '7'
   roles:
     - { role: redis }

+ 4 - 1
dev/provisioning/ansible/roles/haproxy/defaults/main.yml

@@ -15,4 +15,7 @@ network_allowed: '192.168.56.0/24'
 
 # List of backend servers.
 haproxy_backend_servers:
-  - { name: 'web', ip: '192.168.56.14:8000' }
+  - { name: 'web', ip: '192.168.56.88:8000' }
+
+keepalived_priority: 101
+keepalived_vip: "192.168.56.10"

+ 3 - 0
dev/provisioning/ansible/roles/haproxy/handlers/main.yml

@@ -1,3 +1,6 @@
 ---
 - name: restart haproxy
   service: name=haproxy state=restarted
+
+- name: restart keepalived
+  service: name=keepalived state=restarted

+ 59 - 15
dev/provisioning/ansible/roles/haproxy/tasks/main.yml

@@ -1,14 +1,50 @@
 ---
+- name: Include OS specific variables.
+  include_vars: "{{ ansible_os_family }}.yml"
+
+- name: Install packages
+  include_tasks: "setup/{{ ansible_os_family }}.yml"
+
 - name: Get HAProxy version.
   command: haproxy -v
   register: haproxy_version_result
   changed_when: false
   check_mode: false
 
+- name: The HAProxy version.
+  debug: var=haproxy_version_result.stdout
+
 - name: Set HAProxy version.
   set_fact:
     haproxy_version: '{{ haproxy_version_result.stdout_lines[0] | regex_replace("^HA-Proxy version ([0-9]\.[0-9]).*$", "\1") }}'
 
+- name: Get IP range.
+  shell: "echo {{ network_allowed }} | cut -d'.' --fields=1,2,3"
+  register: result
+
+- name: Get interface name.
+  shell: "ip -4 addr show | grep {{ result.stdout }} | rev | cut -d ' ' -f 1 | rev"
+  register: itfn
+
+- name: Set keepalived_bind_interface.
+  set_fact:
+    keepalived_bind_interface: "{{ itfn.stdout }}"
+
+- name: Integration net.ipv4
+  blockinfile:
+    dest: /etc/sysctl.conf
+    block: |
+      net.ipv4.ip_forward = 1 
+      net.ipv4.ip_nonlocal_bind = 1 
+
+- name: Ensure keepalived is started and enabled on boot.
+  service: name=keepalived state=started enabled=yes
+
+- name: Ensure keepalived conf is set 
+  template: >
+    src=templates/keepalived.conf.j2
+    dest=/etc/keepalived/keepalived.conf
+
 - name: Ensure HAProxy is started and enabled on boot.
   service: name=haproxy state=started enabled=yes
 
@@ -45,23 +81,31 @@
     path: /etc/haproxy/dhparams.pem
     size: 2048
 
-- name: Add ssl dhparam file
-  lineinfile:
-    path: /etc/haproxy/haproxy.cfg
-    insertafter: "^.*ssl-default-bind-options.*"
-    line: "\tssl-dh-param-file /etc/haproxy/dhparams.pem"
-    firstmatch: yes
-    state: present 
+    #- name: Add ssl dhparam file
+    #  lineinfile:
+    #    path: /etc/haproxy/haproxy.cfg
+    #    insertafter: "^.*ssl-default-bind-options.*"
+    #    line: "\tssl-dh-param-file /etc/haproxy/dhparams.pem"
+    #    firstmatch: yes
+    #    state: present 
+    #
+    #- name: Copy HAProxy configuration in place
+    #  set_fact:
+    #    cfg_content: "{{ lookup('template', '{{ role_path }}/templates/haproxy.cfg.j2') }}"
+    #
+    #- name: Merge HAProxy config file
+    #  blockinfile:
+    #    dest: "/etc/haproxy/haproxy.cfg"
+    #    content: '{{ cfg_content }}'
+    #    state: present
 
-- name: Copy HAProxy configuration in place
-  set_fact:
-    cfg_content: "{{ lookup('template', '{{ role_path }}/templates/haproxy.cfg.j2') }}"
+- name: Ensure HAProxy conf is set 
+  template: >
+    src=templates/haproxy.cfg.j2
+    dest=/etc/haproxy/haproxy.cfg
 
-- name: Merge HAProxy config file
-  blockinfile:
-    dest: "/etc/haproxy/haproxy.cfg"
-    content: '{{ cfg_content }}'
-    state: present
+- name: keepalived restart
+  service: name=keepalived state=restarted
 
 - name: HAProxy restart
   service: name=haproxy state=restarted

+ 14 - 0
dev/provisioning/ansible/roles/haproxy/tasks/setup/Debian.yml

@@ -0,0 +1,14 @@
+---
+- name: Install all the {{ ansible_distribution }} packages
+  apt:
+    name: "{{ specific_packages }}"
+    state: present
+
+- name: Create ssl private directory
+  file:
+    path: "{{ ssl_crt_path }}"
+    state: directory
+    owner: root
+    group: root
+    mode: 0750
+

+ 19 - 0
dev/provisioning/ansible/roles/haproxy/tasks/setup/RedHat.yml

@@ -0,0 +1,19 @@
+---
+- name: Install all the {{ ansible_distribution }} packages
+  dnf:
+    name: "{{ specific_packages }}"
+    state: present
+
+- name: Create ssl private directory
+  file:
+    path: "{{ ssl_crt_path }}"
+    state: directory
+    owner: root
+    group: root
+    mode: 0750
+
+    #- name: Mariadb service
+    #  service:
+    #    name: "{{ mariadb_service }}"
+    #    state: started
+    #    enabled: yes

+ 13 - 0
dev/provisioning/ansible/roles/haproxy/tasks/setup/Suse.yml

@@ -0,0 +1,13 @@
+---
+# Install mariadb
+
+- name: Install all the Suse mariadb packages
+  zypper:
+    name: "{{ mariadb_packages }}"
+    state: present
+
+- name: Mariadb service
+  service:
+    name: "{{ mariadb_service }}"
+    state: started
+    enabled: yes

+ 77 - 25
dev/provisioning/ansible/roles/haproxy/templates/haproxy.cfg.j2

@@ -1,41 +1,93 @@
+global
+    {{ log_0 }}
+    {{ log_1 }}
+    {{ log_2 }}
+
+    chroot      /var/lib/haproxy
+    pidfile     {{ haproxy_pid }}
+    maxconn     4000
+    user        haproxy
+    group       haproxy
+    daemon
+
+    # turn on stats unix socket
+    stats       socket {{ haproxy_stats_socket }}
+    stats       timeout 30s
+
+    # Default SSL material locations
+    ca-base     /etc/ssl/certs
+    crt-base    /etc/ssl/private
+
+    # See: https://ssl-config.mozilla.org/#server=haproxy&server-version=2.0.3&config=intermediate
+    ssl-default-bind-ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384
+    {{ ssl_ciphersuites }}
+    {{ ssl_options }}
+    {{ ssl_dh }}
+
+defaults
+    mode                    http
+    log                     global
+    option                  httplog
+    option                  dontlognull
+    #option http-server-close
+    #option forwardfor       except 127.0.0.0/8
+    #option                  redispatch
+    #retries                 3
+    #timeout http-request    10s
+    #timeout queue           1m
+    timeout connect         10s
+    timeout client          1m
+    timeout server          1m
+    #timeout http-keep-alive 10s
+    #timeout check           10s
+    maxconn                 5000
+    #errorfile 400 /etc/haproxy/errors/400.http
+    #errorfile 403 /etc/haproxy/errors/403.http
+    #errorfile 408 /etc/haproxy/errors/408.http
+    #errorfile 500 /etc/haproxy/errors/500.http
+    #errorfile 502 /etc/haproxy/errors/502.http
+    #errorfile 503 /etc/haproxy/errors/503.http
+    #errorfile 504 /etc/haproxy/errors/504.http
 
 frontend http_frontend
-  mode {{ frontend_mode }}
-	bind *:80
-	bind *:443 ssl crt  {{ ssl_crt_path }}/{{ ssl_name }}.pem alpn h2,http/1.1
+    mode {{ frontend_mode }}
+    bind *:80
+    bind *:443 ssl crt  {{ ssl_crt_path }}/{{ ssl_name }}.pem alpn h2,http/1.1
 
 {% if nc_settings is sameas true %}
-	maxconn 20000
-	acl url_discovery_dav path /.well-known/caldav /.well-known/carddav
-	acl url_discovery_inf path /.well-known/webfinger /.well-known/nodeinfo
-	http-request redirect location /remote.php/dav/ code 301 if url_discovery_dav
-	http-request redirect location /index.php%[capture.req.uri] code 301 if url_discovery_inf
-	http-response set-header Strict-Transport-Security max-age=63072000
+	  maxconn 20000
+  	acl url_discovery_dav path /.well-known/caldav /.well-known/carddav
+  	acl url_discovery_inf path /.well-known/webfinger /.well-known/nodeinfo
+  	http-request redirect location /remote.php/dav/ code 301 if url_discovery_dav
+  	http-request redirect location /index.php%[capture.req.uri] code 301 if url_discovery_inf
+  	http-response set-header Strict-Transport-Security max-age=63072000
 {% endif %}
-	option forwardfor
-	option http-server-close
+	  option forwardfor
+  	option http-server-close
 
 {% if network_allowed != '' %}
-	#Only allow some services to be available internally
-	acl network_allowed src {{ network_allowed }}
+  	#Only allow some services to be available internally
+  	acl network_allowed src {{ network_allowed }}
 
 {% endif %}
-	redirect scheme https code 301 if !{ ssl_fc }
-	default_backend http_servers
+  	redirect scheme https code 301 if !{ ssl_fc }
+  	default_backend http_servers
 
 backend http_servers
-	mode {{ backend_mode }}
-	balance {{ backend_balance_method }}
+  	mode {{ backend_mode }}
+	  balance {{ backend_balance_method }}
 {% if nc_settings is sameas true %}
-	option httpchk HEAD /
-	cookie SERVERID insert indirect nocache
-	http-check expect rstatus [2-3][0-9][0-9]
-	http-response set-header X-Frame-Options SAMEORIGIN
-	http-response set-header X-XSS-Protection 1;mode=block
-	http-response set-header X-Content-Type-Options nosniff
-	default-server check maxconn 5000
+  	option httpchk HEAD /
+  	cookie SERVERID insert indirect nocache
+  	http-check expect rstatus [2-3][0-9][0-9]
+  	http-response set-header X-Frame-Options SAMEORIGIN
+  	http-response set-header X-XSS-Protection 1;mode=block
+	  http-response set-header X-Content-Type-Options nosniff
+    {{ backend_default_server }}
 {% endif %}
 
 {% if haproxy_backend_servers != '' %}
-	server {{ haproxy_backend_servers.name }} {{ haproxy_backend_servers.ip }}
+{% for dict_item in haproxy_backend_servers %}
+    server {{ dict_item.name }} {{ dict_item.ip }}
+{% endfor %}
 {% endif %}

+ 25 - 0
dev/provisioning/ansible/roles/haproxy/templates/keepalived.conf.j2

@@ -0,0 +1,25 @@
+! Configuration File for keepalived
+
+global_defs {
+   router_id {{ ansible_facts['nodename'] }}
+}
+
+vrrp_script chk_haproxy {
+   script "/usr/bin/killall -0 haproxy"   # verify the pid existance
+   interval 2                    # check every 2 seconds
+   weight 2                      # add 2 points of prio if OK
+}
+
+vrrp_instance VI_1 {
+  virtual_router_id 51
+  advert_int 1
+  priority {{ keepalived_priority }}
+  state {{ keepalived_state }}
+  interface {{ keepalived_bind_interface }}                # interface to monitor
+  virtual_ipaddress {
+    {{ keepalived_vip }} dev {{ keepalived_bind_interface }} # the virtual IP
+  }
+  track_script {
+    chk_haproxy
+  }
+}

+ 14 - 0
dev/provisioning/ansible/roles/haproxy/vars/Debian.yml

@@ -0,0 +1,14 @@
+specific_packages:
+  - haproxy 
+  - keepalived
+
+log_0: "log         /dev/log  local0"
+log_1: "log         /dev/log  local1 notice"
+log_2: "#"
+
+haproxy_stats_socket: "/run/haproxy/admin.sock mode 660 level admin expose-fd listeners"
+haproxy_pid: "/run/haproxy.pid"
+ssl_ciphersuites: "ssl-default-bind-ciphersuites TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256"
+ssl_options: "ssl-default-bind-options ssl-min-ver TLSv1.2 no-tls-tickets"
+ssl_dh: "ssl-dh-param-file /etc/haproxy/dhparams.pem"
+backend_default_server: "default-server check maxconn 5000"

+ 15 - 0
dev/provisioning/ansible/roles/haproxy/vars/RedHat.yml

@@ -0,0 +1,15 @@
+specific_packages:
+  - haproxy 
+  - keepalived
+  - python2-cryptography
+
+log_0: "log         127.0.0.1 local2"
+log_1: "#"
+log_2: "#"
+
+haproxy_stats_socket: "/var/lib/haproxy/stats"
+haproxy_pid: "/var/run/haproxy.pid"
+ssl_ciphersuites: "#"
+ssl_options: "#"
+ssl_dh: "tune.ssl.default-dh-param 2048"
+backend_default_server: "#"

+ 9 - 0
dev/provisioning/ansible/roles/haproxy/vars/Suse.yml

@@ -0,0 +1,9 @@
+mariadb_packages:
+  - mariadb
+  - python3-PyMySQL
+
+mariadb_service: mariadb
+mariadb_config_file: /etc/my.cnf.d/server.cnf
+mariadb_socket: /run/mysql/mysql.sock
+mariadb_config_file_owner: root
+mariadb_config_file_group: root

+ 6 - 7
dev/provisioning/ansible/roles/mariadb/defaults/main.yml

@@ -29,13 +29,12 @@ mariadb_database:
     target: omit
 
 # Add mariabd users
-# default create nothing
-mariadb_user: 
-  - name: web
-    password: secret
-    #host: 10.90.90.14 (replaced with global vars when calling role)
-    priv: 'nextcloudb.*:ALL,GRANT'
-    encrypted: false
+# (replaced with global vars when calling role)
+db_users:
+  - { name: 'web', password: 'secret', host: '192.168.56.15' }
+
+mariadb_user_priv: 'nextcloudb.*:ALL,GRANT'
+mariadb_user_encrypted: false
 
 # Specify slow query log
 mariadb_slow_query_log_enabled: false

+ 6 - 7
dev/provisioning/ansible/roles/mariadb/tasks/database/users.yml

@@ -3,13 +3,12 @@
 - name: Ensure Mariadb users are present.
   mysql_user:
     name: "{{ item.name }}"
-    #host: "{{ item.host | default('localhost') }}"
-    host: "{{ app_bind_address | default('localhost') }}"
+    host: "{{ item.host | default('localhost') }}"
     password: "{{ item.password }}"
-    priv: "{{ item.priv | default('*.*:USAGE') }}"
-    state: "{{ item.state | default('present') }}"
-    append_privs: "{{ item.append_privs | default('no') }}"
-    encrypted: "{{ item.encrypted | default('no') }}"
+    priv: "{{ mariadb_user_priv | default('*.*:USAGE') }}"
+    state: "present"
+    append_privs: "no"
+    encrypted: "{{ mariadb_user_encrypted | default('no') }}"
     login_unix_socket: "{{ mariadb_socket }}"
-  with_items: "{{ mariadb_user }}"
+  with_items: "{{ db_users }}"
   no_log: true

+ 16 - 12
dev/provisioning/ansible/roles/nextcloud/defaults/main.yml

@@ -1,6 +1,12 @@
 ---
+# [NEXTCLOUD CONFIG]
+ssl_name: "nextcloud.test"
+nc_trusted_domain: "127.0.0.1"
+nextcloud_ipv6: false
+nc_multiple: ""
+
 # defaults file for nextcloud
-NEXTCLOUD_VERSION: 24.0.5 # overload from the role
+NEXTCLOUD_VERSION: 24.0.6 # overload from the role
 NEXTCLOUD_TARBALL: "nextcloud-{{ NEXTCLOUD_VERSION }}.tar.bz2"
 NEXTCLOUD_URL: "https://download.nextcloud.com/server/releases/{{ NEXTCLOUD_TARBALL }}"
 NEXTCLOUD_GPG: "https://nextcloud.com/nextcloud.asc"
@@ -28,13 +34,7 @@ nc_pm_max_spare_servers: 3
 use_redis_server: false    # overload from the role
 redis_host: "127.0.0.1"    # overload from the role
 
-# [NEXTCLOUD CONFIG]
-nextcloud_trusted_domain: "nextcloud.test"
-nextcloud_ipv6: false
-debug_speed: false       # overload from the role
-
-nextcloud_instance_name: "{{ nextcloud_trusted_domain }}"
-
+# [WEB CONFIG]
 nextcloud_install_websrv: true
 nextcloud_websrv: "apache2"  # "apache2" | "nginx"
 nextcloud_disable_websrv_default_site: false
@@ -42,6 +42,10 @@ nextcloud_websrv_template: "templates/{{ nextcloud_websrv }}_nc.j2"
 nc_data_dir: "/srv/data"
 nc_admin_name: "pedro"
 nc_admin_pwd: "pedro"
+#
+nfs_server: "127.0.0.1"
+nfs_data_path: "/nextcloud/data"
+nfs_web_path: "/nextcloud/web"
 
 nc_loglevel: 2
 nc_log_rotate_size: 10485760
@@ -59,8 +63,8 @@ nextcloud_config_settings:
   - { name: 'mail_domain', value: 'uclouvain.be' }
   - { name: 'mail_smtphost', value: 'smtp.sgsi.ucl.ac.be' }
   - { name: 'mail_smtpauthtype', value: 'LOGIN' }
-  - { name: 'overwrite.cli.url', value: 'https://{{ nextcloud_trusted_domain }}' }
-  - { name: 'overwritehost', value: 'nextcloud.test' }
+  - { name: 'overwrite.cli.url', value: 'https://{{ ssl_name }}' }
+  - { name: 'overwritehost', value: '{{ ssl_name }}' }
   - { name: 'overwriteprotocol', value: 'https' }
 
 #php /var/www/html/occ config:system:set share_folder --value="/Shared"
@@ -68,8 +72,8 @@ nextcloud_config_settings:
 # [DATABASE]
 db_host: "127.0.0.1" # overload from the role
 nc_db_name: "nextcloudb"
-nc_db_user: "web"
-nc_db_password: "secret"
+nc_db_user: "web" # overload from the role
+nc_db_password: "secret" # overload from the role
 
 # [APPS]
 nextcloud_apps:

+ 70 - 18
dev/provisioning/ansible/roles/nextcloud/tasks/main.yml

@@ -1,38 +1,61 @@
 ---
 # tasks file for nextcloud
 
-- name: Main... Check Nextcloud debug speed
-  set_fact:
-    debug_speed_check: "{{ debug_speed }}"
-
-# include os specific tasks
 - include_tasks: "prep_os/{{ ansible_os_family }}.yml"
 
-# install required packages
 - include_tasks: "prep_php/{{ ansible_os_family }}.yml"
 
-- name: Main... Check if a mysql/mariadb database is available
-  shell: mysql --host={{ db_host }} --user={{ nc_db_user }} --password={{ nc_db_password }} -e 'SHOW DATABASES;' | grep -cx {{ nc_db_name }}
-  register: dbstatus
-  failed_when: "dbstatus.stdout|int != 1"
-  no_log: true
+- name: Main... Create shared directories
+  file:
+    path: "{{ item }}"
+    state: directory
+    owner: "{{ nextcloud_websrv_user }}"
+    group: "{{ nextcloud_websrv_group }}"
+    mode: 0770
+  with_items:
+    - "{{ nc_data_dir }}"
+    - "{{ http_webroot }}/nextcloud"
+  when: nc_multiple != ""
 
-- name: Main... Check Nextcloud installed
+- name: Main... Mount shared directories
+  mount:
+    src: "{{ nfs_server }}:{{ item.src }}"
+    path: "{{ item.path }}"
+    state: mounted
+    fstype: nfs 
+    opts: nosharecache,context="system_u:object_r:httpd_sys_rw_content_t:s0"
+  with_items:
+    - { src: "{{ nfs_data_path }}", path: "{{ nc_data_dir }}" }
+    - { src: "{{ nfs_web_path }}",  path: "{{ http_webroot }}/nextcloud" }
+  when: nc_multiple != ""
+
+- name: Main... Check if Nextcloud is downloaded
   stat:
     path: "{{ http_webroot }}/nextcloud/index.php"
-  register: nc_nextcloud_installed
+  register: nc_nextcloud_downloaded
 
 - name: Main... Download and Install Nextcloud
   include_tasks: "nc_download.yml"
-  when: (nc_nextcloud_installed.stat.isreg is undefined) or (not nc_nextcloud_installed.stat.isreg)
+  when: (nc_nextcloud_downloaded.stat.isreg is undefined) or (not nc_nextcloud_downloaded.stat.isreg)
+
+- name: Main... Check Nextcloud status
+  become_user: "{{ nextcloud_websrv_user }}"
+  become: true
+  shell: "{{ php_bin }} occ status --output=json"
+  args:
+    chdir: "{{ http_webroot }}/nextcloud"
+  register: jsoncontent
+
+- name: Main... Set Nextcloud variables status
+  set_fact:
+    nc_status: "{{ jsoncontent.stdout | from_json }}"
 
-- name: Main... Check Nextcloud is installed
-  shell: grep installed {{ http_webroot }}/nextcloud/config/config.php | grep true | wc -l
-  register: nc_installation_configured
+- name: Main... The nc_status content
+  debug: var=nc_status
 
 - name: Main... Install nextcloud
   include_tasks: nc_install.yml
-  when: nc_installation_configured.stdout|int == 0
+  when: nc_status.installed|bool == false
 
 - name: Main... Check Selinux
   include_tasks: "selinux.yml"
@@ -42,6 +65,34 @@
 
 - name: Main... Setup nextcloud
   include_tasks: nc_setup.yml
+  when: nc_status.installed|bool == false
+
+    #- name: Setup... Set Trusted Local Domain
+    #  become_user: "{{ nextcloud_websrv_user }}"
+    #  become: true
+    #  shell: "{{ php_bin }} occ config:system:set trusted_domains 0 --value={{ nc_trusted_domain }}"
+    #  args:
+    #    chdir: "{{ http_webroot }}/nextcloud"
+
+- name: Setup... Configure Cron
+  cron:
+    name: "Nextcloud Cronjob"
+    minute: "*/{{ nc_cron_period }}"
+    user: "{{ nextcloud_websrv_user }}"
+    job: "{{ php_bin }} -f {{ http_webroot }}/nextcloud/cron.php"
+    cron_file: "nextcloud"
+  when: (nc_background_cron | bool)
+
+  #- name: Main... Setting stronger directories ownership
+  #  file:
+  #    path: "{{ item }}"
+  #    state: directory
+  #    owner: "{{ nextcloud_websrv_user }}"
+  #    group: "{{ nextcloud_websrv_group }}"
+  #    mode: 0770
+  #  with_items:
+  #    - "{{ nc_data_dir }}"
+  #    - "{{ http_webroot }}/nextcloud"
 
 - name: Main... Restart {{ http_service_name }} service
   service:
@@ -54,3 +105,4 @@
   shell: "{{ php_bin }} -f cron.php"
   args:
     chdir: "{{ http_webroot }}/nextcloud"
+

+ 17 - 6
dev/provisioning/ansible/roles/nextcloud/tasks/nc_install.yml

@@ -8,17 +8,29 @@
     path: "{{ http_webroot }}/nextcloud/config/config.php"
     state: absent
 
+- name: Install... Create custom_apps directory
+  file:
+    path: "{{ http_webroot }}/nextcloud/custom_apps"
+    state: directory
+    owner: "{{ nextcloud_websrv_user }}"
+    group: "{{ nextcloud_websrv_group }}"
+    mode: 0770
+
 - name: Install... Create data directory
   file:
-    path: "{{ item }}"
+    path: "{{ nc_data_dir }}"
     state: directory
     owner: "{{ nextcloud_websrv_user }}"
     group: "{{ nextcloud_websrv_group }}"
     mode: 0770
-  with_items:
-    - "{{ nc_data_dir }}"
-    - "{{ http_webroot }}/nextcloud/custom_apps"
-      
+  when: nc_multiple == ""
+
+- name: Install... Check if a mysql/mariadb database is available
+  shell: mysql --host={{ db_host }} --user={{ nc_db_user }} --password={{ nc_db_password }} -e 'SHOW DATABASES;' | grep -cx {{ nc_db_name }}
+  register: dbstatus
+  failed_when: "dbstatus.stdout|int != 1"
+  no_log: true
+
 - name: Install... First setup Nextcloud
   become_user: "{{ nextcloud_websrv_user }}"
   become: true
@@ -32,4 +44,3 @@
   file:
     path: "{{ http_webroot }}/nextcloud/config/config.sample.php"
     state: absent
-

+ 49 - 0
dev/provisioning/ansible/roles/nextcloud/tasks/nc_multiple.yml

@@ -0,0 +1,49 @@
+---
+#########
+# Run command line installation.
+# the web server must be running by now in order to launch the installation
+
+- name: Install... Removing possibly old or incomplete config.php
+  file:
+    path: "{{ http_webroot }}/nextcloud/config/config.php"
+    state: absent
+
+- name: Install... Create custom_apps directory
+  file:
+    path: "{{ http_webroot }}/nextcloud/custom_apps"
+    state: directory
+    owner: "{{ nextcloud_websrv_user }}"
+    group: "{{ nextcloud_websrv_group }}"
+    mode: 0770
+
+- name: Install... Create data directory
+  file:
+    path: "{{ nc_data_dir }}"
+    state: directory
+    owner: "{{ nextcloud_websrv_user }}"
+    group: "{{ nextcloud_websrv_group }}"
+    mode: 0770
+
+- name: Install... Mount NFS directory (or not)
+  mount:
+    src: "{{ nfs_server }}:{{ nfs_path }}"
+    path: "{{ nc_data_dir }}"
+    state: mounted
+    fstype: nfs 
+    opts: nosharecache,context="system_u:object_r:httpd_sys_rw_content_t:s0"
+  when: nfs_data_dir
+
+- name: Install... First setup Nextcloud
+  become_user: "{{ nextcloud_websrv_user }}"
+  become: true
+  shell: "{{ php_bin }} occ maintenance:install --database=mysql --database-host={{ db_host }} --database-name={{ nc_db_name }} --database-user={{ nc_db_user }} --database-pass={{ nc_db_password }} --admin-user={{ nc_admin_name }} --admin-pass={{ nc_admin_pwd }} --data-dir={{ nc_data_dir }}"
+  args:
+    chdir: "{{ http_webroot }}/nextcloud"
+    creates: "{{ http_webroot }}/nextcloud/config/config.php"
+  register: setup_nc
+  when: "numbertables.stdout|int == 0"
+
+- name: Install... Removing possibly sample config
+  file:
+    path: "{{ http_webroot }}/nextcloud/config/config.sample.php"
+    state: absent

+ 9 - 11
dev/provisioning/ansible/roles/nextcloud/tasks/nc_setup.yml

@@ -16,10 +16,17 @@
     group: "{{ nextcloud_websrv_group }}"
     mode: 0640
 
-- name: Setup... Set Trusted Domains
+    #- name: Setup... Set Trusted Local Domain
+    #  become_user: "{{ nextcloud_websrv_user }}"
+    #  become: true
+    #  shell: "{{ php_bin }} occ config:system:set trusted_domains 0 --value={{ nc_trusted_domain }}"
+    #  args:
+    #    chdir: "{{ http_webroot }}/nextcloud"
+
+- name: Setup... Set Master SSL Nextcloud Domain
   become_user: "{{ nextcloud_websrv_user }}"
   become: true
-  shell: "{{ php_bin }} occ config:system:set trusted_domains 0 --value={{ nextcloud_trusted_domain }}"
+  shell: "{{ php_bin }} occ config:system:set trusted_domains 1 --value={{ ssl_name }}"
   args:
     chdir: "{{ http_webroot }}/nextcloud"
 
@@ -123,15 +130,6 @@
   register: nc_apps_installed
   failed_when: nc_apps_installed.rc >= 2
 
-- name: Setup... Configure Cron
-  cron:
-    name: "Nextcloud Cronjob"
-    minute: "*/{{ nc_cron_period }}"
-    user: "{{ nextcloud_websrv_user }}"
-    job: "{{ php_bin }} -f {{ http_webroot }}/nextcloud/cron.php"
-    cron_file: "nextcloud"
-  when: (nc_background_cron | bool)
-
 - name: Setup... Set Cron method to Crontab
   become_user: "{{ nextcloud_websrv_user }}"
   become: true

+ 0 - 3
dev/provisioning/ansible/roles/nextcloud/tasks/prep_os/CentOS.yml

@@ -20,7 +20,6 @@
     disable_gpg_check: yes
     validate_certs: no
     state: latest
-  when: not debug_speed_check
 
 - name: Prep OS... import key from Collabora centos{{ ansible_distribution_major_version|int }} repo
   ansible.builtin.rpm_key:
@@ -51,7 +50,6 @@
     name: '*'
     update_cache: true
     state: latest
-  when: not debug_speed_check
 
 - name: Prep OS... install needed packages
   dnf:
@@ -61,7 +59,6 @@
       - mariadb
     state: latest
     enablerepo: epel
-  when: not debug_speed_check
 
 - name: Prep OS... Ensure Apache is installed on {{ ansible_facts['distribution'] }}
   dnf:

+ 0 - 5
dev/provisioning/ansible/roles/nextcloud/tasks/prep_php/CentOS.yml

@@ -5,22 +5,18 @@
     state: latest
     disable_gpg_check: yes
     validate_certs: no
-  when: not debug_speed_check
 
 - name: Prep php... disable all the php repositories
   shell: yum-config-manager --disable 'remi-php*'
-  when: not debug_speed_check
 
 - name: Prep php... enable the repo php{{ php_version | replace(".","") }}
   shell: yum-config-manager --enable remi-php{{ php_version | replace(".","") }}
-  when: not debug_speed_check
 
 - name: Prep php... update os
   dnf:
     name: '*'
     update_cache: true
     state: latest
-  when: not debug_speed_check
 
 - name: Prep php... install needed packages
   dnf:
@@ -56,7 +52,6 @@
       - php{{ php_version | replace(".","") }}-php-phar 
       - php{{ php_version | replace(".","") }}-php-opcache
     state: latest
-  when: not debug_speed_check
 
 - name: Prep php... Set php env for {{ ansible_facts['distribution'] }}
   set_fact: 

+ 14 - 0
dev/provisioning/ansible/roles/nextcloud/templates/nc_config.php.j2

@@ -0,0 +1,14 @@
+<?php
+$CONFIG = array (
+  'datadirectory' => '{{ nc_data_dir }}',
+  'dbtype' => 'mysql',
+  'version' => '{{ nc_status.version }}',
+  'dbname' => '{{ nc_db_name }}',
+  'dbhost' => '{{ db_host }}',
+  'dbport' => '',
+  'dbtableprefix' => 'oc_',
+  'mysql.utf8mb4' => true,
+  'dbuser' => '{{ nc_db_user }}',
+  'dbpassword' => '{{ nc_db_password }}',
+  'installed' => true,
+);

+ 1 - 1
dev/provisioning/ansible/roles/nextcloud/templates/nextcloud_apache2.j2

@@ -2,7 +2,7 @@ Listen 8000
 <VirtualHost *:8000>
     # Nextcloud dir
     DocumentRoot {{ http_webroot }}/nextcloud/
-    <Directory {{ http_webroot }}>/nextcloud/>
+    <Directory {{ http_webroot }}/nextcloud/>
         Options Indexes FollowSymLinks
         Require all granted
         AllowOverride All

+ 3 - 2
dev/provisioning/install/Centos_7.sh → dev/provisioning/bash/Centos_7.sh

@@ -1,8 +1,9 @@
 #!/usr/bin/env bash
 
-yum install -y python3 dnf \
-               epel-release yum-utils \
+yum install -y yum-utils \
                bash-completion mlocate \
                bzip2 wget curl \
                libselinux-python3 \
                policycoreutils-python
+
+yum install -y nfs-utils nfs-utils-lib portmap

+ 16 - 0
dev/provisioning/bash/common.sh

@@ -0,0 +1,16 @@
+#!/bin/bash
+pkgmanager=""
+if [[ -z "$1" ]]; then
+  echo "!! box variable not set !!"
+  exit 1
+fi
+if [[ $1 == centos* ]]; then
+   pkgmanager="yum"
+   yum install -y python3 dnf epel-release
+elif [[ $1 == ubuntu* ]]; then
+  pkgmanager="apt-get"
+else
+  echo "box unsupported in provision bash common script !"
+  exit 1
+fi
+#$pkgmanager install -y vim

+ 23 - 0
dev/provisioning/bash/nfs-server.sh

@@ -0,0 +1,23 @@
+#!/bin/bash
+
+yum install nfs-utils
+mkdir /nextcloud
+mkdir /nextcloud/data
+mkdir /nextcloud/web
+chmod -R 755 /nextcloud/data
+chmod -R 755 /nextcloud/web
+chown 48:48 /nextcloud/data
+chown 48:48 /nextcloud/web
+systemctl enable rpcbind
+systemctl enable nfs-server
+systemctl enable nfs-lock
+systemctl enable nfs-idmap
+systemctl start rpcbind
+systemctl start nfs-server
+systemctl start nfs-lock
+systemctl start nfs-idmap
+cat > /etc/exports <<EOD
+/nextcloud/web    192.168.56.0/255.255.255.0(rw,sync,no_root_squash,no_all_squash)
+/nextcloud/data   192.168.56.0/255.255.255.0(rw,sync,no_root_squash,no_all_squash)
+EOD
+systemctl restart nfs-server

+ 16 - 0
dev/provisioning/bash/web-setup.sh

@@ -0,0 +1,16 @@
+#!/bin/bash
+
+# Install apache
+/usr/bin/apt-get -y install apache2
+cat > /var/www/html/index.html <<EOD
+<html><head><title>${HOSTNAME}</title></head><body><h1>${HOSTNAME}</h1>
+<p>This is the default web page for ${HOSTNAME}.</p>
+</body></html>
+EOD
+
+sed -i 's/80/8000/g' /etc/apache2/ports.conf 
+sed -i 's/80/8000/g' /etc/apache2/sites-enabled/000-default.conf
+
+# Log the X-Forwarded-For
+perl -pi -e  's/^LogFormat "\%h (.* combined)$/LogFormat "%h %{X-Forwarded-For}i $1/' /etc/apache2/apache2.conf
+/usr/sbin/service apache2 restart

+ 40 - 6
report/Projet_brevet.md

@@ -45,12 +45,12 @@ C'est un système de stockage hiérarchique qui fournit un accès partagé aux d
 SharePoint est une solution de stockage dans un cloud publique en modèle SaaS (Software as a Service ou Logiciel en tant que Service) : entièrement géré et hébergé par Microsoft. Cette solution est parfaitement intégrée à la suite de logiciels bureautique MS Office 365. Il s'agit d'un espace de travail collaboratif partagé, mais exclusivement disponible pour les utilisateurs de l'UCLouvain. L'utilisation de Sharepoint se fait en ligne via un navigateur. Il est possible de l'intégrer davantage (synchronisation, édition locale, etc) à l'environnement de travail via un client OneDrive (mais pas pour un environnement GNU/Linux).  
 Pour une utilisation plus individuelle, OneDrive est plus approprié[^2]. OneDrive (via un compte UCLouvain) permet de stocker et sauvegarder de grande quantité de données en toute sécurité dans l'UE (respectant les recommandations GDPR). Mais les données ne sont pérennes que pour un utilisateur de l'UCLouvain: si ce dernier quitte l'institution, les données disparaissent.  
 
-Les solutions de stockage de Microsoft ne sont en revanche pas ou peu adaptées pour des données sous environnement GNU/Linux. En outre, collaborer (SharePoint) ou partager des données (SharePoint, OneDrive) avec des utilisateurs extérieurs n'est pas sytématique: il est nécessaire d'être authentifié avec un compte Microsoft (UClouvain, personnel ou d'une autre organisation).
+Les solutions de stockage de Microsoft ne sont en revanche pas ou peu adaptées pour des données sous environnement GNU/Linux. En outre, collaborer (SharePoint) ou partager des données (SharePoint, OneDrive) avec des utilisateurs extérieurs n'est pas automatique: il est nécessaire d'être authentifié avec un compte Microsoft (UClouvain, personnel ou d'une autre organisation).
 
 [^2]: https://intranet.uclouvain.be/fr/myucl/services-informatiques/applications-disponibles.html
 
 Pour une partie du parc de machines individuelles de l'UCLouvain utilisant un environnement de travail GNU/Linux (majoritaires par exemple dans les pôles de recherche ELIC, TFL, MODL, ELEN, INMA, etc) mais aussi pour les clusters HPC et les serveurs interactifs partagés, il n'est donc pas aisé de lier efficacement l'un de ces services de stockage au système de fichiers local (du poste de travail ou de la machine partagée). Pour cela, il existe le service du stockage de masse proposé par la plateforme technologique du CISM.
-Cet espace de stockage offre une très grande capacité aux utilisateurs et de hautes performances. En revanche, celui-ci est essentiellement adapté aux environnements Unix (MacOs ou GNU/Linux) car accessible uniquement via les protocoles SSH et FTP. Comme OASIS, le stockage de masse du CISM est un système de fichier dans un réseau privé (réseau CECI: Universités francophones) en "modèle interne" (entièrement géré et hébergé au CISM).
+Cet espace de stockage offre une très grande capacité aux utilisateurs et de hautes performances. En revanche, celui-ci est essentiellement adapté aux environnements Unix (MacOs ou GNU/Linux) car accessible uniquement via le protocole SSH. Comme OASIS, le stockage de masse du CISM est un système de fichier dans un réseau privé en "modèle interne" (entièrement géré et hébergé au CISM).
 
 # Description du projet
 
@@ -211,7 +211,6 @@ Quelques pistes pour les points encore à définir :
 - base de données SQL (MySQL, Postgresql ?) et réplication
 - base de données haute performance Redis pour la mise en cache des requêtes de base de données (via la RAM)
 - stockage Ceph pour des charges élevées, et possibilité d'utiliser le stockage d'objets compatible S3
-- échange de données inter-sites via protocole GridFTP (sans doute hors MVP)
 
 ## Spécifications de réalisation
 
@@ -276,7 +275,7 @@ Une chaîne d'outils DevOps est une combinaison d'outils participant au dévelop
 Les logiciels virtuels et l'informatique sans serveur minimisent la dépendance matérielle et facilitent le processus de développement et d'évolutivité. Ayant une expertise pratique dans les conteneurs, je souhaite compléter mes connaissances dans la bonne utilisation des environnements virtualisés.
 
 Je souhaite souligner une différence d'apprentissage technique en définissant ici les deux terminologies pour mon projet: la *compétence* et \textcolor{default-uclcolor}{l'expertise}.  
-L'acquisition d'une nouvelle compétence consiste en une bonne compréhension de la technologie sous-jacente en vue de son utilisation appropriée dans le cadre du travail. L'expertise implique un développement et une connissance plus poussée de la compétence acquise, que ce soit pour son importance d'utilisation ou pour son rôle clé dans la conception du produit.
+L'acquisition d'une nouvelle compétence consiste en une bonne compréhension de la technologie sous-jacente en vue de son utilisation appropriée dans le cadre du travail. L'expertise implique un développement et une connaissance plus poussée de la compétence acquise, que ce soit pour son importance d'utilisation ou pour son rôle clé dans la conception du produit.
 
 Je vais donc d'abord acquérir de nouvelles *compétences* et \textcolor{default-uclcolor}{expertises} techniques via l'apprentissage de différents outils utilisés en DevOps.
 
@@ -308,7 +307,7 @@ Cobbler est une *compétence* utile à acquérir ici afin d'intégrer efficaceme
 Dans le contexte de la virtualisation, l'orchestration est le résultat de l'automatisation et la gestion de déploiement de systèmes et de services.  
 
 Capable de déployer, gérer et faire évoluer automatiquement des environnements de développement virtuels, Vagrant peut-être qualifié de structure ("framework") d'orchestration. C'est un logiciel libre et open-source pour la création et la configuration de machines virtuelles. Cet outil orchestre l'utilisation des ressources, la gestion des défaillances, la disponibilité, la configuration, l'état souhaité et l’évolutivité de machines autour de logiciels de virtualisation comme VirtualBox.  
-Vagrant est une *compétence* à acquérir: cela sera nécessaire au cours de l'entièreté tout le processus de développement et de test.
+Vagrant est une *compétence* à acquérir, nécessaire durant tout le processus de développement et de test.
 
 OpenStack est une plate-forme logicielle libre et open source permettant de déployer des infrastructures. Des serveurs virtuels ou autres ressources sont mis à disposition des utilisateurs par ce biais. La technologie possède une architecture modulaire avec plusieurs composants reliés les uns aux autres qui contrôlent divers ensembles matériels pour déployer les différentes ressources des machines virtuelles telles que la puissance de calcul, le stockage ou encore le réseau. Un tableau de bord est disponible, donnant aux administrateurs le contrôle tout en permettant à leurs utilisateurs de provisionner des ressources via une interface Web.  
 Au-delà de la fonctionnalité standard d'infrastructure en tant que service (IaaS), des composants supplémentaires assurent l'orchestration, la gestion des pannes et la gestion des services, entre autres services, pour garantir une haute disponibilité des applications utilisateur.
@@ -369,7 +368,7 @@ Ce sont deux outils dont l'objectif est de définir un état, et de le conserver
 :::
 
 Un équilibreur de charge permet de répartir le trafic sur plusieurs serveurs, ce qui facilite la gestion. Il permet également au réseau d'être plus résilient.  
-HAProxy est un équilibreur de charge open source, capable d'équilibrer n'importe quel service basé sur TCP. Il est couramment utilisé pour équilibrer HTTP et peut aider à résoudre les problèmes de trafic. Acquérir cette *compétence* dans la mise en oeuvre de ce proxy est donc indispensable.  
+HAProxy est un équilibreur de charge open source, capable d'équilibrer n'importe quel service basé sur TCP. Il est couramment utilisé pour équilibrer le flux des requêtes HTTP et peut aider à résoudre les problèmes de trafic. Acquérir cette *compétence* dans la mise en oeuvre de ce proxy est donc indispensable.  
 
 **Les outils de monitoring et alerting**
 
@@ -558,3 +557,38 @@ Dans un second temps, les rôles Ansible ci-dessus seront enrichis:
   - [Siege](https://www.joedog.org/siege-home/)
   - [load-test.io](https://load-test.io/)
   - [flood.io](https://www.flood.io/)
+
+- Etat de l'art:
+
+  - [web-performance-testing](https://techbeacon.com/app-dev-testing/web-performance-testing-18-free-open-source-tools-consider)
+  - [nextcloud features](https://docs.nextcloud.com/server/latest/developer_manual/digging_deeper/index.html)
+  - [deployment_recommendations](https://www.edv2.com/nxt/core/doc/admin/installation/deployment_recommendations.html)
+
+Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec a ante in mi ornare volutpat sed sit amet diam. Nullam interdum erat a augue faucibus, nec tempus tortor sagittis. Aenean imperdiet imperdiet dignissim. Nam aliquam blandit ex, sed molestie nibh feugiat ac. Morbi feugiat convallis semper. Ut et consequat purus. Fusce convallis vehicula enim in vulputate. Curabitur a augue arcu. Mauris laoreet lectus arcu, sed elementum turpis scelerisque id. Etiam porta turpis quis ipsum dictum vulputate. In ut convallis urna, at imperdiet nunc. Cras laoreet, massa lobortis gravida egestas, lacus est pellentesque arcu, imperdiet efficitur nibh dolor vel sapien. Sed accumsan condimentum diam non pellentesque.
+
+Zabbix stocke les données recueillies dans une base de données relationnelle. Prometheus utilise sa propre base de données intégrée dans le processus backend (base de données non relationnelle).
+Zabbix utilise son propre protocole de communication basé sur TCP entre les agents et un serveur. Prometheus utilise HTTP avec des tampons de protocole (+ format texte pour une facilité d'utilisation avec curl).
+
+Zabbix propose sa propre interface Web pour la visualisation. Prometheus offre un outil de base pour explorer les données recueillies et les visualiser dans des graphiques simples sur son serveur natif et offre également un constructeur de tableau de bord minimal, mais il est conçu pour être supporté par des outils de visualisation modernes comme Grafana.
+
+Zabbix "pense" en termes de machines. Prometheus n'a pas cette restriction et conçoit de multiples entités: machines, services, centres de données, etc.
+
+![caption KO](./assets/openstack.png){width=30%}\ ![test backslash](./assets/openstack.png){width=30%}
+
+::: twocolumns
+
+Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec a ante in mi ornare volutpat sed sit amet diam. Nullam interdum erat a augue faucibus, nec tempus tortor sagittis. Aenean imperdiet imperdiet dignissim. Nam aliquam blandit ex, sed molestie nibh feugiat ac. Morbi feugiat convallis semper. Ut et consequat purus. Fusce convallis vehicula enim in vulputate. Curabitur a augue arcu. Mauris laoreet lectus arcu, sed elementum turpis scelerisque id. Etiam porta turpis quis ipsum dictum vulputate. In ut convallis urna, at imperdiet nunc. Cras laoreet, massa lobortis gravida egestas, lacus est pellentesque arcu, imperdiet efficitur nibh dolor vel sapien. Sed accumsan condimentum diam non pellentesque.
+
+Vestibulum cursus nisi risus, sit amet consectetur massa suscipit nec. Sed condimentum, est id iaculis ornare, purus risus finibus felis, posuere congue est nibh eget dui. Maecenas orci erat, commodo auctor justo quis, vestibulum mollis ex. Vivamus sed bibendum turpis.
+
+:::
+
+<!-- needs: --filter pandoc-crossref -->
+
+<div id="fig:coolFig">
+![caption a](./assets/openstack.png){#fig:cfa width=30%}
+![caption b](./assets/openstack.png){#fig:cfb width=30%}
+![caption c](./assets/openstack.png){#fig:cfc width=30%}
+
+Cool figure!
+</div>

BIN
report/Projet_brevet.pdf


BIN
report/assets/dia_nc_dev_improved.png


+ 1 - 1
report/compile.sh

@@ -32,7 +32,7 @@ DATE=$(date "+%d %B %Y")
 #python3 scripts/dia_nc_improved.py
 #python3 scripts/dia_db_cluster.py
 #python3 scripts/dia_nc_dev_simple.py
-#python3 scripts/dia_nc_dev_improved.py
+python3 scripts/dia_nc_dev_improved.py
 
 # without mermaid
 

+ 7 - 2
report/scripts/dia_nc_dev_improved.py

@@ -17,7 +17,7 @@ from diagrams.azure.identity import Groups
 from diagrams.custom import Custom
 
 with Diagram(filename="./assets/dia_nc_dev_improved", show=True, direction="TB"):
-    out = Internet("Intranet")
+    out = Internet("Intranet")    
 
     with Cluster("Load Balancing"):
         openstack_lb = Custom("", "../assets/vagrant.png")
@@ -35,6 +35,11 @@ with Diagram(filename="./assets/dia_nc_dev_improved", show=True, direction="TB")
         openstack_ctrl = Custom("", "../assets/vagrant.png")
         ctrl = [Prometheus(), Grafana()]
 
+    with Cluster("Shared storage"):
+        openstack_nfs = Custom("", "../assets/vagrant.png")
+        nfs_web = storage_1.Storage("NFS web")
+        nfs_data = storage_1.Storage("NFS data")
+
     with Cluster("Database cluster"):
         Custom("", "../assets/vagrant.png")
 
@@ -57,4 +62,4 @@ with Diagram(filename="./assets/dia_nc_dev_improved", show=True, direction="TB")
     paSQL << openstack_web >> ppSQL  
     openstack_ctrl << openstack_lb >> openstack_web
     out >> lb
-    openstack_web >> openstack_ctrl << paSQL
+    openstack_nfs >> openstack_web >> openstack_ctrl << paSQL