"Fossies" - the Fresh Open Source Software Archive

Member "cinder-17.1.0/cinder/tests/unit/volume/drivers/test_rbd.py" (8 Mar 2021, 129181 Bytes) of package /linux/misc/openstack/cinder-17.1.0.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Python source code syntax highlighting (style: standard) with prefixed line numbers. Alternatively you can here view or download the uninterpreted source code file. See also the latest Fossies "Diffs" side-by-side code changes report for "test_rbd.py": 17.0.1_vs_17.1.0.

    1 # Copyright 2012 Josh Durgin
    2 # Copyright 2013 Canonical Ltd.
    3 # All Rights Reserved.
    4 #
    5 #    Licensed under the Apache License, Version 2.0 (the "License"); you may
    6 #    not use this file except in compliance with the License. You may obtain
    7 #    a copy of the License at
    8 #
    9 #         http://www.apache.org/licenses/LICENSE-2.0
   10 #
   11 #    Unless required by applicable law or agreed to in writing, software
   12 #    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
   13 #    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
   14 #    License for the specific language governing permissions and limitations
   15 #    under the License.
   16 
   17 import errno
   18 import math
   19 import os
   20 import tempfile
   21 import time
   22 from unittest import mock
   23 from unittest.mock import call
   24 import uuid
   25 
   26 import castellan
   27 import ddt
   28 from oslo_utils import imageutils
   29 from oslo_utils import units
   30 
   31 from cinder import context
   32 from cinder import db
   33 from cinder import exception
   34 import cinder.image.glance
   35 from cinder.image import image_utils
   36 from cinder import objects
   37 from cinder.objects import fields
   38 from cinder.tests.unit import fake_constants as fake
   39 from cinder.tests.unit import fake_snapshot
   40 from cinder.tests.unit import fake_volume
   41 from cinder.tests.unit.keymgr import fake as fake_keymgr
   42 from cinder.tests.unit import test
   43 from cinder.tests.unit import utils
   44 from cinder.tests.unit.volume import test_driver
   45 from cinder.volume import configuration as conf
   46 import cinder.volume.drivers.rbd as driver
   47 from cinder.volume import volume_utils
   48 
   49 
   50 # This is used to collect raised exceptions so that tests may check what was
   51 # raised.
   52 # NOTE: this must be initialised in test setUp().
   53 RAISED_EXCEPTIONS = []
   54 
   55 
   56 class MockException(Exception):
   57 
   58     def __init__(self, *args, **kwargs):
   59         RAISED_EXCEPTIONS.append(self.__class__)
   60 
   61 
   62 class MockImageNotFoundException(MockException):
   63     """Used as mock for rbd.ImageNotFound."""
   64 
   65 
   66 class MockImageBusyException(MockException):
   67     """Used as mock for rbd.ImageBusy."""
   68 
   69 
   70 class MockImageExistsException(MockException):
   71     """Used as mock for rbd.ImageExists."""
   72 
   73 
   74 class MockOSErrorException(MockException):
   75     """Used as mock for rbd.OSError."""
   76 
   77 
   78 class MockPermissionError(MockException):
   79     """Used as mock for PermissionError."""
   80     errno = errno.EPERM
   81 
   82 
   83 class KeyObject(object):
   84     def get_encoded(arg):
   85         return "asdf".encode('utf-8')
   86 
   87 
   88 def common_mocks(f):
   89     """Decorator to set mocks common to all tests.
   90 
   91     The point of doing these mocks here is so that we don't accidentally set
   92     mocks that can't/don't get unset.
   93     """
   94     def _common_inner_inner1(inst, *args, **kwargs):
   95         @mock.patch.object(driver.RBDDriver, '_get_usage_info')
   96         @mock.patch('cinder.volume.drivers.rbd.RBDVolumeProxy')
   97         @mock.patch('cinder.volume.drivers.rbd.RADOSClient')
   98         @mock.patch('cinder.backup.drivers.ceph.rbd')
   99         @mock.patch('cinder.backup.drivers.ceph.rados')
  100         def _common_inner_inner2(mock_rados, mock_rbd, mock_client,
  101                                  mock_proxy, mock_usage_info):
  102             inst.mock_rbd = mock_rbd
  103             inst.mock_rados = mock_rados
  104             inst.mock_client = mock_client
  105             inst.mock_proxy = mock_proxy
  106             inst.mock_rbd.RBD.Error = Exception
  107             inst.mock_rados.Error = Exception
  108             inst.mock_rbd.ImageBusy = MockImageBusyException
  109             inst.mock_rbd.ImageNotFound = MockImageNotFoundException
  110             inst.mock_rbd.ImageExists = MockImageExistsException
  111             inst.mock_rbd.InvalidArgument = MockImageNotFoundException
  112             inst.mock_rbd.PermissionError = MockPermissionError
  113 
  114             inst.driver.rbd = inst.mock_rbd
  115             inst.driver.rados = inst.mock_rados
  116             return f(inst, *args, **kwargs)
  117 
  118         return _common_inner_inner2()
  119 
  120     return _common_inner_inner1
  121 
  122 
  123 CEPH_MON_DUMP = r"""dumped monmap epoch 1
  124 { "epoch": 1,
  125   "fsid": "33630410-6d93-4d66-8e42-3b953cf194aa",
  126   "modified": "2013-05-22 17:44:56.343618",
  127   "created": "2013-05-22 17:44:56.343618",
  128   "mons": [
  129         { "rank": 0,
  130           "name": "a",
  131           "addr": "[::1]:6789\/0"},
  132         { "rank": 1,
  133           "name": "b",
  134           "addr": "[::1]:6790\/0"},
  135         { "rank": 2,
  136           "name": "c",
  137           "addr": "[::1]:6791\/0"},
  138         { "rank": 3,
  139           "name": "d",
  140           "addr": "127.0.0.1:6792\/0"},
  141         { "rank": 4,
  142           "name": "e",
  143           "addr": "example.com:6791\/0"}],
  144   "quorum": [
  145         0,
  146         1,
  147         2]}
  148 """
  149 
  150 
  151 class MockDriverConfig(object):
  152     def __init__(self, **kwargs):
  153         my_dict = vars(self)
  154         my_dict.update(kwargs)
  155         my_dict.setdefault('max_over_subscription_ratio', 1.0)
  156         my_dict.setdefault('reserved_percentage', 0)
  157         my_dict.setdefault('volume_backend_name', 'RBD')
  158         my_dict.setdefault('_default', None)
  159 
  160     def __call__(self, value):
  161         return getattr(self, value, self._default)
  162 
  163 
  164 @ddt.ddt
  165 class RBDTestCase(test.TestCase):
  166 
  167     @staticmethod
  168     def _make_configuration(conf_in=None):
  169         cfg = mock.Mock(spec=conf.Configuration)
  170         cfg.image_conversion_dir = None
  171         cfg.rbd_cluster_name = 'nondefault'
  172         cfg.rbd_pool = 'rbd'
  173         cfg.rbd_ceph_conf = '/etc/ceph/my_ceph.conf'
  174         cfg.rbd_secret_uuid = None
  175         cfg.rbd_user = 'cinder'
  176         cfg.volume_backend_name = None
  177         cfg.volume_dd_blocksize = '1M'
  178         cfg.rbd_store_chunk_size = 4
  179         cfg.rados_connection_retries = 3
  180         cfg.rados_connection_interval = 5
  181         cfg.backup_use_temp_snapshot = False
  182         cfg.enable_deferred_deletion = False
  183 
  184         if conf_in is not None:
  185             for k in conf_in:
  186                 setattr(cfg, k, conf_in[k])
  187 
  188         return cfg
  189 
  190     @staticmethod
  191     def _make_drv(conf_in):
  192         cfg = RBDTestCase._make_configuration(conf_in)
  193 
  194         mock_exec = mock.Mock(return_value=('', ''))
  195 
  196         drv = driver.RBDDriver(execute=mock_exec,
  197                                configuration=cfg,
  198                                rbd=mock.MagicMock())
  199         drv.set_initialized()
  200         return drv
  201 
  202     def setUp(self):
  203         global RAISED_EXCEPTIONS
  204         RAISED_EXCEPTIONS = []
  205         super(RBDTestCase, self).setUp()
  206 
  207         self.cfg = self._make_configuration()
  208 
  209         mock_exec = mock.Mock()
  210         mock_exec.return_value = ('', '')
  211 
  212         self.driver = driver.RBDDriver(execute=mock_exec,
  213                                        configuration=self.cfg)
  214         self.driver.set_initialized()
  215 
  216         self.context = context.get_admin_context()
  217 
  218         self.volume_a = fake_volume.fake_volume_obj(
  219             self.context,
  220             **{'name': u'volume-0000000a',
  221                'id': '4c39c3c7-168f-4b32-b585-77f1b3bf0a38',
  222                'size': 10})
  223 
  224         self.volume_b = fake_volume.fake_volume_obj(
  225             self.context,
  226             **{'name': u'volume-0000000b',
  227                'id': '0c7d1f44-5a06-403f-bb82-ae7ad0d693a6',
  228                'size': 10})
  229 
  230         self.volume_c = fake_volume.fake_volume_obj(
  231             self.context,
  232             **{'name': u'volume-0000000a',
  233                'id': '55555555-222f-4b32-b585-9991b3bf0a99',
  234                'size': 12,
  235                'encryption_key_id': fake.ENCRYPTION_KEY_ID})
  236 
  237         self.snapshot = fake_snapshot.fake_snapshot_obj(
  238             self.context, name='snapshot-0000000a')
  239 
  240         self.snapshot_b = fake_snapshot.fake_snapshot_obj(
  241             self.context,
  242             **{'name': u'snapshot-0000000n',
  243                'expected_attrs': ['volume'],
  244                'volume': {'id': fake.VOLUME_ID,
  245                           'name': 'cinder-volume',
  246                           'size': 128,
  247                           'host': 'host@fakebackend#fakepool'}
  248                })
  249 
  250     @ddt.data({'cluster_name': None, 'pool_name': 'rbd'},
  251               {'cluster_name': 'volumes', 'pool_name': None})
  252     @ddt.unpack
  253     def test_min_config(self, cluster_name, pool_name):
  254         self.cfg.rbd_cluster_name = cluster_name
  255         self.cfg.rbd_pool = pool_name
  256 
  257         with mock.patch('cinder.volume.drivers.rbd.rados'):
  258             self.assertRaises(exception.InvalidConfigurationValue,
  259                               self.driver.check_for_setup_error)
  260 
  261     @mock.patch.object(driver, 'rados', mock.Mock())
  262     @mock.patch.object(driver, 'RADOSClient')
  263     def test_check_for_setup_error_missing_keyring_data(self, mock_client):
  264         self.driver.keyring_file = '/etc/ceph/ceph.client.admin.keyring'
  265         self.driver.keyring_data = None
  266 
  267         self.assertRaises(exception.InvalidConfigurationValue,
  268                           self.driver.check_for_setup_error)
  269         mock_client.assert_called_once_with(self.driver)
  270 
  271     def test_parse_replication_config_empty(self):
  272         self.driver._parse_replication_configs([])
  273         self.assertEqual([], self.driver._replication_targets)
  274 
  275     def test_parse_replication_config_missing(self):
  276         """Parsing replication_device without required backend_id."""
  277         cfg = [{'conf': '/etc/ceph/secondary.conf'}]
  278         self.assertRaises(exception.InvalidConfigurationValue,
  279                           self.driver._parse_replication_configs,
  280                           cfg)
  281 
  282     def test_parse_replication_config_defaults(self):
  283         """Parsing replication_device with default conf and user."""
  284         cfg = [{'backend_id': 'secondary-backend'}]
  285         expected = [{'name': 'secondary-backend',
  286                      'conf': '/etc/ceph/secondary-backend.conf',
  287                      'user': 'cinder',
  288                      'secret_uuid': self.cfg.rbd_secret_uuid}]
  289         self.driver._parse_replication_configs(cfg)
  290         self.assertEqual(expected, self.driver._replication_targets)
  291 
  292     @ddt.data(1, 2)
  293     def test_parse_replication_config(self, num_targets):
  294         cfg = [{'backend_id': 'secondary-backend',
  295                 'conf': 'foo',
  296                 'user': 'bar'},
  297                {'backend_id': 'tertiary-backend'}]
  298         expected = [{'name': 'secondary-backend',
  299                      'conf': 'foo',
  300                      'user': 'bar',
  301                      'secret_uuid': self.cfg.rbd_secret_uuid},
  302                     {'name': 'tertiary-backend',
  303                      'conf': '/etc/ceph/tertiary-backend.conf',
  304                      'user': 'cinder',
  305                      'secret_uuid': self.cfg.rbd_secret_uuid}]
  306         self.driver._parse_replication_configs(cfg[:num_targets])
  307         self.assertEqual(expected[:num_targets],
  308                          self.driver._replication_targets)
  309 
  310     def test_do_setup_replication_disabled(self):
  311         with mock.patch.object(self.driver.configuration, 'safe_get',
  312                                return_value=None):
  313             self.driver.do_setup(self.context)
  314             self.assertFalse(self.driver._is_replication_enabled)
  315             self.assertEqual([], self.driver._replication_targets)
  316             self.assertEqual([], self.driver._target_names)
  317             self.assertEqual({'name': self.cfg.rbd_cluster_name,
  318                               'conf': self.cfg.rbd_ceph_conf,
  319                               'user': self.cfg.rbd_user,
  320                               'secret_uuid': self.cfg.rbd_secret_uuid},
  321                              self.driver._active_config)
  322 
  323     def test_do_setup_replication(self):
  324         cfg = [{'backend_id': 'secondary-backend',
  325                 'conf': 'foo',
  326                 'user': 'bar',
  327                 'secret_uuid': 'secondary_secret_uuid'}]
  328         expected = [{'name': 'secondary-backend',
  329                      'conf': 'foo',
  330                      'user': 'bar',
  331                      'secret_uuid': 'secondary_secret_uuid'}]
  332 
  333         with mock.patch.object(self.driver.configuration, 'safe_get',
  334                                return_value=cfg):
  335             self.driver.do_setup(self.context)
  336             self.assertTrue(self.driver._is_replication_enabled)
  337             self.assertEqual(expected, self.driver._replication_targets)
  338             self.assertEqual({'name': self.cfg.rbd_cluster_name,
  339                               'conf': self.cfg.rbd_ceph_conf,
  340                               'user': self.cfg.rbd_user,
  341                               'secret_uuid': self.cfg.rbd_secret_uuid},
  342                              self.driver._active_config)
  343 
  344     def test_do_setup_replication_failed_over(self):
  345         cfg = [{'backend_id': 'secondary-backend',
  346                 'conf': 'foo',
  347                 'user': 'bar',
  348                 'secret_uuid': 'secondary_secret_uuid'}]
  349         expected = [{'name': 'secondary-backend',
  350                      'conf': 'foo',
  351                      'user': 'bar',
  352                      'secret_uuid': 'secondary_secret_uuid'}]
  353         self.driver._active_backend_id = 'secondary-backend'
  354 
  355         with mock.patch.object(self.driver.configuration, 'safe_get',
  356                                return_value=cfg):
  357             self.driver.do_setup(self.context)
  358             self.assertTrue(self.driver._is_replication_enabled)
  359             self.assertEqual(expected, self.driver._replication_targets)
  360             self.assertEqual(expected[0], self.driver._active_config)
  361 
  362     def test_do_setup_replication_failed_over_unknown(self):
  363         cfg = [{'backend_id': 'secondary-backend',
  364                 'conf': 'foo',
  365                 'user': 'bar'}]
  366         self.driver._active_backend_id = 'unknown-backend'
  367 
  368         with mock.patch.object(self.driver.configuration, 'safe_get',
  369                                return_value=cfg):
  370             self.assertRaises(exception.InvalidReplicationTarget,
  371                               self.driver.do_setup,
  372                               self.context)
  373 
  374     @mock.patch.object(driver.RBDDriver, '_enable_replication',
  375                        return_value={'replication': 'enabled'})
  376     def test_setup_volume_with_replication(self, mock_enable):
  377         self.volume_a.volume_type = fake_volume.fake_volume_type_obj(
  378             self.context,
  379             id=fake.VOLUME_TYPE_ID,
  380             extra_specs={'replication_enabled': '<is> True'})
  381         res = self.driver._setup_volume(self.volume_a)
  382         self.assertEqual('enabled', res['replication'])
  383         mock_enable.assert_called_once_with(self.volume_a)
  384 
  385     @ddt.data(False, True)
  386     @mock.patch.object(driver.RBDDriver, '_enable_replication')
  387     def test_setup_volume_without_replication(self, enabled, mock_enable):
  388         self.driver._is_replication_enabled = enabled
  389         res = self.driver._setup_volume(self.volume_a)
  390         if enabled:
  391             expect = {'replication_status': fields.ReplicationStatus.DISABLED}
  392         else:
  393             expect = {}
  394         self.assertEqual(expect, res)
  395         mock_enable.assert_not_called()
  396 
  397     @ddt.data([True, False], [False, False], [True, True])
  398     @ddt.unpack
  399     @common_mocks
  400     def test_enable_replication(self, exclusive_lock_enabled,
  401                                 journaling_enabled):
  402         """Test _enable_replication method.
  403 
  404         We want to confirm that if the Ceph backend has globally enabled
  405         'exclusive_lock' and 'journaling'. we don't try to enable them
  406         again and we properly indicate with our return value that they were
  407         already enabled.
  408         'journaling' depends on 'exclusive_lock', so if 'exclusive-lock'
  409         is disabled, 'journaling' can't be enabled so the '[False. True]'
  410         case is impossible.
  411         In this test case, there are three test scenarios:
  412         1. 'exclusive_lock' and 'journaling' both enabled,
  413         'image.features()' will not be called.
  414         2. 'exclusive_lock' enabled, 'journaling' disabled,
  415         'image.features()' will be only called for 'journaling'.
  416         3. 'exclusice_lock' and 'journaling' are both disabled,
  417         'image.features()'will be both called for 'exclusive-lock' and
  418         'journaling' in this order.
  419         """
  420         image = self.mock_proxy.return_value.__enter__.return_value
  421 
  422         image_features = 0
  423         if exclusive_lock_enabled:
  424             image_features |= self.driver.RBD_FEATURE_EXCLUSIVE_LOCK
  425         if journaling_enabled:
  426             image_features |= self.driver.RBD_FEATURE_JOURNALING
  427 
  428         image.features.return_value = image_features
  429 
  430         journaling_status = str(journaling_enabled).lower()
  431         exclusive_lock_status = str(exclusive_lock_enabled).lower()
  432 
  433         expected = {
  434             'replication_driver_data': ('{"had_exclusive_lock":%s,'
  435                                         '"had_journaling":%s}' %
  436                                         (exclusive_lock_status,
  437                                          journaling_status)),
  438             'replication_status': 'enabled',
  439         }
  440         res = self.driver._enable_replication(self.volume_a)
  441         self.assertEqual(expected, res)
  442 
  443         if exclusive_lock_enabled and journaling_enabled:
  444             image.update_features.assert_not_called()
  445         elif exclusive_lock_enabled and not journaling_enabled:
  446             image.update_features.assert_called_once_with(
  447                 self.driver.RBD_FEATURE_JOURNALING, True)
  448         else:
  449             calls = [call(self.driver.RBD_FEATURE_EXCLUSIVE_LOCK, True),
  450                      call(self.driver.RBD_FEATURE_JOURNALING, True)]
  451             image.update_features.assert_has_calls(calls, any_order=False)
  452         image.mirror_image_enable.assert_called_once_with()
  453 
  454     @ddt.data(['false', 'true'], ['true', 'true'], ['false', 'false'])
  455     @ddt.unpack
  456     @common_mocks
  457     def test_disable_replication(self, had_journaling, had_exclusive_lock):
  458         driver_data = ('{"had_journaling": %s,"had_exclusive_lock": %s}' %
  459                        (had_journaling, had_exclusive_lock))
  460         self.volume_a.replication_driver_data = driver_data
  461         image = self.mock_proxy.return_value.__enter__.return_value
  462 
  463         res = self.driver._disable_replication(self.volume_a)
  464         expected = {'replication_status': fields.ReplicationStatus.DISABLED,
  465                     'replication_driver_data': None}
  466         self.assertEqual(expected, res)
  467         image.mirror_image_disable.assert_called_once_with(False)
  468 
  469         if had_journaling == 'true' and had_exclusive_lock == 'true':
  470             image.update_features.assert_not_called()
  471         elif had_journaling == 'false' and had_exclusive_lock == 'true':
  472             image.update_features.assert_called_once_with(
  473                 self.driver.RBD_FEATURE_JOURNALING, False)
  474         else:
  475             calls = [call(self.driver.RBD_FEATURE_JOURNALING, False),
  476                      call(self.driver.RBD_FEATURE_EXCLUSIVE_LOCK,
  477                           False)]
  478             image.update_features.assert_has_calls(calls, any_order=False)
  479 
  480     @common_mocks
  481     @mock.patch.object(driver.RBDDriver, '_enable_replication')
  482     def test_create_volume(self, mock_enable_repl):
  483         client = self.mock_client.return_value
  484         client.__enter__.return_value = client
  485 
  486         res = self.driver.create_volume(self.volume_a)
  487 
  488         self.assertEqual({}, res)
  489         chunk_size = self.cfg.rbd_store_chunk_size * units.Mi
  490         order = int(math.log(chunk_size, 2))
  491         args = [client.ioctx, str(self.volume_a.name),
  492                 self.volume_a.size * units.Gi, order]
  493         kwargs = {'old_format': False,
  494                   'features': client.features}
  495         self.mock_rbd.RBD.return_value.create.assert_called_once_with(
  496             *args, **kwargs)
  497         client.__enter__.assert_called_once_with()
  498         client.__exit__.assert_called_once_with(None, None, None)
  499         mock_enable_repl.assert_not_called()
  500 
  501     @common_mocks
  502     @mock.patch.object(driver.RBDDriver, '_enable_replication')
  503     def test_create_volume_replicated(self, mock_enable_repl):
  504         self.volume_a.volume_type = fake_volume.fake_volume_type_obj(
  505             self.context,
  506             id=fake.VOLUME_TYPE_ID,
  507             extra_specs={'replication_enabled': '<is> True'})
  508 
  509         client = self.mock_client.return_value
  510         client.__enter__.return_value = client
  511 
  512         expected_update = {
  513             'replication_status': 'enabled',
  514             'replication_driver_data': '{"had_journaling": false}'
  515         }
  516         mock_enable_repl.return_value = expected_update
  517 
  518         res = self.driver.create_volume(self.volume_a)
  519         self.assertEqual(expected_update, res)
  520         mock_enable_repl.assert_called_once_with(self.volume_a)
  521 
  522         chunk_size = self.cfg.rbd_store_chunk_size * units.Mi
  523         order = int(math.log(chunk_size, 2))
  524         self.mock_rbd.RBD.return_value.create.assert_called_once_with(
  525             client.ioctx, self.volume_a.name, self.volume_a.size * units.Gi,
  526             order, old_format=False, features=client.features)
  527 
  528         client.__enter__.assert_called_once_with()
  529         client.__exit__.assert_called_once_with(None, None, None)
  530 
  531     @common_mocks
  532     def test_manage_existing_get_size(self):
  533         with mock.patch.object(self.driver.rbd.Image(), 'size') as \
  534                 mock_rbd_image_size:
  535             with mock.patch.object(self.driver.rbd.Image(), 'close') \
  536                     as mock_rbd_image_close:
  537                 mock_rbd_image_size.return_value = 2 * units.Gi
  538                 existing_ref = {'source-name': self.volume_a.name}
  539                 return_size = self.driver.manage_existing_get_size(
  540                     self.volume_a,
  541                     existing_ref)
  542                 self.assertEqual(2, return_size)
  543                 mock_rbd_image_size.assert_called_once_with()
  544                 mock_rbd_image_close.assert_called_once_with()
  545 
  546     @common_mocks
  547     def test_manage_existing_get_non_integer_size(self):
  548         rbd_image = self.driver.rbd.Image.return_value
  549         rbd_image.size.return_value = int(1.75 * units.Gi)
  550         existing_ref = {'source-name': self.volume_a.name}
  551         return_size = self.driver.manage_existing_get_size(self.volume_a,
  552                                                            existing_ref)
  553         self.assertEqual(2, return_size)
  554         rbd_image.size.assert_called_once_with()
  555         rbd_image.close.assert_called_once_with()
  556 
  557     @common_mocks
  558     def test_manage_existing_get_invalid_size(self):
  559 
  560         with mock.patch.object(self.driver.rbd.Image(), 'size') as \
  561                 mock_rbd_image_size:
  562             with mock.patch.object(self.driver.rbd.Image(), 'close') \
  563                     as mock_rbd_image_close:
  564                 mock_rbd_image_size.return_value = 'abcd'
  565                 existing_ref = {'source-name': self.volume_a.name}
  566                 self.assertRaises(exception.VolumeBackendAPIException,
  567                                   self.driver.manage_existing_get_size,
  568                                   self.volume_a, existing_ref)
  569 
  570                 mock_rbd_image_size.assert_called_once_with()
  571                 mock_rbd_image_close.assert_called_once_with()
  572 
  573     @common_mocks
  574     def test_manage_existing(self):
  575         client = self.mock_client.return_value
  576         client.__enter__.return_value = client
  577 
  578         with mock.patch.object(self.driver.rbd.RBD(), 'rename') as \
  579                 mock_rbd_image_rename:
  580             exist_volume = 'vol-exist'
  581             existing_ref = {'source-name': exist_volume}
  582             mock_rbd_image_rename.return_value = 0
  583             self.driver.manage_existing(self.volume_a, existing_ref)
  584             mock_rbd_image_rename.assert_called_with(
  585                 client.ioctx,
  586                 exist_volume,
  587                 self.volume_a.name)
  588 
  589     @common_mocks
  590     def test_manage_existing_with_exist_rbd_image(self):
  591         client = self.mock_client.return_value
  592         client.__enter__.return_value = client
  593 
  594         self.mock_rbd.RBD.return_value.rename.side_effect = (
  595             MockImageExistsException)
  596 
  597         exist_volume = 'vol-exist'
  598         existing_ref = {'source-name': exist_volume}
  599         self.assertRaises(self.mock_rbd.ImageExists,
  600                           self.driver.manage_existing,
  601                           self.volume_a, existing_ref)
  602 
  603         # Make sure the exception was raised
  604         self.assertEqual([self.mock_rbd.ImageExists], RAISED_EXCEPTIONS)
  605 
  606     @common_mocks
  607     def test_manage_existing_with_invalid_rbd_image(self):
  608         self.mock_rbd.Image.side_effect = self.mock_rbd.ImageNotFound
  609 
  610         invalid_volume = 'vol-invalid'
  611         invalid_ref = {'source-name': invalid_volume}
  612 
  613         self.assertRaises(exception.ManageExistingInvalidReference,
  614                           self.driver.manage_existing_get_size,
  615                           self.volume_a, invalid_ref)
  616         # Make sure the exception was raised
  617         self.assertEqual([self.mock_rbd.ImageNotFound],
  618                          RAISED_EXCEPTIONS)
  619 
  620     @common_mocks
  621     @mock.patch.object(driver.RBDDriver, '_get_image_status')
  622     def test_get_manageable_volumes(self, mock_get_image_status):
  623         cinder_vols = [{'id': '00000000-0000-0000-0000-000000000000'}]
  624         vols = ['volume-00000000-0000-0000-0000-000000000000', 'vol1', 'vol2',
  625                 'volume-11111111-1111-1111-1111-111111111111.deleted']
  626         self.mock_rbd.RBD.return_value.list.return_value = vols
  627         image = self.mock_proxy.return_value.__enter__.return_value
  628         image.size.side_effect = [2 * units.Gi, 4 * units.Gi, 6 * units.Gi,
  629                                   8 * units.Gi]
  630         mock_get_image_status.side_effect = [
  631             {'watchers': []},
  632             {'watchers': [{"address": "192.168.120.61:0/3012034728",
  633                            "client": 44431941, "cookie": 94077162321152}]},
  634             {'watchers': []}]
  635         res = self.driver.get_manageable_volumes(
  636             cinder_vols, None, 1000, 0, ['size'], ['asc'])
  637         exp = [{'size': 2, 'reason_not_safe': 'already managed',
  638                 'extra_info': None, 'safe_to_manage': False,
  639                 'reference': {'source-name':
  640                               'volume-00000000-0000-0000-0000-000000000000'},
  641                 'cinder_id': '00000000-0000-0000-0000-000000000000'},
  642                {'size': 4, 'reason_not_safe': None,
  643                 'safe_to_manage': True, 'reference': {'source-name': 'vol1'},
  644                 'cinder_id': None, 'extra_info': None},
  645                {'size': 6, 'reason_not_safe': 'volume in use',
  646                 'safe_to_manage': False, 'reference': {'source-name': 'vol2'},
  647                 'cinder_id': None, 'extra_info': None},
  648                {'size': 8, 'reason_not_safe': 'volume marked as deleted',
  649                 'safe_to_manage': False, 'cinder_id': None, 'extra_info': None,
  650                 'reference': {
  651                     'source-name':
  652                         'volume-11111111-1111-1111-1111-111111111111.deleted'}}
  653                ]
  654         self.assertEqual(exp, res)
  655 
  656     @common_mocks
  657     def test_delete_backup_snaps(self):
  658         self.driver.rbd.Image.remove_snap = mock.Mock()
  659         with mock.patch.object(self.driver, '_get_backup_snaps') as \
  660                 mock_get_backup_snaps:
  661             mock_get_backup_snaps.return_value = [{'name': 'snap1'}]
  662             rbd_image = self.driver.rbd.Image()
  663             self.driver._delete_backup_snaps(rbd_image)
  664             mock_get_backup_snaps.assert_called_once_with(rbd_image)
  665             self.assertTrue(
  666                 self.driver.rbd.Image.return_value.remove_snap.called)
  667 
  668     @common_mocks
  669     def test_delete_volume(self):
  670         client = self.mock_client.return_value
  671 
  672         self.driver.rbd.Image.return_value.list_snaps.return_value = []
  673 
  674         with mock.patch.object(self.driver, '_get_clone_info') as \
  675                 mock_get_clone_info:
  676             with mock.patch.object(self.driver, '_delete_backup_snaps') as \
  677                     mock_delete_backup_snaps:
  678                 mock_get_clone_info.return_value = (None, None, None)
  679 
  680                 self.driver.delete_volume(self.volume_a)
  681 
  682                 mock_get_clone_info.assert_called_once_with(
  683                     self.mock_rbd.Image.return_value,
  684                     self.volume_a.name,
  685                     None)
  686                 (self.driver.rbd.Image.return_value
  687                     .list_snaps.assert_called_once_with())
  688                 client.__enter__.assert_called_once_with()
  689                 client.__exit__.assert_called_once_with(None, None, None)
  690                 mock_delete_backup_snaps.assert_called_once_with(
  691                     self.mock_rbd.Image.return_value)
  692                 self.assertFalse(
  693                     self.driver.rbd.Image.return_value.unprotect_snap.called)
  694                 self.assertEqual(
  695                     1, self.driver.rbd.RBD.return_value.remove.call_count)
  696 
  697     @common_mocks
  698     def test_deferred_deletion(self):
  699         drv = self._make_drv({'enable_deferred_deletion': True,
  700                               'deferred_deletion_delay': 0})
  701 
  702         client = self.mock_client.return_value
  703 
  704         with mock.patch.object(drv, '_get_clone_info') as \
  705                 mock_get_clone_info:
  706             with mock.patch.object(drv, '_delete_backup_snaps') as \
  707                     mock_delete_backup_snaps:
  708                 mock_get_clone_info.return_value = (None, None, None)
  709 
  710                 drv.delete_volume(self.volume_a)
  711 
  712                 mock_get_clone_info.assert_called_once_with(
  713                     drv.rbd.Image.return_value,
  714                     self.volume_a.name,
  715                     None)
  716                 drv.rbd.Image.return_value.list_snaps.assert_called_once_with()
  717                 client.__enter__.assert_called_once_with()
  718                 client.__exit__.assert_called_once_with(None, None, None)
  719                 mock_delete_backup_snaps.assert_called_once_with(
  720                     drv.rbd.Image.return_value)
  721                 self.assertFalse(
  722                     drv.rbd.Image.return_value.unprotect_snap.called)
  723                 self.assertEqual(
  724                     1, drv.rbd.RBD.return_value.trash_move.call_count)
  725 
  726     @common_mocks
  727     def test_deferred_deletion_periodic_task(self):
  728         drv = self._make_drv({'rados_connect_timeout': -1,
  729                               'enable_deferred_deletion': True,
  730                               'deferred_deletion_purge_interval': 1})
  731         drv._start_periodic_tasks()
  732 
  733         time.sleep(1)
  734         self.assertTrue(drv.rbd.RBD.return_value.trash_list.called)
  735         self.assertFalse(drv.rbd.RBD.return_value.trash_remove.called)
  736 
  737     @common_mocks
  738     def test_deferred_deletion_trash_purge(self):
  739         drv = self._make_drv({'enable_deferred_deletion': True})
  740         with mock.patch.object(drv.rbd.RBD(), 'trash_list') as mock_trash_list:
  741             mock_trash_list.return_value = [self.volume_a]
  742             drv._trash_purge()
  743 
  744             self.assertEqual(
  745                 1, drv.rbd.RBD.return_value.trash_list.call_count)
  746             self.assertEqual(
  747                 1, drv.rbd.RBD.return_value.trash_remove.call_count)
  748 
  749     @common_mocks
  750     def test_deferred_deletion_trash_purge_not_expired(self):
  751         drv = self._make_drv({'enable_deferred_deletion': True})
  752         with mock.patch.object(drv.rbd.RBD(), 'trash_list') as mock_trash_list:
  753             mock_trash_list.return_value = [self.volume_a]
  754             drv.rbd.RBD.return_value.trash_remove.side_effect = (
  755                 self.mock_rbd.PermissionError)
  756 
  757             drv._trash_purge()
  758 
  759             self.assertEqual(
  760                 1, drv.rbd.RBD.return_value.trash_list.call_count)
  761             self.assertEqual(
  762                 1, drv.rbd.RBD.return_value.trash_remove.call_count)
  763             # Make sure the exception was raised
  764             self.assertEqual(1, len(RAISED_EXCEPTIONS))
  765             self.assertIn(self.mock_rbd.PermissionError, RAISED_EXCEPTIONS)
  766 
  767     @common_mocks
  768     def test_deferred_deletion_w_parent(self):
  769         drv = self._make_drv({'enable_deferred_deletion': True,
  770                               'deferred_deletion_delay': 0})
  771         _get_clone_info_return_values = [
  772             (None, self.volume_b.name, None),
  773             (None, None, None)]
  774         with mock.patch.object(drv, '_get_clone_info',
  775                                side_effect = _get_clone_info_return_values):
  776             drv.delete_volume(self.volume_a)
  777 
  778             self.assertEqual(
  779                 1, drv.rbd.RBD.return_value.trash_move.call_count)
  780 
  781     @common_mocks
  782     def test_deferred_deletion_w_deleted_parent(self):
  783         drv = self._make_drv({'enable_deferred_deletion': True,
  784                               'deferred_deletion_delay': 0})
  785         _get_clone_info_return_values = [
  786             (None, "%s.deleted" % self.volume_b.name, None),
  787             (None, None, None)]
  788         with mock.patch.object(drv, '_get_clone_info',
  789                                side_effect = _get_clone_info_return_values):
  790             drv.delete_volume(self.volume_a)
  791 
  792             self.assertEqual(
  793                 2, drv.rbd.RBD.return_value.trash_move.call_count)
  794 
  795     @common_mocks
  796     def delete_volume_not_found(self):
  797         self.mock_rbd.Image.side_effect = self.mock_rbd.ImageNotFound
  798         self.assertIsNone(self.driver.delete_volume(self.volume_a))
  799         self.mock_rbd.Image.assert_called_once_with()
  800         # Make sure the exception was raised
  801         self.assertEqual([self.mock_rbd.ImageNotFound], RAISED_EXCEPTIONS)
  802 
  803     @common_mocks
  804     def test_delete_busy_volume(self):
  805         self.mock_rbd.Image.return_value.list_snaps.return_value = []
  806 
  807         self.mock_rbd.RBD.return_value.remove.side_effect = (
  808             self.mock_rbd.ImageBusy)
  809 
  810         with mock.patch.object(self.driver, '_get_clone_info') as \
  811                 mock_get_clone_info:
  812             mock_get_clone_info.return_value = (None, None, None)
  813             with mock.patch.object(self.driver, '_delete_backup_snaps') as \
  814                     mock_delete_backup_snaps:
  815                 with mock.patch.object(driver, 'RADOSClient') as \
  816                         mock_rados_client:
  817                     self.assertRaises(exception.VolumeIsBusy,
  818                                       self.driver.delete_volume, self.volume_a)
  819 
  820                     mock_get_clone_info.assert_called_once_with(
  821                         self.mock_rbd.Image.return_value,
  822                         self.volume_a.name,
  823                         None)
  824                     (self.mock_rbd.Image.return_value.list_snaps
  825                      .assert_called_once_with())
  826                     mock_rados_client.assert_called_once_with(self.driver)
  827                     mock_delete_backup_snaps.assert_called_once_with(
  828                         self.mock_rbd.Image.return_value)
  829                     self.assertFalse(
  830                         self.mock_rbd.Image.return_value.unprotect_snap.called)
  831                     self.assertEqual(
  832                         3, self.mock_rbd.RBD.return_value.remove.call_count)
  833                     self.assertEqual(3, len(RAISED_EXCEPTIONS))
  834                     # Make sure the exception was raised
  835                     self.assertIn(self.mock_rbd.ImageBusy, RAISED_EXCEPTIONS)
  836 
  837     @common_mocks
  838     def test_delete_volume_not_found(self):
  839         self.mock_rbd.Image.return_value.list_snaps.return_value = []
  840 
  841         self.mock_rbd.RBD.return_value.remove.side_effect = (
  842             self.mock_rbd.ImageNotFound)
  843 
  844         with mock.patch.object(self.driver, '_get_clone_info') as \
  845                 mock_get_clone_info:
  846             mock_get_clone_info.return_value = (None, None, None)
  847             with mock.patch.object(self.driver, '_delete_backup_snaps') as \
  848                     mock_delete_backup_snaps:
  849                 with mock.patch.object(driver, 'RADOSClient') as \
  850                         mock_rados_client:
  851                     self.assertIsNone(self.driver.delete_volume(self.volume_a))
  852                     mock_get_clone_info.assert_called_once_with(
  853                         self.mock_rbd.Image.return_value,
  854                         self.volume_a.name,
  855                         None)
  856                     (self.mock_rbd.Image.return_value.list_snaps
  857                      .assert_called_once_with())
  858                     mock_rados_client.assert_called_once_with(self.driver)
  859                     mock_delete_backup_snaps.assert_called_once_with(
  860                         self.mock_rbd.Image.return_value)
  861                     self.assertFalse(
  862                         self.mock_rbd.Image.return_value.unprotect_snap.called)
  863                     self.assertEqual(
  864                         1, self.mock_rbd.RBD.return_value.remove.call_count)
  865                     # Make sure the exception was raised
  866                     self.assertEqual([self.mock_rbd.ImageNotFound],
  867                                      RAISED_EXCEPTIONS)
  868 
  869     @common_mocks
  870     @mock.patch('cinder.objects.Volume.get_by_id')
  871     def test_create_snapshot(self, volume_get_by_id):
  872         volume_get_by_id.return_value = self.volume_a
  873         proxy = self.mock_proxy.return_value
  874         proxy.__enter__.return_value = proxy
  875 
  876         self.driver.create_snapshot(self.snapshot)
  877 
  878         args = [str(self.snapshot.name)]
  879         proxy.create_snap.assert_called_with(*args)
  880         proxy.protect_snap.assert_called_with(*args)
  881 
  882     @common_mocks
  883     @mock.patch('cinder.objects.Volume.get_by_id')
  884     @mock.patch.object(driver.RBDDriver, '_resize', mock.Mock())
  885     def test_log_create_vol_from_snap_w_v2_clone_api(self, volume_get_by_id):
  886         volume_get_by_id.return_value = self.volume_a
  887 
  888         self.mock_proxy().__enter__().volume.op_features.return_value = 1
  889         self.mock_rbd.RBD_OPERATION_FEATURE_CLONE_PARENT = 1
  890 
  891         snapshot = mock.Mock()
  892         self.cfg.rbd_flatten_volume_from_snapshot = False
  893 
  894         with mock.patch.object(driver, 'LOG') as \
  895                 mock_log:
  896 
  897             self.driver.create_volume_from_snapshot(self.volume_a, snapshot)
  898 
  899             mock_log.info.assert_called_once()
  900             self.assertTrue(self.driver._clone_v2_api_checked)
  901 
  902     @common_mocks
  903     @mock.patch('cinder.objects.Volume.get_by_id')
  904     @mock.patch.object(driver.RBDDriver, '_resize', mock.Mock())
  905     def test_log_create_vol_from_snap_without_v2_clone_api(self,
  906                                                            volume_get_by_id):
  907         volume_get_by_id.return_value = self.volume_a
  908 
  909         self.mock_proxy().__enter__().volume.op_features.return_value = 0
  910         self.mock_rbd.RBD_OPERATION_FEATURE_CLONE_PARENT = 1
  911 
  912         snapshot = mock.Mock()
  913         self.cfg.rbd_flatten_volume_from_snapshot = False
  914 
  915         with mock.patch.object(driver, 'LOG') as \
  916                 mock_log:
  917 
  918             self.driver.create_volume_from_snapshot(self.volume_a, snapshot)
  919 
  920             mock_log.warning.assert_called_once()
  921             self.assertTrue(self.driver._clone_v2_api_checked)
  922 
  923     @common_mocks
  924     @mock.patch('cinder.objects.Volume.get_by_id')
  925     def test_delete_snapshot(self, volume_get_by_id):
  926         volume_get_by_id.return_value = self.volume_a
  927         proxy = self.mock_proxy.return_value
  928         proxy.__enter__.return_value = proxy
  929 
  930         self.driver.delete_snapshot(self.snapshot)
  931 
  932         proxy.remove_snap.assert_called_with(self.snapshot.name)
  933         proxy.unprotect_snap.assert_called_with(self.snapshot.name)
  934 
  935     @common_mocks
  936     @mock.patch('cinder.objects.Volume.get_by_id')
  937     def test_delete_notfound_snapshot(self, volume_get_by_id):
  938         volume_get_by_id.return_value = self.volume_a
  939         proxy = self.mock_proxy.return_value
  940         proxy.__enter__.return_value = proxy
  941 
  942         proxy.unprotect_snap.side_effect = (
  943             self.mock_rbd.ImageNotFound)
  944 
  945         self.driver.delete_snapshot(self.snapshot)
  946 
  947         proxy.remove_snap.assert_called_with(self.snapshot.name)
  948         proxy.unprotect_snap.assert_called_with(self.snapshot.name)
  949 
  950     @common_mocks
  951     @mock.patch('cinder.objects.Volume.get_by_id')
  952     def test_delete_notfound_on_remove_snapshot(self, volume_get_by_id):
  953         volume_get_by_id.return_value = self.volume_a
  954         proxy = self.mock_proxy.return_value
  955         proxy.__enter__.return_value = proxy
  956 
  957         proxy.remove_snap.side_effect = (
  958             self.mock_rbd.ImageNotFound)
  959 
  960         self.driver.delete_snapshot(self.snapshot)
  961 
  962         proxy.remove_snap.assert_called_with(self.snapshot.name)
  963         proxy.unprotect_snap.assert_called_with(self.snapshot.name)
  964 
  965     @common_mocks
  966     @mock.patch('cinder.objects.Volume.get_by_id')
  967     def test_delete_unprotected_snapshot(self, volume_get_by_id):
  968         volume_get_by_id.return_value = self.volume_a
  969         proxy = self.mock_proxy.return_value
  970         proxy.__enter__.return_value = proxy
  971         proxy.unprotect_snap.side_effect = self.mock_rbd.InvalidArgument
  972 
  973         self.driver.delete_snapshot(self.snapshot)
  974         self.assertTrue(proxy.unprotect_snap.called)
  975         self.assertTrue(proxy.remove_snap.called)
  976 
  977     @common_mocks
  978     @mock.patch('cinder.objects.Volume.get_by_id')
  979     def test_delete_busy_snapshot(self, volume_get_by_id):
  980         volume_get_by_id.return_value = self.volume_a
  981         proxy = self.mock_proxy.return_value
  982         proxy.__enter__.return_value = proxy
  983 
  984         proxy.unprotect_snap.side_effect = (
  985             self.mock_rbd.ImageBusy)
  986 
  987         with mock.patch.object(self.driver, '_get_children_info') as \
  988                 mock_get_children_info:
  989             mock_get_children_info.return_value = [('pool', 'volume2')]
  990 
  991             with mock.patch.object(driver, 'LOG') as \
  992                     mock_log:
  993 
  994                 self.assertRaises(exception.SnapshotIsBusy,
  995                                   self.driver.delete_snapshot,
  996                                   self.snapshot)
  997 
  998                 mock_get_children_info.assert_called_once_with(
  999                     proxy,
 1000                     self.snapshot.name)
 1001 
 1002                 self.assertTrue(mock_log.info.called)
 1003                 self.assertTrue(proxy.unprotect_snap.called)
 1004                 self.assertFalse(proxy.remove_snap.called)
 1005 
 1006     @common_mocks
 1007     def test_snapshot_revert_use_temp_snapshot(self):
 1008         self.assertFalse(self.driver.snapshot_revert_use_temp_snapshot())
 1009 
 1010     @common_mocks
 1011     def test_revert_to_snapshot(self):
 1012         image = self.mock_proxy.return_value.__enter__.return_value
 1013         self.driver.revert_to_snapshot(self.context, self.volume_a,
 1014                                        self.snapshot)
 1015         image.rollback_to_snap.assert_called_once_with(self.snapshot.name)
 1016 
 1017     @common_mocks
 1018     def test_get_children_info(self):
 1019         volume = self.mock_proxy
 1020         volume.set_snap = mock.Mock()
 1021         volume.list_children = mock.Mock()
 1022         list_children = [('pool', 'volume2')]
 1023         volume.list_children.return_value = list_children
 1024 
 1025         info = self.driver._get_children_info(volume,
 1026                                               self.snapshot['name'])
 1027 
 1028         self.assertEqual(list_children, info)
 1029 
 1030     @common_mocks
 1031     def test_get_clone_info(self):
 1032         volume = self.mock_rbd.Image()
 1033         volume.set_snap = mock.Mock()
 1034         volume.parent_info = mock.Mock()
 1035         parent_info = ('a', 'b', '%s.clone_snap' % (self.volume_a.name))
 1036         volume.parent_info.return_value = parent_info
 1037 
 1038         info = self.driver._get_clone_info(volume, self.volume_a.name)
 1039 
 1040         self.assertEqual(parent_info, info)
 1041 
 1042         self.assertFalse(volume.set_snap.called)
 1043         volume.parent_info.assert_called_once_with()
 1044 
 1045     @common_mocks
 1046     def test_get_clone_info_w_snap(self):
 1047         volume = self.mock_rbd.Image()
 1048         volume.set_snap = mock.Mock()
 1049         volume.parent_info = mock.Mock()
 1050         parent_info = ('a', 'b', '%s.clone_snap' % (self.volume_a.name))
 1051         volume.parent_info.return_value = parent_info
 1052 
 1053         snapshot = self.mock_rbd.ImageSnapshot()
 1054 
 1055         info = self.driver._get_clone_info(volume, self.volume_a.name,
 1056                                            snap=snapshot)
 1057 
 1058         self.assertEqual(parent_info, info)
 1059 
 1060         self.assertEqual(2, volume.set_snap.call_count)
 1061         volume.parent_info.assert_called_once_with()
 1062 
 1063     @common_mocks
 1064     def test_get_clone_info_w_exception(self):
 1065         volume = self.mock_rbd.Image()
 1066         volume.set_snap = mock.Mock()
 1067         volume.parent_info = mock.Mock()
 1068         volume.parent_info.side_effect = self.mock_rbd.ImageNotFound
 1069 
 1070         snapshot = self.mock_rbd.ImageSnapshot()
 1071 
 1072         info = self.driver._get_clone_info(volume, self.volume_a.name,
 1073                                            snap=snapshot)
 1074 
 1075         self.assertEqual((None, None, None), info)
 1076 
 1077         self.assertEqual(2, volume.set_snap.call_count)
 1078         volume.parent_info.assert_called_once_with()
 1079         # Make sure the exception was raised
 1080         self.assertEqual([self.mock_rbd.ImageNotFound], RAISED_EXCEPTIONS)
 1081 
 1082     @common_mocks
 1083     def test_get_clone_info_deleted_volume(self):
 1084         volume = self.mock_rbd.Image()
 1085         volume.set_snap = mock.Mock()
 1086         volume.parent_info = mock.Mock()
 1087         parent_info = ('a', 'b', '%s.clone_snap' % (self.volume_a.name))
 1088         volume.parent_info.return_value = parent_info
 1089 
 1090         info = self.driver._get_clone_info(volume,
 1091                                            "%s.deleted" % (self.volume_a.name))
 1092 
 1093         self.assertEqual(parent_info, info)
 1094 
 1095         self.assertFalse(volume.set_snap.called)
 1096         volume.parent_info.assert_called_once_with()
 1097 
 1098     @common_mocks
 1099     @mock.patch.object(driver.RBDDriver, '_enable_replication')
 1100     def test_create_cloned_volume_same_size(self, mock_enable_repl):
 1101         self.cfg.rbd_max_clone_depth = 2
 1102 
 1103         with mock.patch.object(self.driver, '_get_clone_depth') as \
 1104                 mock_get_clone_depth:
 1105             # Try with no flatten required
 1106             with mock.patch.object(self.driver, '_resize') as mock_resize:
 1107                 mock_get_clone_depth.return_value = 1
 1108 
 1109                 res = self.driver.create_cloned_volume(self.volume_b,
 1110                                                        self.volume_a)
 1111 
 1112                 self.assertEqual({}, res)
 1113                 (self.mock_rbd.Image.return_value.create_snap
 1114                     .assert_called_once_with('.'.join(
 1115                         (self.volume_b.name, 'clone_snap'))))
 1116                 (self.mock_rbd.Image.return_value.protect_snap
 1117                     .assert_called_once_with('.'.join(
 1118                         (self.volume_b.name, 'clone_snap'))))
 1119                 # We expect clone() to be called exactly once.
 1120                 self.assertEqual(
 1121                     1, self.mock_rbd.RBD.return_value.clone.call_count)
 1122                 # Without flattening, only the source volume is opened,
 1123                 # so only one call to close() should occur.
 1124                 self.assertEqual(
 1125                     1, self.mock_rbd.Image.return_value.close.call_count)
 1126                 self.assertTrue(mock_get_clone_depth.called)
 1127                 mock_resize.assert_not_called()
 1128                 mock_enable_repl.assert_not_called()
 1129 
 1130     @common_mocks
 1131     @mock.patch.object(driver.RBDDriver, '_get_clone_depth', return_value=1)
 1132     @mock.patch.object(driver.RBDDriver, '_resize')
 1133     @mock.patch.object(driver.RBDDriver, '_enable_replication')
 1134     def test_create_cloned_volume_replicated(self,
 1135                                              mock_enable_repl,
 1136                                              mock_resize,
 1137                                              mock_get_clone_depth):
 1138         self.cfg.rbd_max_clone_depth = 2
 1139         self.volume_b.volume_type = fake_volume.fake_volume_type_obj(
 1140             self.context,
 1141             id=fake.VOLUME_TYPE_ID,
 1142             extra_specs={'replication_enabled': '<is> True'})
 1143 
 1144         expected_update = {
 1145             'replication_status': 'enabled',
 1146             'replication_driver_data': '{"had_journaling": false}'
 1147         }
 1148         mock_enable_repl.return_value = expected_update
 1149 
 1150         res = self.driver.create_cloned_volume(self.volume_b, self.volume_a)
 1151         self.assertEqual(expected_update, res)
 1152         mock_enable_repl.assert_called_once_with(self.volume_b)
 1153 
 1154         name = self.volume_b.name
 1155         image = self.mock_rbd.Image.return_value
 1156 
 1157         image.create_snap.assert_called_once_with(name + '.clone_snap')
 1158         image.protect_snap.assert_called_once_with(name + '.clone_snap')
 1159         self.assertEqual(1, self.mock_rbd.RBD.return_value.clone.call_count)
 1160         self.assertEqual(
 1161             1, self.mock_rbd.Image.return_value.close.call_count)
 1162         mock_get_clone_depth.assert_called_once_with(
 1163             self.mock_client().__enter__(), self.volume_a.name)
 1164         mock_resize.assert_not_called()
 1165 
 1166     @common_mocks
 1167     @mock.patch.object(driver.RBDDriver, '_enable_replication')
 1168     def test_create_cloned_volume_different_size(self, mock_enable_repl):
 1169         self.cfg.rbd_max_clone_depth = 2
 1170 
 1171         with mock.patch.object(self.driver, '_get_clone_depth') as \
 1172                 mock_get_clone_depth:
 1173             # Try with no flatten required
 1174             with mock.patch.object(self.driver, '_resize') as mock_resize:
 1175                 mock_get_clone_depth.return_value = 1
 1176 
 1177                 self.volume_b.size = 20
 1178                 res = self.driver.create_cloned_volume(self.volume_b,
 1179                                                        self.volume_a)
 1180 
 1181                 self.assertEqual({}, res)
 1182                 (self.mock_rbd.Image.return_value.create_snap
 1183                     .assert_called_once_with('.'.join(
 1184                         (self.volume_b.name, 'clone_snap'))))
 1185                 (self.mock_rbd.Image.return_value.protect_snap
 1186                     .assert_called_once_with('.'.join(
 1187                         (self.volume_b.name, 'clone_snap'))))
 1188                 self.assertEqual(
 1189                     1, self.mock_rbd.RBD.return_value.clone.call_count)
 1190                 self.assertEqual(
 1191                     1, self.mock_rbd.Image.return_value.close.call_count)
 1192                 self.assertTrue(mock_get_clone_depth.called)
 1193                 self.assertEqual(
 1194                     1, mock_resize.call_count)
 1195                 mock_enable_repl.assert_not_called()
 1196 
 1197     @common_mocks
 1198     def test_create_cloned_volume_different_size_copy_only(self):
 1199         self.cfg.rbd_max_clone_depth = 0
 1200 
 1201         with mock.patch.object(self.driver, '_get_clone_depth') as \
 1202                 mock_get_clone_depth:
 1203             # Try with no flatten required
 1204             with mock.patch.object(self.driver, '_resize') as mock_resize:
 1205                 mock_get_clone_depth.return_value = 1
 1206 
 1207                 self.volume_b.size = 20
 1208                 self.driver.create_cloned_volume(self.volume_b, self.volume_a)
 1209 
 1210                 self.assertEqual(1, mock_resize.call_count)
 1211 
 1212     @common_mocks
 1213     @mock.patch.object(driver.RBDDriver, '_enable_replication')
 1214     def test_create_cloned_volume_w_flatten(self, mock_enable_repl):
 1215         self.cfg.rbd_max_clone_depth = 1
 1216 
 1217         client = self.mock_client.return_value
 1218         client.__enter__.return_value = client
 1219 
 1220         with mock.patch.object(self.driver, '_get_clone_info') as \
 1221                 mock_get_clone_info:
 1222             mock_get_clone_info.return_value = (
 1223                 ('fake_pool', self.volume_b.name,
 1224                  '.'.join((self.volume_b.name, 'clone_snap'))))
 1225             with mock.patch.object(self.driver, '_get_clone_depth') as \
 1226                     mock_get_clone_depth:
 1227                 # Try with no flatten required
 1228                 mock_get_clone_depth.return_value = 1
 1229 
 1230                 res = self.driver.create_cloned_volume(self.volume_b,
 1231                                                        self.volume_a)
 1232 
 1233                 self.assertEqual({}, res)
 1234                 (self.mock_rbd.Image.return_value.create_snap
 1235                  .assert_called_once_with('.'.join(
 1236                      (self.volume_b.name, 'clone_snap'))))
 1237                 (self.mock_rbd.Image.return_value.protect_snap
 1238                  .assert_called_once_with('.'.join(
 1239                      (self.volume_b.name, 'clone_snap'))))
 1240                 self.assertEqual(
 1241                     1, self.mock_rbd.RBD.return_value.clone.call_count)
 1242                 (self.mock_rbd.Image.return_value.unprotect_snap
 1243                  .assert_called_once_with('.'.join(
 1244                      (self.volume_b.name, 'clone_snap'))))
 1245                 (self.mock_rbd.Image.return_value.remove_snap
 1246                  .assert_called_once_with('.'.join(
 1247                      (self.volume_b.name, 'clone_snap'))))
 1248 
 1249                 self.mock_proxy.assert_called_once_with(
 1250                     self.driver, self.volume_b.name,
 1251                     client=client, ioctx=client.ioctx)
 1252 
 1253                 # Source volume is closed by direct call of close()
 1254                 self.assertEqual(
 1255                     1, self.mock_rbd.Image.return_value.close.call_count)
 1256                 self.assertTrue(mock_get_clone_depth.called)
 1257                 mock_enable_repl.assert_not_called()
 1258 
 1259     @common_mocks
 1260     @mock.patch.object(driver.RBDDriver, '_enable_replication')
 1261     def test_create_cloned_volume_w_clone_exception(self, mock_enable_repl):
 1262         self.cfg.rbd_max_clone_depth = 2
 1263         self.mock_rbd.RBD.return_value.clone.side_effect = (
 1264             self.mock_rbd.RBD.Error)
 1265         with mock.patch.object(self.driver, '_get_clone_depth') as \
 1266                 mock_get_clone_depth:
 1267             # Try with no flatten required
 1268             mock_get_clone_depth.return_value = 1
 1269 
 1270             self.assertRaises(self.mock_rbd.RBD.Error,
 1271                               self.driver.create_cloned_volume,
 1272                               self.volume_b, self.volume_a)
 1273 
 1274             (self.mock_rbd.Image.return_value.create_snap
 1275                 .assert_called_once_with('.'.join(
 1276                     (self.volume_b.name, 'clone_snap'))))
 1277             (self.mock_rbd.Image.return_value.protect_snap
 1278                 .assert_called_once_with('.'.join(
 1279                     (self.volume_b.name, 'clone_snap'))))
 1280             self.assertEqual(
 1281                 1, self.mock_rbd.RBD.return_value.clone.call_count)
 1282             (self.mock_rbd.Image.return_value.unprotect_snap
 1283              .assert_called_once_with('.'.join(
 1284                  (self.volume_b.name, 'clone_snap'))))
 1285             (self.mock_rbd.Image.return_value.remove_snap
 1286              .assert_called_once_with('.'.join(
 1287                  (self.volume_b.name, 'clone_snap'))))
 1288             self.assertEqual(
 1289                 1, self.mock_rbd.Image.return_value.close.call_count)
 1290             mock_enable_repl.assert_not_called()
 1291 
 1292     @common_mocks
 1293     def test_good_locations(self):
 1294         locations = ['rbd://fsid/pool/image/snap',
 1295                      'rbd://%2F/%2F/%2F/%2F', ]
 1296         map(self.driver._parse_location, locations)
 1297 
 1298     @common_mocks
 1299     def test_bad_locations(self):
 1300         locations = ['rbd://image',
 1301                      'http://path/to/somewhere/else',
 1302                      'rbd://image/extra',
 1303                      'rbd://image/',
 1304                      'rbd://fsid/pool/image/',
 1305                      'rbd://fsid/pool/image/snap/',
 1306                      'rbd://///', ]
 1307         for loc in locations:
 1308             self.assertRaises(exception.ImageUnacceptable,
 1309                               self.driver._parse_location,
 1310                               loc)
 1311             self.assertFalse(
 1312                 self.driver._is_cloneable(loc, {'disk_format': 'raw'}))
 1313 
 1314     @common_mocks
 1315     def test_cloneable(self):
 1316         with mock.patch.object(self.driver, '_get_fsid') as mock_get_fsid:
 1317             mock_get_fsid.return_value = 'abc'
 1318             location = 'rbd://abc/pool/image/snap'
 1319             info = {'disk_format': 'raw'}
 1320             self.assertTrue(self.driver._is_cloneable(location, info))
 1321             self.assertTrue(mock_get_fsid.called)
 1322 
 1323     @common_mocks
 1324     def test_uncloneable_different_fsid(self):
 1325         with mock.patch.object(self.driver, '_get_fsid') as mock_get_fsid:
 1326             mock_get_fsid.return_value = 'abc'
 1327             location = 'rbd://def/pool/image/snap'
 1328             self.assertFalse(
 1329                 self.driver._is_cloneable(location, {'disk_format': 'raw'}))
 1330             self.assertTrue(mock_get_fsid.called)
 1331 
 1332     @common_mocks
 1333     def test_uncloneable_unreadable(self):
 1334         with mock.patch.object(self.driver, '_get_fsid') as mock_get_fsid:
 1335             mock_get_fsid.return_value = 'abc'
 1336             location = 'rbd://abc/pool/image/snap'
 1337 
 1338             self.driver.rbd.Error = Exception
 1339             self.mock_proxy.side_effect = Exception
 1340 
 1341             args = [location, {'disk_format': 'raw'}]
 1342             self.assertFalse(self.driver._is_cloneable(*args))
 1343             self.assertEqual(1, self.mock_proxy.call_count)
 1344             self.assertTrue(mock_get_fsid.called)
 1345 
 1346     @common_mocks
 1347     def test_uncloneable_bad_format(self):
 1348         with mock.patch.object(self.driver, '_get_fsid') as mock_get_fsid:
 1349             mock_get_fsid.return_value = 'abc'
 1350             location = 'rbd://abc/pool/image/snap'
 1351             formats = ['qcow2', 'vmdk', 'vdi']
 1352             for f in formats:
 1353                 self.assertFalse(
 1354                     self.driver._is_cloneable(location, {'disk_format': f}))
 1355             self.assertTrue(mock_get_fsid.called)
 1356 
 1357     def _copy_image(self, volume_busy=False):
 1358         with mock.patch.object(tempfile, 'NamedTemporaryFile'):
 1359             with mock.patch.object(os.path, 'exists') as mock_exists:
 1360                 mock_exists.return_value = True
 1361                 with mock.patch.object(image_utils, 'fetch_to_raw'):
 1362                     with mock.patch.object(self.driver, 'delete_volume') \
 1363                             as mock_dv:
 1364                         with mock.patch.object(self.driver, '_resize'):
 1365                             mock_image_service = mock.MagicMock()
 1366                             args = [None, self.volume_a,
 1367                                     mock_image_service, None]
 1368                             if volume_busy:
 1369                                 mock_dv.side_effect = (
 1370                                     exception.VolumeIsBusy("doh"))
 1371                                 self.assertRaises(
 1372                                     exception.VolumeIsBusy,
 1373                                     self.driver.copy_image_to_volume,
 1374                                     *args)
 1375                                 self.assertEqual(
 1376                                     self.cfg.rados_connection_retries,
 1377                                     mock_dv.call_count)
 1378                             else:
 1379                                 self.driver.copy_image_to_volume(*args)
 1380 
 1381     @mock.patch('cinder.volume.drivers.rbd.fileutils.delete_if_exists')
 1382     @mock.patch('cinder.volume.volume_utils.check_encryption_provider',
 1383                 return_value={'encryption_key_id': fake.ENCRYPTION_KEY_ID})
 1384     @mock.patch('cinder.image.image_utils.convert_image')
 1385     def _copy_image_encrypted(self, mock_convert, mock_encrypt_key,
 1386                               mock_temp_delete):
 1387         key_mgr = fake_keymgr.fake_api()
 1388         self.mock_object(castellan.key_manager, 'API', return_value=key_mgr)
 1389         key_id = key_mgr.store(self.context, KeyObject())
 1390         self.volume_a.encryption_key_id = key_id
 1391 
 1392         enc_info = {'encryption_key_id': key_id,
 1393                     'cipher': 'aes-xts-essiv',
 1394                     'key_size': 256}
 1395         with mock.patch('cinder.volume.volume_utils.check_encryption_provider',
 1396                         return_value=enc_info), \
 1397                 mock.patch('cinder.volume.drivers.rbd.open'), \
 1398                 mock.patch('os.rename'):
 1399             with mock.patch.object(tempfile, 'NamedTemporaryFile'):
 1400                 with mock.patch.object(os.path, 'exists') as mock_exists:
 1401                     mock_exists.return_value = True
 1402                     with mock.patch.object(image_utils, 'fetch_to_raw'):
 1403                         with mock.patch.object(self.driver, 'delete_volume'):
 1404                             with mock.patch.object(self.driver, '_resize'):
 1405                                 mock_image_service = mock.MagicMock()
 1406                                 args = [self.context, self.volume_a,
 1407                                         mock_image_service, None]
 1408                                 self.driver.copy_image_to_encrypted_volume(
 1409                                     *args)
 1410                                 mock_temp_delete.assert_called()
 1411                                 self.assertEqual(1,
 1412                                                  mock_temp_delete.call_count)
 1413 
 1414     @common_mocks
 1415     def test_copy_image_no_volume_tmp(self):
 1416         self.cfg.image_conversion_dir = None
 1417         self._copy_image()
 1418 
 1419     @common_mocks
 1420     def test_copy_image_volume_tmp(self):
 1421         self.cfg.image_conversion_dir = '/var/run/cinder/tmp'
 1422         self._copy_image()
 1423 
 1424     @common_mocks
 1425     def test_copy_image_volume_tmp_encrypted(self):
 1426         self.cfg.image_conversion_dir = '/var/run/cinder/tmp'
 1427         self._copy_image_encrypted()
 1428 
 1429     @common_mocks
 1430     def test_copy_image_busy_volume(self):
 1431         self.cfg.image_conversion_dir = '/var/run/cinder/tmp'
 1432         self._copy_image(volume_busy=True)
 1433 
 1434     @ddt.data(True, False)
 1435     @common_mocks
 1436     @mock.patch('cinder.volume.drivers.rbd.RBDDriver._get_usage_info')
 1437     @mock.patch('cinder.volume.drivers.rbd.RBDDriver._get_pool_stats')
 1438     def test_update_volume_stats(self, replication_enabled, stats_mock,
 1439                                  usage_mock):
 1440         stats_mock.return_value = (mock.sentinel.free_capacity_gb,
 1441                                    mock.sentinel.total_capacity_gb)
 1442         usage_mock.return_value = mock.sentinel.provisioned_capacity_gb
 1443 
 1444         expected_fsid = 'abc'
 1445         expected_location_info = ('nondefault:%s:%s:%s:rbd' %
 1446                                   (self.cfg.rbd_ceph_conf, expected_fsid,
 1447                                    self.cfg.rbd_user))
 1448         expected = dict(
 1449             volume_backend_name='RBD',
 1450             replication_enabled=replication_enabled,
 1451             vendor_name='Open Source',
 1452             driver_version=self.driver.VERSION,
 1453             storage_protocol='ceph',
 1454             total_capacity_gb=mock.sentinel.total_capacity_gb,
 1455             free_capacity_gb=mock.sentinel.free_capacity_gb,
 1456             reserved_percentage=0,
 1457             thin_provisioning_support=True,
 1458             provisioned_capacity_gb=mock.sentinel.provisioned_capacity_gb,
 1459             max_over_subscription_ratio=1.0,
 1460             multiattach=True,
 1461             location_info=expected_location_info,
 1462             backend_state='up')
 1463 
 1464         if replication_enabled:
 1465             targets = [{'backend_id': 'secondary-backend'},
 1466                        {'backend_id': 'tertiary-backend'}]
 1467             with mock.patch.object(self.driver.configuration, 'safe_get',
 1468                                    return_value=targets):
 1469                 self.driver._do_setup_replication()
 1470             expected['replication_targets'] = [t['backend_id']for t in targets]
 1471             expected['replication_targets'].append('default')
 1472 
 1473         my_safe_get = MockDriverConfig(rbd_exclusive_cinder_pool=False)
 1474         self.mock_object(self.driver.configuration, 'safe_get',
 1475                          my_safe_get)
 1476 
 1477         with mock.patch.object(self.driver, '_get_fsid') as mock_get_fsid:
 1478             mock_get_fsid.return_value = expected_fsid
 1479             actual = self.driver.get_volume_stats(True)
 1480             self.assertDictEqual(expected, actual)
 1481 
 1482     @common_mocks
 1483     @mock.patch('cinder.volume.drivers.rbd.RBDDriver._get_usage_info')
 1484     @mock.patch('cinder.volume.drivers.rbd.RBDDriver._get_pool_stats')
 1485     def test_update_volume_stats_exclusive_pool(self, stats_mock, usage_mock):
 1486         stats_mock.return_value = (mock.sentinel.free_capacity_gb,
 1487                                    mock.sentinel.total_capacity_gb)
 1488 
 1489         expected_fsid = 'abc'
 1490         expected_location_info = ('nondefault:%s:%s:%s:rbd' %
 1491                                   (self.cfg.rbd_ceph_conf, expected_fsid,
 1492                                    self.cfg.rbd_user))
 1493         expected = dict(
 1494             volume_backend_name='RBD',
 1495             replication_enabled=False,
 1496             vendor_name='Open Source',
 1497             driver_version=self.driver.VERSION,
 1498             storage_protocol='ceph',
 1499             total_capacity_gb=mock.sentinel.total_capacity_gb,
 1500             free_capacity_gb=mock.sentinel.free_capacity_gb,
 1501             reserved_percentage=0,
 1502             thin_provisioning_support=True,
 1503             max_over_subscription_ratio=1.0,
 1504             multiattach=True,
 1505             location_info=expected_location_info,
 1506             backend_state='up')
 1507 
 1508         my_safe_get = MockDriverConfig(rbd_exclusive_cinder_pool=True)
 1509         self.mock_object(self.driver.configuration, 'safe_get',
 1510                          my_safe_get)
 1511 
 1512         with mock.patch.object(self.driver, '_get_fsid',
 1513                                return_value=expected_fsid):
 1514             actual = self.driver.get_volume_stats(True)
 1515 
 1516         self.assertDictEqual(expected, actual)
 1517         usage_mock.assert_not_called()
 1518 
 1519     @common_mocks
 1520     @mock.patch('cinder.volume.drivers.rbd.RBDDriver._get_usage_info')
 1521     @mock.patch('cinder.volume.drivers.rbd.RBDDriver._get_pool_stats')
 1522     def test_update_volume_stats_error(self, stats_mock, usage_mock):
 1523         my_safe_get = MockDriverConfig(rbd_exclusive_cinder_pool=False)
 1524         self.mock_object(self.driver.configuration, 'safe_get',
 1525                          my_safe_get)
 1526 
 1527         expected_fsid = 'abc'
 1528         expected_location_info = ('nondefault:%s:%s:%s:rbd' %
 1529                                   (self.cfg.rbd_ceph_conf, expected_fsid,
 1530                                    self.cfg.rbd_user))
 1531         expected = dict(volume_backend_name='RBD',
 1532                         replication_enabled=False,
 1533                         vendor_name='Open Source',
 1534                         driver_version=self.driver.VERSION,
 1535                         storage_protocol='ceph',
 1536                         total_capacity_gb='unknown',
 1537                         free_capacity_gb='unknown',
 1538                         reserved_percentage=0,
 1539                         multiattach=True,
 1540                         max_over_subscription_ratio=1.0,
 1541                         thin_provisioning_support=True,
 1542                         location_info=expected_location_info,
 1543                         backend_state='down')
 1544 
 1545         with mock.patch.object(self.driver, '_get_fsid') as mock_get_fsid:
 1546             mock_get_fsid.return_value = expected_fsid
 1547             actual = self.driver.get_volume_stats(True)
 1548             self.assertDictEqual(expected, actual)
 1549 
 1550     @ddt.data(
 1551         # Normal case, no quota and dynamic total
 1552         {'free_capacity': 27.0, 'total_capacity': 28.44},
 1553         # No quota and static total
 1554         {'dynamic_total': False,
 1555          'free_capacity': 27.0, 'total_capacity': 59.96},
 1556         # Quota and dynamic total
 1557         {'quota_max_bytes': 3221225472, 'max_avail': 1073741824,
 1558          'free_capacity': 1, 'total_capacity': 2.44},
 1559         # Quota and static total
 1560         {'quota_max_bytes': 3221225472, 'max_avail': 1073741824,
 1561          'dynamic_total': False,
 1562          'free_capacity': 1, 'total_capacity': 3.00},
 1563         # Quota and dynamic total when free would be negative
 1564         {'quota_max_bytes': 1073741824,
 1565          'free_capacity': 0, 'total_capacity': 1.44},
 1566     )
 1567     @ddt.unpack
 1568     @common_mocks
 1569     def test_get_pool(self, free_capacity, total_capacity,
 1570                       max_avail=28987613184, quota_max_bytes=0,
 1571                       dynamic_total=True):
 1572         client = self.mock_client.return_value
 1573         client.__enter__.return_value = client
 1574         client.cluster.mon_command.side_effect = [
 1575             (0, '{"stats":{"total_bytes":64385286144,'
 1576              '"total_used_bytes":3289628672,"total_avail_bytes":61095657472},'
 1577              '"pools":[{"name":"rbd","id":2,"stats":{"kb_used":1510197,'
 1578              '"bytes_used":1546440971,"max_avail":%s,"objects":412}},'
 1579              '{"name":"volumes","id":3,"stats":{"kb_used":0,"bytes_used":0,'
 1580              '"max_avail":28987613184,"objects":0}}]}\n' % max_avail, ''),
 1581             (0, '{"pool_name":"volumes","pool_id":4,"quota_max_objects":0,'
 1582              '"quota_max_bytes":%s}\n' % quota_max_bytes, ''),
 1583         ]
 1584         with mock.patch.object(self.driver.configuration, 'safe_get',
 1585                                return_value=dynamic_total):
 1586             result = self.driver._get_pool_stats()
 1587         client.cluster.mon_command.assert_has_calls([
 1588             mock.call('{"prefix":"df", "format":"json"}', b''),
 1589             mock.call('{"prefix":"osd pool get-quota", "pool": "rbd",'
 1590                       ' "format":"json"}', b''),
 1591         ])
 1592         self.assertEqual((free_capacity, total_capacity), result)
 1593 
 1594     @common_mocks
 1595     def test_get_pool_bytes(self):
 1596         """Test for mon_commands returning bytes instead of strings."""
 1597         client = self.mock_client.return_value
 1598         client.__enter__.return_value = client
 1599         client.cluster.mon_command.side_effect = [
 1600             (0, b'{"stats":{"total_bytes":64385286144,'
 1601              b'"total_used_bytes":3289628672,"total_avail_bytes":61095657472},'
 1602              b'"pools":[{"name":"rbd","id":2,"stats":{"kb_used":1510197,'
 1603              b'"bytes_used":1546440971,"max_avail":2897613184,"objects":412}},'
 1604              b'{"name":"volumes","id":3,"stats":{"kb_used":0,"bytes_used":0,'
 1605              b'"max_avail":28987613184,"objects":0}}]}\n', ''),
 1606             (0, b'{"pool_name":"volumes","pool_id":4,"quota_max_objects":0,'
 1607              b'"quota_max_bytes":3221225472}\n', ''),
 1608         ]
 1609         result = self.driver._get_pool_stats()
 1610         client.cluster.mon_command.assert_has_calls([
 1611             mock.call('{"prefix":"df", "format":"json"}', b''),
 1612             mock.call('{"prefix":"osd pool get-quota", "pool": "rbd",'
 1613                       ' "format":"json"}', b''),
 1614         ])
 1615         free_capacity = 1.56
 1616         total_capacity = 3.0
 1617         self.assertEqual((free_capacity, total_capacity), result)
 1618 
 1619     @common_mocks
 1620     def test_get_pool_stats_failure(self):
 1621         client = self.mock_client.return_value
 1622         client.__enter__.return_value = client
 1623         client.cluster.mon_command.return_value = (-1, '', '')
 1624 
 1625         result = self.driver._get_pool_stats()
 1626         self.assertEqual(('unknown', 'unknown'), result)
 1627 
 1628     @common_mocks
 1629     def test_get_mon_addrs(self):
 1630         with mock.patch.object(self.driver, '_execute') as mock_execute:
 1631             mock_execute.return_value = (CEPH_MON_DUMP, '')
 1632             hosts = ['::1', '::1', '::1', '127.0.0.1', 'example.com']
 1633             ports = ['6789', '6790', '6791', '6792', '6791']
 1634             self.assertEqual((hosts, ports), self.driver._get_mon_addrs())
 1635 
 1636     @common_mocks
 1637     def _initialize_connection_helper(self, expected, hosts, ports):
 1638 
 1639         with mock.patch.object(self.driver, '_get_mon_addrs') as \
 1640                 mock_get_mon_addrs:
 1641             mock_get_mon_addrs.return_value = (hosts, ports)
 1642             actual = self.driver.initialize_connection(self.volume_a, None)
 1643             self.assertDictEqual(expected, actual)
 1644             self.assertTrue(mock_get_mon_addrs.called)
 1645 
 1646     def test_initialize_connection(self):
 1647         hosts = ['::1', '::1', '::1', '127.0.0.1', 'example.com']
 1648         ports = ['6789', '6790', '6791', '6792', '6791']
 1649 
 1650         self.driver._active_config = {'name': 'secondary_id',
 1651                                       'user': 'foo',
 1652                                       'conf': 'bar'}
 1653         expected = {
 1654             'driver_volume_type': 'rbd',
 1655             'data': {
 1656                 'name': '%s/%s' % (self.cfg.rbd_pool,
 1657                                    self.volume_a.name),
 1658                 'hosts': hosts,
 1659                 'ports': ports,
 1660                 'cluster_name': 'secondary_id',
 1661                 'auth_enabled': True,
 1662                 'auth_username': 'foo',
 1663                 'secret_type': 'ceph',
 1664                 'secret_uuid': self.cfg.rbd_secret_uuid,
 1665                 'volume_id': self.volume_a.id,
 1666                 'discard': True,
 1667             }
 1668         }
 1669         self._initialize_connection_helper(expected, hosts, ports)
 1670 
 1671         # Check how it will work with keyring data (for cinderlib)
 1672         keyring_data = "[client.cinder]\n  key = test\n"
 1673         self.driver.keyring_data = keyring_data
 1674         expected['data']['keyring'] = keyring_data
 1675         self._initialize_connection_helper(expected, hosts, ports)
 1676 
 1677         self.driver._active_config = {'name': 'secondary_id',
 1678                                       'user': 'foo',
 1679                                       'conf': 'bar',
 1680                                       'secret_uuid': 'secondary_secret_uuid'}
 1681         expected['data']['secret_uuid'] = 'secondary_secret_uuid'
 1682         self._initialize_connection_helper(expected, hosts, ports)
 1683 
 1684     def test__set_keyring_attributes_openstack(self):
 1685         # OpenStack usage doesn't have the rbd_keyring_conf Oslo Config option
 1686         self.assertFalse(hasattr(self.driver.configuration,
 1687                                  'rbd_keyring_conf'))
 1688         # Set initial values so we can confirm that we set them to None
 1689         self.driver.keyring_file = mock.sentinel.keyring_file
 1690         self.driver.keyring_data = mock.sentinel.keyring_data
 1691 
 1692         self.driver._set_keyring_attributes()
 1693 
 1694         self.assertIsNone(self.driver.keyring_file)
 1695         self.assertIsNone(self.driver.keyring_data)
 1696 
 1697     def test__set_keyring_attributes_cinderlib(self):
 1698         # OpenStack usage doesn't have the rbd_keyring_conf Oslo Config option
 1699         cfg_file = '/etc/ceph/ceph.client.admin.keyring'
 1700         self.driver.configuration.rbd_keyring_conf = cfg_file
 1701         with mock.patch('os.path.isfile', return_value=False):
 1702             self.driver._set_keyring_attributes()
 1703         self.assertEqual(cfg_file, self.driver.keyring_file)
 1704         self.assertIsNone(self.driver.keyring_data)
 1705 
 1706     @mock.patch('os.path.isfile')
 1707     @mock.patch.object(driver, 'open')
 1708     def test__set_keyring_attributes_cinderlib_read_file(self, mock_open,
 1709                                                          mock_isfile):
 1710         cfg_file = '/etc/ceph/ceph.client.admin.keyring'
 1711         # This is how cinderlib sets the config option
 1712         setattr(self.driver.configuration, 'rbd_keyring_conf', cfg_file)
 1713 
 1714         keyring_data = "[client.cinder]\n  key = test\n"
 1715         mock_read = mock_open.return_value.__enter__.return_value.read
 1716         mock_read.return_value = keyring_data
 1717 
 1718         self.assertIsNone(self.driver.keyring_file)
 1719         self.assertIsNone(self.driver.keyring_data)
 1720 
 1721         self.driver._set_keyring_attributes()
 1722 
 1723         mock_isfile.assert_called_once_with(cfg_file)
 1724         mock_open.assert_called_once_with(cfg_file, 'r')
 1725         mock_read.assert_called_once_with()
 1726         self.assertEqual(cfg_file, self.driver.keyring_file)
 1727         self.assertEqual(keyring_data, self.driver.keyring_data)
 1728 
 1729     @mock.patch('os.path.isfile')
 1730     @mock.patch.object(driver, 'open', side_effect=IOError)
 1731     def test__set_keyring_attributes_cinderlib_error(self, mock_open,
 1732                                                      mock_isfile):
 1733         cfg_file = '/etc/ceph/ceph.client.admin.keyring'
 1734         # This is how cinderlib sets the config option
 1735         setattr(self.driver.configuration, 'rbd_keyring_conf', cfg_file)
 1736 
 1737         self.assertIsNone(self.driver.keyring_file)
 1738         self.driver.keyring_data = mock.sentinel.keyring_data
 1739 
 1740         self.driver._set_keyring_attributes()
 1741 
 1742         mock_isfile.assert_called_once_with(cfg_file)
 1743         mock_open.assert_called_once_with(cfg_file, 'r')
 1744         self.assertEqual(cfg_file, self.driver.keyring_file)
 1745         self.assertIsNone(self.driver.keyring_data)
 1746 
 1747     @ddt.data({'rbd_chunk_size': 1, 'order': 20},
 1748               {'rbd_chunk_size': 8, 'order': 23},
 1749               {'rbd_chunk_size': 32, 'order': 25})
 1750     @ddt.unpack
 1751     @common_mocks
 1752     @mock.patch.object(driver.RBDDriver, '_enable_replication')
 1753     def test_clone(self, mock_enable_repl, rbd_chunk_size, order):
 1754         self.cfg.rbd_store_chunk_size = rbd_chunk_size
 1755         src_pool = u'images'
 1756         src_image = u'image-name'
 1757         src_snap = u'snapshot-name'
 1758 
 1759         client_stack = []
 1760 
 1761         def mock__enter__(inst):
 1762             def _inner():
 1763                 client_stack.append(inst)
 1764                 return inst
 1765             return _inner
 1766 
 1767         client = self.mock_client.return_value
 1768         # capture both rados client used to perform the clone
 1769         client.__enter__.side_effect = mock__enter__(client)
 1770 
 1771         res = self.driver._clone(self.volume_a, src_pool, src_image, src_snap)
 1772 
 1773         self.assertEqual({}, res)
 1774 
 1775         args = [client_stack[0].ioctx, str(src_image), str(src_snap),
 1776                 client_stack[1].ioctx, str(self.volume_a.name)]
 1777         kwargs = {'features': client.features,
 1778                   'order': order}
 1779         self.mock_rbd.RBD.return_value.clone.assert_called_once_with(
 1780             *args, **kwargs)
 1781         self.assertEqual(2, client.__enter__.call_count)
 1782         mock_enable_repl.assert_not_called()
 1783 
 1784     @common_mocks
 1785     @mock.patch.object(driver.RBDDriver, '_enable_replication')
 1786     def test_clone_replicated(self, mock_enable_repl):
 1787         rbd_chunk_size = 1
 1788         order = 20
 1789         self.volume_a.volume_type = fake_volume.fake_volume_type_obj(
 1790             self.context,
 1791             id=fake.VOLUME_TYPE_ID,
 1792             extra_specs={'replication_enabled': '<is> True'})
 1793 
 1794         expected_update = {
 1795             'replication_status': 'enabled',
 1796             'replication_driver_data': '{"had_journaling": false}'
 1797         }
 1798         mock_enable_repl.return_value = expected_update
 1799 
 1800         self.cfg.rbd_store_chunk_size = rbd_chunk_size
 1801         src_pool = u'images'
 1802         src_image = u'image-name'
 1803         src_snap = u'snapshot-name'
 1804 
 1805         client_stack = []
 1806 
 1807         def mock__enter__(inst):
 1808             def _inner():
 1809                 client_stack.append(inst)
 1810                 return inst
 1811             return _inner
 1812 
 1813         client = self.mock_client.return_value
 1814         # capture both rados client used to perform the clone
 1815         client.__enter__.side_effect = mock__enter__(client)
 1816 
 1817         res = self.driver._clone(self.volume_a, src_pool, src_image, src_snap)
 1818 
 1819         self.assertEqual(expected_update, res)
 1820         mock_enable_repl.assert_called_once_with(self.volume_a)
 1821 
 1822         args = [client_stack[0].ioctx, str(src_image), str(src_snap),
 1823                 client_stack[1].ioctx, str(self.volume_a.name)]
 1824         kwargs = {'features': client.features,
 1825                   'order': order}
 1826         self.mock_rbd.RBD.return_value.clone.assert_called_once_with(
 1827             *args, **kwargs)
 1828         self.assertEqual(2, client.__enter__.call_count)
 1829 
 1830     @ddt.data({},
 1831               {'replication_status': 'enabled',
 1832                'replication_driver_data': '{"had_journaling": false}'})
 1833     @common_mocks
 1834     @mock.patch.object(driver.RBDDriver, '_is_cloneable', return_value=True)
 1835     def test_clone_image_replication(self, return_value, mock_cloneable):
 1836         mock_clone = self.mock_object(self.driver, '_clone',
 1837                                       return_value=return_value)
 1838         image_loc = ('rbd://fee/fi/fo/fum', None)
 1839         image_meta = {'disk_format': 'raw', 'id': 'id.foo'}
 1840 
 1841         res = self.driver.clone_image(self.context,
 1842                                       self.volume_a,
 1843                                       image_loc,
 1844                                       image_meta,
 1845                                       mock.Mock())
 1846 
 1847         expected = return_value.copy()
 1848         expected['provider_location'] = None
 1849         self.assertEqual((expected, True), res)
 1850 
 1851         mock_clone.assert_called_once_with(self.volume_a, 'fi', 'fo', 'fum')
 1852 
 1853     @common_mocks
 1854     @mock.patch.object(driver.RBDDriver, '_clone',
 1855                        return_value=mock.sentinel.volume_update)
 1856     @mock.patch.object(driver.RBDDriver, '_resize', mock.Mock())
 1857     def test_create_vol_from_snap_replication(self, mock_clone):
 1858         self.cfg.rbd_flatten_volume_from_snapshot = False
 1859         snapshot = mock.Mock()
 1860 
 1861         res = self.driver.create_volume_from_snapshot(self.volume_a, snapshot)
 1862 
 1863         self.assertEqual(mock.sentinel.volume_update, res)
 1864         mock_clone.assert_called_once_with(self.volume_a,
 1865                                            self.cfg.rbd_pool,
 1866                                            snapshot.volume_name,
 1867                                            snapshot.name)
 1868 
 1869     @common_mocks
 1870     def test_extend_volume(self):
 1871         fake_size = '20'
 1872         size = int(fake_size) * units.Gi
 1873         with mock.patch.object(self.driver, '_resize') as mock_resize:
 1874             self.driver.extend_volume(self.volume_a, fake_size)
 1875             mock_resize.assert_called_once_with(self.volume_a, size=size)
 1876 
 1877     @ddt.data(False, True)
 1878     @common_mocks
 1879     def test_retype(self, enabled):
 1880         """Test retyping a non replicated volume.
 1881 
 1882         We will test on a system that doesn't have replication enabled and on
 1883         one that hast it enabled.
 1884         """
 1885         self.driver._is_replication_enabled = enabled
 1886         if enabled:
 1887             expect = {'replication_status': fields.ReplicationStatus.DISABLED}
 1888         else:
 1889             expect = {}
 1890         context = {}
 1891         diff = {'encryption': {},
 1892                 'extra_specs': {}}
 1893         updates = {'name': 'testvolume',
 1894                    'host': 'currenthost',
 1895                    'id': fake.VOLUME_ID}
 1896         fake_type = fake_volume.fake_volume_type_obj(context)
 1897         volume = fake_volume.fake_volume_obj(context, **updates)
 1898         volume.volume_type = None
 1899 
 1900         # The hosts have been checked same before rbd.retype
 1901         # is called.
 1902         # RBD doesn't support multiple pools in a driver.
 1903         host = {'host': 'currenthost'}
 1904         self.assertEqual((True, expect),
 1905                          self.driver.retype(context, volume,
 1906                                             fake_type, diff, host))
 1907 
 1908         # The encryptions have been checked as same before rbd.retype
 1909         # is called.
 1910         diff['encryption'] = {}
 1911         self.assertEqual((True, expect),
 1912                          self.driver.retype(context, volume,
 1913                                             fake_type, diff, host))
 1914 
 1915         # extra_specs changes are supported.
 1916         diff['extra_specs'] = {'non-empty': 'non-empty'}
 1917         self.assertEqual((True, expect),
 1918                          self.driver.retype(context, volume,
 1919                                             fake_type, diff, host))
 1920         diff['extra_specs'] = {}
 1921 
 1922         self.assertEqual((True, expect),
 1923                          self.driver.retype(context, volume,
 1924                                             fake_type, diff, host))
 1925 
 1926     @ddt.data({'old_replicated': False, 'new_replicated': False},
 1927               {'old_replicated': False, 'new_replicated': True},
 1928               {'old_replicated': True, 'new_replicated': False},
 1929               {'old_replicated': True, 'new_replicated': True})
 1930     @ddt.unpack
 1931     @common_mocks
 1932     @mock.patch.object(driver.RBDDriver, '_disable_replication',
 1933                        return_value={'replication': 'disabled'})
 1934     @mock.patch.object(driver.RBDDriver, '_enable_replication',
 1935                        return_value={'replication': 'enabled'})
 1936     def test_retype_replicated(self, mock_disable, mock_enable, old_replicated,
 1937                                new_replicated):
 1938         """Test retyping a non replicated volume.
 1939 
 1940         We will test on a system that doesn't have replication enabled and on
 1941         one that hast it enabled.
 1942         """
 1943         self.driver._is_replication_enabled = True
 1944         replicated_type = fake_volume.fake_volume_type_obj(
 1945             self.context,
 1946             id=fake.VOLUME_TYPE_ID,
 1947             extra_specs={'replication_enabled': '<is> True'})
 1948 
 1949         self.volume_a.volume_type = replicated_type if old_replicated else None
 1950 
 1951         if new_replicated:
 1952             new_type = replicated_type
 1953             if old_replicated:
 1954                 update = {}
 1955             else:
 1956                 update = {'replication': 'enabled'}
 1957         else:
 1958             new_type = fake_volume.fake_volume_type_obj(
 1959                 self.context,
 1960                 id=fake.VOLUME_TYPE2_ID),
 1961             if old_replicated:
 1962                 update = {'replication': 'disabled'}
 1963             else:
 1964                 update = {'replication_status':
 1965                           fields.ReplicationStatus.DISABLED}
 1966 
 1967         res = self.driver.retype(self.context, self.volume_a, new_type, None,
 1968                                  None)
 1969         self.assertEqual((True, update), res)
 1970 
 1971     @common_mocks
 1972     def test_update_migrated_volume(self):
 1973         client = self.mock_client.return_value
 1974         client.__enter__.return_value = client
 1975 
 1976         with mock.patch.object(self.driver.rbd.RBD(), 'rename') as mock_rename:
 1977             context = {}
 1978             mock_rename.return_value = 0
 1979             model_update = self.driver.update_migrated_volume(context,
 1980                                                               self.volume_a,
 1981                                                               self.volume_b,
 1982                                                               'available')
 1983             mock_rename.assert_called_with(client.ioctx,
 1984                                            'volume-%s' % self.volume_b.id,
 1985                                            'volume-%s' % self.volume_a.id)
 1986             self.assertEqual({'_name_id': None,
 1987                               'provider_location': None}, model_update)
 1988 
 1989     @common_mocks
 1990     def test_update_migrated_volume_in_use(self):
 1991         client = self.mock_client.return_value
 1992         client.__enter__.return_value = client
 1993 
 1994         with mock.patch.object(self.driver.rbd.RBD(), 'rename') as mock_rename:
 1995             context = {}
 1996             mock_rename.return_value = 0
 1997             model_update = self.driver.update_migrated_volume(context,
 1998                                                               self.volume_a,
 1999                                                               self.volume_b,
 2000                                                               'in-use')
 2001             mock_rename.assert_not_called()
 2002             self.assertEqual({'_name_id': self.volume_b.id,
 2003                               'provider_location':
 2004                                   self.volume_b['provider_location']},
 2005                              model_update)
 2006 
 2007     @common_mocks
 2008     def test_update_migrated_volume_image_exists(self):
 2009         client = self.mock_client.return_value
 2010         client.__enter__.return_value = client
 2011 
 2012         with mock.patch.object(self.driver.rbd.RBD(), 'rename') as mock_rename:
 2013             context = {}
 2014             mock_rename.return_value = 1
 2015             mock_rename.side_effect = MockImageExistsException
 2016 
 2017             model_update = self.driver.update_migrated_volume(context,
 2018                                                               self.volume_a,
 2019                                                               self.volume_b,
 2020                                                               'available')
 2021             mock_rename.assert_called_with(client.ioctx,
 2022                                            'volume-%s' % self.volume_b.id,
 2023                                            'volume-%s' % self.volume_a.id)
 2024             self.assertEqual({'_name_id': self.volume_b.id,
 2025                               'provider_location': None}, model_update)
 2026 
 2027     def test_rbd_volume_proxy_init(self):
 2028         mock_driver = mock.Mock(name='driver')
 2029         mock_driver._connect_to_rados.return_value = (None, None)
 2030         with driver.RBDVolumeProxy(mock_driver, self.volume_a.name):
 2031             self.assertEqual(1, mock_driver._connect_to_rados.call_count)
 2032             self.assertFalse(mock_driver._disconnect_from_rados.called)
 2033 
 2034         self.assertEqual(1, mock_driver._disconnect_from_rados.call_count)
 2035 
 2036         mock_driver.reset_mock()
 2037 
 2038         snap = u'snapshot-name'
 2039         with driver.RBDVolumeProxy(mock_driver, self.volume_a.name,
 2040                                    snapshot=snap):
 2041             self.assertEqual(1, mock_driver._connect_to_rados.call_count)
 2042             self.assertFalse(mock_driver._disconnect_from_rados.called)
 2043 
 2044         self.assertEqual(1, mock_driver._disconnect_from_rados.call_count)
 2045 
 2046     def test_rbd_volume_proxy_external_conn(self):
 2047         mock_driver = mock.Mock(name='driver')
 2048         mock_driver._connect_to_rados.return_value = (None, None)
 2049         with driver.RBDVolumeProxy(mock_driver, self.volume_a.name,
 2050                                    client='fake_cl', ioctx='fake_io'):
 2051 
 2052             mock_driver._connect_to_rados.assert_not_called()
 2053 
 2054         mock_driver._disconnect_from_rados.assert_not_called()
 2055 
 2056     def test_rbd_volume_proxy_external_conn_no_iocxt(self):
 2057         mock_driver = mock.Mock(name='driver')
 2058         mock_driver._connect_to_rados.return_value = ('fake_cl', 'fake_io')
 2059         with driver.RBDVolumeProxy(mock_driver, self.volume_a.name,
 2060                                    client='fake_cl', pool='vol_pool'):
 2061             mock_driver._connect_to_rados.assert_called_once_with(
 2062                 'vol_pool', None, None)
 2063 
 2064         mock_driver._disconnect_from_rados.assert_called_once_with(
 2065             'fake_cl', 'fake_io')
 2066 
 2067     def test_rbd_volume_proxy_external_conn_error(self):
 2068         mock_driver = mock.Mock(name='driver')
 2069         mock_driver._connect_to_rados.return_value = (None, None)
 2070 
 2071         class RBDError(Exception):
 2072             pass
 2073 
 2074         mock_driver.rbd.Error = RBDError
 2075         mock_driver.rbd.Image.side_effect = RBDError()
 2076 
 2077         self.assertRaises(RBDError, driver.RBDVolumeProxy,
 2078                           mock_driver, self.volume_a.name,
 2079                           client='fake_cl', ioctx='fake_io')
 2080 
 2081         mock_driver._connect_to_rados.assert_not_called()
 2082         mock_driver._disconnect_from_rados.assert_not_called()
 2083 
 2084     def test_rbd_volume_proxy_conn_error(self):
 2085         mock_driver = mock.Mock(name='driver')
 2086         mock_driver._connect_to_rados.return_value = (
 2087             'fake_client', 'fake_ioctx')
 2088 
 2089         class RBDError(Exception):
 2090             pass
 2091 
 2092         mock_driver.rbd.Error = RBDError
 2093         mock_driver.rbd.Image.side_effect = RBDError()
 2094 
 2095         self.assertRaises(RBDError, driver.RBDVolumeProxy,
 2096                           mock_driver, self.volume_a.name,
 2097                           pool='fake-volumes')
 2098 
 2099         mock_driver._connect_to_rados.assert_called_once_with(
 2100             'fake-volumes', None, None)
 2101         mock_driver._disconnect_from_rados.assert_called_once_with(
 2102             'fake_client', 'fake_ioctx')
 2103 
 2104     @common_mocks
 2105     def test_connect_to_rados(self):
 2106         # Default
 2107         self.cfg.rados_connect_timeout = -1
 2108 
 2109         self.mock_rados.Rados.return_value.open_ioctx.return_value = \
 2110             self.mock_rados.Rados.return_value.ioctx
 2111 
 2112         # default configured pool
 2113         ret = self.driver._connect_to_rados()
 2114         self.assertTrue(self.mock_rados.Rados.return_value.connect.called)
 2115         # Expect no timeout if default is used
 2116         self.mock_rados.Rados.return_value.connect.assert_called_once_with()
 2117         self.assertTrue(self.mock_rados.Rados.return_value.open_ioctx.called)
 2118         self.assertEqual(self.mock_rados.Rados.return_value.ioctx, ret[1])
 2119         self.mock_rados.Rados.return_value.open_ioctx.assert_called_with(
 2120             self.cfg.rbd_pool)
 2121         conf_set = self.mock_rados.Rados.return_value.conf_set
 2122         conf_set.assert_not_called()
 2123 
 2124         # different pool
 2125         ret = self.driver._connect_to_rados('alt_pool')
 2126         self.assertTrue(self.mock_rados.Rados.return_value.connect.called)
 2127         self.assertTrue(self.mock_rados.Rados.return_value.open_ioctx.called)
 2128         self.assertEqual(self.mock_rados.Rados.return_value.ioctx, ret[1])
 2129         self.mock_rados.Rados.return_value.open_ioctx.assert_called_with(
 2130             'alt_pool')
 2131 
 2132         # With timeout
 2133         self.cfg.rados_connect_timeout = 1
 2134         self.mock_rados.Rados.return_value.connect.reset_mock()
 2135         self.driver._connect_to_rados()
 2136         conf_set.assert_has_calls((mock.call('rados_osd_op_timeout', '1'),
 2137                                    mock.call('rados_mon_op_timeout', '1'),
 2138                                    mock.call('client_mount_timeout', '1')))
 2139         self.mock_rados.Rados.return_value.connect.assert_called_once_with()
 2140 
 2141         # error
 2142         self.mock_rados.Rados.return_value.open_ioctx.reset_mock()
 2143         self.mock_rados.Rados.return_value.shutdown.reset_mock()
 2144         self.mock_rados.Rados.return_value.open_ioctx.side_effect = (
 2145             self.mock_rados.Error)
 2146         self.assertRaises(exception.VolumeBackendAPIException,
 2147                           self.driver._connect_to_rados)
 2148         self.assertTrue(self.mock_rados.Rados.return_value.open_ioctx.called)
 2149         self.assertEqual(
 2150             3, self.mock_rados.Rados.return_value.shutdown.call_count)
 2151 
 2152     @common_mocks
 2153     def test_failover_host_no_replication(self):
 2154         self.driver._is_replication_enabled = False
 2155         self.assertRaises(exception.UnableToFailOver,
 2156                           self.driver.failover_host,
 2157                           self.context, [self.volume_a], [])
 2158 
 2159     @ddt.data(None, 'tertiary-backend')
 2160     @common_mocks
 2161     @mock.patch.object(driver.RBDDriver, '_get_failover_target_config')
 2162     @mock.patch.object(driver.RBDDriver, '_failover_volume', autospec=True)
 2163     def test_failover_host(self, secondary_id, mock_failover_vol,
 2164                            mock_get_cfg):
 2165         mock_failover_vol.side_effect = lambda self, v, r, d, s: v
 2166         self.mock_object(self.driver.configuration, 'safe_get',
 2167                          return_value=[{'backend_id': 'secondary-backend'},
 2168                                        {'backend_id': 'tertiary-backend'}])
 2169         self.driver._do_setup_replication()
 2170         volumes = [self.volume_a, self.volume_b]
 2171         remote = self.driver._replication_targets[1 if secondary_id else 0]
 2172         mock_get_cfg.return_value = (remote['name'], remote)
 2173 
 2174         res = self.driver.failover_host(self.context, volumes, secondary_id,
 2175                                         [])
 2176 
 2177         self.assertEqual((remote['name'], volumes, []), res)
 2178         self.assertEqual(remote, self.driver._active_config)
 2179         mock_failover_vol.assert_has_calls(
 2180             [mock.call(mock.ANY, v, remote, False,
 2181                        fields.ReplicationStatus.FAILED_OVER)
 2182              for v in volumes])
 2183         mock_get_cfg.assert_called_with(secondary_id)
 2184 
 2185     @mock.patch.object(driver.RBDDriver, '_failover_volume', autospec=True)
 2186     def test_failover_host_failback(self, mock_failover_vol):
 2187         mock_failover_vol.side_effect = lambda self, v, r, d, s: v
 2188         self.driver._active_backend_id = 'secondary-backend'
 2189         self.mock_object(self.driver.configuration, 'safe_get',
 2190                          return_value=[{'backend_id': 'secondary-backend'},
 2191                                        {'backend_id': 'tertiary-backend'}])
 2192         self.driver._do_setup_replication()
 2193 
 2194         remote = self.driver._get_target_config('default')
 2195         volumes = [self.volume_a, self.volume_b]
 2196         res = self.driver.failover_host(self.context, volumes, 'default', [])
 2197 
 2198         self.assertEqual(('default', volumes, []), res)
 2199         self.assertEqual(remote, self.driver._active_config)
 2200         mock_failover_vol.assert_has_calls(
 2201             [mock.call(mock.ANY, v, remote, False,
 2202                        fields.ReplicationStatus.ENABLED)
 2203              for v in volumes])
 2204 
 2205     @mock.patch.object(driver.RBDDriver, '_failover_volume')
 2206     def test_failover_host_no_more_replica_targets(self, mock_failover_vol):
 2207         mock_failover_vol.side_effect = lambda w, x, y, z: w
 2208         self.driver._active_backend_id = 'secondary-backend'
 2209         self.mock_object(self.driver.configuration, 'safe_get',
 2210                          return_value=[{'backend_id': 'secondary-backend'}])
 2211         self.driver._do_setup_replication()
 2212 
 2213         volumes = [self.volume_a, self.volume_b]
 2214         self.assertRaises(exception.InvalidReplicationTarget,
 2215                           self.driver.failover_host,
 2216                           self.context, volumes, None, [])
 2217 
 2218     @ddt.data(True, False)
 2219     @mock.patch.object(driver.RBDDriver, '_exec_on_volume',
 2220                        side_effect=Exception)
 2221     def test_failover_volume_error(self, is_demoted, mock_exec):
 2222         self.volume_a.replication_driver_data = '{"had_journaling": false}'
 2223         self.volume_a.volume_type = fake_volume.fake_volume_type_obj(
 2224             self.context,
 2225             id=fake.VOLUME_TYPE_ID,
 2226             extra_specs={'replication_enabled': '<is> True'})
 2227         remote = {'name': 'name', 'user': 'user', 'conf': 'conf',
 2228                   'pool': 'pool'}
 2229         repl_status = fields.ReplicationStatus.FAILOVER_ERROR
 2230         expected = {'volume_id': self.volume_a.id,
 2231                     'updates': {'status': 'error',
 2232                                 'previous_status': self.volume_a.status,
 2233                                 'replication_status': repl_status}}
 2234         res = self.driver._failover_volume(
 2235             self.volume_a, remote, is_demoted,
 2236             fields.ReplicationStatus.FAILED_OVER)
 2237         self.assertEqual(expected, res)
 2238         mock_exec.assert_called_once_with(self.volume_a.name, remote,
 2239                                           'mirror_image_promote',
 2240                                           not is_demoted)
 2241 
 2242     @mock.patch.object(driver.RBDDriver, '_exec_on_volume')
 2243     def test_failover_volume(self, mock_exec):
 2244         self.volume_a.replication_driver_data = '{"had_journaling": false}'
 2245         self.volume_a.volume_type = fake_volume.fake_volume_type_obj(
 2246             self.context,
 2247             id=fake.VOLUME_TYPE_ID,
 2248             extra_specs={'replication_enabled': '<is> True'})
 2249         remote = {'name': 'name', 'user': 'user', 'conf': 'conf',
 2250                   'pool': 'pool'}
 2251         repl_status = fields.ReplicationStatus.FAILED_OVER
 2252         expected = {'volume_id': self.volume_a.id,
 2253                     'updates': {'replication_status': repl_status}}
 2254         res = self.driver._failover_volume(self.volume_a, remote, True,
 2255                                            repl_status)
 2256         self.assertEqual(expected, res)
 2257         mock_exec.assert_called_once_with(self.volume_a.name, remote,
 2258                                           'mirror_image_promote', False)
 2259 
 2260     @common_mocks
 2261     def test_manage_existing_snapshot_get_size(self):
 2262         with mock.patch.object(self.driver.rbd.Image(), 'size') as \
 2263                 mock_rbd_image_size:
 2264             with mock.patch.object(self.driver.rbd.Image(), 'close') \
 2265                     as mock_rbd_image_close:
 2266                 mock_rbd_image_size.return_value = 2 * units.Gi
 2267                 existing_ref = {'source-name': self.snapshot_b.name}
 2268                 return_size = self.driver.manage_existing_snapshot_get_size(
 2269                     self.snapshot_b,
 2270                     existing_ref)
 2271                 self.assertEqual(2, return_size)
 2272                 mock_rbd_image_size.assert_called_once_with()
 2273                 mock_rbd_image_close.assert_called_once_with()
 2274 
 2275     @common_mocks
 2276     def test_manage_existing_snapshot_get_non_integer_size(self):
 2277         rbd_snapshot = self.driver.rbd.Image.return_value
 2278         rbd_snapshot.size.return_value = int(1.75 * units.Gi)
 2279         existing_ref = {'source-name': self.snapshot_b.name}
 2280         return_size = self.driver.manage_existing_snapshot_get_size(
 2281             self.snapshot_b, existing_ref)
 2282         self.assertEqual(2, return_size)
 2283         rbd_snapshot.size.assert_called_once_with()
 2284         rbd_snapshot.close.assert_called_once_with()
 2285 
 2286     @common_mocks
 2287     def test_manage_existing_snapshot_get_invalid_size(self):
 2288 
 2289         with mock.patch.object(self.driver.rbd.Image(), 'size') as \
 2290                 mock_rbd_image_size:
 2291             with mock.patch.object(self.driver.rbd.Image(), 'close') \
 2292                     as mock_rbd_image_close:
 2293                 mock_rbd_image_size.return_value = 'abcd'
 2294                 existing_ref = {'source-name': self.snapshot_b.name}
 2295                 self.assertRaises(
 2296                     exception.VolumeBackendAPIException,
 2297                     self.driver.manage_existing_snapshot_get_size,
 2298                     self.snapshot_b, existing_ref)
 2299 
 2300                 mock_rbd_image_size.assert_called_once_with()
 2301                 mock_rbd_image_close.assert_called_once_with()
 2302 
 2303     @common_mocks
 2304     def test_manage_existing_snapshot_with_invalid_rbd_image(self):
 2305         self.mock_rbd.Image.side_effect = self.mock_rbd.ImageNotFound
 2306 
 2307         invalid_snapshot = 'snapshot-invalid'
 2308         invalid_ref = {'source-name': invalid_snapshot}
 2309 
 2310         self.assertRaises(exception.ManageExistingInvalidReference,
 2311                           self.driver.manage_existing_snapshot_get_size,
 2312                           self.snapshot_b, invalid_ref)
 2313         # Make sure the exception was raised
 2314         self.assertEqual([self.mock_rbd.ImageNotFound],
 2315                          RAISED_EXCEPTIONS)
 2316 
 2317     @common_mocks
 2318     def test_manage_existing_snapshot(self):
 2319         proxy = self.mock_proxy.return_value
 2320         proxy.__enter__.return_value = proxy
 2321         exist_snapshot = 'snapshot-exist'
 2322         existing_ref = {'source-name': exist_snapshot}
 2323         proxy.rename_snap.return_value = 0
 2324         proxy.is_protected_snap.return_value = False
 2325         self.driver.manage_existing_snapshot(self.snapshot_b, existing_ref)
 2326         proxy.rename_snap.assert_called_with(exist_snapshot,
 2327                                              self.snapshot_b.name)
 2328         proxy.protect_snap.assert_called_with(self.snapshot_b.name)
 2329 
 2330     @common_mocks
 2331     def test_manage_existing_snapshot_with_exist_rbd_image(self):
 2332         proxy = self.mock_proxy.return_value
 2333         proxy.__enter__.return_value = proxy
 2334         proxy.rename_snap.side_effect = MockImageExistsException
 2335 
 2336         exist_snapshot = 'snapshot-exist'
 2337         existing_ref = {'source-name': exist_snapshot}
 2338         self.assertRaises(self.mock_rbd.ImageExists,
 2339                           self.driver.manage_existing_snapshot,
 2340                           self.snapshot_b, existing_ref)
 2341 
 2342         # Make sure the exception was raised
 2343         self.assertEqual([self.mock_rbd.ImageExists], RAISED_EXCEPTIONS)
 2344 
 2345     @common_mocks
 2346     def test_get_manageable_snapshots(self):
 2347         cinder_snaps = [{'id': '00000000-0000-0000-0000-000000000000',
 2348                          'volume_id': '11111111-1111-1111-1111-111111111111'}]
 2349         vols = ['volume-11111111-1111-1111-1111-111111111111', 'vol1']
 2350         self.mock_rbd.RBD.return_value.list.return_value = vols
 2351         image = self.mock_proxy.return_value.__enter__.return_value
 2352         image.list_snaps.side_effect = [
 2353             [{'id': 1, 'name': 'snapshot-00000000-0000-0000-0000-000000000000',
 2354               'size': 2 * units.Gi},
 2355              {'id': 2, 'name': 'snap1', 'size': 6 * units.Gi},
 2356              {'id': 3, 'size': 8 * units.Gi,
 2357               'name': 'volume-22222222-2222-2222-2222-222222222222.clone_snap'
 2358               },
 2359              {'id': 4, 'size': 5 * units.Gi,
 2360               'name': 'backup.33333333-3333-3333-3333-333333333333.snap.123'}],
 2361             [{'id': 1, 'name': 'snap2', 'size': 4 * units.Gi}]]
 2362         res = self.driver.get_manageable_snapshots(
 2363             cinder_snaps, None, 1000, 0, ['size'], ['desc'])
 2364         exp = [
 2365             {'size': 8, 'safe_to_manage': False, 'extra_info': None,
 2366              'reason_not_safe': 'used for clone snap', 'cinder_id': None,
 2367              'reference': {
 2368                  'source-name':
 2369                      'volume-22222222-2222-2222-2222-222222222222.clone_snap'},
 2370              'source_reference': {
 2371                  'source-name': 'volume-11111111-1111-1111-1111-111111111111'}
 2372              },
 2373             {'size': 6, 'safe_to_manage': True, 'extra_info': None,
 2374              'reason_not_safe': None, 'cinder_id': None,
 2375              'reference': {'source-name': 'snap1'},
 2376              'source_reference': {
 2377                  'source-name': 'volume-11111111-1111-1111-1111-111111111111'}
 2378              },
 2379             {'size': 5, 'safe_to_manage': False, 'extra_info': None,
 2380              'reason_not_safe': 'used for volume backup', 'cinder_id': None,
 2381              'reference': {
 2382                  'source-name':
 2383                      'backup.33333333-3333-3333-3333-333333333333.snap.123'},
 2384              'source_reference': {
 2385                  'source-name': 'volume-11111111-1111-1111-1111-111111111111'}
 2386              },
 2387             {'size': 4, 'safe_to_manage': True, 'extra_info': None,
 2388              'reason_not_safe': None, 'cinder_id': None,
 2389              'reference': {'source-name': 'snap2'},
 2390              'source_reference': {'source-name': 'vol1'}
 2391              },
 2392             {'size': 2, 'safe_to_manage': False, 'extra_info': None,
 2393              'reason_not_safe': 'already managed',
 2394              'cinder_id': '00000000-0000-0000-0000-000000000000',
 2395              'reference': {'source-name':
 2396                            'snapshot-00000000-0000-0000-0000-000000000000'},
 2397              'source_reference': {
 2398                  'source-name': 'volume-11111111-1111-1111-1111-111111111111'}
 2399              }]
 2400         self.assertEqual(exp, res)
 2401 
 2402     @common_mocks
 2403     def test_unmanage_snapshot(self):
 2404         proxy = self.mock_proxy.return_value
 2405         proxy.__enter__.return_value = proxy
 2406         proxy.list_children.return_value = []
 2407         proxy.is_protected_snap.return_value = True
 2408         self.driver.unmanage_snapshot(self.snapshot_b)
 2409         proxy.unprotect_snap.assert_called_with(self.snapshot_b.name)
 2410 
 2411     @mock.patch('cinder.volume.drivers.rbd.RBDVolumeProxy')
 2412     @mock.patch('cinder.volume.drivers.rbd.RADOSClient')
 2413     @mock.patch('cinder.volume.drivers.rbd.RBDDriver.RBDProxy')
 2414     def test__get_usage_info(self, rbdproxy_mock, client_mock, volproxy_mock):
 2415 
 2416         def FakeVolProxy(size_or_exc):
 2417             return mock.Mock(return_value=mock.Mock(
 2418                 size=mock.Mock(side_effect=(size_or_exc,))))
 2419 
 2420         volumes = [
 2421             'volume-1',
 2422             'non-existent',
 2423             'non-existent',
 2424             'non-cinder-volume'
 2425         ]
 2426 
 2427         client = client_mock.return_value.__enter__.return_value
 2428         rbdproxy_mock.return_value.list.return_value = volumes
 2429 
 2430         with mock.patch.object(self.driver, 'rbd',
 2431                                ImageNotFound=MockImageNotFoundException,
 2432                                OSError=MockOSErrorException):
 2433             volproxy_mock.side_effect = [
 2434                 mock.MagicMock(**{'__enter__': FakeVolProxy(s)})
 2435                 for s in (1.0 * units.Gi,
 2436                           self.driver.rbd.ImageNotFound,
 2437                           self.driver.rbd.OSError,
 2438                           2.0 * units.Gi)
 2439             ]
 2440             total_provision = self.driver._get_usage_info()
 2441 
 2442         rbdproxy_mock.return_value.list.assert_called_once_with(client.ioctx)
 2443 
 2444         expected_volproxy_calls = [
 2445             mock.call(self.driver, v, read_only=True,
 2446                       client=client.cluster, ioctx=client.ioctx)
 2447             for v in volumes]
 2448         self.assertEqual(expected_volproxy_calls, volproxy_mock.mock_calls)
 2449 
 2450         self.assertEqual(3.00, total_provision)
 2451 
 2452     def test_migrate_volume_bad_volume_status(self):
 2453         self.volume_a.status = 'backingup'
 2454         ret = self.driver.migrate_volume(context, self.volume_a, None)
 2455         self.assertEqual((False, None), ret)
 2456 
 2457     def test_migrate_volume_bad_host(self):
 2458         host = {
 2459             'capabilities': {
 2460                 'storage_protocol': 'not-ceph'}}
 2461         ret = self.driver.migrate_volume(context, self.volume_a, host)
 2462         self.assertEqual((False, None), ret)
 2463 
 2464     def test_migrate_volume_missing_location_info(self):
 2465         host = {
 2466             'capabilities': {
 2467                 'storage_protocol': 'ceph'}}
 2468         ret = self.driver.migrate_volume(context, self.volume_a, host)
 2469         self.assertEqual((False, None), ret)
 2470 
 2471     def test_migrate_volume_invalid_location_info(self):
 2472         host = {
 2473             'capabilities': {
 2474                 'storage_protocol': 'ceph',
 2475                 'location_info': 'foo:bar:baz'}}
 2476         ret = self.driver.migrate_volume(context, self.volume_a, host)
 2477         self.assertEqual((False, None), ret)
 2478 
 2479     @mock.patch('os_brick.initiator.linuxrbd.rbd')
 2480     @mock.patch('os_brick.initiator.linuxrbd.RBDClient')
 2481     def test_migrate_volume_mismatch_fsid(self, mock_client, mock_rbd):
 2482         host = {
 2483             'capabilities': {
 2484                 'storage_protocol': 'ceph',
 2485                 'location_info': 'nondefault:None:abc:None:rbd'}}
 2486 
 2487         mock_client().__enter__().client.get_fsid.return_value = 'abc'
 2488 
 2489         with mock.patch.object(self.driver, '_get_fsid') as mock_get_fsid:
 2490             mock_get_fsid.return_value = 'not-abc'
 2491             ret = self.driver.migrate_volume(context, self.volume_a, host)
 2492             self.assertEqual((False, None), ret)
 2493 
 2494         mock_client().__enter__().client.get_fsid.return_value = 'not-abc'
 2495 
 2496         with mock.patch.object(self.driver, '_get_fsid') as mock_get_fsid:
 2497             mock_get_fsid.return_value = 'abc'
 2498             ret = self.driver.migrate_volume(context, self.volume_a, host)
 2499             self.assertEqual((False, None), ret)
 2500 
 2501         host = {
 2502             'capabilities': {
 2503                 'storage_protocol': 'ceph',
 2504                 'location_info': 'nondefault:None:not-abc:None:rbd'}}
 2505 
 2506         mock_client().__enter__().client.get_fsid.return_value = 'abc'
 2507 
 2508         with mock.patch.object(self.driver, '_get_fsid') as mock_get_fsid:
 2509             mock_get_fsid.return_value = 'abc'
 2510             ret = self.driver.migrate_volume(context, self.volume_a, host)
 2511             self.assertEqual((False, None), ret)
 2512 
 2513     @mock.patch('os_brick.initiator.linuxrbd.rbd')
 2514     @mock.patch('os_brick.initiator.linuxrbd.RBDClient')
 2515     def test_migrate_volume_same_pool(self, mock_client, mock_rbd):
 2516         host = {
 2517             'capabilities': {
 2518                 'storage_protocol': 'ceph',
 2519                 'location_info': 'nondefault:None:abc:None:rbd'}}
 2520 
 2521         mock_client().__enter__().client.get_fsid.return_value = 'abc'
 2522 
 2523         with mock.patch.object(self.driver, '_get_fsid') as mock_get_fsid:
 2524             mock_get_fsid.return_value = 'abc'
 2525             ret = self.driver.migrate_volume(context, self.volume_a, host)
 2526             self.assertEqual((True, None), ret)
 2527 
 2528     @mock.patch('os_brick.initiator.linuxrbd.rbd')
 2529     @mock.patch('os_brick.initiator.linuxrbd.RBDClient')
 2530     def test_migrate_volume_insue_different_pool(self, mock_client, mock_rbd):
 2531         self.volume_a.status = 'in-use'
 2532         host = {
 2533             'capabilities': {
 2534                 'storage_protocol': 'ceph',
 2535                 'location_info': 'nondefault:None:abc:None:rbd2'}}
 2536 
 2537         mock_client().__enter__().client.get_fsid.return_value = 'abc'
 2538 
 2539         with mock.patch.object(self.driver, '_get_fsid') as mock_get_fsid:
 2540             mock_get_fsid.return_value = 'abc'
 2541             ret = self.driver.migrate_volume(context, self.volume_a, host)
 2542             self.assertEqual((False, None), ret)
 2543 
 2544     @mock.patch('os_brick.initiator.linuxrbd.rbd')
 2545     @mock.patch('os_brick.initiator.linuxrbd.RBDClient')
 2546     @mock.patch('cinder.volume.drivers.rbd.RBDVolumeProxy')
 2547     def test_migrate_volume_different_pool(self, mock_proxy, mock_client,
 2548                                            mock_rbcd):
 2549         host = {
 2550             'capabilities': {
 2551                 'storage_protocol': 'ceph',
 2552                 'location_info': 'nondefault:None:abc:None:rbd2'}}
 2553 
 2554         mock_client().__enter__().client.get_fsid.return_value = 'abc'
 2555 
 2556         with mock.patch.object(self.driver, '_get_fsid') as mock_get_fsid, \
 2557                 mock.patch.object(self.driver, 'delete_volume') as mock_delete:
 2558             mock_get_fsid.return_value = 'abc'
 2559             proxy = mock_proxy.return_value
 2560             proxy.__enter__.return_value = proxy
 2561             ret = self.driver.migrate_volume(context, self.volume_a,
 2562                                              host)
 2563             proxy.copy.assert_called_once_with(
 2564                 mock_client.return_value.__enter__.return_value.ioctx,
 2565                 self.volume_a.name)
 2566             mock_delete.assert_called_once_with(self.volume_a)
 2567             self.assertEqual((True, None), ret)
 2568 
 2569     @mock.patch('tempfile.NamedTemporaryFile')
 2570     @mock.patch('cinder.volume.volume_utils.check_encryption_provider',
 2571                 return_value={'encryption_key_id': fake.ENCRYPTION_KEY_ID})
 2572     def test_create_encrypted_volume(self,
 2573                                      mock_check_enc_prov,
 2574                                      mock_temp_file):
 2575         class DictObj(object):
 2576             # convert a dict to object w/ attributes
 2577             def __init__(self, d):
 2578                 self.__dict__ = d
 2579 
 2580         mock_temp_file.return_value.__enter__.side_effect = [
 2581             DictObj({'name': '/imgfile'}),
 2582             DictObj({'name': '/passfile'})]
 2583 
 2584         key_mgr = fake_keymgr.fake_api()
 2585 
 2586         self.mock_object(castellan.key_manager, 'API', return_value=key_mgr)
 2587         key_id = key_mgr.store(self.context, KeyObject())
 2588         self.volume_c.encryption_key_id = key_id
 2589 
 2590         enc_info = {'encryption_key_id': key_id,
 2591                     'cipher': 'aes-xts-essiv',
 2592                     'key_size': 256}
 2593 
 2594         with mock.patch('cinder.volume.volume_utils.check_encryption_provider',
 2595                         return_value=enc_info), \
 2596                 mock.patch('cinder.volume.drivers.rbd.open') as mock_open, \
 2597                 mock.patch.object(self.driver, '_execute') as mock_exec:
 2598             self.driver._create_encrypted_volume(self.volume_c,
 2599                                                  self.context)
 2600             mock_open.assert_called_with('/passfile', 'w')
 2601 
 2602             mock_exec.assert_any_call(
 2603                 'qemu-img', 'create', '-f', 'luks', '-o',
 2604                 'cipher-alg=aes-256,cipher-mode=xts,ivgen-alg=essiv',
 2605                 '--object',
 2606                 'secret,id=luks_sec,format=raw,file=/passfile',
 2607                 '-o', 'key-secret=luks_sec', '/imgfile', '12288M')
 2608             mock_exec.assert_any_call(
 2609                 'rbd', 'import', '--pool', 'rbd', '--order', 22,
 2610                 '/imgfile', self.volume_c.name)
 2611 
 2612     @mock.patch('cinder.objects.Volume.get_by_id')
 2613     @mock.patch('cinder.db.volume_glance_metadata_get', return_value={})
 2614     @common_mocks
 2615     def test_get_backup_device_ceph(self, mock_gm_get, volume_get_by_id):
 2616         # Use the same volume for backup (volume_a)
 2617         volume_get_by_id.return_value = self.volume_a
 2618         driver = self.driver
 2619 
 2620         self._create_backup_db_entry(fake.BACKUP_ID, self.volume_a['id'], 1)
 2621         backup = objects.Backup.get_by_id(self.context, fake.BACKUP_ID)
 2622         backup.service = 'cinder.backup.drivers.ceph'
 2623 
 2624         ret = driver.get_backup_device(self.context, backup)
 2625         self.assertEqual(ret, (self.volume_a, False))
 2626 
 2627     def _create_backup_db_entry(self, backupid, volid, size,
 2628                                 userid=str(uuid.uuid4()),
 2629                                 projectid=str(uuid.uuid4())):
 2630         backup = {'id': backupid, 'size': size, 'volume_id': volid,
 2631                   'user_id': userid, 'project_id': projectid}
 2632         return db.backup_create(self.context, backup)['id']
 2633 
 2634     @mock.patch('cinder.volume.driver.BaseVD._get_backup_volume_temp_snapshot')
 2635     @mock.patch('cinder.volume.driver.BaseVD._get_backup_volume_temp_volume')
 2636     @mock.patch('cinder.objects.Volume.get_by_id')
 2637     @mock.patch('cinder.db.volume_glance_metadata_get', return_value={})
 2638     @common_mocks
 2639     def test_get_backup_device_other(self,
 2640                                      mock_gm_get,
 2641                                      volume_get_by_id,
 2642                                      mock_get_temp_volume,
 2643                                      mock_get_temp_snapshot):
 2644         # Use a cloned volume for backup (volume_b)
 2645         self.volume_a.previous_status = 'in-use'
 2646         mock_get_temp_volume.return_value = self.volume_b
 2647         mock_get_temp_snapshot.return_value = (self.volume_b, False)
 2648 
 2649         volume_get_by_id.return_value = self.volume_a
 2650         driver = self.driver
 2651 
 2652         self._create_backup_db_entry(fake.BACKUP_ID, self.volume_a['id'], 1)
 2653         backup = objects.Backup.get_by_id(self.context, fake.BACKUP_ID)
 2654         backup.service = 'asdf'
 2655 
 2656         ret = driver.get_backup_device(self.context, backup)
 2657         self.assertEqual(ret, (self.volume_b, False))
 2658 
 2659     @common_mocks
 2660     def test_multiattach_exclusions(self):
 2661         self.assertEqual(
 2662             self.driver.RBD_FEATURE_JOURNALING |
 2663             self.driver.RBD_FEATURE_FAST_DIFF |
 2664             self.driver.RBD_FEATURE_OBJECT_MAP |
 2665             self.driver.RBD_FEATURE_EXCLUSIVE_LOCK,
 2666             self.driver.MULTIATTACH_EXCLUSIONS)
 2667 
 2668     MULTIATTACH_FULL_FEATURES = (
 2669         driver.RBDDriver.RBD_FEATURE_LAYERING |
 2670         driver.RBDDriver.RBD_FEATURE_EXCLUSIVE_LOCK |
 2671         driver.RBDDriver.RBD_FEATURE_OBJECT_MAP |
 2672         driver.RBDDriver.RBD_FEATURE_FAST_DIFF |
 2673         driver.RBDDriver.RBD_FEATURE_JOURNALING)
 2674 
 2675     MULTIATTACH_REDUCED_FEATURES = (
 2676         driver.RBDDriver.RBD_FEATURE_LAYERING |
 2677         driver.RBDDriver.RBD_FEATURE_EXCLUSIVE_LOCK)
 2678 
 2679     @ddt.data(MULTIATTACH_FULL_FEATURES, MULTIATTACH_REDUCED_FEATURES)
 2680     @common_mocks
 2681     def test_enable_multiattach(self, features):
 2682         image = self.mock_proxy.return_value.__enter__.return_value
 2683         image_features = features
 2684         image.features.return_value = image_features
 2685 
 2686         ret = self.driver._enable_multiattach(self.volume_a)
 2687 
 2688         image.update_features.assert_called_once_with(
 2689             self.driver.MULTIATTACH_EXCLUSIONS & image_features, False)
 2690 
 2691         self.assertEqual(
 2692             {'provider_location':
 2693              "{\"saved_features\":%s}" % image_features}, ret)
 2694 
 2695     @ddt.data(MULTIATTACH_FULL_FEATURES, MULTIATTACH_REDUCED_FEATURES)
 2696     @common_mocks
 2697     def test_disable_multiattach(self, features):
 2698         image = self.mock_proxy.return_value.__enter__.return_value
 2699         self.volume_a.provider_location = '{"saved_features": %s}' % features
 2700 
 2701         ret = self.driver._disable_multiattach(self.volume_a)
 2702 
 2703         image.update_features.assert_called_once_with(
 2704             self.driver.MULTIATTACH_EXCLUSIONS & features, True)
 2705 
 2706         self.assertEqual({'provider_location': None}, ret)
 2707 
 2708 
 2709 class ManagedRBDTestCase(test_driver.BaseDriverTestCase):
 2710     driver_name = "cinder.volume.drivers.rbd.RBDDriver"
 2711 
 2712     def setUp(self):
 2713         super(ManagedRBDTestCase, self).setUp()
 2714         self.volume.driver.set_initialized()
 2715         self.volume.stats = {'allocated_capacity_gb': 0,
 2716                              'pools': {}}
 2717         self.called = []
 2718 
 2719     def _create_volume_from_image(self, expected_status, raw=False,
 2720                                   clone_error=False):
 2721         """Try to clone a volume from an image, and check status afterwards.
 2722 
 2723         NOTE: if clone_error is True we force the image type to raw otherwise
 2724               clone_image is not called
 2725         """
 2726 
 2727         # See tests.image.fake for image types.
 2728         if raw:
 2729             image_id = '155d900f-4e14-4e4c-a73d-069cbf4541e6'
 2730         else:
 2731             image_id = 'c905cedb-7281-47e4-8a62-f26bc5fc4c77'
 2732 
 2733         # creating volume testdata
 2734         db_volume = {'display_description': 'Test Desc',
 2735                      'size': 20,
 2736                      'status': 'creating',
 2737                      'availability_zone': 'fake_zone',
 2738                      'attach_status': fields.VolumeAttachStatus.DETACHED,
 2739                      'host': 'dummy'}
 2740         volume = objects.Volume(context=self.context, **db_volume)
 2741         volume.create()
 2742 
 2743         try:
 2744             if not clone_error:
 2745                 self.volume.create_volume(self.context, volume,
 2746                                           request_spec={'image_id': image_id})
 2747             else:
 2748                 self.assertRaises(exception.CinderException,
 2749                                   self.volume.create_volume,
 2750                                   self.context,
 2751                                   volume,
 2752                                   request_spec={'image_id': image_id})
 2753 
 2754             volume = objects.Volume.get_by_id(self.context, volume.id)
 2755             self.assertEqual(expected_status, volume.status)
 2756         finally:
 2757             # cleanup
 2758             volume.destroy()
 2759 
 2760     @mock.patch('cinder.image.image_utils.check_available_space')
 2761     @mock.patch.object(cinder.image.glance, 'get_default_image_service')
 2762     def test_create_vol_from_image_status_available(self, mock_gdis,
 2763                                                     mock_check_space):
 2764         """Clone raw image then verify volume is in available state."""
 2765 
 2766         def _mock_clone_image(context, volume, image_location,
 2767                               image_meta, image_service):
 2768             return {'provider_location': None}, True
 2769 
 2770         with mock.patch.object(self.volume.driver, 'clone_image') as \
 2771                 mock_clone_image:
 2772             mock_clone_image.side_effect = _mock_clone_image
 2773             with mock.patch.object(self.volume.driver, 'create_volume') as \
 2774                     mock_create:
 2775                 with mock.patch.object(volume_utils,
 2776                                        'copy_image_to_volume') as mock_copy:
 2777                     self._create_volume_from_image('available', raw=True)
 2778                     self.assertFalse(mock_copy.called)
 2779 
 2780                 self.assertTrue(mock_clone_image.called)
 2781                 self.assertFalse(mock_create.called)
 2782                 self.assertTrue(mock_gdis.called)
 2783 
 2784     @mock.patch('cinder.image.image_utils.check_available_space')
 2785     @mock.patch.object(cinder.image.glance, 'get_default_image_service')
 2786     @mock.patch('cinder.image.image_utils.TemporaryImages.fetch')
 2787     @mock.patch('cinder.image.image_utils.qemu_img_info')
 2788     @mock.patch('cinder.image.image_utils.verify_glance_image_signature')
 2789     def test_create_vol_from_non_raw_image_status_available(
 2790             self, mock_verify, mock_qemu_info, mock_fetch, mock_gdis,
 2791             mock_check_space):
 2792         """Clone non-raw image then verify volume is in available state."""
 2793 
 2794         def _mock_clone_image(context, volume, image_location,
 2795                               image_meta, image_service):
 2796             return {'provider_location': None}, False
 2797 
 2798         image_info = imageutils.QemuImgInfo()
 2799         image_info.virtual_size = '1073741824'
 2800         mock_qemu_info.return_value = image_info
 2801         self.flags(verify_glance_signatures='disabled')
 2802 
 2803         mock_fetch.return_value = mock.MagicMock(spec=utils.get_file_spec())
 2804         with mock.patch.object(self.volume.driver, 'clone_image') as \
 2805                 mock_clone_image:
 2806             mock_clone_image.side_effect = _mock_clone_image
 2807             with mock.patch.object(self.volume.driver, 'create_volume') as \
 2808                     mock_create:
 2809                 with mock.patch.object(volume_utils,
 2810                                        'copy_image_to_volume') as mock_copy:
 2811                     self._create_volume_from_image('available', raw=False)
 2812                     self.assertTrue(mock_copy.called)
 2813 
 2814                 self.assertTrue(mock_clone_image.called)
 2815                 self.assertTrue(mock_create.called)
 2816                 self.assertTrue(mock_gdis.called)
 2817 
 2818     @mock.patch('cinder.image.image_utils.check_available_space')
 2819     @mock.patch.object(cinder.image.glance, 'get_default_image_service')
 2820     def test_create_vol_from_image_status_error(self, mock_gdis,
 2821                                                 mock_check_space):
 2822         """Fail to clone raw image then verify volume is in error state."""
 2823         with mock.patch.object(self.volume.driver, 'clone_image') as \
 2824                 mock_clone_image:
 2825             mock_clone_image.side_effect = exception.CinderException
 2826             with mock.patch.object(self.volume.driver, 'create_volume'):
 2827                 with mock.patch.object(volume_utils,
 2828                                        'copy_image_to_volume') as mock_copy:
 2829                     self._create_volume_from_image('error', raw=True,
 2830                                                    clone_error=True)
 2831                     self.assertFalse(mock_copy.called)
 2832 
 2833                 self.assertTrue(mock_clone_image.called)
 2834                 self.assertFalse(self.volume.driver.create_volume.called)
 2835                 self.assertTrue(mock_gdis.called)
 2836 
 2837     def test_clone_failure(self):
 2838         driver = self.volume.driver
 2839 
 2840         with mock.patch.object(driver, '_is_cloneable', lambda *args: False):
 2841             image_loc = (mock.Mock(), None)
 2842             actual = driver.clone_image(mock.Mock(),
 2843                                         mock.Mock(),
 2844                                         image_loc,
 2845                                         {},
 2846                                         mock.Mock())
 2847             self.assertEqual(({}, False), actual)
 2848 
 2849         self.assertEqual(({}, False),
 2850                          driver.clone_image('', object(), None, {}, ''))
 2851 
 2852     def test_clone_success(self):
 2853         expected = ({'provider_location': None}, True)
 2854         driver = self.volume.driver
 2855 
 2856         with mock.patch.object(self.volume.driver, '_is_cloneable') as \
 2857                 mock_is_cloneable:
 2858             mock_is_cloneable.return_value = True
 2859             with mock.patch.object(self.volume.driver, '_clone') as \
 2860                     mock_clone:
 2861                 with mock.patch.object(self.volume.driver, '_resize') as \
 2862                         mock_resize:
 2863                     mock_clone.return_value = {}
 2864                     image_loc = ('rbd://fee/fi/fo/fum', None)
 2865 
 2866                     volume = {'name': 'vol1'}
 2867                     actual = driver.clone_image(mock.Mock(),
 2868                                                 volume,
 2869                                                 image_loc,
 2870                                                 {'disk_format': 'raw',
 2871                                                  'id': 'id.foo'},
 2872                                                 mock.Mock())
 2873 
 2874                     self.assertEqual(expected, actual)
 2875                     mock_clone.assert_called_once_with(volume,
 2876                                                        'fi', 'fo', 'fum')
 2877                     mock_resize.assert_called_once_with(volume)
 2878 
 2879     def test_clone_multilocation_success(self):
 2880         expected = ({'provider_location': None}, True)
 2881         driver = self.volume.driver
 2882 
 2883         def cloneable_side_effect(url_location, image_meta):
 2884             return url_location == 'rbd://fee/fi/fo/fum'
 2885 
 2886         with mock.patch.object(self.volume.driver, '_is_cloneable') \
 2887             as mock_is_cloneable, \
 2888             mock.patch.object(self.volume.driver, '_clone') as mock_clone, \
 2889             mock.patch.object(self.volume.driver, '_resize') \
 2890                 as mock_resize:
 2891             mock_is_cloneable.side_effect = cloneable_side_effect
 2892             mock_clone.return_value = {}
 2893             image_loc = ('rbd://bee/bi/bo/bum',
 2894                          [{'url': 'rbd://bee/bi/bo/bum'},
 2895                           {'url': 'rbd://fee/fi/fo/fum'}])
 2896             volume = {'name': 'vol1'}
 2897             image_meta = mock.sentinel.image_meta
 2898             image_service = mock.sentinel.image_service
 2899 
 2900             actual = driver.clone_image(self.context,
 2901                                         volume,
 2902                                         image_loc,
 2903                                         image_meta,
 2904                                         image_service)
 2905 
 2906             self.assertEqual(expected, actual)
 2907             self.assertEqual(2, mock_is_cloneable.call_count)
 2908             mock_clone.assert_called_once_with(volume,
 2909                                                'fi', 'fo', 'fum')
 2910             mock_is_cloneable.assert_called_with('rbd://fee/fi/fo/fum',
 2911                                                  image_meta)
 2912             mock_resize.assert_called_once_with(volume)
 2913 
 2914     def test_clone_multilocation_failure(self):
 2915         expected = ({}, False)
 2916         driver = self.volume.driver
 2917 
 2918         with mock.patch.object(driver, '_is_cloneable', return_value=False) \
 2919             as mock_is_cloneable, \
 2920             mock.patch.object(self.volume.driver, '_clone') as mock_clone, \
 2921             mock.patch.object(self.volume.driver, '_resize') \
 2922                 as mock_resize:
 2923             image_loc = ('rbd://bee/bi/bo/bum',
 2924                          [{'url': 'rbd://bee/bi/bo/bum'},
 2925                           {'url': 'rbd://fee/fi/fo/fum'}])
 2926 
 2927             volume = {'name': 'vol1'}
 2928             image_meta = mock.sentinel.image_meta
 2929             image_service = mock.sentinel.image_service
 2930             actual = driver.clone_image(self.context,
 2931                                         volume,
 2932                                         image_loc,
 2933                                         image_meta,
 2934                                         image_service)
 2935 
 2936             self.assertEqual(expected, actual)
 2937             self.assertEqual(2, mock_is_cloneable.call_count)
 2938             mock_is_cloneable.assert_any_call('rbd://bee/bi/bo/bum',
 2939                                               image_meta)
 2940             mock_is_cloneable.assert_any_call('rbd://fee/fi/fo/fum',
 2941                                               image_meta)
 2942             self.assertFalse(mock_clone.called)
 2943             self.assertFalse(mock_resize.called)