From efbfd7912b5fff41a3d3060af9e64c35c7e89f3b Mon Sep 17 00:00:00 2001
From: Sam Yaple <sam@yaple.net>
Date: Thu, 25 Jun 2015 14:33:28 +0000
Subject: [PATCH] Ansible Mariadb w/ Galera support

Adds initial support for Mariadb with Galera replication in Ansible
using the CONFIG_EXTERNAL methods.

Additionally, this refactors some of the Galera config script to allow
for reuse by CONFIG_EXTERNAL.

Partially Implements: blueprint ansible-service

Change-Id: I566fea0376ecca39fc8a5167f9ff9ff434ea7b7e
---
 ansible/library/merge_configs                 |  3 +
 ansible/roles/database/defaults/main.yml      | 21 +++++++
 ansible/roles/database/tasks/bootstrap.yml    | 61 +++++++++++++++++++
 ansible/roles/database/tasks/config.yml       | 10 +++
 ansible/roles/database/tasks/main.yml         | 18 ++++--
 ansible/roles/database/tasks/register.yml     | 18 ++++++
 ansible/roles/database/tasks/start.yml        | 13 ++++
 .../roles/database/templates/galera.cnf.j2    | 18 ++++++
 ansible/roles/start.yml                       | 10 +--
 docker/centos/binary/galera/Dockerfile        |  5 +-
 .../centos/binary/galera/config-external.sh   |  1 +
 .../centos/binary/galera/config-internal.sh   |  1 +
 docker/common/base/kolla-common.sh            | 10 +--
 docker/common/galera/config-external.sh       |  8 +++
 docker/common/galera/config-galera.sh         | 20 +++---
 docker/common/galera/config-internal.sh       | 20 ++++++
 docker/common/galera/start.sh                 | 26 +++++---
 17 files changed, 226 insertions(+), 37 deletions(-)
 create mode 100644 ansible/roles/database/defaults/main.yml
 create mode 100644 ansible/roles/database/tasks/bootstrap.yml
 create mode 100644 ansible/roles/database/tasks/config.yml
 create mode 100644 ansible/roles/database/tasks/register.yml
 create mode 100644 ansible/roles/database/tasks/start.yml
 create mode 100644 ansible/roles/database/templates/galera.cnf.j2
 create mode 120000 docker/centos/binary/galera/config-external.sh
 create mode 120000 docker/centos/binary/galera/config-internal.sh
 create mode 100644 docker/common/galera/config-external.sh
 create mode 100755 docker/common/galera/config-internal.sh

