From 8e3b79440c2720961a3d7da7a1a4cbc11c8fcd27 Mon Sep 17 00:00:00 2001
From: Alexandru Bogdan Pica <alexandru.pica@1and1.ro>
Date: Sun, 14 Jan 2018 20:16:43 +0200
Subject: [PATCH] Implement external MariaDB and pre-configured Databases
 support

This change allows the following use cases:

1. Using an already-configured MariaDB / MySQL server / Cluster
2. Using already-created DB users, without requiring root DB access.

Update: added external mariadb precheck

Change-Id: I78b0d178306d7c5293b0bf53e445f19f18b4b824
Implements: blueprint external-mariadb-support.
Closes-Bug: #1603121
---
 ansible/group_vars/all.yml                    |  12 +
 ansible/roles/aodh/defaults/main.yml          |   2 +-
 ansible/roles/aodh/tasks/bootstrap.yml        |   6 +-
 ansible/roles/aodh/templates/aodh.conf.j2     |   1 -
 ansible/roles/barbican/defaults/main.yml      |   2 +-
 ansible/roles/barbican/tasks/bootstrap.yml    |   6 +-
 ansible/roles/cinder/defaults/main.yml        |   2 +-
 ansible/roles/cinder/tasks/bootstrap.yml      |   6 +-
 ansible/roles/cloudkitty/defaults/main.yml    |   2 +-
 ansible/roles/cloudkitty/tasks/bootstrap.yml  |   6 +-
 ansible/roles/congress/defaults/main.yml      |   2 +-
 ansible/roles/congress/tasks/bootstrap.yml    |   6 +-
 ansible/roles/designate/defaults/main.yml     |   4 +-
 ansible/roles/designate/tasks/bootstrap.yml   |   6 +-
 ansible/roles/freezer/defaults/main.yml       |   2 +-
 ansible/roles/glance/defaults/main.yml        |   2 +-
 ansible/roles/glance/tasks/bootstrap.yml      |   6 +-
 ansible/roles/gnocchi/defaults/main.yml       |   2 +-
 ansible/roles/gnocchi/tasks/bootstrap.yml     |   6 +-
 .../roles/gnocchi/templates/gnocchi.conf.j2   |   1 -
 ansible/roles/grafana/defaults/main.yml       |   2 +-
 ansible/roles/grafana/tasks/bootstrap.yml     |   4 +
 ansible/roles/haproxy/handlers/main.yml       |   3 +
 .../roles/haproxy/templates/haproxy.cfg.j2    |  10 +-
 ansible/roles/heat/defaults/main.yml          |   2 +-
 ansible/roles/heat/tasks/bootstrap.yml        |   6 +-
 ansible/roles/horizon/defaults/main.yml       |   2 +-
 ansible/roles/horizon/tasks/bootstrap.yml     |   6 +-
 ansible/roles/ironic/defaults/main.yml        |   4 +-
 ansible/roles/ironic/tasks/bootstrap.yml      |   8 +-
 ansible/roles/karbor/defaults/main.yml        |   2 +-
 ansible/roles/karbor/tasks/bootstrap.yml      |   6 +-
 ansible/roles/keystone/defaults/main.yml      |   2 +-
 ansible/roles/keystone/tasks/bootstrap.yml    |   6 +-
 ansible/roles/magnum/defaults/main.yml        |   2 +-
 ansible/roles/magnum/tasks/bootstrap.yml      |   6 +-
 ansible/roles/manila/defaults/main.yml        |   2 +-
 ansible/roles/manila/tasks/bootstrap.yml      |   6 +-
 ansible/roles/mistral/defaults/main.yml       |   2 +-
 ansible/roles/mistral/tasks/bootstrap.yml     |   6 +-
 ansible/roles/murano/defaults/main.yml        |   2 +-
 ansible/roles/murano/tasks/bootstrap.yml      |   6 +-
 ansible/roles/neutron/defaults/main.yml       |   2 +-
 ansible/roles/neutron/tasks/bootstrap.yml     |   6 +-
 ansible/roles/nova/defaults/main.yml          |   4 +-
 ansible/roles/nova/tasks/bootstrap.yml        |   5 +-
 ansible/roles/octavia/defaults/main.yml       |   2 +-
 ansible/roles/octavia/tasks/bootstrap.yml     |   6 +-
 ansible/roles/panko/defaults/main.yml         |   2 +-
 ansible/roles/panko/tasks/bootstrap.yml       |   3 +
 ansible/roles/panko/templates/panko.conf.j2   |   1 -
 .../roles/prechecks/tasks/database_checks.yml |  20 ++
 ansible/roles/prechecks/tasks/main.yml        |   2 +
 ansible/roles/rally/defaults/main.yml         |   2 +-
 ansible/roles/rally/tasks/bootstrap.yml       |   6 +-
 ansible/roles/sahara/defaults/main.yml        |   2 +-
 ansible/roles/sahara/tasks/bootstrap.yml      |   6 +-
 ansible/roles/senlin/defaults/main.yml        |   2 +-
 ansible/roles/senlin/tasks/bootstrap.yml      |   6 +-
 ansible/roles/solum/defaults/main.yml         |   2 +-
 ansible/roles/solum/tasks/bootstrap.yml       |   6 +-
 ansible/roles/solum/templates/solum.conf.j2   |   1 -
 ansible/roles/tacker/defaults/main.yml        |   2 +-
 ansible/roles/tacker/tasks/bootstrap.yml      |   6 +-
 .../roles/telegraf/templates/telegraf.conf.j2 |   2 +-
 ansible/roles/trove/defaults/main.yml         |   2 +-
 ansible/roles/trove/tasks/bootstrap.yml       |   6 +-
 ansible/roles/vitrage/defaults/main.yml       |   4 +-
 ansible/roles/vitrage/tasks/bootstrap.yml     |   6 +-
 ansible/roles/watcher/defaults/main.yml       |   2 +-
 ansible/roles/watcher/tasks/bootstrap.yml     |   6 +-
 ansible/roles/zun/defaults/main.yml           |   2 +-
 ansible/roles/zun/tasks/bootstrap.yml         |   6 +-
 .../reference/external-mariadb-guide.rst      | 211 ++++++++++++++++++
 doc/source/reference/index.rst                |   1 +
 ...rnal-mariadb-support-131440d3c984dd67.yaml |   8 +
 76 files changed, 454 insertions(+), 73 deletions(-)
 create mode 100644 ansible/roles/prechecks/tasks/database_checks.yml
 create mode 100644 doc/source/reference/external-mariadb-guide.rst
 create mode 100644 releasenotes/notes/external-mariadb-support-131440d3c984dd67.yaml

diff --git a/ansible/group_vars/all.yml b/ansible/group_vars/all.yml
index eb1d6e436c..679a951e6c 100644
--- a/ansible/group_vars/all.yml
+++ b/ansible/group_vars/all.yml
@@ -733,3 +733,15 @@ xenserver_username: "root"
 xenserver_connect_protocol: "https"
 # File used to save XenAPI's facts variables formatted as json.
 xenapi_facts_file: "/etc/kolla/xenapi.json"
+
+#############################################
+# MariaDB component-specific database details
+#############################################
+# Whether to configure haproxy to load balance
+# the external MariaDB server(s)
+enable_external_mariadb_load_balancer: "no"
+# Whether to use pre-configured databases / users
+use_preconfigured_databases: "no"
+# whether to use a common, preconfigured user
+# for all component databases
+use_common_mariadb_user: "no"
diff --git a/ansible/roles/aodh/defaults/main.yml b/ansible/roles/aodh/defaults/main.yml
index cc16b2ab72..b1646e28b1 100644
--- a/ansible/roles/aodh/defaults/main.yml
+++ b/ansible/roles/aodh/defaults/main.yml
@@ -45,7 +45,7 @@ aodh_services:
 # Database
 ####################
 aodh_database_name: "aodh"
-aodh_database_user: "aodh"
+aodh_database_user: "{% if use_preconfigured_databases | bool and use_common_mariadb_user | bool %}{{ database_user }}{% else %}aodh{% endif %}"
 aodh_database_address: "{{ database_address }}:{{ database_port }}"
 
 ####################
diff --git a/ansible/roles/aodh/tasks/bootstrap.yml b/ansible/roles/aodh/tasks/bootstrap.yml
index c743a3a459..c4d38ef409 100644
--- a/ansible/roles/aodh/tasks/bootstrap.yml
+++ b/ansible/roles/aodh/tasks/bootstrap.yml
@@ -11,6 +11,8 @@
   register: database
   run_once: True
   delegate_to: "{{ groups['aodh-api'][0] }}"
+  when:
+    - not use_preconfigured_databases | bool
 
 - name: Creating aodh database user and setting permissions
   kolla_toolbox:
@@ -27,6 +29,8 @@
       append_privs: "yes"
   run_once: True
   delegate_to: "{{ groups['aodh-api'][0] }}"
+  when:
+    - not use_preconfigured_databases | bool
 
 - include: bootstrap_service.yml
-  when: database.changed
+  when: database.changed or use_preconfigured_databases | bool
diff --git a/ansible/roles/aodh/templates/aodh.conf.j2 b/ansible/roles/aodh/templates/aodh.conf.j2
index b16d4fc311..e256fa9185 100644
--- a/ansible/roles/aodh/templates/aodh.conf.j2
+++ b/ansible/roles/aodh/templates/aodh.conf.j2
@@ -14,7 +14,6 @@ host = {{ api_interface_address }}
 [database]
 connection = mysql+pymysql://{{ aodh_database_user }}:{{ aodh_database_password }}@{{ aodh_database_address }}/{{ aodh_database_name }}
 
