From c33aa3d15b5f116e13d8f533683fdcc6bf806702 Mon Sep 17 00:00:00 2001
From: James Page <james.page@ubuntu.com>
Date: Fri, 17 Nov 2017 16:40:11 +0000
Subject: [PATCH] Avoiding conflicting CRUSH bucket keys

As of Ceph Luminous, bucket keys within the CRUSH map must be
unique; The root bucket is always called 'default' so remap
Juju and configuration provided bucket keys to
'default-{row,rack}' ensuring that keys are unique.

Change-Id: I7fa3dd9e001cca40e678e8983a1d7ed19d51e2fe
Closes-Bug: 1729911
---
 hooks/ceph_hooks.py           |  8 ++++++
 unit_tests/test_ceph_hooks.py | 51 +++++++++++++++++++++++++++++++++++
 2 files changed, 59 insertions(+)

diff --git a/hooks/ceph_hooks.py b/hooks/ceph_hooks.py
index 928a49f..22925d1 100755
--- a/hooks/ceph_hooks.py
+++ b/hooks/ceph_hooks.py
@@ -195,8 +195,16 @@ def az_info():
     config_az = config("availability_zone")
     juju_az_info = os.environ.get('JUJU_AVAILABILITY_ZONE')
     if juju_az_info:
+        # NOTE(jamespage): avoid conflicting key with root
+        #                  of crush hierarchy
+        if juju_az_info == 'default':
+            juju_az_info = 'default-rack'
         az_info = "{} rack={}".format(az_info, juju_az_info)
     if config_az:
+        # NOTE(jamespage): avoid conflicting key with root
+        #                  of crush hierarchy
+        if config_az == 'default':
+            config_az = 'default-row'
         az_info = "{} row={}".format(az_info, config_az)
     if az_info != "":
         log("AZ Info: " + az_info)
diff --git a/unit_tests/test_ceph_hooks.py b/unit_tests/test_ceph_hooks.py
index f40d07f..6be78cc 100644
--- a/unit_tests/test_ceph_hooks.py
+++ b/unit_tests/test_ceph_hooks.py
@@ -388,3 +388,54 @@ class CephHooksTestCase(unittest.TestCase):
         mock_os_path_exists.assert_called()
         mock_get_blacklist.assert_called()
         self.assertEqual(devices, set(['/dev/vdb']))
+
+    @patch.object(ceph_hooks, 'log')
+    @patch.object(ceph_hooks, 'config')
+    @patch('os.environ')
+    def test_az_info_unset(self, environ, config, log):
+        config.return_value = None
+        environ.get.return_value = None
+
+        self.assertEqual(ceph_hooks.az_info(), None)
+
+        config.assert_called_with('availability_zone')
+        environ.get.assert_called_with('JUJU_AVAILABILITY_ZONE')
+
+    @patch.object(ceph_hooks, 'log')
+    @patch.object(ceph_hooks, 'config')
+    @patch('os.environ')
+    def test_az_info_config(self, environ, config, log):
+        config.return_value = 'dc-01'
+        environ.get.return_value = None
+
+        self.assertEqual(ceph_hooks.az_info(),
+                         ' row=dc-01')
+
+        config.assert_called_with('availability_zone')
+        environ.get.assert_called_with('JUJU_AVAILABILITY_ZONE')
+
+    @patch.object(ceph_hooks, 'log')
+    @patch.object(ceph_hooks, 'config')
+    @patch('os.environ')
+    def test_az_info_juju_az(self, environ, config, log):
+        config.return_value = 'dc-01'
+        environ.get.return_value = 'zone1'
+
+        self.assertEqual(ceph_hooks.az_info(),
+                         ' rack=zone1 row=dc-01')
+
+        config.assert_called_with('availability_zone')
+        environ.get.assert_called_with('JUJU_AVAILABILITY_ZONE')
+
+    @patch.object(ceph_hooks, 'log')
+    @patch.object(ceph_hooks, 'config')
+    @patch('os.environ')
+    def test_az_info_default_remap(self, environ, config, log):
+        config.return_value = 'default'
+        environ.get.return_value = 'default'
+
+        self.assertEqual(ceph_hooks.az_info(),
+                         ' rack=default-rack row=default-row')
+
+        config.assert_called_with('availability_zone')
+        environ.get.assert_called_with('JUJU_AVAILABILITY_ZONE')
-- 
GitLab