diff --git a/os_brick/initiator/connectors/rbd.py b/os_brick/initiator/connectors/rbd.py index 08b67e462..0dcc1328f 100644 --- a/os_brick/initiator/connectors/rbd.py +++ b/os_brick/initiator/connectors/rbd.py @@ -12,6 +12,10 @@ # License for the specific language governing permissions and limitations # under the License. + +import os +import tempfile + from oslo_concurrency import processutils as putils from oslo_log import log as logging @@ -56,19 +60,46 @@ class RBDConnector(base.BaseLinuxConnector): # TODO(e0ne): Implement this for local volume. return [] + def _create_ceph_conf(self, monitor_ips, monitor_ports, + cluster_name, user): + try: + fd, ceph_conf_path = tempfile.mkstemp() + monitors = ["%s:%s" % (ip, port) for ip, port + in zip(monitor_ips, monitor_ports)] + mon_hosts = "mon_host = %s" % (','.join(monitors)) + client_section = "[client.%s]" % user + keyring = ("keyring = /etc/ceph/%s.client.%s.keyring" % + (cluster_name, user)) + with os.fdopen(fd, 'w') as conf_file: + conf_file.writelines([mon_hosts, "\n", + client_section, "\n", keyring]) + return ceph_conf_path + except IOError: + msg = (_("Failed to write data to %s.") % (ceph_conf_path)) + raise exception.BrickException(msg=msg) + def _get_rbd_handle(self, connection_properties): try: user = connection_properties['auth_username'] pool, volume = connection_properties['name'].split('/') - conf = connection_properties.get('conffile') + cluster_name = connection_properties.get('cluster_name') + monitor_ips = connection_properties.get('hosts') + monitor_ports = connection_properties.get('ports') except IndexError: msg = _("Connect volume failed, malformed connection properties") raise exception.BrickException(msg=msg) - rbd_client = linuxrbd.RBDClient(user, pool) + conf = self._create_ceph_conf(monitor_ips, monitor_ports, + str(cluster_name), user) + rbd_client = linuxrbd.RBDClient(user, pool, conffile=conf, + rbd_cluster_name=str(cluster_name)) rbd_volume = linuxrbd.RBDVolume(rbd_client, volume) rbd_handle = linuxrbd.RBDVolumeIOWrapper( linuxrbd.RBDImageMetadata(rbd_volume, pool, user, conf)) + + if os.path.exists(conf): + os.remove(conf) + return rbd_handle @staticmethod diff --git a/os_brick/tests/initiator/connectors/test_rbd.py b/os_brick/tests/initiator/connectors/test_rbd.py index 2613e3757..fcbeabb39 100644 --- a/os_brick/tests/initiator/connectors/test_rbd.py +++ b/os_brick/tests/initiator/connectors/test_rbd.py @@ -28,10 +28,16 @@ class RBDConnectorTestCase(test_connector.ConnectorTestCase): self.user = 'fake_user' self.pool = 'fake_pool' self.volume = 'fake_volume' + self.clustername = 'fake_ceph' + self.hosts = ['192.168.10.2'] + self.ports = ['6789'] self.connection_properties = { 'auth_username': self.user, 'name': '%s/%s' % (self.pool, self.volume), + 'cluster_name': self.clustername, + 'hosts': self.hosts, + 'ports': self.ports, } def test_get_search_path(self): @@ -56,16 +62,20 @@ class RBDConnectorTestCase(test_connector.ConnectorTestCase): @mock.patch('os_brick.initiator.linuxrbd.rbd') @mock.patch('os_brick.initiator.linuxrbd.rados') - def test_connect_volume(self, mock_rados, mock_rbd): + @mock.patch.object(rbd.RBDConnector, '_create_ceph_conf') + @mock.patch('os.path.exists') + def test_connect_volume(self, mock_path, mock_conf, mock_rados, mock_rbd): """Test the connect volume case.""" rbd_connector = rbd.RBDConnector(None) + mock_path.return_value = False + mock_conf.return_value = "/tmp/fake_dir/fake_ceph.conf" device_info = rbd_connector.connect_volume(self.connection_properties) # Ensure rados is instantiated correctly mock_rados.Rados.assert_called_once_with( - clustername='ceph', + clustername=self.clustername, rados_id=utils.convert_str(self.user), - conffile='/etc/ceph/ceph.conf') + conffile='/tmp/fake_dir/fake_ceph.conf') # Ensure correct calls to connect to cluster self.assertEqual(1, mock_rados.Rados.return_value.connect.call_count) @@ -82,6 +92,20 @@ class RBDConnectorTestCase(test_connector.ConnectorTestCase): self.assertTrue(isinstance(device_info['path'], linuxrbd.RBDVolumeIOWrapper)) + @mock.patch('os_brick.initiator.connectors.rbd.tempfile.mkstemp') + def test_create_ceph_conf(self, mock_mkstemp): + mockopen = mock.mock_open() + fd = mock.sentinel.fd + tmpfile = mock.sentinel.tmpfile + mock_mkstemp.return_value = (fd, tmpfile) + + with mock.patch('os.fdopen', mockopen, create=True): + rbd_connector = rbd.RBDConnector(None) + conf_path = rbd_connector._create_ceph_conf( + self.hosts, self.ports, self.clustername, self.user) + self.assertEqual(conf_path, tmpfile) + mock_mkstemp.assert_called_once_with() + @mock.patch.object(priv_rootwrap, 'execute') def test_connect_local_volume(self, mock_execute): rbd_connector = rbd.RBDConnector(None, do_local_attach=True)