-
 [keystone_authtoken]
 memcache_security_strategy = ENCRYPT
 memcache_secret_key = {{ memcache_secret_key }}
diff --git a/ansible/roles/barbican/defaults/main.yml b/ansible/roles/barbican/defaults/main.yml
index d235d32ae1..199303f052 100644
--- a/ansible/roles/barbican/defaults/main.yml
+++ b/ansible/roles/barbican/defaults/main.yml
@@ -35,7 +35,7 @@ barbican_services:
 # Database
 ####################
 barbican_database_name: "barbican"
-barbican_database_user: "barbican"
+barbican_database_user: "{% if use_preconfigured_databases | bool and use_common_mariadb_user | bool %}{{ database_user }}{% else %}barbican{% endif %}"
 barbican_database_address: "{{ database_address }}:{{ database_port }}"
 
 
diff --git a/ansible/roles/barbican/tasks/bootstrap.yml b/ansible/roles/barbican/tasks/bootstrap.yml
index b9ffb8f1d8..adecdf0faf 100644
--- a/ansible/roles/barbican/tasks/bootstrap.yml
+++ b/ansible/roles/barbican/tasks/bootstrap.yml
@@ -11,6 +11,8 @@
   register: database
   run_once: True
   delegate_to: "{{ groups['barbican-api'][0] }}"
+  when:
+    - not use_preconfigured_databases | bool
 
 - name: Creating barbican database user and setting permissions
   kolla_toolbox:
@@ -27,6 +29,8 @@
       append_privs: "yes"
   run_once: True
   delegate_to: "{{ groups['barbican-api'][0] }}"
+  when:
+    - not use_preconfigured_databases | bool
 
 - include: bootstrap_service.yml
-  when: database.changed
+  when: database.changed or use_preconfigured_databases | bool
diff --git a/ansible/roles/cinder/defaults/main.yml b/ansible/roles/cinder/defaults/main.yml
index 2696353483..57302319e9 100644
--- a/ansible/roles/cinder/defaults/main.yml
+++ b/ansible/roles/cinder/defaults/main.yml
@@ -78,7 +78,7 @@ cinder_backup_pool_pgp_num: "{{ ceph_pool_pgp_num }}"
 # Database
 ####################
 cinder_database_name: "cinder"
-cinder_database_user: "cinder"
+cinder_database_user: "{% if use_preconfigured_databases | bool and use_common_mariadb_user | bool %}{{ database_user }}{% else %}cinder{% endif %}"
 cinder_database_address: "{{ database_address }}:{{ database_port }}"
 
 
diff --git a/ansible/roles/cinder/tasks/bootstrap.yml b/ansible/roles/cinder/tasks/bootstrap.yml
index 7a453a76cc..59f2a0ec15 100644
--- a/ansible/roles/cinder/tasks/bootstrap.yml
+++ b/ansible/roles/cinder/tasks/bootstrap.yml
@@ -11,6 +11,8 @@
   register: database
   run_once: True
   delegate_to: "{{ groups['cinder-api'][0] }}"
+  when:
+    - not use_preconfigured_databases | bool
 
 - name: Creating Cinder database user and setting permissions
   kolla_toolbox:
@@ -27,6 +29,8 @@
       append_privs: "yes"
   run_once: True
   delegate_to: "{{ groups['cinder-api'][0] }}"
+  when:
+    - not use_preconfigured_databases | bool
 
 - include: bootstrap_service.yml
-  when: database.changed
+  when: database.changed or use_preconfigured_databases | bool
diff --git a/ansible/roles/cloudkitty/defaults/main.yml b/ansible/roles/cloudkitty/defaults/main.yml
index 24b1b0e5b1..aa8f3f33da 100644
--- a/ansible/roles/cloudkitty/defaults/main.yml
+++ b/ansible/roles/cloudkitty/defaults/main.yml
@@ -26,7 +26,7 @@ cloudkitty_services:
 # Database
 ####################
 cloudkitty_database_name: "cloudkitty"
-cloudkitty_database_user: "cloudkitty"
+cloudkitty_database_user: "{% if use_preconfigured_databases | bool and use_common_mariadb_user | bool %}{{ database_user }}{% else %}cloudkitty{% endif %}"
 cloudkitty_database_address: "{{ database_address }}:{{ database_port }}"
 
 
diff --git a/ansible/roles/cloudkitty/tasks/bootstrap.yml b/ansible/roles/cloudkitty/tasks/bootstrap.yml
index 32e3605a98..14aeeeceb4 100644
--- a/ansible/roles/cloudkitty/tasks/bootstrap.yml
+++ b/ansible/roles/cloudkitty/tasks/bootstrap.yml
@@ -11,6 +11,8 @@
   register: database
   run_once: True
   delegate_to: "{{ groups['cloudkitty-api'][0] }}"
+  when:
+    - not use_preconfigured_databases | bool
 
 - name: Creating Cloudkitty database user and setting permissions
   kolla_toolbox:
@@ -27,6 +29,8 @@
       append_privs: "yes"
   run_once: True
   delegate_to: "{{ groups['cloudkitty-api'][0] }}"
+  when:
+    - not use_preconfigured_databases | bool
 
 - include: bootstrap_service.yml
-  when: database.changed
+  when: database.changed or use_preconfigured_databases | bool
diff --git a/ansible/roles/congress/defaults/main.yml b/ansible/roles/congress/defaults/main.yml
index e0747bca3c..4d2bac2e92 100644
--- a/ansible/roles/congress/defaults/main.yml
+++ b/ansible/roles/congress/defaults/main.yml
@@ -35,7 +35,7 @@ congress_services:
 # Database
 ####################
 congress_database_name: "congress"
-congress_database_user: "congress"
+congress_database_user: "{% if use_preconfigured_databases | bool and use_common_mariadb_user | bool %}{{ database_user }}{% else %}congress{% endif %}"
 congress_database_address: "{{ database_address }}:{{ database_port }}"
 
 
diff --git a/ansible/roles/congress/tasks/bootstrap.yml b/ansible/roles/congress/tasks/bootstrap.yml
index 054b688f4e..90de9d8ece 100644
--- a/ansible/roles/congress/tasks/bootstrap.yml
+++ b/ansible/roles/congress/tasks/bootstrap.yml
@@ -11,6 +11,8 @@
   register: database
   run_once: True
   delegate_to: "{{ groups['congress-api'][0] }}"
+  when:
+    - not use_preconfigured_databases | bool
 
 - name: Creating congress database user and setting permissions
   kolla_toolbox:
@@ -27,6 +29,8 @@
       append_privs: "yes"
   run_once: True
   delegate_to: "{{ groups['congress-api'][0] }}"
+  when:
+    - not use_preconfigured_databases | bool
 
 - include: bootstrap_service.yml
-  when: database.changed
+  when: database.changed or use_preconfigured_databases | bool
diff --git a/ansible/roles/designate/defaults/main.yml b/ansible/roles/designate/defaults/main.yml
index 33398b39ad..db8e85021e 100644
--- a/ansible/roles/designate/defaults/main.yml
+++ b/ansible/roles/designate/defaults/main.yml
@@ -72,11 +72,11 @@ designate_services:
 # Database
 ####################
 designate_database_name: "designate"
-designate_database_user: "designate"
+designate_database_user: "{% if use_preconfigured_databases | bool and use_common_mariadb_user | bool %}{{ database_user }}{% else %}designate{% endif %}"
 designate_database_address: "{{ database_address }}:{{ database_port }}"
 
 designate_pool_manager_database_name: "designate_pool_manager"
-designate_pool_manager_database_user: "designate_pool_manager"
+designate_pool_manager_database_user: "{% if use_preconfigured_databases | bool and use_common_mariadb_user | bool %}{{ database_user }}{% else %}designate_pool_manager{% endif %}"
 designate_pool_manager_database_address: "{{ database_address }}:{{ database_port }}"
 
 
diff --git a/ansible/roles/designate/tasks/bootstrap.yml b/ansible/roles/designate/tasks/bootstrap.yml
index cd3934e86a..ec69c45d72 100644
--- a/ansible/roles/designate/tasks/bootstrap.yml
+++ b/ansible/roles/designate/tasks/bootstrap.yml
@@ -14,6 +14,8 @@
   with_items:
     - "{{ designate_database_name }}"
     - "{{ designate_pool_manager_database_name }}"
+  when:
+    - not use_preconfigured_databases | bool
 
 - name: Creating Designate databases user and setting permissions
   kolla_toolbox:
@@ -37,6 +39,8 @@
     - database_name: "{{ designate_pool_manager_database_name }}"
       database_user: "{{ designate_pool_manager_database_user }}"
       database_password: "{{ designate_pool_manager_database_password }}"
+  when:
+    - not use_preconfigured_databases | bool
 
 - include: bootstrap_service.yml
-  when: database.changed
+  when: database.changed or use_preconfigured_databases | bool
diff --git a/ansible/roles/freezer/defaults/main.yml b/ansible/roles/freezer/defaults/main.yml
index 002af65d8e..157ecb91c2 100644
--- a/ansible/roles/freezer/defaults/main.yml
+++ b/ansible/roles/freezer/defaults/main.yml
@@ -18,7 +18,7 @@ freezer_services:
 # Database
 ####################
 freezer_database_name: "freezer"
