diff --git a/cinder/tests/unit/volume/drivers/test_rbd.py b/cinder/tests/unit/volume/drivers/test_rbd.py index 1daf8ce3ffe..deb8be8b4d7 100644 --- a/cinder/tests/unit/volume/drivers/test_rbd.py +++ b/cinder/tests/unit/volume/drivers/test_rbd.py @@ -155,6 +155,7 @@ class RBDTestCase(test.TestCase): self.cfg.rbd_cluster_name = 'nondefault' self.cfg.rbd_pool = 'rbd' self.cfg.rbd_ceph_conf = '/etc/ceph/my_ceph.conf' + self.cfg.rbd_keyring_conf = '/etc/ceph/my_ceph.client.keyring' self.cfg.rbd_secret_uuid = None self.cfg.rbd_user = 'cinder' self.cfg.volume_backend_name = None @@ -1149,34 +1150,72 @@ class RBDTestCase(test.TestCase): self.assertEqual((hosts, ports), self.driver._get_mon_addrs()) @common_mocks - def test_initialize_connection(self): - hosts = ['::1', '::1', '::1', '127.0.0.1', 'example.com'] - ports = ['6789', '6790', '6791', '6792', '6791'] + def _initialize_connection_helper(self, expected, hosts, ports): with mock.patch.object(self.driver, '_get_mon_addrs') as \ mock_get_mon_addrs: mock_get_mon_addrs.return_value = (hosts, ports) - - expected = { - 'driver_volume_type': 'rbd', - 'data': { - 'name': '%s/%s' % (self.cfg.rbd_pool, - self.volume_a.name), - 'hosts': hosts, - 'ports': ports, - 'cluster_name': self.cfg.rbd_cluster_name, - 'auth_enabled': True, - 'auth_username': self.cfg.rbd_user, - 'secret_type': 'ceph', - 'secret_uuid': None, - 'volume_id': self.volume_a.id, - 'discard': True, - } - } actual = self.driver.initialize_connection(self.volume_a, None) self.assertDictEqual(expected, actual) self.assertTrue(mock_get_mon_addrs.called) + @mock.patch.object(cinder.volume.drivers.rbd.RBDDriver, + '_get_keyring_contents') + def test_initialize_connection(self, mock_keyring): + hosts = ['::1', '::1', '::1', '127.0.0.1', 'example.com'] + ports = ['6789', '6790', '6791', '6792', '6791'] + + keyring_data = "[client.cinder]\n key = test\n" + mock_keyring.return_value = keyring_data + + expected = { + 'driver_volume_type': 'rbd', + 'data': { + 'name': '%s/%s' % (self.cfg.rbd_pool, + self.volume_a.name), + 'hosts': hosts, + 'ports': ports, + 'cluster_name': self.cfg.rbd_cluster_name, + 'auth_enabled': True, + 'auth_username': self.cfg.rbd_user, + 'secret_type': 'ceph', + 'secret_uuid': None, + 'volume_id': self.volume_a.id, + 'discard': True, + 'keyring': keyring_data, + } + } + self._initialize_connection_helper(expected, hosts, ports) + + # Check how it will work with empty keyring path + mock_keyring.return_value = None + expected['data']['keyring'] = None + self._initialize_connection_helper(expected, hosts, ports) + + def test__get_keyring_contents_no_config_file(self): + self.cfg.rbd_keyring_conf = '' + self.assertIsNone(self.driver._get_keyring_contents()) + + @mock.patch('os.path.isfile') + def test__get_keyring_contents_read_file(self, mock_isfile): + mock_isfile.return_value = True + keyring_data = "[client.cinder]\n key = test\n" + mockopen = mock.mock_open(read_data=keyring_data) + mockopen.return_value.__exit__ = mock.Mock() + with mock.patch('cinder.volume.drivers.rbd.open', mockopen, + create=True): + self.assertEqual(self.driver._get_keyring_contents(), keyring_data) + + @mock.patch('os.path.isfile') + def test__get_keyring_contents_raise_error(self, mock_isfile): + mock_isfile.return_value = True + mockopen = mock.mock_open() + mockopen.return_value.__exit__ = mock.Mock() + with mock.patch('cinder.volume.drivers.rbd.open', mockopen, + create=True) as mock_keyring_file: + mock_keyring_file.side_effect = IOError + self.assertIsNone(self.driver._get_keyring_contents()) + @ddt.data({'rbd_chunk_size': 1, 'order': 20}, {'rbd_chunk_size': 8, 'order': 23}, {'rbd_chunk_size': 32, 'order': 25}) diff --git a/cinder/volume/drivers/rbd.py b/cinder/volume/drivers/rbd.py index 3cc5e0e68a9..11adad483a6 100644 --- a/cinder/volume/drivers/rbd.py +++ b/cinder/volume/drivers/rbd.py @@ -59,6 +59,9 @@ RBD_OPTS = [ cfg.StrOpt('rbd_ceph_conf', default='', # default determined by librados help='Path to the ceph configuration file'), + cfg.StrOpt('rbd_keyring_conf', + default='', + help='Path to the ceph keyring file'), cfg.BoolOpt('rbd_flatten_volume_from_snapshot', default=False, help='Flatten volumes created from snapshots to remove ' @@ -1023,6 +1026,20 @@ class RBDDriver(driver.CloneableImageVD, """Removes an export for a logical volume.""" pass + def _get_keyring_contents(self): + # NOTE(danpawlik) If keyring is not provided in Cinder configuration, + # os-brick library will take keyring from default path. + keyring_file = self.configuration.rbd_keyring_conf + keyring_data = None + try: + if os.path.isfile(keyring_file): + with open(keyring_file, 'r') as k_file: + keyring_data = k_file.read() + except IOError: + LOG.debug('Cannot read RBD keyring file: %s.', keyring_file) + + return keyring_data + def initialize_connection(self, volume, connector): hosts, ports = self._get_mon_addrs() data = { @@ -1039,6 +1056,7 @@ class RBDDriver(driver.CloneableImageVD, 'secret_uuid': self.configuration.rbd_secret_uuid, 'volume_id': volume.id, "discard": True, + 'keyring': self._get_keyring_contents(), } } LOG.debug('connection data: %s', data) diff --git a/releasenotes/notes/add_ceph_custom_keyring_path-43a3b8c21a1ab3c4.yaml b/releasenotes/notes/add_ceph_custom_keyring_path-43a3b8c21a1ab3c4.yaml new file mode 100644 index 00000000000..157714fb868 --- /dev/null +++ b/releasenotes/notes/add_ceph_custom_keyring_path-43a3b8c21a1ab3c4.yaml @@ -0,0 +1,5 @@ +--- +features: + - | + Added RBD keyring configuration parameter ``rbd_keyring_conf`` to define + custom path of Ceph keyring file.