diff --git a/cinder/tests/unit/volume/drivers/test_kaminario.py b/cinder/tests/unit/volume/drivers/test_kaminario.py index 06ed7d23016..2acc3e789c3 100644 --- a/cinder/tests/unit/volume/drivers/test_kaminario.py +++ b/cinder/tests/unit/volume/drivers/test_kaminario.py @@ -14,6 +14,7 @@ # under the License. """Unit tests for kaminario driver.""" import mock +from oslo_utils import units from cinder import context from cinder import exception @@ -46,6 +47,9 @@ class FakeSaveObject(FakeK2Obj): self.snapshot = FakeK2Obj() self.name = 'test' self.pwwn = '50024f4053300300' + self.volume_group = self + self.is_dedup = True + self.size = units.Mi def save(self): return FakeSaveObject() @@ -103,6 +107,7 @@ class TestKaminarioISCSI(test.TestCase): self.context = context.get_admin_context() self.vol = fake_volume.fake_volume_obj(self.context) self.vol.volume_type = fake_volume.fake_volume_type_obj(self.context) + self.vol.volume_type.extra_specs = {'foo': None} self.snap = fake_snapshot.fake_snapshot_obj(self.context) self.snap.volume = self.vol @@ -265,6 +270,42 @@ class TestKaminarioISCSI(test.TestCase): result = self.driver.k2_initialize_connection(self.vol, CONNECTOR) self.assertEqual(548, result) + def test_manage_existing(self): + """Test manage_existing.""" + self.driver._get_replica_status = mock.Mock(return_value=False) + result = self.driver.manage_existing(self.vol, {'source-name': 'test'}) + self.assertIsNone(result) + + def test_manage_existing_exp(self): + self.driver._get_replica_status = mock.Mock(return_value=True) + self.assertRaises(exception.ManageExistingInvalidReference, + self.driver.manage_existing, self.vol, + {'source-name': 'test'}) + + def test_manage_existing_get_size(self): + """Test manage_existing_get_size.""" + self.driver.client.search().hits[0].size = units.Mi + result = self.driver.manage_existing_get_size(self.vol, + {'source-name': 'test'}) + self.assertEqual(1, result) + + def test_get_is_dedup(self): + """Test _get_is_dedup.""" + result = self.driver._get_is_dedup(self.vol.volume_type) + self.assertTrue(result) + + def test_get_is_dedup_false(self): + """Test _get_is_dedup_false.""" + specs = {'kaminario:thin_prov_type': 'nodedup'} + self.vol.volume_type.extra_specs = specs + result = self.driver._get_is_dedup(self.vol.volume_type) + self.assertFalse(result) + + def test_get_replica_status(self): + """Test _get_replica_status.""" + result = self.driver._get_replica_status(self.vol) + self.assertTrue(result) + class TestKaminarioFC(TestKaminarioISCSI): diff --git a/cinder/volume/drivers/kaminario/kaminario_common.py b/cinder/volume/drivers/kaminario/kaminario_common.py index 5945c8a3ae3..bc34ec3a2ae 100644 --- a/cinder/volume/drivers/kaminario/kaminario_common.py +++ b/cinder/volume/drivers/kaminario/kaminario_common.py @@ -14,6 +14,7 @@ # under the License. """Volume driver for Kaminario K2 all-flash arrays.""" +import math import re import six @@ -25,7 +26,7 @@ from oslo_utils import versionutils import cinder from cinder import exception -from cinder.i18n import _, _LE, _LW +from cinder.i18n import _, _LE, _LW, _LI from cinder import utils from cinder.volume.drivers.san import san from cinder.volume import utils as vol_utils @@ -38,7 +39,9 @@ kaminario1_opts = [ default='K2-nodedup', help="If volume-type name contains this substring " "nodedup volume will be created, otherwise " - "dedup volume wil be created.")] + "dedup volume wil be created. Note: this option is " + "deprecated in favour of 'kaminario:thin_prov_type' in " + "extra-specs and will be removed in the Ocata release.")] kaminario2_opts = [ cfg.BoolOpt('auto_calc_max_oversubscription_ratio', default=False, @@ -73,7 +76,6 @@ def kaminario_logger(func): class KaminarioCinderDriver(cinder.volume.driver.ISCSIDriver): VENDOR = "Kaminario" - VERSION = "1.0" stats = {} def __init__(self, *args, **kwargs): @@ -132,10 +134,7 @@ class KaminarioCinderDriver(cinder.volume.driver.ISCSIDriver): """ vg_name = self.get_volume_group_name(volume.id) vol_name = self.get_volume_name(volume.id) - if CONF.kaminario_nodedup_substring in volume.volume_type.name: - prov_type = False - else: - prov_type = True + prov_type = self._get_is_dedup(volume.get('volume_type')) try: LOG.debug("Creating volume group with name: %(name)s, " "quota: unlimited and dedup_support: %(dedup)s", @@ -375,7 +374,8 @@ class KaminarioCinderDriver(cinder.volume.driver.ISCSIDriver): 'total_volumes': total_volumes, 'thick_provisioning_support': False, 'provisioned_capacity_gb': provisioned_vol / units.Mi, - 'max_oversubscription_ratio': ratio} + 'max_oversubscription_ratio': ratio, + 'kaminario:thin_prov_type': 'dedup/nodedup'} @kaminario_logger def get_initiator_host_name(self, connector): @@ -511,3 +511,79 @@ class KaminarioCinderDriver(cinder.volume.driver.ISCSIDriver): def _get_host_object(self, connector): pass + + def _get_is_dedup(self, vol_type): + if vol_type: + specs_val = vol_type.get('extra_specs', {}).get( + 'kaminario:thin_prov_type') + if specs_val == 'nodedup': + return False + elif CONF.kaminario_nodedup_substring in vol_type.name: + LOG.info(_LI("'kaminario_nodedup_substring' option is " + "deprecated in favour of 'kaminario:thin_prov_" + "type' in extra-specs and will be removed in " + "the Ocata release.")) + return False + else: + return True + else: + return True + + def _get_replica_status(self, vg_name): + status = False + rvg = self.client.search("replication/peer_volume_groups", + name=vg_name) + if rvg.total != 0: + status = True + return status + + def manage_existing(self, volume, existing_ref): + vol_name = existing_ref['source-name'] + new_name = self.get_volume_name(volume.id) + vg_new_name = self.get_volume_group_name(volume.id) + vg_name = None + is_dedup = self._get_is_dedup(volume.get('volume_type')) + try: + LOG.debug("Searching volume: %s in K2.", vol_name) + vol = self.client.search("volumes", name=vol_name).hits[0] + vg = vol.volume_group + vg_replica = self._get_replica_status(vg.name) + vol_map = False + if self.client.search("mappings", volume=vol).total != 0: + vol_map = True + if is_dedup != vg.is_dedup or vg_replica or vol_map: + raise exception.ManageExistingInvalidReference( + existing_ref=existing_ref, + reason=_('Manage volume type invalid.')) + vol.name = new_name + vg_name = vg.name + LOG.debug("Manage new volume name: %s", new_name) + vg.name = vg_new_name + LOG.debug("Manage volume group name: %s", vg_new_name) + vg.save() + LOG.debug("Manage volume: %s in K2.", vol_name) + vol.save() + except Exception as ex: + vg_rs = self.client.search("volume_groups", name=vg_new_name) + if hasattr(vg_rs, 'hits') and vg_rs.total != 0: + vg = vg_rs.hits[0] + if vg_name and vg.name == vg_new_name: + vg.name = vg_name + LOG.debug("Updating vg new name to old name: %s ", vg_name) + vg.save() + LOG.exception(_LE("manage volume: %s failed."), vol_name) + raise exception.ManageExistingInvalidReference( + existing_ref=existing_ref, + reason=six.text_type(ex.message)) + + def manage_existing_get_size(self, volume, existing_ref): + vol_name = existing_ref['source-name'] + v_rs = self.client.search("volumes", name=vol_name) + if hasattr(v_rs, 'hits') and v_rs.total != 0: + vol = v_rs.hits[0] + size = vol.size / units.Mi + return math.ceil(size) + else: + raise exception.ManageExistingInvalidReference( + existing_ref=existing_ref, + reason=_('Unable to get size of manage volume.')) diff --git a/cinder/volume/drivers/kaminario/kaminario_fc.py b/cinder/volume/drivers/kaminario/kaminario_fc.py index e8df19e6630..f569a32fee3 100644 --- a/cinder/volume/drivers/kaminario/kaminario_fc.py +++ b/cinder/volume/drivers/kaminario/kaminario_fc.py @@ -27,7 +27,14 @@ kaminario_logger = common.kaminario_logger class KaminarioFCDriver(common.KaminarioCinderDriver): - """Kaminario K2 FC Volume Driver.""" + """Kaminario K2 FC Volume Driver. + + Version history: + 1.0 - Initial driver + 1.1 - Added manage/unmanage and extra-specs support for nodedup + """ + + VERSION = '1.1' @kaminario_logger def __init__(self, *args, **kwargs): diff --git a/cinder/volume/drivers/kaminario/kaminario_iscsi.py b/cinder/volume/drivers/kaminario/kaminario_iscsi.py index 39003a32911..6b21fb8e12d 100644 --- a/cinder/volume/drivers/kaminario/kaminario_iscsi.py +++ b/cinder/volume/drivers/kaminario/kaminario_iscsi.py @@ -29,7 +29,14 @@ kaminario_logger = common.kaminario_logger @interface.volumedriver class KaminarioISCSIDriver(common.KaminarioCinderDriver): - """Kaminario K2 iSCSI Volume Driver.""" + """Kaminario K2 iSCSI Volume Driver. + + Version history: + 1.0 - Initial driver + 1.1 - Added manage/unmanage and extra-specs support for nodedup + """ + + VERSION = '1.1' @kaminario_logger def __init__(self, *args, **kwargs): diff --git a/releasenotes/notes/kaminario-cinder-driver-15f2208655590d0a.yaml b/releasenotes/notes/kaminario-cinder-driver-15f2208655590d0a.yaml new file mode 100644 index 00000000000..2fbf3ff3654 --- /dev/null +++ b/releasenotes/notes/kaminario-cinder-driver-15f2208655590d0a.yaml @@ -0,0 +1,4 @@ +--- +features: + - Add manage/unmanage and extra-specs feature in Kaminario iSCSI and FC Cinder drivers. +