-freezer_database_user: "freezer"
+freezer_database_user: "{% if use_preconfigured_databases | bool and use_common_mariadb_user | bool %}{{ database_user }}{% else %}freezer{% endif %}"
 freezer_database_address: "{{ database_address }}:{{ database_port }}"
 
 
diff --git a/ansible/roles/glance/defaults/main.yml b/ansible/roles/glance/defaults/main.yml
index 4fa18ec816..af772b5fda 100644
--- a/ansible/roles/glance/defaults/main.yml
+++ b/ansible/roles/glance/defaults/main.yml
@@ -43,7 +43,7 @@ glance_pool_pgp_num: "{{ ceph_pool_pgp_num }}"
 # Database
 ####################
 glance_database_name: "glance"
-glance_database_user: "glance"
+glance_database_user: "{% if use_preconfigured_databases | bool and use_common_mariadb_user | bool %}{{ database_user }}{% else %}glance{% endif %}"
 glance_database_address: "{{ database_address }}:{{ database_port }}"
 
 
diff --git a/ansible/roles/glance/tasks/bootstrap.yml b/ansible/roles/glance/tasks/bootstrap.yml
index 73dc2fe62a..2ec00fc28e 100644
--- a/ansible/roles/glance/tasks/bootstrap.yml
+++ b/ansible/roles/glance/tasks/bootstrap.yml
@@ -11,6 +11,8 @@
   register: database
   run_once: True
   delegate_to: "{{ groups['glance-api'][0] }}"
+  when:
+    - not use_preconfigured_databases | bool
 
 - name: Creating Glance database user and setting permissions
   kolla_toolbox:
@@ -27,6 +29,8 @@
       append_privs: "yes"
   run_once: True
   delegate_to: "{{ groups['glance-api'][0] }}"
+  when:
+    - not use_preconfigured_databases | bool
 
 - include: bootstrap_service.yml
-  when: database.changed
+  when: database.changed or use_preconfigured_databases | bool
diff --git a/ansible/roles/gnocchi/defaults/main.yml b/ansible/roles/gnocchi/defaults/main.yml
index 02d83b1c87..6fd320c461 100644
--- a/ansible/roles/gnocchi/defaults/main.yml
+++ b/ansible/roles/gnocchi/defaults/main.yml
@@ -53,7 +53,7 @@ gnocchi_pool_pgp_num: "{{ ceph_pool_pgp_num }}"
 # Database
 ####################
 gnocchi_database_name: "gnocchi"
-gnocchi_database_user: "gnocchi"
+gnocchi_database_user: "{% if use_preconfigured_databases | bool and use_common_mariadb_user | bool %}{{ database_user }}{% else %}gnocchi{% endif %}"
 gnocchi_database_address: "{{ database_address }}:{{ database_port }}"
 
 
diff --git a/ansible/roles/gnocchi/tasks/bootstrap.yml b/ansible/roles/gnocchi/tasks/bootstrap.yml
index 8dcf250225..e8292f56e4 100644
--- a/ansible/roles/gnocchi/tasks/bootstrap.yml
+++ b/ansible/roles/gnocchi/tasks/bootstrap.yml
@@ -11,6 +11,8 @@
   register: database
   run_once: True
   delegate_to: "{{ groups['gnocchi-api'][0] }}"
+  when:
+    - not use_preconfigured_databases | bool
 
 - name: Creating gnocchi database user and setting permissions
   kolla_toolbox:
@@ -27,6 +29,8 @@
       append_privs: "yes"
   run_once: True
   delegate_to: "{{ groups['gnocchi-api'][0] }}"
+  when:
+    - not use_preconfigured_databases | bool
 
 - include: bootstrap_service.yml
-  when: database.changed
+  when: database.changed or use_preconfigured_databases | bool
diff --git a/ansible/roles/gnocchi/templates/gnocchi.conf.j2 b/ansible/roles/gnocchi/templates/gnocchi.conf.j2
index 1f7d6b6e2f..40955035be 100644
--- a/ansible/roles/gnocchi/templates/gnocchi.conf.j2
+++ b/ansible/roles/gnocchi/templates/gnocchi.conf.j2
@@ -35,7 +35,6 @@ workers = {{ openstack_service_workers }}
 [indexer]
 url = mysql+pymysql://{{ gnocchi_database_user }}:{{ gnocchi_database_password }}@{{ gnocchi_database_address }}/{{ gnocchi_database_name }}
 
-
 [keystone_authtoken]
 auth_uri = {{ internal_protocol }}://{{ kolla_internal_fqdn }}:{{ keystone_public_port }}/v3
 project_domain_id = {{ default_project_domain_id }}
diff --git a/ansible/roles/grafana/defaults/main.yml b/ansible/roles/grafana/defaults/main.yml
index 4826612940..1ece342728 100644
--- a/ansible/roles/grafana/defaults/main.yml
+++ b/ansible/roles/grafana/defaults/main.yml
@@ -17,7 +17,7 @@ grafana_services:
 # Database
 ####################
 grafana_database_name: "grafana"
-grafana_database_user: "grafana"
+grafana_database_user: "{% if use_preconfigured_databases | bool and use_common_mariadb_user | bool %}{{ database_user }}{% else %}grafana{% endif %}"
 grafana_database_address: "{{ database_address }}:{{ database_port }}"
 
 ####################
diff --git a/ansible/roles/grafana/tasks/bootstrap.yml b/ansible/roles/grafana/tasks/bootstrap.yml
index 2f744080ef..97727dab7f 100644
--- a/ansible/roles/grafana/tasks/bootstrap.yml
+++ b/ansible/roles/grafana/tasks/bootstrap.yml
@@ -10,6 +10,8 @@
       name: "{{ grafana_database_name }}"
   run_once: True
   delegate_to: "{{ groups['grafana'][0] }}"
+  when:
+    - not use_preconfigured_databases | bool
 
 - name: Creating grafana database user and setting permissions
   kolla_toolbox:
@@ -26,3 +28,5 @@
       append_privs: "yes"
   run_once: True
   delegate_to: "{{ groups['grafana'][0] }}"
+  when:
+    - not use_preconfigured_databases | bool
diff --git a/ansible/roles/haproxy/handlers/main.yml b/ansible/roles/haproxy/handlers/main.yml
index 22cd1fa075..d81ab5d315 100644
--- a/ansible/roles/haproxy/handlers/main.yml
+++ b/ansible/roles/haproxy/handlers/main.yml
@@ -60,3 +60,6 @@
   wait_for:
     host: "{{ kolla_internal_vip_address }}"
     port: "{{ database_port }}"
+  when:
+    - enable_mariadb | bool
+      or enable_external_mariadb_load_balancer | bool
diff --git a/ansible/roles/haproxy/templates/haproxy.cfg.j2 b/ansible/roles/haproxy/templates/haproxy.cfg.j2
index 24a08f78d9..ee59d67b16 100644
--- a/ansible/roles/haproxy/templates/haproxy.cfg.j2
+++ b/ansible/roles/haproxy/templates/haproxy.cfg.j2
@@ -880,18 +880,24 @@ defaults
   timeout server {{ haproxy_server_timeout }}
   timeout check 10s
 
-{% if enable_mariadb | bool %}
+{% if enable_mariadb | bool or enable_external_mariadb_load_balancer | bool %}
 listen mariadb
   mode tcp
   timeout client 3600s
   timeout server 3600s
   option tcplog
   option tcpka
+{% if not enable_external_mariadb_load_balancer | bool %}
   option mysql-check user haproxy post-41
+{% endif %}
   bind {{ kolla_internal_vip_address }}:{{ mariadb_port }}
 {% for host in groups['mariadb'] %}
-  server {{ hostvars[host]['ansible_hostname'] }} {{ hostvars[host]['ansible_' + hostvars[host]['api_interface']]['ipv4']['address'] }}:{{ mariadb_port }} check inter 2000 rise 2 fall 5 {% if not loop.first %}backup{% endif %}
 
+{% if not enable_external_mariadb_load_balancer | bool %}
+  server {{ hostvars[host]['ansible_hostname'] }} {{ hostvars[host]['ansible_' + hostvars[host]['api_interface']]['ipv4']['address'] }}:{{ mariadb_port }} check inter 2000 rise 2 fall 5 {% if not loop.first %}backup{% endif %}
+{% else %}
+  server {{ host }} {{ host }}:{{ mariadb_port }} check inter 2000 rise 2 fall 5 {% if not loop.first %}backup{% endif %}
+{% endif %}
 {% endfor %}
 {% endif %}
 
diff --git a/ansible/roles/heat/defaults/main.yml b/ansible/roles/heat/defaults/main.yml
index 315fe2a10b..d82363d2c5 100644
--- a/ansible/roles/heat/defaults/main.yml
+++ b/ansible/roles/heat/defaults/main.yml
@@ -37,7 +37,7 @@ heat_services:
 # Database
 ####################
 heat_database_name: "heat"
-heat_database_user: "heat"
+heat_database_user: "{% if use_preconfigured_databases | bool and use_common_mariadb_user | bool %}{{ database_user }}{% else %}heat{% endif %}"
 heat_database_address: "{{ database_address }}:{{ database_port }}"
 
 
