From 1999110bbc1d21d9792a761ef17c02503b9868e0 Mon Sep 17 00:00:00 2001
From: Mark Goddard <mark@stackhpc.com>
Date: Wed, 7 Feb 2018 09:04:11 +0000
Subject: [PATCH] Fixes for ceph block device tagging (#1)

* Install galaxy roles before running ansible tests

* Use package module in kolla-ceph to support Debian-based systems

This is required for running tests in TravisCI.

* Fix kolla-ceph unit test

* Add more tests for kolla-ceph

Journal tests are currently failing on my laptop due to partition labels
being truncated.

* Add .gitignore for stackhpc.parted-1-1 galaxy role

* Run all test cases

Run all test cases, collecting failures, then report at the end.
---
 .gitignore                                    |   1 +
 ansible/roles/kolla-ceph/tasks/config.yml     |   6 +-
 ansible/roles/kolla-ceph/tests/main.yml       |   6 +-
 .../tests/test-bootstrapped-journal.yml       | 118 ++++++++++++++++++
 .../kolla-ceph/tests/test-data-journal.yml    | 117 +++++++++++++++++
 .../roles/kolla-ceph/tests/test-journal.yml   |  93 ++++++++++++++
 ...{test-defaults.yml => test-no-journal.yml} |  28 ++---
 tools/test-ansible.sh                         |  17 +++
 tox.ini                                       |  10 +-
 9 files changed, 367 insertions(+), 29 deletions(-)
 create mode 100644 ansible/roles/kolla-ceph/tests/test-bootstrapped-journal.yml
 create mode 100644 ansible/roles/kolla-ceph/tests/test-data-journal.yml
 create mode 100644 ansible/roles/kolla-ceph/tests/test-journal.yml
 rename ansible/roles/kolla-ceph/tests/{test-defaults.yml => test-no-journal.yml} (66%)
 create mode 100755 tools/test-ansible.sh

diff --git a/.gitignore b/.gitignore
index a0fb698d0..961533f5e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -67,6 +67,7 @@ ansible/roles/stackhpc.os-openstackclient/
 ansible/roles/stackhpc.os-networks/
 ansible/roles/stackhpc.os-projects/
 ansible/roles/stackhpc.os-shade/
+ansible/roles/stackhpc.parted-1-1/
 ansible/roles/resmo.ntp/
 ansible/roles/yatesr.timezone/
 
diff --git a/ansible/roles/kolla-ceph/tasks/config.yml b/ansible/roles/kolla-ceph/tasks/config.yml
index d23fe6b92..0a542b30e 100644
--- a/ansible/roles/kolla-ceph/tasks/config.yml
+++ b/ansible/roles/kolla-ceph/tasks/config.yml
@@ -2,7 +2,7 @@
 # (ktibi) Need to remove parted_1_1 module when kayobe will support ansible 2.4
 
 - name: Ensure required packages are installed
-  yum:
+  package:
     name: parted
     state: installed
   become: True
@@ -63,7 +63,7 @@
     label: "{{item.item}}"
   vars:
       part_label: "{% if item.item.journal is defined %}{{ part_label_with_journal }}{% else %}KOLLA_CEPH_OSD_BOOTSTRAP{% endif %}"
-      part_label_with_journal: "KOLLA_CEPH_OSD_BOOTSTRAP_{{ osd_id }}"
+      part_label_with_journal: "KOLLA_CEPH_OSD_BOOTSTRAP_{{ (osd_id | hash('md5'))[:9] }}"
       osd_id: "{{ item.item.osd | basename }}{{ ansible_hostname }}"
 
 - name: Create tag partition for Ceph external journal
@@ -81,6 +81,6 @@
   loop_control:
     label: "{{item.item}}"
   vars:
-      part_label: "KOLLA_CEPH_OSD_BOOTSTRAP_{{ osd_id }}_J"
+      part_label: "KOLLA_CEPH_OSD_BOOTSTRAP_{{ (osd_id | hash('md5'))[:9] }}_J"
       osd_id: "{{ item.item.osd | basename }}{{ ansible_hostname }}"
 
diff --git a/ansible/roles/kolla-ceph/tests/main.yml b/ansible/roles/kolla-ceph/tests/main.yml
index 3460876ee..97e8ab4f2 100644
--- a/ansible/roles/kolla-ceph/tests/main.yml
+++ b/ansible/roles/kolla-ceph/tests/main.yml
@@ -1,6 +1,8 @@
 ---
-
-- include: test-defaults.yml
+- include: test-no-journal.yml
+- include: test-journal.yml
+- include: test-bootstrapped-journal.yml
+- include: test-data-journal.yml
 
 - hosts: localhost
   connection: local
diff --git a/ansible/roles/kolla-ceph/tests/test-bootstrapped-journal.yml b/ansible/roles/kolla-ceph/tests/test-bootstrapped-journal.yml
new file mode 100644
index 000000000..a15a28e21
--- /dev/null
+++ b/ansible/roles/kolla-ceph/tests/test-bootstrapped-journal.yml
@@ -0,0 +1,118 @@
+---
+# Test case with an OSD and external journal that have already been tagged by
+# kayobe with the kolla-ansible bootstrap label, but have not yet been
+# converted to use the in-use label.
+
+- hosts: localhost
+  connection: local
+  tasks:
+    - name: Allocate a temporary file for a fake OSD
+      tempfile:
+      register: osd_tempfile
+
+    - name: Allocate a temporary file for a fake journal
+      tempfile:
+      register: journal_tempfile
+
+    - name: Allocate a fake OSD file
+      command: fallocate -l 10M {{ osd_tempfile.path }}
+
+    - name: Allocate a fake journal file
+      command: fallocate -l 10M {{ journal_tempfile.path }}
+
+    - name: Create tag partition for the fake OSD
+      become: True
+      parted_1_1:
+        device: "{{ osd_tempfile.path }}"
+        number: 1
+        label: gpt
+        name: "{{ part_label }}"
+        state: present
+      vars:
+        part_label: "KOLLA_CEPH_OSD_BOOTSTRAP_{{ (osd_id | hash('md5'))[:9] }}"
+        osd_id: "{{ osd_tempfile.path | basename }}{{ ansible_hostname }}"
+
+    - name: Create tag partition for the fake journal
+      become: True
+      parted_1_1:
+        device: "{{ journal_tempfile.path }}"
+        number: 1
+        label: gpt
+        name: "{{ part_label }}"
+        state: present
+      vars:
+        part_label: "KOLLA_CEPH_OSD_BOOTSTRAP_{{ (osd_id | hash('md5'))[:9] }}_J"
+        osd_id: "{{ journal_tempfile.path | basename }}{{ ansible_hostname }}"
+
+    - block:
+        - name: Import parted role
+          include_role:
+            name: ../../stackhpc.parted-1-1
+
+        - name: Test the kolla-ceph role
+          include_role:
+            name:  ../../kolla-ceph
+          vars:
+              ceph_disks:
+                 - osd: "{{ osd_tempfile.path }}"
+                   journal: "{{ journal_tempfile.path }}"
+
+        - name: Get name of fake OSD partition
+          parted_1_1:
+            device: "{{ osd_tempfile.path }}"
+          register: "disk_osd_info"
+          become: True
+
+        - name: Validate number of OSD partitions
+          assert:
+            that: disk_osd_info.partitions | length == 1
+            msg: >
+              Number of OSD partitions is not correct. Expected 1,
+              actual {{ disk_osd_info.partitions | length }}
+
+        - name: Validate OSD tag is present
+          assert:
+            that: "disk_osd_info.partitions.0.name == expected"
+            msg: >
+              Name of OSD partition is not correct. Expected {{ expected }},
+              actual {{ disk_osd_info.partitions.0.name }}.
+          vars:
+            expected: "{{ 'KOLLA_CEPH_OSD_BOOTSTRAP_' ~ ((osd_tempfile.path | basename ~ ansible_hostname) | hash('md5'))[:9] }}"
+
+        - name: Get name of fake journal partition
+          parted_1_1:
+            device: "{{ journal_tempfile.path }}"
+          register: "disk_journal_info"
+          become: True
+
+        - name: Validate number of journal partitions
+          assert:
+            that: disk_journal_info.partitions | length == 1
+            msg: >
+              Number of journal partitions is not correct. Expected 1,
+              actual {{ disk_journal_info.partitions | length }}
+
+        - name: Validate journal tag is present
+          assert:
+            that: "disk_journal_info.partitions.0.name == expected"
+            msg: >
+              Name of journal partition is not correct. Expected {{ expected }},
+              actual {{ disk_journal_info.partitions.0.name }}.
+          vars:
+            expected: "{{ 'KOLLA_CEPH_OSD_BOOTSTRAP_' ~ (( journal_tempfile.path | basename ~ ansible_hostname) | hash('md5'))[:9] ~ '_J' }}"
+
+      always:
+        - name: Remove the fake OSD file
+          file:
+            name: "{{ osd_tempfile.path }}"
+            state: absent
+
+        - name: Remove the fake journal file
+          file:
+            name: "{{ journal_tempfile.path }}"
+            state: absent
+
+      rescue:
+        - name: Flag that a failure occurred
+          set_fact:
+            test_failures: "{{ test_failures | default(0) | int + 1 }}"
diff --git a/ansible/roles/kolla-ceph/tests/test-data-journal.yml b/ansible/roles/kolla-ceph/tests/test-data-journal.yml
new file mode 100644
index 000000000..4a8efecb1
--- /dev/null
+++ b/ansible/roles/kolla-ceph/tests/test-data-journal.yml
@@ -0,0 +1,117 @@
+---
+# Test case with an OSD and external journal that have been converted by
+# kolla-ansible to use the in-use label.
+
+- hosts: localhost
+  connection: local
+  tasks:
+    - name: Allocate a temporary file for a fake OSD
+      tempfile:
+      register: osd_tempfile
+
+    - name: Allocate a temporary file for a fake journal
+      tempfile:
+      register: journal_tempfile
+
+    - name: Allocate a fake OSD file
+      command: fallocate -l 10M {{ osd_tempfile.path }}
+
+    - name: Allocate a fake journal file
+      command: fallocate -l 10M {{ journal_tempfile.path }}
+
+    - name: Create tag partition for the fake OSD
+      become: True
+      parted_1_1:
+        device: "{{ osd_tempfile.path }}"
+        number: 1
+        label: gpt
+        name: "{{ part_label }}"
+        state: present
+      vars:
+        part_label: "KOLLA_CEPH_DATA_{{ (osd_id | hash('md5'))[:9]}}"
+        osd_id: "{{ (osd_tempfile.path | basename ~ ansible_hostname) }}"
+
+    - name: Create tag partition for the fake journal
+      become: True
+      parted_1_1:
+        device: "{{ journal_tempfile.path }}"
+        number: 1
+        label: gpt
+        name: "{{ part_label }}"
+        state: present
+      vars:
+        part_label: "KOLLA_CEPH_DATA_{{ (osd_id | hash('md5'))[:9] }}_J"
+        osd_id: "{{ (journal_tempfile.path | basename ~ ansible_hostname) }}"
+
+    - block:
+        - name: Import parted role
+          include_role:
+            name: ../../stackhpc.parted-1-1
+
+        - name: Test the kolla-ceph role
+          include_role:
+            name:  ../../kolla-ceph
+          vars:
+              ceph_disks:
+                 - osd: "{{ osd_tempfile.path }}"
+                   journal: "{{ journal_tempfile.path }}"
+
+        - name: Get name of fake OSD partition
+          parted_1_1:
+            device: "{{ osd_tempfile.path }}"
+          register: "disk_osd_info"
+          become: True
+
+        - name: Validate number of OSD partitions
+          assert:
+            that: disk_osd_info.partitions | length == 1
+            msg: >
+              Number of OSD partitions is not correct. Expected 1,
+              actual {{ disk_osd_info.partitions | length }}
+
+        - name: Validate OSD tag is present
+          assert:
+            that: "disk_osd_info.partitions.0.name == expected"
+            msg: >
+              Name of OSD partition is not correct. Expected {{ expected }},
+              actual {{ disk_osd_info.partitions.0.name }}.
+          vars:
+            expected: "{{ 'KOLLA_CEPH_DATA_' ~ ((osd_tempfile.path | basename ~ ansible_hostname)| hash('md5'))[:9] }}"
+
+        - name: Get name of fake journal partition
+          parted_1_1:
+            device: "{{ journal_tempfile.path }}"
+          register: "disk_journal_info"
+          become: True
+
+        - name: Validate number of journal partitions
+          assert:
+            that: disk_journal_info.partitions | length == 1
+            msg: >
+              Number of journal partitions is not correct. Expected 1,
+              actual {{ disk_journal_info.partitions | length }}
+
+        - name: Validate journal tag is present
+          assert:
+            that: "disk_journal_info.partitions.0.name == expected"
+            msg: >
+              Name of journal partition is not correct. Expected {{ expected }},
+              actual {{ disk_journal_info.partitions.0.name }}.
+          vars:
+            expected: "{{ 'KOLLA_CEPH_DATA_' ~ ((journal_tempfile.path | basename ~ ansible_hostname)| hash('md5'))[:9] ~ '_J' }}"
+
+      always:
+        - name: Remove the fake OSD file
+          file:
+            name: "{{ osd_tempfile.path }}"
+            state: absent
+
+        - name: Remove the fake journal file
+          file:
+            name: "{{ journal_tempfile.path }}"
+            state: absent
+
+      rescue:
+        - name: Flag that a failure occurred
+          set_fact:
+            test_failures: "{{ test_failures | default(0) | int + 1 }}"
diff --git a/ansible/roles/kolla-ceph/tests/test-journal.yml b/ansible/roles/kolla-ceph/tests/test-journal.yml
new file mode 100644
index 000000000..d66b3cbf6
--- /dev/null
+++ b/ansible/roles/kolla-ceph/tests/test-journal.yml
@@ -0,0 +1,93 @@
+---
+# Test case with an OSD and external journal that have not yet been tagged by
+# kayobe with the kolla-ansible bootstrap label.
+
+- hosts: localhost
+  connection: local
+  tasks:
+    - name: Allocate a temporary file for a fake OSD
+      tempfile:
+      register: osd_tempfile
+
+    - name: Allocate a temporary file for a fake journal
+      tempfile:
+      register: journal_tempfile
+
+    - name: Allocate a fake OSD file
+      command: fallocate -l 10M {{ osd_tempfile.path }}
+
+    - name: Allocate a fake journal file
+      command: fallocate -l 10M {{ journal_tempfile.path }}
+
+    - block:
+        - name: Import parted role
+          include_role:
+            name: ../../stackhpc.parted-1-1
+
+        - name: Test the kolla-ceph role
+          include_role:
+            name:  ../../kolla-ceph
+          vars:
+              ceph_disks:
+                 - osd: "{{ osd_tempfile.path }}"
+                   journal: "{{ journal_tempfile.path }}"
+
+        - name: Get name of fake OSD partition
+          parted_1_1:
+            device: "{{ osd_tempfile.path }}"
+          register: "disk_osd_info"
+          become: True
+
+        - name: Validate number of OSD partitions
+          assert:
+            that: disk_osd_info.partitions | length == 1
+            msg: >
+              Number of OSD partitions is not correct. Expected 1,
+              actual {{ disk_osd_info.partitions | length }}
+
+        - name: Validate OSD tag is present
+          assert:
+            that: "disk_osd_info.partitions.0.name == expected"
+            msg: >
+              Name of OSD partition is not correct. Expected {{ expected }},
+              actual {{ disk_osd_info.partitions.0.name }}.
+          vars:
+            expected: "{{ 'KOLLA_CEPH_OSD_BOOTSTRAP_' ~ ((osd_tempfile.path | basename ~ ansible_hostname)| hash('md5'))[:9] }}"
+
+        - name: Get name of fake journal partition
+          parted_1_1:
+            device: "{{ journal_tempfile.path }}"
+          register: "disk_journal_info"
+          become: True
+
+        - name: Validate number of journal partitions
+          assert:
+            that: disk_journal_info.partitions | length == 1
+            msg: >
+              Number of journal partitions is not correct. Expected 1,
+              actual {{ disk_journal_info.partitions | length }}
+
+        - name: Validate journal tag is present
+          assert:
+            that: "disk_journal_info.partitions.0.name == expected"
+            msg: >
+              Name of journal partition is not correct. Expected {{ expected }},
+              actual {{ disk_journal_info.partitions.0.name }}.
+          vars:
+            expected: "{{ 'KOLLA_CEPH_OSD_BOOTSTRAP_' ~ ((journal_tempfile.path | basename ~ ansible_hostname)| hash('md5'))[:9] ~ '_J' }}"
+
+      always:
+        - name: Remove the fake OSD file
+          file:
+            name: "{{ osd_tempfile.path }}"
+            state: absent
+
+        - name: Remove the fake journal file
+          file:
+            name: "{{ journal_tempfile.path }}"
+            state: absent
+
+      rescue:
+        - name: Flag that a failure occurred
+          set_fact:
+            test_failures: "{{ test_failures | default(0) | int + 1 }}"
diff --git a/ansible/roles/kolla-ceph/tests/test-defaults.yml b/ansible/roles/kolla-ceph/tests/test-no-journal.yml
similarity index 66%
rename from ansible/roles/kolla-ceph/tests/test-defaults.yml
rename to ansible/roles/kolla-ceph/tests/test-no-journal.yml
index 9a0056516..8c0dec3f8 100644
--- a/ansible/roles/kolla-ceph/tests/test-defaults.yml
+++ b/ansible/roles/kolla-ceph/tests/test-no-journal.yml
@@ -1,4 +1,7 @@
 ---
+# Test case with an OSD and no external journal that has not yet been tagged by
+# kayobe with the kolla-ansible bootstrap label.
+
 - hosts: localhost
   connection: local
   tasks:
@@ -7,32 +10,25 @@
       register: tempfile
 
     - name: Allocate a fake OSD file
-      command: fallocate -l "{{ device_size }} {{ tempfile.path }}"
-
-    - name: Find a free loopback device
-      command: losetup -f 
-      register: loopback_result
-
-    - name: Create a loopback device for the fake OSD file
-      command: losetup "{{ loopback_result.stdout }} {{ tempfile.path }}"
-      become: True
+      command: fallocate -l 10M {{ tempfile.path }}
 
     - block:
         - name: Import parted role
           include_role:
-            name: stackhpc.parted-1-1
+            name: ../../stackhpc.parted-1-1
 
         - name: Test the kolla-ceph role
           include_role:
             name:  ../../kolla-ceph
           vars:
               ceph_disks:
-                 - osd: "{{ loopback_result.stdout }}"
+                 - osd: "{{ tempfile.path }}"
 
         - name: Get name of fake partition
-          parted_kayobe:
-            device: "{{ loopback_result.stdout }}"
+          parted_1_1:
+            device: "{{ tempfile.path }}"
           register: "disk_osd_info"
+          become: True
 
         - name: Validate number of partition
           assert:
@@ -47,10 +43,6 @@
               Name of partition is not correct.
 
       always:
-        - name: Detach the loopback device
-          command: losetup -d {{ loopback_result.stdout }}
-          become: True
-
         - name: Remove the fake OSD file
           file:
             name: "{{ tempfile.path }}"
@@ -60,5 +52,3 @@
         - name: Flag that a failure occurred
           set_fact:
             test_failures: "{{ test_failures | default(0) | int + 1 }}"
-
-
diff --git a/tools/test-ansible.sh b/tools/test-ansible.sh
new file mode 100755
index 000000000..e54dc56fc
--- /dev/null
+++ b/tools/test-ansible.sh
@@ -0,0 +1,17 @@
+#!/bin/bash
+
+# Run ansible tests. Any arguments passed to this script will be passed onto
+# ansible-playbook.
+
+set -e
+
+failed=0
+for playbook in ansible/roles/*/tests/main.yml; do
+    if ! ansible-playbook --connection=local $playbook $*; then
+        failed=$((failed + 1))
+    fi
+done
+if [[ $failed -ne 0 ]]; then
+    echo "Failed $failed test cases"
+    exit 1
+fi
diff --git a/tox.ini b/tox.ini
index 6819d60d6..a882221f4 100644
--- a/tox.ini
+++ b/tox.ini
@@ -45,11 +45,11 @@ usedevelop = True
 sitepackages = True
 install_command = pip install -c{env:UPPER_CONSTRAINTS_FILE:https://git.openstack.org/cgit/openstack/requirements/plain/upper-constraints.txt?h=stable/pike} {opts} {packages}
 commands =
-    bash -c \
-        "ansible-playbook \
-        --connection=local \
-        {toxinidir}/ansible/roles/*/tests/main.yml \
-        {posargs}"
+    # Install ansible role dependencies from Galaxy.
+    ansible-galaxy install \
+        -r {toxinidir}/requirements.yml \
+        -p {toxinidir}/ansible/roles
+    {toxinidir}/tools/test-ansible.sh {posargs}
 
 [testenv:alint]
 commands = ansible-lint ansible/*.yaml