diff --git a/ansible/library/merge_configs b/ansible/library/merge_configs
index 9991833aa..b1baeb3d1 100644
--- a/ansible/library/merge_configs
+++ b/ansible/library/merge_configs
@@ -14,6 +14,9 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+# TODO(SamYaple): Provide idempotency for module (Note to self: pull logic from
+#                 pervious bslurp module in yaodu)
+
 DOCUMENTATION = '''
 ---
 module: merge_configs
diff --git a/ansible/roles/database/defaults/main.yml b/ansible/roles/database/defaults/main.yml
new file mode 100644
index 000000000..469ad8355
--- /dev/null
+++ b/ansible/roles/database/defaults/main.yml
@@ -0,0 +1,21 @@
+---
+project_name: "mariadb"
+
+####################
+# Database
+####################
+database_cluster_name: "kolla"
+
+
+####################
+# Docker
+####################
+docker_database_registry: "{{ docker_registry }}"
+docker_database_namespace: "{{ docker_namespace }}"
+kolla_database_base_distro: "{{ kolla_base_distro }}"
+kolla_database_install_type: "{{ kolla_install_type }}"
+kolla_database_container_name: "galera"
+
+docker_database_image: "{{ docker_database_registry }}{{ docker_database_namespace }}/{{ kolla_database_base_distro }}-{{ kolla_database_install_type }}-{{ kolla_database_container_name }}"
+docker_database_tag: "{{ openstack_release }}"
+docker_database_image_full: "{{ docker_database_image }}:{{ docker_database_tag }}"
diff --git a/ansible/roles/database/tasks/bootstrap.yml b/ansible/roles/database/tasks/bootstrap.yml
new file mode 100644
index 000000000..4a46ee7cb
--- /dev/null
+++ b/ansible/roles/database/tasks/bootstrap.yml
@@ -0,0 +1,61 @@
+---
+- name: Creating temp file on localhost
+  local_action: shell echo 'None' > /tmp/kolla_mariadb_cluster
+  register: status
+  changed_when: False
+  failed_when: status.rc != 0
+  always_run: True
+  run_once: True
+
+# TODO(SamYaple): Improve failed_when check
+- name: Checking if a previous cluster exists
+  command: docker exec mariadb stat /var/lib/mysql/grastate.dat
+  register: exists
+  changed_when: False
+  failed_when: False
+  always_run: True
+  run_once: True
+
+- name: Writing hostname of host with existing cluster files to temp file
+  local_action: shell echo "{{ ansible_hostname }}" > /tmp/kolla_mariadb_cluster
+  register: status
+  changed_when: False
+  failed_when: status.rc != 0
+  always_run: True
+  when: exists.rc == 0
+
+- name: Registering host from temp file
+  set_fact:
+    delegate_host: "{{ lookup('file', '/tmp/kolla_mariadb_cluster') }}"
+
+- name: Cleaning up temp file on localhost
+  local_action: shell rm /tmp/kolla_mariadb_cluster
+  register: status
+  changed_when: False
+  failed_when: status.rc != 0
+  always_run: True
+  run_once: True
+
+- include: ../../start.yml
+  vars:
+    container_environment:
+      KOLLA_BOOTSTRAP:
+    container_image: "{{ docker_database_image_full }}"
+    container_name: "mariadb_data"
+    container_volumes:
+      - "/var/lib/mysql/"
+    container_command: "/bin/sleep infinity"
+
+- include: ../../start.yml
+  vars:
+    container_environment:
+      KOLLA_BOOTSTRAP:
+      KOLLA_CONFIG_STRATEGY: "{{ config_strategy }}"
+      DB_ROOT_PASSWORD: "{{ database_password }}"
+    container_image: "{{ docker_database_image_full }}"
+    container_name: "mariadb"
+    container_volumes:
+      - "{{ node_config_directory }}/mariadb/:/opt/kolla/mariadb/:ro"
+    container_volumes_from:
+      - "mariadb_data"
+  when: delegate_host == 'None' and inventory_hostname == groups['database'][0]
diff --git a/ansible/roles/database/tasks/config.yml b/ansible/roles/database/tasks/config.yml
new file mode 100644
index 000000000..a5f55483f
--- /dev/null
+++ b/ansible/roles/database/tasks/config.yml
@@ -0,0 +1,10 @@
+---
+
+
+- include: ../../config.yml
+  vars:
+    config_source:
+      - "roles/database/templates/galera.cnf.j2"
+    config_template_dest:
+      - "{{ node_templates_directory }}/mariadb/galera.cnf_minimal"
+    config_dest: "{{ node_config_directory }}/mariadb/galera.cnf"
diff --git a/ansible/roles/database/tasks/main.yml b/ansible/roles/database/tasks/main.yml
index 559b4bd1c..c7474dde5 100644
--- a/ansible/roles/database/tasks/main.yml
+++ b/ansible/roles/database/tasks/main.yml
@@ -1,7 +1,13 @@
 ---
-- name: Bringing up mariadb service(s)
-  docker_compose:
-    project_name: mariadb
-    compose_file: "{{ koalla_directory }}/compose/mariadb.yml"
-    command: up
-    no_recreate: true
+- include: config.yml
+
+- include: bootstrap.yml
+
+- include: start.yml
+
+- include: register.yml
+
+# This will restart the container we initially used to bootstrap the cluster to
+# make it match the other containers environment-wise. This also prevents a
+# change from showing up when rerunning the playbooks
+- include: start.yml
diff --git a/ansible/roles/database/tasks/register.yml b/ansible/roles/database/tasks/register.yml
new file mode 100644
index 000000000..ba6021090
--- /dev/null
+++ b/ansible/roles/database/tasks/register.yml
@@ -0,0 +1,18 @@
+---
+- name: Creating haproxy mysql user
+  mysql_user:
+    login_host: "{{ database_address }}"
+    login_user: "{{ database_user }}"
+    login_password: "{{ database_password }}"
+    name: "haproxy"
+    password: ""
+    host: "%"
+    priv: "*.*:USAGE"
+  register: status
+  until: status|success
+  retries: 10
+  delay: 5
+
+- name: Cleaning up facts
+  set_fact:
+    delegate_host: "bootstraped"
diff --git a/ansible/roles/database/tasks/start.yml b/ansible/roles/database/tasks/start.yml
new file mode 100644
index 000000000..e60d49d4e
--- /dev/null
+++ b/ansible/roles/database/tasks/start.yml
@@ -0,0 +1,13 @@
+---
+- include: ../../start.yml
+  vars:
+    container_environment:
+      KOLLA_CONFIG_STRATEGY: "{{ config_strategy }}"
+    container_image: "{{ docker_database_image_full }}"
+    container_name: "mariadb"
+    container_volumes:
+      - "{{ node_config_directory }}/mariadb/:/opt/kolla/mariadb/:ro"
+    container_volumes_from:
+      - "mariadb_data"
+  when: delegate_host != 'None' or
+        ( delegate_host == 'None' and inventory_hostname != groups['database'][0])
diff --git a/ansible/roles/database/templates/galera.cnf.j2 b/ansible/roles/database/templates/galera.cnf.j2
new file mode 100644
index 000000000..9d9d725d4
--- /dev/null
+++ b/ansible/roles/database/templates/galera.cnf.j2
@@ -0,0 +1,18 @@
+[mysqld]
+bind-address={{ hostvars[inventory_hostname]['ansible_' + api_interface]['ipv4']['address'] }}
+binlog_format=ROW
+default-storage-engine=innodb
+innodb_autoinc_lock_mode=2
+query_cache_size=0
+query_cache_type=0
+innodb_log_file_size=48M
+
+wsrep_provider=/usr/lib64/galera/libgalera_smm.so
+wsrep_cluster_address=gcomm://{% for host in groups['database'] %}{{ hostvars[host]['ansible_hostname'] }}{% if not loop.last %},{% endif %}{% endfor %}
+
+wsrep_cluster_name="{{ database_cluster_name }}"
+wsrep_node_address={{ hostvars[inventory_hostname]['ansible_' + api_interface]['ipv4']['address'] }}
+wsrep_node_name={{ ansible_hostname }}
+wsrep_sst_method=xtrabackup-v2
+wsrep_sst_auth={{ database_user }}:{{ database_password }}
+wsrep_slave_threads=4
diff --git a/ansible/roles/start.yml b/ansible/roles/start.yml
index 44d9b0c67..8aadfb3fc 100644
--- a/ansible/roles/start.yml
+++ b/ansible/roles/start.yml
@@ -9,22 +9,24 @@
 # docker_api_version from docker-py, so we specify it here. This will be
 # removed when the bugfix makes it downstream
 
-- name: Starting the container
+- name: Starting container(s)
   docker:
+    command: "{{ container_command | default(None) }}"
     detach: "{{ container_detach | default('True') }}"
-    env: "{{ container_environment }}"
+    env: "{{ container_environment | default(None) }}"
     docker_api_version: "{{ docker_api_version }}"
     image: "{{ container_image }}"
     insecure_registry: "{{ docker_insecure_registry }}"
     name: "{{ container_name }}"
-    net: host
+    net: "host"
     password: "{{ docker_registry_password }}"
     privileged: "{{ container_privileged | default('False') }}"
     pull: "{{ docker_pull_policy }}"
     registry: "{{ docker_registry }}"
     restart_policy: "{{ docker_restart_policy }}"
     restart_policy_retry: "{{ docker_restart_policy_retry }}"
-    state: reloaded
+    state: "reloaded"
     username: "{{ docker_registry_username }}"
     volumes: "{{ container_volumes }}"
+    volumes_from: "{{ container_volumes_from | default([]) }}"
   run_once: "{{ run_once | default('False') }}"
diff --git a/docker/centos/binary/galera/Dockerfile b/docker/centos/binary/galera/Dockerfile
index ce5477097..6e0e90f25 100644
--- a/docker/centos/binary/galera/Dockerfile
+++ b/docker/centos/binary/galera/Dockerfile
@@ -21,7 +21,8 @@ RUN echo "[mariadb]" > /etc/yum.repos.d/MariaDB.repo && \
       tar \
       expect
 
-COPY config-galera.sh /opt/kolla/config-galera.sh
-COPY start.sh /start.sh
+COPY config-galera.sh /opt/kolla/config/
+COPY config-internal.sh config-external.sh /opt/kolla/
+COPY start.sh /
 
 CMD ["/start.sh"]
diff --git a/docker/centos/binary/galera/config-external.sh b/docker/centos/binary/galera/config-external.sh
new file mode 120000
index 000000000..72a04836e
--- /dev/null
+++ b/docker/centos/binary/galera/config-external.sh
@@ -0,0 +1 @@
+../../../common/galera/config-external.sh
\ No newline at end of file
diff --git a/docker/centos/binary/galera/config-internal.sh b/docker/centos/binary/galera/config-internal.sh
new file mode 120000
index 000000000..edab3d51f
--- /dev/null
+++ b/docker/centos/binary/galera/config-internal.sh
@@ -0,0 +1 @@
+../../../common/galera/config-internal.sh
\ No newline at end of file
diff --git a/docker/common/base/kolla-common.sh b/docker/common/base/kolla-common.sh
index bd6a539a0..5c07b375d 100644
--- a/docker/common/base/kolla-common.sh
+++ b/docker/common/base/kolla-common.sh
@@ -175,19 +175,19 @@ set_configs() {
             exec /opt/kolla/config-internal.sh
             ;;
         CONFIG_EXTERNAL_COPY_ALWAYS)
-            source /opt/kolla/config-exernal.sh
+            source /opt/kolla/config-external.sh
             ;;
         CONFIG_EXTERNAL_COPY_ONCE)
             if [[ -f /configured ]]; then
                 echo 'INFO - This container has already been configured; Refusing to copy new configs'
-                return
+            else
+                source /opt/kolla/config-external.sh
+                touch /configured
             fi
-            source /opt/kolla/config-exernal.sh
-            touch /configured
             ;;
 
         *)
-            echo '$CONFIG_STRATEGY is not set properly'
+            echo '$KOLLA_CONFIG_STRATEGY is not set properly'
             exit 1
             ;;
     esac
diff --git a/docker/common/galera/config-external.sh b/docker/common/galera/config-external.sh
new file mode 100644
index 000000000..a90f7fe6f
--- /dev/null
+++ b/docker/common/galera/config-external.sh
@@ -0,0 +1,8 @@
+#!/bin/bash
+
+# Cluster configuration
+if [[ -f /opt/kolla/mariadb/galera.cnf ]]; then
+    cp /opt/kolla/mariadb/galera.cnf /etc/my.cnf.d/galera.cnf
+    chown mysql: /etc/my.cnf.d/galera.cnf
+    chmod 0600 /etc/my.cnf.d/galera.cnf
+fi
diff --git a/docker/common/galera/config-galera.sh b/docker/common/galera/config-galera.sh
index 9cbd2c764..8aab3cfe1 100755
--- a/docker/common/galera/config-galera.sh
+++ b/docker/common/galera/config-galera.sh
@@ -1,13 +1,5 @@
 #!/bin/bash
 
-. /opt/kolla/kolla-common.sh
-
-check_required_vars DB_CLUSTER_BIND_ADDRESS DB_CLUSTER_NAME DB_CLUSTER_NODES \
-                    DB_ROOT_PASSWORD DB_CLUSTER_WSREP_METHOD
-
-CFG=/etc/my.cnf.d/server.cnf
-DB_CLUSTER_INIT_SQL=/tmp/mysql-first-time.sql
-
 function configure_files {
     crudini --set $CFG mariadb bind-address "${DB_CLUSTER_BIND_ADDRESS}"
     crudini --set $CFG mariadb binlog_format "ROW"
@@ -30,8 +22,9 @@ function configure_files {
     crudini --set $CFG mariadb wsrep_sst_method "${DB_CLUSTER_WSREP_METHOD}"
 }
 
-function bootstrap_database {
-    mysqld_safe &
+function bootstrap_db {
+    mysqld_safe --wsrep-new-cluster &
+
     # Waiting for deamon
     sleep 10
     expect -c '
@@ -54,11 +47,14 @@ function bootstrap_database {
     expect "Reload privilege tables now?"
     send "y\r"
     expect eof'
+
+    mysql -u root --password="${DB_ROOT_PASSWORD}" -e "GRANT ALL PRIVILEGES ON *.* TO 'root'@'localhost' IDENTIFIED BY '${DB_ROOT_PASSWORD}' WITH GRANT OPTION;"
+    mysql -u root --password="${DB_ROOT_PASSWORD}" -e "GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' IDENTIFIED BY '${DB_ROOT_PASSWORD}' WITH GRANT OPTION;"
     mysqladmin -p"${DB_ROOT_PASSWORD}" shutdown
 }
 
 function configure_db {
-    bootstrap_database
+    bootstrap_db
 
     echo "GRANT ALL ON *.* TO 'root'@'%' IDENTIFIED BY '$DB_ROOT_PASSWORD' ;" > $DB_CLUSTER_INIT_SQL
 
@@ -81,7 +77,7 @@ function populate_db {
     if [[ $(ls /var/lib/mysql) == "" ]]; then
         echo "POPULATING NEW DB"
         mysql_install_db
-        chown -R mysql /var/lib/mysql
+        chown -R mysql: /var/lib/mysql
     else
         echo "DB ALREADY EXISTS"
     fi
diff --git a/docker/common/galera/config-internal.sh b/docker/common/galera/config-internal.sh
new file mode 100755
index 000000000..5e55dcde4
--- /dev/null
+++ b/docker/common/galera/config-internal.sh
@@ -0,0 +1,20 @@
+#!/bin/bash
+
+. /opt/kolla/kolla-common.sh
+. /opt/kolla/config-galera.sh
+
+check_required_vars DB_CLUSTER_BIND_ADDRESS DB_CLUSTER_NAME DB_CLUSTER_NODES \
+                    DB_ROOT_PASSWORD DB_CLUSTER_WSREP_METHOD DB_CLUSTER_INIT_DB
+
+CFG=/etc/my.cnf.d/server.cnf
+DB_CLUSTER_INIT_SQL=/tmp/mysql-first-time.sql
+
+prepare_db
+
+if [[ "${DB_CLUSTER_INIT_DB}" == "true" ]] && ! [[ -a /var/lib/mysql/cluster.exists ]]; then
+    DB_CLUSTER_IS_MASTER_NODE="--wsrep-new-cluster"
+    touch /var/lib/mysql/cluster.exists
+fi
+
+mysqld_safe --init-file=$DB_CLUSTER_INIT_SQL $DB_CLUSTER_IS_MASTER_NODE
+
diff --git a/docker/common/galera/start.sh b/docker/common/galera/start.sh
index 2ac683f11..42bd882b1 100755
--- a/docker/common/galera/start.sh
+++ b/docker/common/galera/start.sh
@@ -1,15 +1,25 @@
 #!/bin/bash
 
-. /opt/kolla/kolla-common.sh
-. /opt/kolla/config-galera.sh
+set -o errexit
 
-check_required_vars DB_CLUSTER_INIT_DB
-prepare_db
+CMD="/usr/bin/mysqld_safe"
+ARGS=""
 
-if [[ "${DB_CLUSTER_INIT_DB}" == "true" ]] && ! [[ -a /var/lib/mysql/cluster.exists ]]; then
-    DB_CLUSTER_IS_MASTER_NODE="--wsrep-new-cluster"
+# loading common functions
+source /opt/kolla/kolla-common.sh
+
+# config-internal script exec out of this function, it does not return here
+set_configs
+
+# loading functions
+source /opt/kolla/config/config-galera.sh
+
+# This catches all cases of the BOOTSTRAP variable being set, including empty
+if [[ "${!KOLLA_BOOTSTRAP[@]}" ]] && [[ ! -e /var/lib/mysql/cluster.exists ]]; then
+    ARGS="--wsrep-new-cluster"
     touch /var/lib/mysql/cluster.exists
+    populate_db
+    bootstrap_db
 fi
 
-mysqld_safe --init-file=$DB_CLUSTER_INIT_SQL $DB_CLUSTER_IS_MASTER_NODE
-
+exec $CMD $ARGS
-- 
GitLab