diff --git a/ansible/roles/heat/tasks/bootstrap.yml b/ansible/roles/heat/tasks/bootstrap.yml
index 1f2daba39d..f47b4305e0 100644
--- a/ansible/roles/heat/tasks/bootstrap.yml
+++ b/ansible/roles/heat/tasks/bootstrap.yml
@@ -11,6 +11,8 @@
   register: database
   run_once: True
   delegate_to: "{{ groups['heat-api'][0] }}"
+  when:
+    - not use_preconfigured_databases | bool
 
 - name: Creating Heat database user and setting permissions
   kolla_toolbox:
@@ -27,6 +29,8 @@
       append_privs: "yes"
   run_once: True
   delegate_to: "{{ groups['heat-api'][0] }}"
+  when:
+    - not use_preconfigured_databases | bool
 
 - include: bootstrap_service.yml
-  when: database.changed
+  when: database.changed or use_preconfigured_databases | bool
diff --git a/ansible/roles/horizon/defaults/main.yml b/ansible/roles/horizon/defaults/main.yml
index e3a6b03b6d..58a2894043 100644
--- a/ansible/roles/horizon/defaults/main.yml
+++ b/ansible/roles/horizon/defaults/main.yml
@@ -45,7 +45,7 @@ horizon_keystone_domain_choices:
 # Database
 ####################
 horizon_database_name: "horizon"
-horizon_database_user: "horizon"
+horizon_database_user: "{% if use_preconfigured_databases | bool and use_common_mariadb_user | bool %}{{ database_user }}{% else %}horizon{% endif %}"
 horizon_database_address: "{{ database_address }}:{{ database_port }}"
 
 ####################
diff --git a/ansible/roles/horizon/tasks/bootstrap.yml b/ansible/roles/horizon/tasks/bootstrap.yml
index 05142ae646..5de5496ec2 100644
--- a/ansible/roles/horizon/tasks/bootstrap.yml
+++ b/ansible/roles/horizon/tasks/bootstrap.yml
@@ -11,6 +11,8 @@
   register: database
   run_once: True
   delegate_to: "{{ groups['horizon'][0] }}"
+  when:
+    - not use_preconfigured_databases | bool
 
 - name: Creating Horizon database user and setting permissions
   kolla_toolbox:
@@ -27,6 +29,8 @@
       append_privs: "yes"
   run_once: True
   delegate_to: "{{ groups['horizon'][0] }}"
+  when:
+    - not use_preconfigured_databases | bool
 
 - include: bootstrap_service.yml
-  when: database.changed
+  when: database.changed or use_preconfigured_databases | bool
diff --git a/ansible/roles/ironic/defaults/main.yml b/ansible/roles/ironic/defaults/main.yml
index 3aae604132..2240f06a07 100644
--- a/ansible/roles/ironic/defaults/main.yml
+++ b/ansible/roles/ironic/defaults/main.yml
@@ -5,11 +5,11 @@ project_name: "ironic"
 # Database
 ####################
 ironic_database_name: "ironic"
-ironic_database_user: "ironic"
+ironic_database_user: "{% if use_preconfigured_databases | bool and use_common_mariadb_user | bool %}{{ database_user }}{% else %}ironic{% endif %}"
 ironic_database_address: "{{ database_address }}:{{ database_port }}"
 
 ironic_inspector_database_name: "ironic_inspector"
-ironic_inspector_database_user: "ironic_inspector"
+ironic_inspector_database_user: "{% if use_preconfigured_databases | bool and use_common_mariadb_user | bool %}{{ database_user }}{% else %}ironic_inspector{% endif %}"
 ironic_inspector_database_address: "{{ database_address }}:{{ database_port }}"
 
 
diff --git a/ansible/roles/ironic/tasks/bootstrap.yml b/ansible/roles/ironic/tasks/bootstrap.yml
index 5ad3ebf5e7..17d85f8aaf 100644
--- a/ansible/roles/ironic/tasks/bootstrap.yml
+++ b/ansible/roles/ironic/tasks/bootstrap.yml
@@ -16,7 +16,9 @@
       group: "ironic-api"
     - database_name: "{{ ironic_inspector_database_name }}"
       group: "ironic-inspector"
-  when: inventory_hostname in groups[item.group]
+  when:
+    - not use_preconfigured_databases | bool
+    - inventory_hostname in groups[item.group]
 
 - name: Creating Ironic database user and setting permissions
   kolla_toolbox:
@@ -42,7 +44,9 @@
       database_user: "{{ ironic_inspector_database_user }}"
       database_password: "{{ ironic_inspector_database_password }}"
       group: "ironic-inspector"
-  when: inventory_hostname in groups[item.group]
+  when:
+    - not use_preconfigured_databases | bool
+    - inventory_hostname in groups[item.group]
 
 - include: bootstrap_service.yml
   when: database.changed
diff --git a/ansible/roles/karbor/defaults/main.yml b/ansible/roles/karbor/defaults/main.yml
index fe98ff19a0..2be3cc6b4f 100644
--- a/ansible/roles/karbor/defaults/main.yml
+++ b/ansible/roles/karbor/defaults/main.yml
@@ -35,7 +35,7 @@ karbor_services:
 # Database
 ####################
 karbor_database_name: "karbor"
-karbor_database_user: "karbor"
+karbor_database_user: "{% if use_preconfigured_databases | bool and use_common_mariadb_user | bool %}{{ database_user }}{% else %}karbor{% endif %}"
 karbor_database_address: "{{ database_address }}:{{ database_port }}"
 
 
diff --git a/ansible/roles/karbor/tasks/bootstrap.yml b/ansible/roles/karbor/tasks/bootstrap.yml
index 9eab028069..f770733746 100644
--- a/ansible/roles/karbor/tasks/bootstrap.yml
+++ b/ansible/roles/karbor/tasks/bootstrap.yml
@@ -11,6 +11,8 @@
   register: database
   run_once: True
   delegate_to: "{{ groups['karbor-api'][0] }}"
+  when:
+    - not use_preconfigured_databases | bool
 
 - name: Creating Karbor database user and setting permissions
   kolla_toolbox:
@@ -27,6 +29,8 @@
       append_privs: "yes"
   run_once: True
   delegate_to: "{{ groups['karbor-api'][0] }}"
+  when:
+    - not use_preconfigured_databases | bool
 
 - include: bootstrap_service.yml
-  when: database | changed
+  when: database.changed or use_preconfigured_databases | bool
diff --git a/ansible/roles/keystone/defaults/main.yml b/ansible/roles/keystone/defaults/main.yml
index fae9122f78..4f55a40b56 100644
--- a/ansible/roles/keystone/defaults/main.yml
+++ b/ansible/roles/keystone/defaults/main.yml
@@ -39,7 +39,7 @@ keystone_services:
 # Database
 ####################
 keystone_database_name: "keystone"
-keystone_database_user: "keystone"
+keystone_database_user: "{% if use_preconfigured_databases | bool and use_common_mariadb_user | bool %}{{ database_user }}{% else %}keystone{% endif %}"
 keystone_database_address: "{{ database_address }}:{{ database_port }}"
 
 
diff --git a/ansible/roles/keystone/tasks/bootstrap.yml b/ansible/roles/keystone/tasks/bootstrap.yml
index 7401563e27..c4cd3f70c5 100644
--- a/ansible/roles/keystone/tasks/bootstrap.yml
+++ b/ansible/roles/keystone/tasks/bootstrap.yml
@@ -11,6 +11,8 @@
   register: database
   run_once: True
   delegate_to: "{{ groups['keystone'][0] }}"
+  when:
+    - not use_preconfigured_databases | bool
 
 - name: Creating Keystone database user and setting permissions
   kolla_toolbox:
@@ -27,6 +29,8 @@
       append_privs: "yes"
   run_once: True
   delegate_to: "{{ groups['keystone'][0] }}"
+  when:
+    - not use_preconfigured_databases | bool
 
 - include: bootstrap_service.yml
-  when: database.changed
+  when: database.changed or use_preconfigured_databases | bool
diff --git a/ansible/roles/magnum/defaults/main.yml b/ansible/roles/magnum/defaults/main.yml
index 1c2c0d9d19..faef6b827c 100644
--- a/ansible/roles/magnum/defaults/main.yml
+++ b/ansible/roles/magnum/defaults/main.yml
@@ -32,7 +32,7 @@ magnum_services:
 # Database
 ####################
 magnum_database_name: "magnum"
-magnum_database_user: "magnum"
+magnum_database_user: "{% if use_preconfigured_databases | bool and use_common_mariadb_user | bool %}{{ database_user }}{% else %}magnum{% endif %}"
 magnum_database_address: "{{ database_address }}:{{ database_port }}"
 
 
diff --git a/ansible/roles/magnum/tasks/bootstrap.yml b/ansible/roles/magnum/tasks/bootstrap.yml
index aeefb0b9c8..7ae82fdf4f 100644
--- a/ansible/roles/magnum/tasks/bootstrap.yml
+++ b/ansible/roles/magnum/tasks/bootstrap.yml
@@ -11,6 +11,8 @@
   register: database
   run_once: True
   delegate_to: "{{ groups['magnum-api'][0] }}"
+  when:
+    - not use_preconfigured_databases | bool
 
 - name: Creating Magnum database user and setting permissions
   kolla_toolbox:
@@ -27,6 +29,8 @@
       append_privs: "yes"
   run_once: True
   delegate_to: "{{ groups['magnum-api'][0] }}"
