Fujitsu Driver: Multiple pools support
Add multiple pools support for Fujitsu driver. Change-Id: Icb8e04a0b623d46ec97c85bfe61d466ff983d9e5
This commit is contained in:
parent
fb0bebe788
commit
d988ee3468
@ -43,6 +43,7 @@ CONF = """<?xml version='1.0' encoding='UTF-8'?>
|
||||
<EternusPassword>testpass</EternusPassword>
|
||||
<EternusISCSIIP>10.0.0.3</EternusISCSIIP>
|
||||
<EternusPool>abcd1234_TPP</EternusPool>
|
||||
<EternusPool>abcd1234_RG</EternusPool>
|
||||
<EternusSnapPool>abcd1234_OSVD</EternusSnapPool>
|
||||
</FUJITSU>"""
|
||||
|
||||
@ -51,8 +52,19 @@ TEST_VOLUME = {
|
||||
'name': 'volume1',
|
||||
'display_name': 'volume1',
|
||||
'provider_location': None,
|
||||
'volume_metadata': [],
|
||||
'metadata': {},
|
||||
'size': 1,
|
||||
'host': 'controller@113#abcd1234_TPP'
|
||||
}
|
||||
|
||||
TEST_VOLUME2 = {
|
||||
'id': '98179912-2495-42e9-97f0-6a0d3511700a',
|
||||
'name': 'volume2',
|
||||
'display_name': 'volume2',
|
||||
'provider_location': None,
|
||||
'metadata': {},
|
||||
'size': 1,
|
||||
'host': 'controller@113#abcd1234_RG'
|
||||
}
|
||||
|
||||
TEST_SNAP = {
|
||||
@ -72,7 +84,8 @@ TEST_CLONE = {
|
||||
'project_id': 'project',
|
||||
'display_name': 'clone1',
|
||||
'display_description': 'volume created from snapshot',
|
||||
'volume_metadata': [],
|
||||
'metadata': {},
|
||||
'host': 'controller@113#abcd1234_TPP'
|
||||
}
|
||||
|
||||
ISCSI_INITIATOR = 'iqn.1993-08.org.debian:01:8261afe17e4c'
|
||||
@ -96,24 +109,76 @@ AUTH_PRIV = 'FUJITSU_AuthorizedPrivilege'
|
||||
STOR_SYNC = 'FUJITSU_StorageSynchronized'
|
||||
PROT_CTRL_UNIT = 'CIM_ProtocolControllerForUnit'
|
||||
STORAGE_TYPE = 'abcd1234_TPP'
|
||||
STORAGE_TYPE2 = 'abcd1234_RG'
|
||||
LUNMASKCTRL_IDS = ['AFG0010_CM00CA00P00', 'AFG0011_CM01CA00P00']
|
||||
|
||||
MAP_STAT = '0'
|
||||
VOL_STAT = '0'
|
||||
|
||||
FAKE_CAPACITY = 1170368102400
|
||||
# Volume1 in pool abcd1234_TPP
|
||||
FAKE_LUN_ID1 = '600000E00D2A0000002A011500140000'
|
||||
FAKE_LUN_NO1 = '0x0014'
|
||||
# Snapshot1 in pool abcd1234_OSVD
|
||||
FAKE_LUN_ID2 = '600000E00D2A0000002A0115001E0000'
|
||||
FAKE_LUN_NO2 = '0x001E'
|
||||
# Volume2 in pool abcd1234_RG
|
||||
FAKE_LUN_ID3 = '600000E00D2800000028075301140000'
|
||||
FAKE_LUN_NO3 = '0x0114'
|
||||
FAKE_SYSTEM_NAME = 'ET603SA4621302115'
|
||||
# abcd1234_TPP pool
|
||||
FAKE_USEGB = 2.0
|
||||
# abcd1234_RG pool
|
||||
FAKE_USEGB2 = 1.0
|
||||
FAKE_POOLS = [{
|
||||
'path': {'InstanceID': 'FUJITSU:TPP0004'},
|
||||
'pool_name': 'abcd1234_TPP',
|
||||
'useable_capacity_gb': (FAKE_CAPACITY / units.Gi) * 20 - FAKE_USEGB,
|
||||
'multiattach': False,
|
||||
'thick_provisioning_support': False,
|
||||
'provisioned_capacity_gb': FAKE_USEGB,
|
||||
'total_volumes': 2,
|
||||
'thin_provisioning_support': True,
|
||||
'free_capacity_gb': FAKE_CAPACITY / units.Gi - FAKE_USEGB,
|
||||
'total_capacity_gb': FAKE_CAPACITY / units.Gi,
|
||||
'max_over_subscription_ratio': '20.0',
|
||||
}, {
|
||||
'path': {'InstanceID': 'FUJITSU:RSP0005'},
|
||||
'pool_name': 'abcd1234_RG',
|
||||
'useable_capacity_gb': FAKE_CAPACITY / units.Gi - FAKE_USEGB2,
|
||||
'multiattach': False,
|
||||
'thick_provisioning_support': True,
|
||||
'provisioned_capacity_gb': FAKE_USEGB2,
|
||||
'total_volumes': 1,
|
||||
'thin_provisioning_support': False,
|
||||
'free_capacity_gb': FAKE_CAPACITY / units.Gi - FAKE_USEGB2,
|
||||
'total_capacity_gb': FAKE_CAPACITY / units.Gi,
|
||||
'max_over_subscription_ratio': 1,
|
||||
}]
|
||||
|
||||
FAKE_STATS = {
|
||||
'driver_version': '1.3.0',
|
||||
'storage_protocol': 'iSCSI',
|
||||
'vendor_name': 'FUJITSU',
|
||||
'total_capacity_gb': FAKE_CAPACITY / units.Gi,
|
||||
'free_capacity_gb': FAKE_CAPACITY / units.Gi,
|
||||
'QoS_support': False,
|
||||
'volume_backend_name': 'volume_backend_name',
|
||||
'shared_targets': True,
|
||||
'backend_state': 'up',
|
||||
'pools': FAKE_POOLS,
|
||||
}
|
||||
FAKE_STATS2 = {
|
||||
'driver_version': '1.3.0',
|
||||
'storage_protocol': 'FC',
|
||||
'vendor_name': 'FUJITSU',
|
||||
'QoS_support': False,
|
||||
'volume_backend_name': 'volume_backend_name',
|
||||
'shared_targets': True,
|
||||
'backend_state': 'up',
|
||||
'pools': FAKE_POOLS,
|
||||
}
|
||||
|
||||
|
||||
# Volume1 in pool abcd1234_TPP
|
||||
FAKE_KEYBIND1 = {
|
||||
'CreationClassName': 'FUJITSU_StorageVolume',
|
||||
'SystemName': STORAGE_SYSTEM,
|
||||
@ -121,11 +186,27 @@ FAKE_KEYBIND1 = {
|
||||
'SystemCreationClassName': 'FUJITSU_StorageComputerSystem',
|
||||
}
|
||||
|
||||
# Volume2 in pool abcd1234_RG
|
||||
FAKE_KEYBIND3 = {
|
||||
'CreationClassName': 'FUJITSU_StorageVolume',
|
||||
'SystemName': STORAGE_SYSTEM,
|
||||
'DeviceID': FAKE_LUN_ID3,
|
||||
'SystemCreationClassName': 'FUJITSU_StorageComputerSystem',
|
||||
}
|
||||
|
||||
# Volume1
|
||||
FAKE_LOCATION1 = {
|
||||
'classname': 'FUJITSU_StorageVolume',
|
||||
'keybindings': FAKE_KEYBIND1,
|
||||
}
|
||||
|
||||
# Volume2
|
||||
FAKE_LOCATION3 = {
|
||||
'classname': 'FUJITSU_StorageVolume',
|
||||
'keybindings': FAKE_KEYBIND3,
|
||||
}
|
||||
|
||||
# Volume1 metadata info.
|
||||
FAKE_LUN_META1 = {
|
||||
'FJ_Pool_Type': 'Thinporvisioning_POOL',
|
||||
'FJ_Volume_No': FAKE_LUN_NO1,
|
||||
@ -134,10 +215,24 @@ FAKE_LUN_META1 = {
|
||||
'FJ_Backend': FAKE_SYSTEM_NAME,
|
||||
}
|
||||
|
||||
# Volume2 metadata info.
|
||||
FAKE_LUN_META3 = {
|
||||
'FJ_Pool_Type': 'RAID_GROUP',
|
||||
'FJ_Volume_No': FAKE_LUN_NO3,
|
||||
'FJ_Volume_Name': u'FJosv_4whcadwDac7ANKHA2O719A==',
|
||||
'FJ_Pool_Name': STORAGE_TYPE2,
|
||||
'FJ_Backend': FAKE_SYSTEM_NAME,
|
||||
}
|
||||
# Volume1
|
||||
FAKE_MODEL_INFO1 = {
|
||||
'provider_location': six.text_type(FAKE_LOCATION1),
|
||||
'metadata': FAKE_LUN_META1,
|
||||
}
|
||||
# Volume2
|
||||
FAKE_MODEL_INFO3 = {
|
||||
'provider_location': six.text_type(FAKE_LOCATION3),
|
||||
'metadata': FAKE_LUN_META3,
|
||||
}
|
||||
|
||||
FAKE_KEYBIND2 = {
|
||||
'CreationClassName': 'FUJITSU_StorageVolume',
|
||||
@ -151,7 +246,9 @@ FAKE_LOCATION2 = {
|
||||
'keybindings': FAKE_KEYBIND2,
|
||||
}
|
||||
|
||||
FAKE_SNAP_INFO = {'provider_location': six.text_type(FAKE_LOCATION2)}
|
||||
FAKE_SNAP_INFO = {
|
||||
'provider_location': six.text_type(FAKE_LOCATION2)
|
||||
}
|
||||
|
||||
FAKE_LUN_META2 = {
|
||||
'FJ_Pool_Type': 'Thinporvisioning_POOL',
|
||||
@ -205,6 +302,9 @@ class FakeEternusConnection(object):
|
||||
VOL_STAT = '1'
|
||||
rc = 0
|
||||
vol = self._enum_volumes()
|
||||
if InPool.get('InstanceID') == 'FUJITSU:RSP0005':
|
||||
job = {'TheElement': vol[1].path}
|
||||
else:
|
||||
job = {'TheElement': vol[0].path}
|
||||
elif MethodName == 'ReturnToStoragePool':
|
||||
VOL_STAT = '0'
|
||||
@ -262,7 +362,7 @@ class FakeEternusConnection(object):
|
||||
|
||||
return result
|
||||
|
||||
def EnumerateInstances(self, name):
|
||||
def EnumerateInstances(self, name, **param_dict):
|
||||
result = None
|
||||
if name == 'FUJITSU_StorageProduct':
|
||||
result = self._enum_sysnames()
|
||||
@ -315,6 +415,8 @@ class FakeEternusConnection(object):
|
||||
result = self._assoc_storagevolume(objectpath)
|
||||
elif ResultClass == 'FUJITSU_AuthorizedPrivilege':
|
||||
result = self._assoc_authpriv()
|
||||
elif AssocClass == 'FUJITSU_AllocatedFromStoragePool':
|
||||
result = self._assocnames_pool(objectpath)
|
||||
else:
|
||||
result = self._default_assoc(objectpath)
|
||||
|
||||
@ -329,6 +431,9 @@ class FakeEternusConnection(object):
|
||||
result = self._assocnames_tcp_endpoint()
|
||||
elif ResultClass == 'FUJITSU_AffinityGroupController':
|
||||
result = self._assocnames_afngroup()
|
||||
elif (ResultClass == 'FUJITSU_StorageVolume' and
|
||||
AssocClass == 'FUJITSU_AllocatedFromStoragePool'):
|
||||
result = self._assocnames_volumelist(objectpath)
|
||||
else:
|
||||
result = self._default_assocnames(objectpath)
|
||||
|
||||
@ -407,6 +512,26 @@ class FakeEternusConnection(object):
|
||||
def _assocnames_afngroup(self):
|
||||
return self._enum_afntyservice()
|
||||
|
||||
def _assocnames_volumelist(self, poolpath):
|
||||
volumelist = self._enum_volumes(force=True)
|
||||
inpool = []
|
||||
for vol in volumelist:
|
||||
vol_pool = vol.get('poolpath')
|
||||
if poolpath['InstanceID'] == vol_pool:
|
||||
inpool.append(vol)
|
||||
|
||||
return inpool
|
||||
|
||||
def _assocnames_pool(self, volumepath):
|
||||
poollist = self._enum_pool_details('RAID')
|
||||
poollist += self._enum_pool_details('TPP')
|
||||
volpool = []
|
||||
for pool in poollist:
|
||||
if volumepath['poolpath'] == pool['InstanceID']:
|
||||
volpool.append(pool)
|
||||
|
||||
return volpool
|
||||
|
||||
def _default_assocnames(self, objectpath):
|
||||
return objectpath
|
||||
|
||||
@ -520,34 +645,48 @@ class FakeEternusConnection(object):
|
||||
def _enum_pool_details(self, pooltype):
|
||||
pools = []
|
||||
pool = FJ_StoragePool()
|
||||
pool2 = FJ_StoragePool()
|
||||
|
||||
if pooltype == 'RAID':
|
||||
pool['InstanceID'] = 'FUJITSU:RSP0004'
|
||||
pool['CreationClassName'] = 'FUJITSU_RAIDStoragePool'
|
||||
pool['ElementName'] = 'abcd1234_OSVD'
|
||||
pool['TotalManagedSpace'] = 1170368102400
|
||||
pool['RemainingManagedSpace'] = 1170368102400
|
||||
pool.path = pool
|
||||
pool['RemainingManagedSpace'] = 1170368102400 - 1 * units.Gi
|
||||
pool.path = FJ_StoragePool()
|
||||
pool.path['InstanceID'] = 'FUJITSU:RSP0004'
|
||||
pool.path.classname = 'FUJITSU_RAIDStoragePool'
|
||||
pools.append(pool)
|
||||
|
||||
pool2['InstanceID'] = 'FUJITSU:RSP0005'
|
||||
pool2['CreationClassName'] = 'FUJITSU_RAIDStoragePool'
|
||||
pool2['ElementName'] = 'abcd1234_RG'
|
||||
pool2['TotalManagedSpace'] = 1170368102400
|
||||
pool2['RemainingManagedSpace'] = 1170368102400 - 1 * units.Gi
|
||||
pool2.path = FJ_StoragePool()
|
||||
pool2.path['InstanceID'] = 'FUJITSU:RSP0005'
|
||||
pool2.path.classname = 'FUJITSU_RAIDStoragePool'
|
||||
pools.append(pool2)
|
||||
else:
|
||||
pool = FJ_StoragePool()
|
||||
pool['InstanceID'] = 'FUJITSU:TPP0004'
|
||||
pool['CreationClassName'] = 'FUJITSU_ThinProvisioningPool'
|
||||
pool['ElementName'] = 'abcd1234_TPP'
|
||||
pool['TotalManagedSpace'] = 1170368102400
|
||||
pool['RemainingManagedSpace'] = 1170368102400
|
||||
pool.path = pool
|
||||
pool['RemainingManagedSpace'] = 1170368102400 - 2 * units.Gi
|
||||
pool.path = FJ_StoragePool()
|
||||
pool.path['InstanceID'] = 'FUJITSU:TPP0004'
|
||||
pool.path.classname = 'FUJITSU_ThinProvisioningPool'
|
||||
|
||||
pools.append(pool)
|
||||
|
||||
return pools
|
||||
|
||||
def _enum_volumes(self):
|
||||
def _enum_volumes(self, force=False):
|
||||
volumes = []
|
||||
if VOL_STAT == '0':
|
||||
if VOL_STAT == '0' and not force:
|
||||
return volumes
|
||||
volume = FJ_StorageVolume()
|
||||
volume['name'] = TEST_VOLUME['name']
|
||||
volume['poolpath'] = 'FUJITSU:TPP0004'
|
||||
volume['CreationClassName'] = 'FUJITSU_StorageVolume'
|
||||
volume['Name'] = FAKE_LUN_ID1
|
||||
volume['DeviceID'] = FAKE_LUN_ID1
|
||||
@ -558,20 +697,46 @@ class FakeEternusConnection(object):
|
||||
volume.path = volume
|
||||
volume.path.classname = volume['CreationClassName']
|
||||
|
||||
name = {}
|
||||
name['classname'] = 'FUJITSU_StorageVolume'
|
||||
keys = {}
|
||||
keys['CreationClassName'] = 'FUJITSU_StorageVolume'
|
||||
keys['SystemName'] = STORAGE_SYSTEM
|
||||
keys['DeviceID'] = volume['DeviceID']
|
||||
keys['SystemCreationClassName'] = 'FUJITSU_StorageComputerSystem'
|
||||
name['keybindings'] = keys
|
||||
name = {
|
||||
'classname': 'FUJITSU_StorageVolume',
|
||||
'keybindings': {
|
||||
'CreationClassName': 'FUJITSU_StorageVolume',
|
||||
'SystemName': STORAGE_SYSTEM,
|
||||
'DeviceID': volume['DeviceID'],
|
||||
'SystemCreationClassName': 'FUJITSU_StorageComputerSystem',
|
||||
},
|
||||
}
|
||||
volume['provider_location'] = str(name)
|
||||
|
||||
volumes.append(volume)
|
||||
|
||||
volume3 = FJ_StorageVolume()
|
||||
volume3['name'] = TEST_VOLUME2['name']
|
||||
volume3['poolpath'] = 'FUJITSU:RSP0005'
|
||||
volume3['CreationClassName'] = 'FUJITSU_StorageVolume'
|
||||
volume3['Name'] = FAKE_LUN_ID3
|
||||
volume3['DeviceID'] = FAKE_LUN_ID3
|
||||
volume3['SystemCreationClassName'] = 'FUJITSU_StorageComputerSystem'
|
||||
volume3['SystemName'] = STORAGE_SYSTEM
|
||||
volume3['ElementName'] = 'FJosv_4whcadwDac7ANKHA2O719A=='
|
||||
volume3['volume_type_id'] = None
|
||||
volume3.path = volume3
|
||||
volume3.path.classname = volume3['CreationClassName']
|
||||
|
||||
name3 = {
|
||||
'classname': 'FUJITSU_StorageVolume',
|
||||
'keybindings': {
|
||||
'CreationClassName': 'FUJITSU_StorageVolume',
|
||||
'SystemName': STORAGE_SYSTEM,
|
||||
'DeviceID': volume3['DeviceID'],
|
||||
'SystemCreationClassName': 'FUJITSU_StorageComputerSystem',
|
||||
},
|
||||
}
|
||||
volume3['provider_location'] = str(name3)
|
||||
volumes.append(volume3)
|
||||
|
||||
snap_vol = FJ_StorageVolume()
|
||||
snap_vol['name'] = TEST_SNAP['name']
|
||||
snap_vol['poolpath'] = 'FUJITSU:RSP0004'
|
||||
snap_vol['CreationClassName'] = 'FUJITSU_StorageVolume'
|
||||
snap_vol['Name'] = FAKE_LUN_ID2
|
||||
snap_vol['DeviceID'] = FAKE_LUN_ID2
|
||||
@ -581,20 +746,21 @@ class FakeEternusConnection(object):
|
||||
snap_vol.path = snap_vol
|
||||
snap_vol.path.classname = snap_vol['CreationClassName']
|
||||
|
||||
name2 = {}
|
||||
name2['classname'] = 'FUJITSU_StorageVolume'
|
||||
keys2 = {}
|
||||
keys2['CreationClassName'] = 'FUJITSU_StorageVolume'
|
||||
keys2['SystemName'] = STORAGE_SYSTEM
|
||||
keys2['DeviceID'] = snap_vol['DeviceID']
|
||||
keys2['SystemCreationClassName'] = 'FUJITSU_StorageComputerSystem'
|
||||
name2['keybindings'] = keys2
|
||||
name2 = {
|
||||
'classname': 'FUJITSU_StorageVolume',
|
||||
'keybindings': {
|
||||
'CreationClassName': 'FUJITSU_StorageVolume',
|
||||
'SystemName': STORAGE_SYSTEM,
|
||||
'DeviceID': snap_vol['DeviceID'],
|
||||
'SystemCreationClassName': 'FUJITSU_StorageComputerSystem',
|
||||
},
|
||||
}
|
||||
snap_vol['provider_location'] = str(name2)
|
||||
|
||||
volumes.append(snap_vol)
|
||||
|
||||
clone_vol = FJ_StorageVolume()
|
||||
clone_vol['name'] = TEST_CLONE['name']
|
||||
clone_vol['poolpath'] = 'FUJITSU:TPP0004'
|
||||
clone_vol['CreationClassName'] = 'FUJITSU_StorageVolume'
|
||||
clone_vol['ElementName'] = TEST_CLONE['name']
|
||||
clone_vol['DeviceID'] = FAKE_LUN_ID2
|
||||
@ -678,7 +844,6 @@ class FakeEternusConnection(object):
|
||||
return targetlist
|
||||
|
||||
def _getinstance_storagevolume(self, objpath):
|
||||
foundinstance = None
|
||||
instance = FJ_StorageVolume()
|
||||
volumes = self._enum_volumes()
|
||||
for volume in volumes:
|
||||
@ -713,6 +878,8 @@ class FJFCDriverTestCase(test.TestCase):
|
||||
# Make fake Object by using mock as configuration object.
|
||||
self.configuration = mock.Mock(spec=conf.Configuration)
|
||||
self.configuration.cinder_eternus_config_file = self.config_file.name
|
||||
self.configuration.safe_get = self.fake_safe_get
|
||||
self.configuration.max_over_subscription_ratio = '20.0'
|
||||
|
||||
self.mock_object(dx_common.FJDXCommon, '_get_eternus_connection',
|
||||
self.fake_eternus_connection)
|
||||
@ -725,22 +892,27 @@ class FJFCDriverTestCase(test.TestCase):
|
||||
driver = dx_fc.FJDXFCDriver(configuration=self.configuration)
|
||||
self.driver = driver
|
||||
|
||||
def fake_safe_get(self, str=None):
|
||||
return str
|
||||
|
||||
def fake_eternus_connection(self):
|
||||
conn = FakeEternusConnection()
|
||||
return conn
|
||||
|
||||
def test_get_volume_stats(self):
|
||||
ret = self.driver.get_volume_stats(True)
|
||||
stats = {'vendor_name': ret['vendor_name'],
|
||||
'total_capacity_gb': ret['total_capacity_gb'],
|
||||
'free_capacity_gb': ret['free_capacity_gb']}
|
||||
self.assertEqual(FAKE_STATS, stats)
|
||||
|
||||
self.assertEqual(FAKE_STATS2, ret)
|
||||
|
||||
def test_create_and_delete_volume(self):
|
||||
model_info = self.driver.create_volume(TEST_VOLUME)
|
||||
self.assertEqual(FAKE_MODEL_INFO1, model_info)
|
||||
|
||||
model_info = self.driver.create_volume(TEST_VOLUME2)
|
||||
self.assertEqual(FAKE_MODEL_INFO3, model_info)
|
||||
|
||||
self.driver.delete_volume(TEST_VOLUME)
|
||||
self.driver.delete_volume(TEST_VOLUME2)
|
||||
|
||||
@mock.patch.object(dx_common.FJDXCommon, '_get_mapdata')
|
||||
def test_map_unmap(self, mock_mapdata):
|
||||
@ -816,7 +988,16 @@ class FJFCDriverTestCase(test.TestCase):
|
||||
model_info = self.driver.create_volume(TEST_VOLUME)
|
||||
self.assertEqual(FAKE_MODEL_INFO1, model_info)
|
||||
|
||||
self.driver.extend_volume(TEST_VOLUME, 10)
|
||||
volume_info = {}
|
||||
for key in TEST_VOLUME:
|
||||
if key == 'provider_location':
|
||||
volume_info[key] = model_info[key]
|
||||
elif key == 'metadata':
|
||||
volume_info[key] = model_info[key]
|
||||
else:
|
||||
volume_info[key] = TEST_VOLUME[key]
|
||||
|
||||
self.driver.extend_volume(volume_info, 10)
|
||||
|
||||
|
||||
class FJISCSIDriverTestCase(test.TestCase):
|
||||
@ -835,6 +1016,8 @@ class FJISCSIDriverTestCase(test.TestCase):
|
||||
# Make fake Object by using mock as configuration object.
|
||||
self.configuration = mock.Mock(spec=conf.Configuration)
|
||||
self.configuration.cinder_eternus_config_file = self.config_file.name
|
||||
self.configuration.safe_get = self.fake_safe_get
|
||||
self.configuration.max_over_subscription_ratio = '20.0'
|
||||
|
||||
self.mock_object(dx_common.FJDXCommon, '_get_eternus_connection',
|
||||
self.fake_eternus_connection)
|
||||
@ -850,6 +1033,9 @@ class FJISCSIDriverTestCase(test.TestCase):
|
||||
driver = dx_iscsi.FJDXISCSIDriver(configuration=self.configuration)
|
||||
self.driver = driver
|
||||
|
||||
def fake_safe_get(self, str=None):
|
||||
return str
|
||||
|
||||
def fake_eternus_connection(self):
|
||||
conn = FakeEternusConnection()
|
||||
return conn
|
||||
@ -867,16 +1053,18 @@ class FJISCSIDriverTestCase(test.TestCase):
|
||||
|
||||
def test_get_volume_stats(self):
|
||||
ret = self.driver.get_volume_stats(True)
|
||||
stats = {'vendor_name': ret['vendor_name'],
|
||||
'total_capacity_gb': ret['total_capacity_gb'],
|
||||
'free_capacity_gb': ret['free_capacity_gb']}
|
||||
self.assertEqual(FAKE_STATS, stats)
|
||||
|
||||
self.assertEqual(FAKE_STATS, ret)
|
||||
|
||||
def test_create_and_delete_volume(self):
|
||||
model_info = self.driver.create_volume(TEST_VOLUME)
|
||||
self.assertEqual(FAKE_MODEL_INFO1, model_info)
|
||||
|
||||
model_info = self.driver.create_volume(TEST_VOLUME2)
|
||||
self.assertEqual(FAKE_MODEL_INFO3, model_info)
|
||||
|
||||
self.driver.delete_volume(TEST_VOLUME)
|
||||
self.driver.delete_volume(TEST_VOLUME2)
|
||||
|
||||
def test_map_unmap(self):
|
||||
fake_mapdata = self.fake_get_mapdata(None, {}, None)
|
||||
@ -942,4 +1130,13 @@ class FJISCSIDriverTestCase(test.TestCase):
|
||||
model_info = self.driver.create_volume(TEST_VOLUME)
|
||||
self.assertEqual(FAKE_MODEL_INFO1, model_info)
|
||||
|
||||
self.driver.extend_volume(TEST_VOLUME, 10)
|
||||
volume_info = {}
|
||||
for key in TEST_VOLUME:
|
||||
if key == 'provider_location':
|
||||
volume_info[key] = model_info[key]
|
||||
elif key == 'metadata':
|
||||
volume_info[key] = model_info[key]
|
||||
else:
|
||||
volume_info[key] = TEST_VOLUME[key]
|
||||
|
||||
self.driver.extend_volume(volume_info, 10)
|
||||
|
@ -42,6 +42,10 @@ POOL_TYPE_dic = {
|
||||
RAIDGROUP: 'RAID_GROUP',
|
||||
TPPOOL: 'Thinporvisioning_POOL',
|
||||
}
|
||||
POOL_TYPE_list = [
|
||||
'RAID',
|
||||
'TPP'
|
||||
]
|
||||
OPERATION_dic = {
|
||||
SNAPOPC: RETURN_TO_RESOURCEPOOL,
|
||||
OPC: DETACH,
|
||||
|
@ -36,6 +36,7 @@ from cinder.i18n import _
|
||||
from cinder import utils
|
||||
from cinder.volume import configuration as conf
|
||||
from cinder.volume.drivers.fujitsu.eternus_dx import constants as CONSTANTS
|
||||
from cinder.volume import volume_utils
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
CONF = cfg.CONF
|
||||
@ -61,10 +62,7 @@ class FJDXCommon(object):
|
||||
VERSION = "1.3.0"
|
||||
stats = {
|
||||
'driver_version': VERSION,
|
||||
'free_capacity_gb': 0,
|
||||
'reserved_percentage': 0,
|
||||
'storage_protocol': None,
|
||||
'total_capacity_gb': 0,
|
||||
'vendor_name': 'FUJITSU',
|
||||
'QoS_support': False,
|
||||
'volume_backend_name': None,
|
||||
@ -82,6 +80,7 @@ class FJDXCommon(object):
|
||||
# Get iSCSI ipaddress from driver configuration file.
|
||||
self.configuration.iscsi_ip_address = (
|
||||
self._get_drvcfg('EternusISCSIIP'))
|
||||
self.conn = None
|
||||
|
||||
@staticmethod
|
||||
def get_driver_options():
|
||||
@ -103,7 +102,7 @@ class FJDXCommon(object):
|
||||
'volumesize': volumesize})
|
||||
|
||||
# get poolname from driver configuration file
|
||||
eternus_pool = self._get_drvcfg('EternusPool')
|
||||
eternus_pool = volume_utils.extract_host(volume['host'], 'pool')
|
||||
# Existence check the pool
|
||||
pool = self._find_pool(eternus_pool)
|
||||
|
||||
@ -216,6 +215,47 @@ class FJDXCommon(object):
|
||||
|
||||
return (element_path, metadata)
|
||||
|
||||
def create_pool_info(self, pool_instance, volume_count, pool_type):
|
||||
"""Create pool information from pool instance."""
|
||||
LOG.debug('create_pool_info, pool_instance: %(pool)s, '
|
||||
'volume_count: %(volcount)s, pool_type: %(ptype)s.',
|
||||
{'pool': pool_instance,
|
||||
'volcount': volume_count, 'ptype': pool_type})
|
||||
|
||||
if pool_type not in CONSTANTS.POOL_TYPE_list:
|
||||
msg = (_('Invalid pool type was specified : %s.') % pool_type)
|
||||
LOG.error(msg)
|
||||
raise exception.VolumeBackendAPIException(data=msg)
|
||||
|
||||
total_gb = pool_instance['TotalManagedSpace'] / units.Gi
|
||||
free_gb = pool_instance['RemainingManagedSpace'] / units.Gi
|
||||
|
||||
if hasattr(pool_instance, 'provisioned_capacity_gb'):
|
||||
prov_gb = pool_instance.provisioned_capacity_gb
|
||||
else:
|
||||
prov_gb = total_gb - free_gb
|
||||
|
||||
if pool_type == 'RAID':
|
||||
useable_gb = free_gb
|
||||
else:
|
||||
max_capacity = total_gb * float(
|
||||
self.configuration.max_over_subscription_ratio)
|
||||
useable_gb = max_capacity - prov_gb
|
||||
|
||||
pool = {
|
||||
'name': pool_instance['ElementName'],
|
||||
'path': pool_instance.path,
|
||||
'total_capacity_gb': total_gb,
|
||||
'free_capacity_gb': free_gb,
|
||||
'type': pool_type,
|
||||
'volume_count': volume_count,
|
||||
'provisioned_capacity_gb': prov_gb,
|
||||
'useable_capacity_gb': useable_gb
|
||||
}
|
||||
|
||||
LOG.debug('create_pool_info, pool: %s.', pool)
|
||||
return pool
|
||||
|
||||
def create_volume_from_snapshot(self, volume, snapshot):
|
||||
"""Creates a volume from a snapshot."""
|
||||
LOG.debug('create_volume_from_snapshot, '
|
||||
@ -716,14 +756,12 @@ class FJDXCommon(object):
|
||||
'vol_instance': vol_instance.path})
|
||||
|
||||
# Get poolname from driver configuration file.
|
||||
eternus_pool = self._get_drvcfg('EternusPool')
|
||||
# Check the existence of volume.
|
||||
pool = self._find_pool(eternus_pool)
|
||||
pool_name, pool = self._find_pool_from_volume(vol_instance)
|
||||
if pool is None:
|
||||
msg = (_('extend_volume, '
|
||||
'eternus_pool: %(eternus_pool)s, '
|
||||
'pool not found.')
|
||||
% {'eternus_pool': eternus_pool})
|
||||
% {'eternus_pool': pool_name})
|
||||
LOG.error(msg)
|
||||
raise exception.VolumeBackendAPIException(data=msg)
|
||||
|
||||
@ -734,14 +772,14 @@ class FJDXCommon(object):
|
||||
pooltype = CONSTANTS.TPPOOL
|
||||
|
||||
configservice = self._find_eternus_service(CONSTANTS.STOR_CONF)
|
||||
if configservice is None:
|
||||
if not configservice:
|
||||
msg = (_('extend_volume, volume: %(volume)s, '
|
||||
'volumename: %(volumename)s, '
|
||||
'eternus_pool: %(eternus_pool)s, '
|
||||
'Storage Configuration Service not found.')
|
||||
% {'volume': volume,
|
||||
'volumename': volumename,
|
||||
'eternus_pool': eternus_pool})
|
||||
'eternus_pool': pool_name})
|
||||
LOG.error(msg)
|
||||
raise exception.VolumeBackendAPIException(data=msg)
|
||||
|
||||
@ -755,7 +793,7 @@ class FJDXCommon(object):
|
||||
'TheElement: %(vol_instance)s.',
|
||||
{'service': configservice,
|
||||
'volumename': volumename,
|
||||
'eternus_pool': eternus_pool,
|
||||
'eternus_pool': pool_name,
|
||||
'pooltype': pooltype,
|
||||
'volumesize': volumesize,
|
||||
'vol_instance': vol_instance.path})
|
||||
@ -793,48 +831,21 @@ class FJDXCommon(object):
|
||||
{'volumename': volumename,
|
||||
'rc': rc,
|
||||
'errordesc': errordesc,
|
||||
'eternus_pool': eternus_pool,
|
||||
'eternus_pool': pool_name,
|
||||
'pooltype': CONSTANTS.POOL_TYPE_dic[pooltype]})
|
||||
|
||||
return eternus_pool
|
||||
return pool_name
|
||||
|
||||
@lockutils.synchronized('ETERNUS-update', 'cinder-', True)
|
||||
def update_volume_stats(self):
|
||||
"""get pool capacity."""
|
||||
"""Get pool capacity."""
|
||||
|
||||
self.conn = self._get_eternus_connection()
|
||||
eternus_pool = self._get_drvcfg('EternusPool')
|
||||
|
||||
LOG.debug('update_volume_stats, pool name: %s.', eternus_pool)
|
||||
poolname_list = self._get_drvcfg('EternusPool', multiple=True)
|
||||
self._find_pools(poolname_list, self.conn)
|
||||
|
||||
pool = self._find_pool(eternus_pool, True)
|
||||
if pool:
|
||||
# pool is found
|
||||
self.stats['total_capacity_gb'] = (
|
||||
pool['TotalManagedSpace'] / units.Gi)
|
||||
|
||||
self.stats['free_capacity_gb'] = (
|
||||
pool['RemainingManagedSpace'] / units.Gi)
|
||||
else:
|
||||
# if pool information is unknown, set 0 GB to capacity information
|
||||
LOG.warning('update_volume_stats, '
|
||||
'eternus_pool:%(eternus_pool)s, '
|
||||
'specified pool is not found.',
|
||||
{'eternus_pool': eternus_pool})
|
||||
self.stats['total_capacity_gb'] = 0
|
||||
self.stats['free_capacity_gb'] = 0
|
||||
|
||||
self.stats['multiattach'] = False
|
||||
|
||||
LOG.debug('update_volume_stats, '
|
||||
'eternus_pool:%(eternus_pool)s, '
|
||||
'total capacity[%(total)s], '
|
||||
'free capacity[%(free)s].',
|
||||
{'eternus_pool': eternus_pool,
|
||||
'total': self.stats['total_capacity_gb'],
|
||||
'free': self.stats['free_capacity_gb']})
|
||||
|
||||
return (self.stats, eternus_pool)
|
||||
return (self.stats, poolname_list)
|
||||
|
||||
def _get_mapdata(self, vol_instance, connector, target_portlist):
|
||||
"""return mapping information."""
|
||||
@ -1012,9 +1023,9 @@ class FJDXCommon(object):
|
||||
return mapdata
|
||||
|
||||
def _get_drvcfg(self, tagname, filename=None, multiple=False):
|
||||
"""read from driver configuration file."""
|
||||
if filename is None:
|
||||
# set default configuration file name
|
||||
"""Read from driver configuration file."""
|
||||
if not filename:
|
||||
# Set default configuration file name.
|
||||
filename = self.configuration.cinder_eternus_config_file
|
||||
|
||||
LOG.debug("_get_drvcfg, input[%(filename)s][%(tagname)s].",
|
||||
@ -1023,7 +1034,6 @@ class FJDXCommon(object):
|
||||
tree = ET.parse(filename)
|
||||
elem = tree.getroot()
|
||||
|
||||
ret = None
|
||||
if not multiple:
|
||||
ret = elem.findtext(".//" + tagname)
|
||||
else:
|
||||
@ -1141,6 +1151,116 @@ class FJDXCommon(object):
|
||||
LOG.debug('_find_pool, pool: %s.', ret)
|
||||
return ret
|
||||
|
||||
def _find_pools(self, poolname_list, conn):
|
||||
"""Find Instance or InstanceName of pool by pool name on ETERNUS."""
|
||||
LOG.debug('_find_pool, pool name: %s.', poolname_list)
|
||||
|
||||
target_poolname = list(poolname_list)
|
||||
pools = []
|
||||
|
||||
# Get pools info form CIM instance(include info about instance path).
|
||||
try:
|
||||
tppoollist = self._enum_eternus_instances(
|
||||
'FUJITSU_ThinProvisioningPool', conn=conn)
|
||||
rgpoollist = self._enum_eternus_instances(
|
||||
'FUJITSU_RAIDStoragePool', conn=conn)
|
||||
except Exception:
|
||||
msg = (_('_find_pool, '
|
||||
'eternus_pool:%(eternus_pool)s, '
|
||||
'EnumerateInstances, '
|
||||
'cannot connect to ETERNUS.')
|
||||
% {'eternus_pool': target_poolname})
|
||||
LOG.error(msg)
|
||||
raise exception.VolumeBackendAPIException(data=msg)
|
||||
|
||||
# Make total pools list.
|
||||
tppools = [(tppool, 'TPP') for tppool in tppoollist]
|
||||
rgpools = [(rgpool, 'RAID') for rgpool in rgpoollist]
|
||||
poollist = tppools + rgpools
|
||||
|
||||
# One eternus backend has only one special pool name
|
||||
# so just use pool name can get the target pool.
|
||||
for pool, ptype in poollist:
|
||||
poolname = pool['ElementName']
|
||||
|
||||
LOG.debug('_find_pools, '
|
||||
'pool: %(pool)s, ptype: %(ptype)s.',
|
||||
{'pool': poolname, 'ptype': ptype})
|
||||
if poolname in target_poolname:
|
||||
try:
|
||||
volume_list = self._assoc_eternus_names(
|
||||
pool.path,
|
||||
conn=conn,
|
||||
AssocClass='FUJITSU_AllocatedFromStoragePool',
|
||||
ResultClass='FUJITSU_StorageVolume')
|
||||
|
||||
volume_count = len(volume_list)
|
||||
except Exception:
|
||||
msg = (_('_find_pools, '
|
||||
'poolname: %(poolname)s, '
|
||||
'pooltype: %(ptype)s, '
|
||||
'Associator Names, '
|
||||
'cannot connect to ETERNUS.')
|
||||
% {'ptype': ptype,
|
||||
'poolname': poolname})
|
||||
LOG.error(msg)
|
||||
raise exception.VolumeBackendAPIException(data=msg)
|
||||
|
||||
poolinfo = self.create_pool_info(pool, volume_count, ptype)
|
||||
|
||||
target_poolname.remove(poolname)
|
||||
pools.append((poolinfo, poolname))
|
||||
|
||||
if not target_poolname:
|
||||
break
|
||||
|
||||
if not pools:
|
||||
LOG.warning('_find_pools, all the EternusPools in driver '
|
||||
'configuration file do not exist. '
|
||||
'Please edit the driver configuration file '
|
||||
'to include EternusPool names.')
|
||||
|
||||
# Sort pools in the order defined in driver configuration file.
|
||||
sorted_pools = (
|
||||
[pool for name in poolname_list for pool, pname in pools
|
||||
if name == pname])
|
||||
|
||||
LOG.debug('_find_pools, '
|
||||
'pools: %(pools)s, '
|
||||
'notfound_pools: %(notfound_pools)s.',
|
||||
{'pools': pools,
|
||||
'notfound_pools': target_poolname})
|
||||
pools_stats = {'pools': []}
|
||||
for pool in sorted_pools:
|
||||
single_pool = {}
|
||||
if pool['type'] == 'TPP':
|
||||
thin_enabled = True
|
||||
max_ratio = self.configuration.max_over_subscription_ratio
|
||||
else:
|
||||
thin_enabled = False
|
||||
max_ratio = 1
|
||||
|
||||
single_pool.update(dict(
|
||||
path=pool['path'],
|
||||
pool_name=pool['name'],
|
||||
total_capacity_gb=pool['total_capacity_gb'],
|
||||
total_volumes=pool['volume_count'],
|
||||
free_capacity_gb=pool['free_capacity_gb'],
|
||||
provisioned_capacity_gb=pool['provisioned_capacity_gb'],
|
||||
useable_capacity_gb=pool['useable_capacity_gb'],
|
||||
thin_provisioning_support=thin_enabled,
|
||||
thick_provisioning_support=not thin_enabled,
|
||||
max_over_subscription_ratio=max_ratio,
|
||||
))
|
||||
single_pool['multiattach'] = False
|
||||
pools_stats['pools'].append(single_pool)
|
||||
|
||||
self.stats['shared_targets'] = True
|
||||
self.stats['backend_state'] = 'up'
|
||||
self.stats['pools'] = pools_stats['pools']
|
||||
|
||||
return self.stats, target_poolname
|
||||
|
||||
def _find_eternus_service(self, classname):
|
||||
"""find CIM instance about service information."""
|
||||
LOG.debug('_find_eternus_service, '
|
||||
@ -1176,7 +1296,8 @@ class FJDXCommon(object):
|
||||
{'a': classname,
|
||||
'b': instanceNameList,
|
||||
'c': param_dict})
|
||||
|
||||
rc = None
|
||||
retdata = None
|
||||
# Use InvokeMethod.
|
||||
try:
|
||||
rc, retdata = self.conn.InvokeMethod(
|
||||
@ -1223,11 +1344,14 @@ class FJDXCommon(object):
|
||||
|
||||
@lockutils.synchronized('ETERNUS-SMIS-other', 'cinder-', True)
|
||||
@utils.retry(exception.VolumeBackendAPIException)
|
||||
def _enum_eternus_instances(self, classname):
|
||||
def _enum_eternus_instances(self, classname, conn=None, **param_dict):
|
||||
"""Enumerate Instances."""
|
||||
LOG.debug('_enum_eternus_instances, classname: %s.', classname)
|
||||
|
||||
ret = self.conn.EnumerateInstances(classname)
|
||||
if not conn:
|
||||
conn = self.conn
|
||||
|
||||
ret = conn.EnumerateInstances(classname, **param_dict)
|
||||
|
||||
LOG.debug('_enum_eternus_instances, enum %d instances.', len(ret))
|
||||
return ret
|
||||
@ -1258,26 +1382,32 @@ class FJDXCommon(object):
|
||||
|
||||
@lockutils.synchronized('ETERNUS-SMIS-other', 'cinder-', True)
|
||||
@utils.retry(exception.VolumeBackendAPIException)
|
||||
def _assoc_eternus(self, classname, **param_dict):
|
||||
def _assoc_eternus(self, classname, conn=None, **param_dict):
|
||||
"""Associator."""
|
||||
LOG.debug('_assoc_eternus, '
|
||||
'classname: %(cls)s, param: %(param)s.',
|
||||
{'cls': classname, 'param': param_dict})
|
||||
|
||||
ret = self.conn.Associators(classname, **param_dict)
|
||||
if not conn:
|
||||
conn = self.conn
|
||||
|
||||
ret = conn.Associators(classname, **param_dict)
|
||||
|
||||
LOG.debug('_assoc_eternus, enum %d instances.', len(ret))
|
||||
return ret
|
||||
|
||||
@lockutils.synchronized('ETERNUS-SMIS-other', 'cinder-', True)
|
||||
@utils.retry(exception.VolumeBackendAPIException)
|
||||
def _assoc_eternus_names(self, classname, **param_dict):
|
||||
def _assoc_eternus_names(self, classname, conn=None, **param_dict):
|
||||
"""Associator Names."""
|
||||
LOG.debug('_assoc_eternus_names, '
|
||||
'classname: %(cls)s, param: %(param)s.',
|
||||
{'cls': classname, 'param': param_dict})
|
||||
|
||||
ret = self.conn.AssociatorNames(classname, **param_dict)
|
||||
if not conn:
|
||||
conn = self.conn
|
||||
|
||||
ret = conn.AssociatorNames(classname, **param_dict)
|
||||
|
||||
LOG.debug('_assoc_eternus_names, enum %d names.', len(ret))
|
||||
return ret
|
||||
@ -2103,3 +2233,66 @@ class FJDXCommon(object):
|
||||
result = num
|
||||
|
||||
return result
|
||||
|
||||
def _find_pool_from_volume(self, vol_instance, manage_type='volume'):
|
||||
"""Find Instance or InstanceName of pool by volume instance."""
|
||||
LOG.debug('_find_pool_from_volume, volume: %(volume)s.',
|
||||
{'volume': vol_instance})
|
||||
poolname = None
|
||||
target_pool = None
|
||||
filename = None
|
||||
conn = self.conn
|
||||
|
||||
# Get poolname of volume on Eternus.
|
||||
try:
|
||||
pools = self._assoc_eternus(
|
||||
vol_instance.path,
|
||||
conn=conn,
|
||||
AssocClass='FUJITSU_AllocatedFromStoragePool',
|
||||
ResultClass='CIM_StoragePool')
|
||||
except Exception:
|
||||
msg = (_('_find_pool_from_volume, '
|
||||
'vol_instance: %s, '
|
||||
'Associators: FUJITSU_AllocatedFromStoragePool, '
|
||||
'cannot connect to ETERNUS.')
|
||||
% vol_instance.path)
|
||||
LOG.error(msg)
|
||||
raise exception.VolumeBackendAPIException(data=msg)
|
||||
|
||||
if not pools:
|
||||
msg = (_('_find_pool_from_volume, '
|
||||
'vol_instance: %s, '
|
||||
'pool not found.')
|
||||
% vol_instance.path)
|
||||
LOG.error(msg)
|
||||
raise exception.VolumeBackendAPIException(data=msg)
|
||||
|
||||
# Get poolname from driver configuration file.
|
||||
if manage_type == 'volume':
|
||||
cfgpool_list = list(self._get_drvcfg('EternusPool',
|
||||
filename=filename,
|
||||
multiple=True))
|
||||
elif manage_type == 'snapshot':
|
||||
cfgpool_list = list(self._get_drvcfg('EternusSnapPool',
|
||||
filename=filename,
|
||||
multiple=True))
|
||||
LOG.debug('_find_pool_from_volume, cfgpool_list: %(cfgpool_list)s.',
|
||||
{'cfgpool_list': cfgpool_list})
|
||||
for pool in pools:
|
||||
if pool['ElementName'] in cfgpool_list:
|
||||
poolname = pool['ElementName']
|
||||
target_pool = pool.path
|
||||
break
|
||||
|
||||
if not target_pool:
|
||||
msg = (_('_find_pool_from_volume, '
|
||||
'vol_instance: %s, '
|
||||
'the pool of volume not in driver configuration file.')
|
||||
% vol_instance.path)
|
||||
LOG.error(msg)
|
||||
raise exception.VolumeBackendAPIException(data=msg)
|
||||
|
||||
LOG.debug('_find_pool_from_volume, poolname: %(poolname)s, '
|
||||
'target_pool: %(target_pool)s.',
|
||||
{'poolname': poolname, 'target_pool': target_pool})
|
||||
return poolname, target_pool
|
||||
|
@ -132,6 +132,8 @@ Configuration
|
||||
<EternusUser>smisuser</EternusUser>
|
||||
<EternusPassword>smispassword</EternusPassword>
|
||||
<EternusPool>raid5_0001</EternusPool>
|
||||
<EternusPool>tpp_0001</EternusPool>
|
||||
<EternusPool>raid_0002</EternusPool>
|
||||
<EternusSnapPool>raid5_0001</EternusSnapPool>
|
||||
</FUJITSU>
|
||||
|
||||
@ -146,6 +148,8 @@ Configuration
|
||||
<EternusUser>smisuser</EternusUser>
|
||||
<EternusPassword>smispassword</EternusPassword>
|
||||
<EternusPool>raid5_0001</EternusPool>
|
||||
<EternusPool>tpp_0001</EternusPool>
|
||||
<EternusPool>raid_0002</EternusPool>
|
||||
<EternusSnapPool>raid5_0001</EternusSnapPool>
|
||||
<EternusISCSIIP>1.1.1.1</EternusISCSIIP>
|
||||
<EternusISCSIIP>1.1.1.2</EternusISCSIIP>
|
||||
@ -169,7 +173,7 @@ Configuration
|
||||
``EternusPassword``
|
||||
Password for the SMI-S connection of the ETERNUS DX.
|
||||
|
||||
``EternusPool``
|
||||
``EternusPool`` (Multiple setting allowed)
|
||||
Storage pool name for volumes.
|
||||
|
||||
Enter RAID Group name or TPP name in the ETERNUS DX.
|
||||
@ -188,6 +192,8 @@ Configuration
|
||||
and cannot specify TPP name.
|
||||
* You can specify the same RAID Group name for ``EternusPool`` and ``EternusSnapPool``
|
||||
if you create volumes and snapshots on a same storage pool.
|
||||
* For ``EternusPool``, when multiple pools are specified,
|
||||
cinder-scheduler will select one from multiple pools to create the volume.
|
||||
|
||||
Configuration example
|
||||
~~~~~~~~~~~~~~~~~~~~~
|
||||
|
@ -0,0 +1,4 @@
|
||||
---
|
||||
features:
|
||||
- |
|
||||
Fujitsu Driver: Added multiple pools support.
|
Loading…
Reference in New Issue
Block a user