+  when:
+    - not use_preconfigured_databases | bool
 
 - include: bootstrap_service.yml
-  when: database.changed
+  when: database.changed or use_preconfigured_databases | bool
diff --git a/ansible/roles/manila/defaults/main.yml b/ansible/roles/manila/defaults/main.yml
index 989009eac1..0d4a5d876e 100644
--- a/ansible/roles/manila/defaults/main.yml
+++ b/ansible/roles/manila/defaults/main.yml
@@ -48,7 +48,7 @@ manila_services:
 ## Database
 #####################
 manila_database_name: "manila"
-manila_database_user: "manila"
+manila_database_user: "{% if use_preconfigured_databases | bool and use_common_mariadb_user | bool %}{{ database_user }}{% else %}manila{% endif %}"
 manila_database_address: "{{ database_address }}:{{ database_port }}"
 
 
diff --git a/ansible/roles/manila/tasks/bootstrap.yml b/ansible/roles/manila/tasks/bootstrap.yml
index 38bc70d12f..fb0d3e7452 100644
--- a/ansible/roles/manila/tasks/bootstrap.yml
+++ b/ansible/roles/manila/tasks/bootstrap.yml
@@ -11,6 +11,8 @@
   register: database
   run_once: True
   delegate_to: "{{ groups['manila-api'][0] }}"
+  when:
+    - not use_preconfigured_databases | bool
 
 - name: Creating Manila database user and setting permissions
   kolla_toolbox:
@@ -27,6 +29,8 @@
       append_privs: "yes"
   run_once: True
   delegate_to: "{{ groups['manila-api'][0] }}"
+  when:
+    - not use_preconfigured_databases | bool
 
 - include: bootstrap_service.yml
-  when: database.changed
+  when: database.changed or use_preconfigured_databases | bool
diff --git a/ansible/roles/mistral/defaults/main.yml b/ansible/roles/mistral/defaults/main.yml
index be70331279..7fa09e23f9 100644
--- a/ansible/roles/mistral/defaults/main.yml
+++ b/ansible/roles/mistral/defaults/main.yml
@@ -35,7 +35,7 @@ mistral_services:
 # Database
 ####################
 mistral_database_name: "mistral"
-mistral_database_user: "mistral"
+mistral_database_user: "{% if use_preconfigured_databases | bool and use_common_mariadb_user | bool %}{{ database_user }}{% else %}mistral{% endif %}"
 mistral_database_address: "{{ database_address }}:{{ database_port }}"
 
 
diff --git a/ansible/roles/mistral/tasks/bootstrap.yml b/ansible/roles/mistral/tasks/bootstrap.yml
index ddd5c83bba..a53df2f6b3 100644
--- a/ansible/roles/mistral/tasks/bootstrap.yml
+++ b/ansible/roles/mistral/tasks/bootstrap.yml
@@ -11,6 +11,8 @@
   register: database
   run_once: True
   delegate_to: "{{ groups['mistral-api'][0] }}"
+  when:
+    - not use_preconfigured_databases | bool
 
 - name: Creating Mistral database user and setting permissions
   kolla_toolbox:
@@ -27,6 +29,8 @@
       append_privs: "yes"
   run_once: True
   delegate_to: "{{ groups['mistral-api'][0] }}"
+  when:
+    - not use_preconfigured_databases | bool
 
 - include: bootstrap_service.yml
-  when: database.changed
+  when: database.changed or use_preconfigured_databases | bool
diff --git a/ansible/roles/murano/defaults/main.yml b/ansible/roles/murano/defaults/main.yml
index 3f58e5a1c7..72429829d6 100644
--- a/ansible/roles/murano/defaults/main.yml
+++ b/ansible/roles/murano/defaults/main.yml
@@ -5,7 +5,7 @@ project_name: "murano"
 # Database
 ####################
 murano_database_name: "murano"
-murano_database_user: "murano"
+murano_database_user: "{% if use_preconfigured_databases | bool and use_common_mariadb_user | bool %}{{ database_user }}{% else %}murano{% endif %}"
 murano_database_address: "{{ database_address }}:{{ database_port }}"
 
 
diff --git a/ansible/roles/murano/tasks/bootstrap.yml b/ansible/roles/murano/tasks/bootstrap.yml
index 8c6e833422..434e50cb21 100644
--- a/ansible/roles/murano/tasks/bootstrap.yml
+++ b/ansible/roles/murano/tasks/bootstrap.yml
@@ -11,6 +11,8 @@
   register: database
   run_once: True
   delegate_to: "{{ groups['murano-api'][0] }}"
+  when:
+    - not use_preconfigured_databases | bool
 
 - name: Creating Murano database user and setting permissions
   kolla_toolbox:
@@ -27,6 +29,8 @@
       append_privs: "yes"
   run_once: True
   delegate_to: "{{ groups['murano-api'][0] }}"
+  when:
+    - not use_preconfigured_databases | bool
 
 - include: bootstrap_service.yml
-  when: database.changed
+  when: database.changed or use_preconfigured_databases | bool
diff --git a/ansible/roles/neutron/defaults/main.yml b/ansible/roles/neutron/defaults/main.yml
index 2efce3c0d3..1f2d2e0e24 100644
--- a/ansible/roles/neutron/defaults/main.yml
+++ b/ansible/roles/neutron/defaults/main.yml
@@ -168,7 +168,7 @@ neutron_services:
 # Database
 ####################
 neutron_database_name: "neutron"
-neutron_database_user: "neutron"
+neutron_database_user: "{% if use_preconfigured_databases | bool and use_common_mariadb_user | bool %}{{ database_user }}{% else %}neutron{% endif %}"
 neutron_database_address: "{{ database_address }}:{{ database_port }}"
 
 
diff --git a/ansible/roles/neutron/tasks/bootstrap.yml b/ansible/roles/neutron/tasks/bootstrap.yml
index fde1004a36..16242365d6 100644
--- a/ansible/roles/neutron/tasks/bootstrap.yml
+++ b/ansible/roles/neutron/tasks/bootstrap.yml
@@ -11,6 +11,8 @@
   register: database
   run_once: True
   delegate_to: "{{ groups['neutron-server'][0] }}"
+  when:
+    - not use_preconfigured_databases | bool
 
 - name: Creating Neutron database user and setting permissions
   kolla_toolbox:
@@ -27,6 +29,8 @@
       append_privs: "yes"
   run_once: True
   delegate_to: "{{ groups['neutron-server'][0] }}"
+  when:
+    - not use_preconfigured_databases | bool
 
 - include: bootstrap_service.yml
-  when: database.changed
+  when: database.changed or use_preconfigured_databases | bool
diff --git a/ansible/roles/nova/defaults/main.yml b/ansible/roles/nova/defaults/main.yml
index 28701c42a4..ebf9404a27 100644
--- a/ansible/roles/nova/defaults/main.yml
+++ b/ansible/roles/nova/defaults/main.yml
@@ -157,11 +157,11 @@ nova_hw_disk_discard: "unmap"
 # Database
 ####################
 nova_database_name: "nova"
-nova_database_user: "nova"
+nova_database_user: "{% if use_preconfigured_databases | bool and use_common_mariadb_user | bool %}{{ database_user }}{% else %}nova{% endif %}"
 nova_database_address: "{{ database_address }}:{{ database_port }}"
 
 nova_api_database_name: "nova_api"
-nova_api_database_user: "nova_api"
+nova_api_database_user: "{% if use_preconfigured_databases | bool and use_common_mariadb_user | bool %}{{ database_user }}{% else %}nova_api{% endif %}"
 nova_api_database_address: "{{ database_address }}:{{ database_port }}"
 
 ####################
diff --git a/ansible/roles/nova/tasks/bootstrap.yml b/ansible/roles/nova/tasks/bootstrap.yml
index 076ddca3d6..19e768371b 100644
--- a/ansible/roles/nova/tasks/bootstrap.yml
+++ b/ansible/roles/nova/tasks/bootstrap.yml
@@ -15,6 +15,8 @@
     - "{{ nova_database_name }}"
     - "{{ nova_database_name }}_cell0"
     - "{{ nova_api_database_name }}"
+  when:
+    - not use_preconfigured_databases | bool
 
 - name: Creating Nova databases user and setting permissions
   kolla_toolbox:
@@ -42,9 +44,10 @@
       database_password: "{{ nova_api_database_password }}"
   run_once: True
   delegate_to: "{{ groups['nova-api'][0] }}"
+  when: database.changed or not use_preconfigured_databases | bool
 
 - include: bootstrap_service.yml
-  when: database.changed
+  when: database.changed or use_preconfigured_databases | bool
 
 - include: bootstrap_xenapi.yml
   when:
diff --git a/ansible/roles/octavia/defaults/main.yml b/ansible/roles/octavia/defaults/main.yml
index 46c06fe605..8ddf478411 100644
--- a/ansible/roles/octavia/defaults/main.yml
+++ b/ansible/roles/octavia/defaults/main.yml
@@ -44,7 +44,7 @@ octavia_services:
 # Database
 ####################
 octavia_database_name: "octavia"
-octavia_database_user: "octavia"
+octavia_database_user: "{% if use_preconfigured_databases | bool and use_common_mariadb_user | bool %}{{ database_user }}{% else %}octavia{% endif %}"
 octavia_database_address: "{{ database_address }}:{{ database_port }}"
 
 
diff --git a/ansible/roles/octavia/tasks/bootstrap.yml b/ansible/roles/octavia/tasks/bootstrap.yml
index 23c609c2f3..26a94c674b 100644
--- a/ansible/roles/octavia/tasks/bootstrap.yml
+++ b/ansible/roles/octavia/tasks/bootstrap.yml
@@ -11,6 +11,8 @@
   register: database
   run_once: True
   delegate_to: "{{ groups['octavia-api'][0] }}"
+  when:
+    - not use_preconfigured_databases | bool
 
 - name: Creating Octavia database user and setting permissions
   kolla_toolbox:
@@ -27,6 +29,8 @@
       append_privs: "yes"
   run_once: True
   delegate_to: "{{ groups['octavia-api'][0] }}"
+  when:
+    - not use_preconfigured_databases | bool
 
 - include: bootstrap_service.yml
-  when: database.changed
+  when: database.changed or use_preconfigured_databases | bool
diff --git a/ansible/roles/panko/defaults/main.yml b/ansible/roles/panko/defaults/main.yml
index 87ce0f6960..ce22d3a369 100644
--- a/ansible/roles/panko/defaults/main.yml
+++ b/ansible/roles/panko/defaults/main.yml
@@ -17,7 +17,7 @@ panko_services:
 # Database
 ####################
 panko_database_name: "panko"
-panko_database_user: "panko"
+panko_database_user: "{% if use_preconfigured_databases | bool and use_common_mariadb_user | bool %}{{ database_user }}{% else %}panko{% endif %}"
 panko_database_port: "{{ mongodb_port if panko_database_type == 'mongodb' else database_port }}"
 panko_database_mongodb_address: "{% for host in groups['mongodb'] %}{{ hostvars[host]['ansible_' + hostvars[host]['api_interface']]['ipv4']['address'] }}:{{ panko_database_port }}{% if not loop.last %},{% endif %}{% endfor %}"
 panko_database_mysql_address: "{{ database_address }}:{{ database_port }}"
diff --git a/ansible/roles/panko/tasks/bootstrap.yml b/ansible/roles/panko/tasks/bootstrap.yml
index 71d3d156ba..fdfc5b16de 100644
--- a/ansible/roles/panko/tasks/bootstrap.yml
+++ b/ansible/roles/panko/tasks/bootstrap.yml
@@ -22,6 +22,7 @@
   run_once: True
   delegate_to: "{{ groups['panko-api'][0] }}"
   when:
+    - not use_preconfigured_databases | bool
     - panko_database_type == "mysql"
 
 - name: Creating Panko mysql database user and setting permissions
@@ -40,8 +41,10 @@
   run_once: True
   delegate_to: "{{ groups['panko-api'][0] }}"
   when:
+    - not use_preconfigured_databases | bool
     - panko_database_type == "mysql"
 
 - include: bootstrap_service.yml
   when: (panko_database_type == "mongodb" and mongodb_panko_database.changed)
          or (panko_database_type == "mysql" and mysql_panko_database.changed)
+         or use_preconfigured_databases | bool
diff --git a/ansible/roles/panko/templates/panko.conf.j2 b/ansible/roles/panko/templates/panko.conf.j2
index 56a315cb3d..05034db1bb 100644
--- a/ansible/roles/panko/templates/panko.conf.j2
+++ b/ansible/roles/panko/templates/panko.conf.j2
@@ -16,7 +16,6 @@ event_connection = mysql+pymysql://{{ panko_database_user }}:{{ panko_database_p
 metering_connection = mysql+pymysql://{{ panko_database_user }}:{{ panko_database_password }}@{{ panko_database_mysql_address }}:{{ panko_database_port }}/{{ panko_database_name }}
 {% endif %}
 
-
 [keystone_authtoken]
 auth_uri = {{ internal_protocol }}://{{ kolla_internal_fqdn }}:{{ keystone_public_port }}
 project_domain_name = {{ default_project_domain_name }}
diff --git a/ansible/roles/prechecks/tasks/database_checks.yml b/ansible/roles/prechecks/tasks/database_checks.yml
new file mode 100644
index 0000000000..883a462b8c
--- /dev/null
+++ b/ansible/roles/prechecks/tasks/database_checks.yml
@@ -0,0 +1,20 @@
+---
+- name: "Check if external mariadb hosts are reachable from the load balancer"
+  wait_for:
+    msg: "The {{ item }} host is not accessible from {{ inventory_hostname }}"
+    host: "{{ item }}"
+    port: "{{ database_port }}"
+  with_items: "{{ groups['mariadb'] }}"
+  when:
+      - not enable_mariadb | bool
+      - enable_external_mariadb_load_balancer | bool
+      - inventory_hostname in groups['haproxy']
+
+- name: "Check if external database address is reachable from all hosts"
+  wait_for:
+    msg: "The {{ database_address }} host is not accessible from {{ inventory_hostname }}"
+    host: "{{ database_address }}"
+    port: "{{ database_port }}"
+  when:
+      - not enable_mariadb | bool
+      - not enable_external_mariadb_load_balancer | bool
diff --git a/ansible/roles/prechecks/tasks/main.yml b/ansible/roles/prechecks/tasks/main.yml
index 8065afb7ff..ae2fbd1808 100644
--- a/ansible/roles/prechecks/tasks/main.yml
+++ b/ansible/roles/prechecks/tasks/main.yml
@@ -11,3 +11,5 @@
 - include: package_checks.yml
 
 - include: user_checks.yml
+
+- include: database_checks.yml
diff --git a/ansible/roles/rally/defaults/main.yml b/ansible/roles/rally/defaults/main.yml
index f3bf0d4a11..c6227150f8 100644
--- a/ansible/roles/rally/defaults/main.yml
+++ b/ansible/roles/rally/defaults/main.yml
@@ -25,5 +25,5 @@ rally_image_full: "{{ rally_image }}:{{ rally_tag }}"
 # Database
 ####################
 rally_database_name: "rally"
-rally_database_user: "rally"
+rally_database_user: "{% if use_preconfigured_databases | bool and use_common_mariadb_user | bool %}{{ database_user }}{% else %}rally{% endif %}"
 rally_database_address: "{{ database_address }}:{{ database_port }}"
diff --git a/ansible/roles/rally/tasks/bootstrap.yml b/ansible/roles/rally/tasks/bootstrap.yml
index 691f75ec7d..d75360c33b 100644
--- a/ansible/roles/rally/tasks/bootstrap.yml
+++ b/ansible/roles/rally/tasks/bootstrap.yml
@@ -11,6 +11,8 @@
   register: database
   run_once: True
   delegate_to: "{{ groups['rally'][0] }}"
+  when:
+    - not use_preconfigured_databases | bool
 
 - name: Creating rally database user and setting permissions
   kolla_toolbox:
@@ -27,6 +29,8 @@
       append_privs: "yes"
   run_once: True
   delegate_to: "{{ groups['rally'][0] }}"
+  when:
+    - not use_preconfigured_databases | bool
 
 - include: bootstrap_service.yml
-  when: database.changed
+  when: database.changed or use_preconfigured_databases | bool
diff --git a/ansible/roles/sahara/defaults/main.yml b/ansible/roles/sahara/defaults/main.yml
index 5bf9dcc974..929f9fc36a 100644
--- a/ansible/roles/sahara/defaults/main.yml
+++ b/ansible/roles/sahara/defaults/main.yml
@@ -30,7 +30,7 @@ sahara_services:
 # Database
 ####################
 sahara_database_name: "sahara"
-sahara_database_user: "sahara"
+sahara_database_user: "{% if use_preconfigured_databases | bool and use_common_mariadb_user | bool %}{{ database_user }}{% else %}sahara{% endif %}"
 sahara_database_address: "{{ database_address }}:{{ database_port }}"
 
 
diff --git a/ansible/roles/sahara/tasks/bootstrap.yml b/ansible/roles/sahara/tasks/bootstrap.yml
index 09206f435a..d82ae2ec6e 100644
--- a/ansible/roles/sahara/tasks/bootstrap.yml
+++ b/ansible/roles/sahara/tasks/bootstrap.yml
@@ -11,6 +11,8 @@
   register: database
   run_once: True
   delegate_to: "{{ groups['sahara-api'][0] }}"
+  when:
+    - not use_preconfigured_databases | bool
 
 - name: Creating sahara database user and setting permissions
   kolla_toolbox:
@@ -27,6 +29,8 @@
       append_privs: "yes"
   run_once: True
   delegate_to: "{{ groups['sahara-api'][0] }}"
+  when:
+    - not use_preconfigured_databases | bool
 
 - include: bootstrap_service.yml
-  when: database.changed
+  when: database.changed or use_preconfigured_databases | bool
diff --git a/ansible/roles/senlin/defaults/main.yml b/ansible/roles/senlin/defaults/main.yml
index 0c27dafd41..65ebf3b7e1 100644
--- a/ansible/roles/senlin/defaults/main.yml
+++ b/ansible/roles/senlin/defaults/main.yml
@@ -25,7 +25,7 @@ senlin_services:
 # Database
 ####################
 senlin_database_name: "senlin"
-senlin_database_user: "senlin"
+senlin_database_user: "{% if use_preconfigured_databases | bool and use_common_mariadb_user | bool %}{{ database_user }}{% else %}senlin{% endif %}"
 senlin_database_address: "{{ database_address }}:{{ database_port }}"
 
 
diff --git a/ansible/roles/senlin/tasks/bootstrap.yml b/ansible/roles/senlin/tasks/bootstrap.yml
index f9a44bda7e..cece215d46 100644
--- a/ansible/roles/senlin/tasks/bootstrap.yml
+++ b/ansible/roles/senlin/tasks/bootstrap.yml
@@ -11,6 +11,8 @@
   register: database
   run_once: True
   delegate_to: "{{ groups['senlin-api'][0] }}"
+  when:
+    - not use_preconfigured_databases | bool
 
 - name: Creating Senlin database user and setting permissions
   kolla_toolbox:
@@ -27,6 +29,8 @@
       append_privs: "yes"
   run_once: True
   delegate_to: "{{ groups['senlin-api'][0] }}"
+  when:
+    - not use_preconfigured_databases | bool
 
 - include: bootstrap_service.yml
-  when: database.changed
+  when: database.changed or use_preconfigured_databases | bool
diff --git a/ansible/roles/solum/defaults/main.yml b/ansible/roles/solum/defaults/main.yml
index e720d4d1e3..434d03bfca 100644
--- a/ansible/roles/solum/defaults/main.yml
+++ b/ansible/roles/solum/defaults/main.yml
@@ -44,7 +44,7 @@ solum_services:
 # Database
 ####################
 solum_database_name: "solum"
-solum_database_user: "solum"
+solum_database_user: "{% if use_preconfigured_databases | bool and use_common_mariadb_user | bool %}{{ database_user }}{% else %}solum{% endif %}"
 solum_database_address: "{{ database_address }}:{{ database_port }}"
 
 
diff --git a/ansible/roles/solum/tasks/bootstrap.yml b/ansible/roles/solum/tasks/bootstrap.yml
index 2df4f7c73e..e8975bbdaa 100644
--- a/ansible/roles/solum/tasks/bootstrap.yml
+++ b/ansible/roles/solum/tasks/bootstrap.yml
@@ -11,6 +11,8 @@
   register: database
   run_once: True
   delegate_to: "{{ groups['solum-api'][0] }}"
+  when:
+    - not use_preconfigured_databases | bool
 
 - name: Creating Solum database user and setting permissions
   kolla_toolbox:
@@ -27,6 +29,8 @@
       append_privs: "yes"
   run_once: True
   delegate_to: "{{ groups['solum-api'][0] }}"
+  when:
+    - not use_preconfigured_databases | bool
 
 - include: bootstrap_service.yml
-  when: database.changed
+  when: database.changed or use_preconfigured_databases | bool
diff --git a/ansible/roles/solum/templates/solum.conf.j2 b/ansible/roles/solum/templates/solum.conf.j2
index 116cc8332d..ba1fa3c30b 100644
--- a/ansible/roles/solum/templates/solum.conf.j2
+++ b/ansible/roles/solum/templates/solum.conf.j2
@@ -58,4 +58,3 @@ memcached_servers = {% for host in groups['memcached'] %}{{ hostvars[host]['ansi
 
 [oslo_messaging_notifications]
 transport_url = {{ notify_transport_url }}
-
diff --git a/ansible/roles/tacker/defaults/main.yml b/ansible/roles/tacker/defaults/main.yml
index f12b40157e..833c014958 100644
--- a/ansible/roles/tacker/defaults/main.yml
+++ b/ansible/roles/tacker/defaults/main.yml
@@ -27,7 +27,7 @@ tacker_services:
 # Database
 ####################
 tacker_database_name: "tacker"
-tacker_database_user: "tacker"
+tacker_database_user: "{% if use_preconfigured_databases | bool and use_common_mariadb_user | bool %}{{ database_user }}{% else %}tacker{% endif %}"
 tacker_database_address: "{{ database_address }}:{{ database_port }}"
 
 ########
diff --git a/ansible/roles/tacker/tasks/bootstrap.yml b/ansible/roles/tacker/tasks/bootstrap.yml
index 2cf830713b..642d791b55 100644
--- a/ansible/roles/tacker/tasks/bootstrap.yml
+++ b/ansible/roles/tacker/tasks/bootstrap.yml
@@ -11,6 +11,8 @@
   register: database
   run_once: True
   delegate_to: "{{ groups['tacker-server'][0] }}"
+  when:
+    - not use_preconfigured_databases | bool
 
 - name: Creating tacker database user and setting permissions
   kolla_toolbox:
@@ -27,6 +29,8 @@
       append_privs: "yes"
   run_once: True
   delegate_to: "{{ groups['tacker-server'][0] }}"
+  when:
+    - not use_preconfigured_databases | bool
 
 - include: bootstrap_service.yml
-  when: database.changed
+  when: database.changed or use_preconfigured_databases | bool
diff --git a/ansible/roles/telegraf/templates/telegraf.conf.j2 b/ansible/roles/telegraf/templates/telegraf.conf.j2
index 9040f95f86..52e67f0ff4 100644
--- a/ansible/roles/telegraf/templates/telegraf.conf.j2
+++ b/ansible/roles/telegraf/templates/telegraf.conf.j2
@@ -74,7 +74,7 @@
   username = "{{ outward_rabbitmq_user }}"
   password = "{{ outward_rabbitmq_password }}"
 {% endif %}
-{% if inventory_hostname in groups['mariadb'] and enable_mariadb | bool %}
+{% if inventory_hostname in groups['mariadb'] and (enable_mariadb or enable_external_mariadb_load_balancer) | bool %}
 [[inputs.mysql]]
   servers = ["{{ database_user }}:{{ database_password }}@{{ mariadb_proto }}({{ api_interface_address }}:{{ database_port }})/"]
   perf_events_statements_digest_text_limit  = 120
diff --git a/ansible/roles/trove/defaults/main.yml b/ansible/roles/trove/defaults/main.yml
index a4fe29b429..0c2ff8959e 100644
--- a/ansible/roles/trove/defaults/main.yml
+++ b/ansible/roles/trove/defaults/main.yml
@@ -38,7 +38,7 @@ trove_services:
 # Database
 ####################
 trove_database_name: "trove"
-trove_database_user: "trove"
+trove_database_user: "{% if use_preconfigured_databases | bool and use_common_mariadb_user | bool %}{{ database_user }}{% else %}trove{% endif %}"
 trove_database_address: "{{ database_address }}:{{ database_port }}"
 
 
diff --git a/ansible/roles/trove/tasks/bootstrap.yml b/ansible/roles/trove/tasks/bootstrap.yml
index 9e79da98a8..f88c95b6ff 100644
--- a/ansible/roles/trove/tasks/bootstrap.yml
+++ b/ansible/roles/trove/tasks/bootstrap.yml
@@ -11,6 +11,8 @@
   register: database
   run_once: True
   delegate_to: "{{ groups['trove-api'][0] }}"
+  when:
+    - not use_preconfigured_databases | bool
 
 - name: Creating trove database user and setting permissions
   kolla_toolbox:
@@ -27,6 +29,8 @@
       append_privs: "yes"
   run_once: True
   delegate_to: "{{ groups['trove-api'][0] }}"
+  when:
+    - not use_preconfigured_databases | bool
 
 - include: bootstrap_service.yml
-  when: database.changed
+  when: database.changed or use_preconfigured_databases | bool
diff --git a/ansible/roles/vitrage/defaults/main.yml b/ansible/roles/vitrage/defaults/main.yml
index 51996e37d6..eb0ba109ad 100644
--- a/ansible/roles/vitrage/defaults/main.yml
+++ b/ansible/roles/vitrage/defaults/main.yml
@@ -52,8 +52,8 @@ vitrage_services:
 ## Database
 #####################
 vitrage_database_name: "vitrage"
-vitrage_database_user: "vitrage"
-vitrage_database_address: "{{ kolla_internal_fqdn }}:{{ database_port }}"
+vitrage_database_user: "{% if use_preconfigured_databases | bool and use_common_mariadb_user | bool %}{{ database_user }}{% else %}vitrage{% endif %}"
+vitrage_database_address: "{{ database_address }}:{{ database_port }}"
 
 ####################
 # Docker
diff --git a/ansible/roles/vitrage/tasks/bootstrap.yml b/ansible/roles/vitrage/tasks/bootstrap.yml
index aa267e1d07..d29120c20e 100644
--- a/ansible/roles/vitrage/tasks/bootstrap.yml
+++ b/ansible/roles/vitrage/tasks/bootstrap.yml
@@ -11,6 +11,8 @@
   register: database
   run_once: True
   delegate_to: "{{ groups['vitrage-api'][0] }}"
+  when:
+    - not use_preconfigured_databases | bool
 
 - name: Creating vitrage database user and setting permissions
   kolla_toolbox:
@@ -27,6 +29,8 @@
       append_privs: "yes"
   run_once: True
   delegate_to: "{{ groups['vitrage-api'][0] }}"
+  when:
+    - not use_preconfigured_databases | bool
 
 - include: bootstrap_service.yml
-  when: database.changed
+  when: database.changed or use_preconfigured_databases | bool
diff --git a/ansible/roles/watcher/defaults/main.yml b/ansible/roles/watcher/defaults/main.yml
index a59d573fd2..854afe1bae 100644
--- a/ansible/roles/watcher/defaults/main.yml
+++ b/ansible/roles/watcher/defaults/main.yml
@@ -35,7 +35,7 @@ watcher_services:
 # Database
 ####################
 watcher_database_name: "watcher"
-watcher_database_user: "watcher"
+watcher_database_user: "{% if use_preconfigured_databases | bool and use_common_mariadb_user | bool %}{{ database_user }}{% else %}watcher{% endif %}"
 watcher_database_address: "{{ database_address }}:{{ database_port }}"
 
 
diff --git a/ansible/roles/watcher/tasks/bootstrap.yml b/ansible/roles/watcher/tasks/bootstrap.yml
index 3060cbe40d..3f80a50239 100644
--- a/ansible/roles/watcher/tasks/bootstrap.yml
+++ b/ansible/roles/watcher/tasks/bootstrap.yml
@@ -11,6 +11,8 @@
   register: database
   run_once: True
   delegate_to: "{{ groups['watcher-api'][0] }}"
+  when:
+    - not use_preconfigured_databases | bool
 
 - name: Creating Watcher database user and setting permissions
   kolla_toolbox:
@@ -27,6 +29,8 @@
       append_privs: "yes"
   run_once: True
   delegate_to: "{{ groups['watcher-api'][0] }}"
+  when:
+    - not use_preconfigured_databases | bool
 
 - include: bootstrap_service.yml
-  when: database.changed
+  when: database.changed or use_preconfigured_databases | bool
diff --git a/ansible/roles/zun/defaults/main.yml b/ansible/roles/zun/defaults/main.yml
index 5c962045fa..840a60c4bc 100644
--- a/ansible/roles/zun/defaults/main.yml
+++ b/ansible/roles/zun/defaults/main.yml
@@ -28,7 +28,7 @@ zun_services:
 ## Database
 ####################
 zun_database_name: "zun"
-zun_database_user: "zun"
+zun_database_user: "{% if use_preconfigured_databases | bool and use_common_mariadb_user | bool %}{{ database_user }}{% else %}zun{% endif %}"
 zun_database_address: "{{ database_address }}:{{ database_port }}"
 
 
diff --git a/ansible/roles/zun/tasks/bootstrap.yml b/ansible/roles/zun/tasks/bootstrap.yml
index 98d0799b38..1c727c7149 100644
--- a/ansible/roles/zun/tasks/bootstrap.yml
+++ b/ansible/roles/zun/tasks/bootstrap.yml
@@ -11,6 +11,8 @@
   register: database
   run_once: True
   delegate_to: "{{ groups['zun-api'][0] }}"
+  when:
+    - not use_preconfigured_databases | bool
 
 - name: Creating Zun database user and setting permissions
   kolla_toolbox:
@@ -27,6 +29,8 @@
       append_privs: "yes"
   run_once: True
   delegate_to: "{{ groups['zun-api'][0] }}"
+  when:
+    - not use_preconfigured_databases | bool
 
 - include: bootstrap_service.yml
-  when: database.changed
+  when: database.changed or use_preconfigured_databases | bool
diff --git a/doc/source/reference/external-mariadb-guide.rst b/doc/source/reference/external-mariadb-guide.rst
new file mode 100644
index 0000000000..cd812148a8
--- /dev/null
+++ b/doc/source/reference/external-mariadb-guide.rst
@@ -0,0 +1,211 @@
+.. _external-mariadb-guide:
+
+================
+External MariaDB
+================
+
+Sometimes, for various reasons (Redundancy, organisational policies, etc.),
+it might be necessary to use an externally managed database.
+This use case can be achieved by simply taking some extra steps:
+
+Requirements
+============
+
+* An existing MariaDB cluster / server, reachable from all of your
+  nodes.
+* If you choose to use preconfigured databases and users
+  (**use_preconfigured_databases** is set to "yes"), databases and
+  user accounts for all enabled services should exist on the
+  database.
+* If you choose not to use preconfigured databases and users
+  (**use_preconfigured_databases** is set to "no"), root access to
+  the database must be available in order to configure databases and
+  user accounts for all enabled services.
+
+Enabling External MariaDB support
+=================================
+
+In order to enable external mariadb support,
+you will first need to disable mariadb deployment,
+by ensuring the following line exists within ``/etc/kolla/globals.yml`` :
+
+.. code-block:: yaml
+
+  enable_mariadb: "no"
+
+.. end
+
+There are two ways in which you can use
+external MariaDB:
+
+Using an already load-balanced MariaDB address (recommended)
+------------------------------------------------------------
+
+If your external database already has a
+load balancer, you will need to do the following:
+
+* Within your inventory file, just add the hostname
+  of the load balancer within the mariadb group,
+  described as below:
+
+Change the following
+
+.. code-block:: ini
+
+  [mariadb:children]
+  control
+
+.. end
+
+so that it looks like below:
+
+.. code-block:: ini
+
+  [mariadb]
+  myexternalmariadbloadbalancer.com
+
+.. end
+
+* Define **database_address** within ``/etc/kolla/globals.yml``
+
+.. code-block:: yaml
+
+  database_address: myexternalloadbalancer.com
+
+.. end
+
+Please note that if **enable_external_mariadb_load_balancer** is
+set to "no" - **default**, the external DB load balancer will need to be
+accessible from all nodes within your deployment, which might
+connect to it.
+
+Using an external MariaDB cluster:
+----------------------------------
+
+Then, you will need to adjust your inventory file:
+
+Change the following
+
+.. code-block:: ini
+
+  [mariadb:children]
+  control
+
+.. end
+
+so that it looks like below:
+
+.. code-block:: ini
+
+  [mariadb]
+  myexternaldbserver1.com
+  myexternaldbserver2.com
+  myexternaldbserver3.com
+
+.. end
+
+If you choose to use haproxy for load balancing between the
+members of the cluster, every node within this group
+needs to be resolvable and reachable and resolvable from all
+the hosts within the **[haproxy:children]**  group
+of your inventory (defaults to **[network]**).
+
+In addition to that, you also need to set the following within
+``/etc/kolla/globals.yml``:
+
+.. code-block:: yaml
+
+  enable_external_mariadb_load_balancer: yes
+
+.. end
+
+Using External MariaDB with a privileged user
+=============================================
+
+In case your MariaDB user is root, just leave
+everything as it is within globals.yml (Except the
+internal mariadb deployment, which should be disabled),
+and set the **database_password** field within
+``/etc/kolla/passwords.yml``
+
+.. code-block:: yaml
+
+  database_password: mySuperSecurePassword
+
+.. end
+
+In case your username is other than **root**, you will
+need to also set it, within ``/etc/kolla/globals.yml``
+
+.. code-block:: yaml
+
+  database_username: "privillegeduser"
+
+.. end
+
+Using preconfigured databases / users:
+======================================
+
+The first step you need to take is the following:
+
+Within ``/etc/kolla/globals.yml``, set the following:
+
+.. code-block:: yaml
+
+  use_preconfigured_databases: "yes"
+
+.. end
+
+Using External MariaDB with separated, preconfigured users and databases
+------------------------------------------------------------------------
+
+In order to achieve this, you will need to define the user names within
+``/etc/kolla/globals.yml``, as illustrated by the example below:
+
+
+.. code-block:: yaml
+
+  keystone_database_user: preconfigureduser1
+  nova_database_user: preconfigureduser2
+
+.. end
+
+You will need to also set the passwords for all databases within
+``/etc/kolla/passwords.yml``
+
+
+However, fortunately, using a common user across
+all databases is also possible.
+
+
+Using External MariaDB with a common user across databases
+----------------------------------------------------------
+
+In order to use a common, preconfigured user across all databases,
+all you need to do is the following:
+
+* Within ``/etc/kolla/globals.yml``, add the following:
+
+.. code-block:: yaml
+
+  use_common_mariadb_user: "yes"
+
+.. end
+
+* Set the database_user within ``/etc/kolla/globals.yml`` to
+  the one provided to you:
+
+.. code-block:: yaml
+
+  database_user: mycommondatabaseuser
+
+.. end
+
+* Set the common password for all components within ``/etc/kolla/passwords.yml```.
+  In order to achieve that you could use the following command:
+
+.. code-block:: console
+
+  sed -i -r -e 's/([a-z_]{0,}database_password:+)$/\1 mycommonpass/gi' /etc/kolla/passwords.yml
+
+.. end
diff --git a/doc/source/reference/index.rst b/doc/source/reference/index.rst
index aa819df04b..7c9dbaa5bd 100644
--- a/doc/source/reference/index.rst
+++ b/doc/source/reference/index.rst
@@ -7,6 +7,7 @@ Reference
    ceph-guide
    central-logging-guide
    external-ceph-guide
+   external-mariadb-guide
    cinder-guide
    cinder-guide-hnas
    designate-guide
diff --git a/releasenotes/notes/external-mariadb-support-131440d3c984dd67.yaml b/releasenotes/notes/external-mariadb-support-131440d3c984dd67.yaml
new file mode 100644
index 0000000000..fe3f8dcf4e
--- /dev/null
+++ b/releasenotes/notes/external-mariadb-support-131440d3c984dd67.yaml
@@ -0,0 +1,8 @@
+---
+features:
+  - >
+    [`blueprint external-mariadb-support <https://blueprints.launchpad.net/kolla-ansible/+spec/external-mariadb-support>`_]
+    Added External MariaDB server/cluster support
+  - Added enable_external_mariadb_load_balancer flag
+  - Added use_preconfigured_databases flag in order to add support for previously created databases / users
+  - Added use_common_mariadb_user in order to allow the use of a common database user across all databases