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>
|
<EternusPassword>testpass</EternusPassword>
|
||||||
<EternusISCSIIP>10.0.0.3</EternusISCSIIP>
|
<EternusISCSIIP>10.0.0.3</EternusISCSIIP>
|
||||||
<EternusPool>abcd1234_TPP</EternusPool>
|
<EternusPool>abcd1234_TPP</EternusPool>
|
||||||
|
<EternusPool>abcd1234_RG</EternusPool>
|
||||||
<EternusSnapPool>abcd1234_OSVD</EternusSnapPool>
|
<EternusSnapPool>abcd1234_OSVD</EternusSnapPool>
|
||||||
</FUJITSU>"""
|
</FUJITSU>"""
|
||||||
|
|
||||||
@ -51,8 +52,19 @@ TEST_VOLUME = {
|
|||||||
'name': 'volume1',
|
'name': 'volume1',
|
||||||
'display_name': 'volume1',
|
'display_name': 'volume1',
|
||||||
'provider_location': None,
|
'provider_location': None,
|
||||||
'volume_metadata': [],
|
'metadata': {},
|
||||||
'size': 1,
|
'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 = {
|
TEST_SNAP = {
|
||||||
@ -72,7 +84,8 @@ TEST_CLONE = {
|
|||||||
'project_id': 'project',
|
'project_id': 'project',
|
||||||
'display_name': 'clone1',
|
'display_name': 'clone1',
|
||||||
'display_description': 'volume created from snapshot',
|
'display_description': 'volume created from snapshot',
|
||||||
'volume_metadata': [],
|
'metadata': {},
|
||||||
|
'host': 'controller@113#abcd1234_TPP'
|
||||||
}
|
}
|
||||||
|
|
||||||
ISCSI_INITIATOR = 'iqn.1993-08.org.debian:01:8261afe17e4c'
|
ISCSI_INITIATOR = 'iqn.1993-08.org.debian:01:8261afe17e4c'
|
||||||
@ -96,24 +109,76 @@ AUTH_PRIV = 'FUJITSU_AuthorizedPrivilege'
|
|||||||
STOR_SYNC = 'FUJITSU_StorageSynchronized'
|
STOR_SYNC = 'FUJITSU_StorageSynchronized'
|
||||||
PROT_CTRL_UNIT = 'CIM_ProtocolControllerForUnit'
|
PROT_CTRL_UNIT = 'CIM_ProtocolControllerForUnit'
|
||||||
STORAGE_TYPE = 'abcd1234_TPP'
|
STORAGE_TYPE = 'abcd1234_TPP'
|
||||||
|
STORAGE_TYPE2 = 'abcd1234_RG'
|
||||||
LUNMASKCTRL_IDS = ['AFG0010_CM00CA00P00', 'AFG0011_CM01CA00P00']
|
LUNMASKCTRL_IDS = ['AFG0010_CM00CA00P00', 'AFG0011_CM01CA00P00']
|
||||||
|
|
||||||
MAP_STAT = '0'
|
MAP_STAT = '0'
|
||||||
VOL_STAT = '0'
|
VOL_STAT = '0'
|
||||||
|
|
||||||
FAKE_CAPACITY = 1170368102400
|
FAKE_CAPACITY = 1170368102400
|
||||||
|
# Volume1 in pool abcd1234_TPP
|
||||||
FAKE_LUN_ID1 = '600000E00D2A0000002A011500140000'
|
FAKE_LUN_ID1 = '600000E00D2A0000002A011500140000'
|
||||||
FAKE_LUN_NO1 = '0x0014'
|
FAKE_LUN_NO1 = '0x0014'
|
||||||
|
# Snapshot1 in pool abcd1234_OSVD
|
||||||
FAKE_LUN_ID2 = '600000E00D2A0000002A0115001E0000'
|
FAKE_LUN_ID2 = '600000E00D2A0000002A0115001E0000'
|
||||||
FAKE_LUN_NO2 = '0x001E'
|
FAKE_LUN_NO2 = '0x001E'
|
||||||
|
# Volume2 in pool abcd1234_RG
|
||||||
|
FAKE_LUN_ID3 = '600000E00D2800000028075301140000'
|
||||||
|
FAKE_LUN_NO3 = '0x0114'
|
||||||
FAKE_SYSTEM_NAME = 'ET603SA4621302115'
|
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 = {
|
FAKE_STATS = {
|
||||||
|
'driver_version': '1.3.0',
|
||||||
|
'storage_protocol': 'iSCSI',
|
||||||
'vendor_name': 'FUJITSU',
|
'vendor_name': 'FUJITSU',
|
||||||
'total_capacity_gb': FAKE_CAPACITY / units.Gi,
|
'QoS_support': False,
|
||||||
'free_capacity_gb': FAKE_CAPACITY / units.Gi,
|
'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 = {
|
FAKE_KEYBIND1 = {
|
||||||
'CreationClassName': 'FUJITSU_StorageVolume',
|
'CreationClassName': 'FUJITSU_StorageVolume',
|
||||||
'SystemName': STORAGE_SYSTEM,
|
'SystemName': STORAGE_SYSTEM,
|
||||||
@ -121,11 +186,27 @@ FAKE_KEYBIND1 = {
|
|||||||
'SystemCreationClassName': 'FUJITSU_StorageComputerSystem',
|
'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 = {
|
FAKE_LOCATION1 = {
|
||||||
'classname': 'FUJITSU_StorageVolume',
|
'classname': 'FUJITSU_StorageVolume',
|
||||||
'keybindings': FAKE_KEYBIND1,
|
'keybindings': FAKE_KEYBIND1,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Volume2
|
||||||
|
FAKE_LOCATION3 = {
|
||||||
|
'classname': 'FUJITSU_StorageVolume',
|
||||||
|
'keybindings': FAKE_KEYBIND3,
|
||||||
|
}
|
||||||
|
|
||||||
|
# Volume1 metadata info.
|
||||||
FAKE_LUN_META1 = {
|
FAKE_LUN_META1 = {
|
||||||
'FJ_Pool_Type': 'Thinporvisioning_POOL',
|
'FJ_Pool_Type': 'Thinporvisioning_POOL',
|
||||||
'FJ_Volume_No': FAKE_LUN_NO1,
|
'FJ_Volume_No': FAKE_LUN_NO1,
|
||||||
@ -134,10 +215,24 @@ FAKE_LUN_META1 = {
|
|||||||
'FJ_Backend': FAKE_SYSTEM_NAME,
|
'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 = {
|
FAKE_MODEL_INFO1 = {
|
||||||
'provider_location': six.text_type(FAKE_LOCATION1),
|
'provider_location': six.text_type(FAKE_LOCATION1),
|
||||||
'metadata': FAKE_LUN_META1,
|
'metadata': FAKE_LUN_META1,
|
||||||
}
|
}
|
||||||
|
# Volume2
|
||||||
|
FAKE_MODEL_INFO3 = {
|
||||||
|
'provider_location': six.text_type(FAKE_LOCATION3),
|
||||||
|
'metadata': FAKE_LUN_META3,
|
||||||
|
}
|
||||||
|
|
||||||
FAKE_KEYBIND2 = {
|
FAKE_KEYBIND2 = {
|
||||||
'CreationClassName': 'FUJITSU_StorageVolume',
|
'CreationClassName': 'FUJITSU_StorageVolume',
|
||||||
@ -151,7 +246,9 @@ FAKE_LOCATION2 = {
|
|||||||
'keybindings': FAKE_KEYBIND2,
|
'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 = {
|
FAKE_LUN_META2 = {
|
||||||
'FJ_Pool_Type': 'Thinporvisioning_POOL',
|
'FJ_Pool_Type': 'Thinporvisioning_POOL',
|
||||||
@ -205,6 +302,9 @@ class FakeEternusConnection(object):
|
|||||||
VOL_STAT = '1'
|
VOL_STAT = '1'
|
||||||
rc = 0
|
rc = 0
|
||||||
vol = self._enum_volumes()
|
vol = self._enum_volumes()
|
||||||
|
if InPool.get('InstanceID') == 'FUJITSU:RSP0005':
|
||||||
|
job = {'TheElement': vol[1].path}
|
||||||
|
else:
|
||||||
job = {'TheElement': vol[0].path}
|
job = {'TheElement': vol[0].path}
|
||||||
elif MethodName == 'ReturnToStoragePool':
|
elif MethodName == 'ReturnToStoragePool':
|
||||||
VOL_STAT = '0'
|
VOL_STAT = '0'
|
||||||
@ -262,7 +362,7 @@ class FakeEternusConnection(object):
|
|||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def EnumerateInstances(self, name):
|
def EnumerateInstances(self, name, **param_dict):
|
||||||
result = None
|
result = None
|
||||||
if name == 'FUJITSU_StorageProduct':
|
if name == 'FUJITSU_StorageProduct':
|
||||||
result = self._enum_sysnames()
|
result = self._enum_sysnames()
|
||||||
@ -315,6 +415,8 @@ class FakeEternusConnection(object):
|
|||||||
result = self._assoc_storagevolume(objectpath)
|
result = self._assoc_storagevolume(objectpath)
|
||||||
elif ResultClass == 'FUJITSU_AuthorizedPrivilege':
|
elif ResultClass == 'FUJITSU_AuthorizedPrivilege':
|
||||||
result = self._assoc_authpriv()
|
result = self._assoc_authpriv()
|
||||||
|
elif AssocClass == 'FUJITSU_AllocatedFromStoragePool':
|
||||||
|
result = self._assocnames_pool(objectpath)
|
||||||
else:
|
else:
|
||||||
result = self._default_assoc(objectpath)
|
result = self._default_assoc(objectpath)
|
||||||
|
|
||||||
@ -329,6 +431,9 @@ class FakeEternusConnection(object):
|
|||||||
result = self._assocnames_tcp_endpoint()
|
result = self._assocnames_tcp_endpoint()
|
||||||
elif ResultClass == 'FUJITSU_AffinityGroupController':
|
elif ResultClass == 'FUJITSU_AffinityGroupController':
|
||||||
result = self._assocnames_afngroup()
|
result = self._assocnames_afngroup()
|
||||||
|
elif (ResultClass == 'FUJITSU_StorageVolume' and
|
||||||
|
AssocClass == 'FUJITSU_AllocatedFromStoragePool'):
|
||||||
|
result = self._assocnames_volumelist(objectpath)
|
||||||
else:
|
else:
|
||||||
result = self._default_assocnames(objectpath)
|
result = self._default_assocnames(objectpath)
|
||||||
|
|
||||||
@ -407,6 +512,26 @@ class FakeEternusConnection(object):
|
|||||||
def _assocnames_afngroup(self):
|
def _assocnames_afngroup(self):
|
||||||
return self._enum_afntyservice()
|
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):
|
def _default_assocnames(self, objectpath):
|
||||||
return objectpath
|
return objectpath
|
||||||
|
|
||||||
@ -520,34 +645,48 @@ class FakeEternusConnection(object):
|
|||||||
def _enum_pool_details(self, pooltype):
|
def _enum_pool_details(self, pooltype):
|
||||||
pools = []
|
pools = []
|
||||||
pool = FJ_StoragePool()
|
pool = FJ_StoragePool()
|
||||||
|
pool2 = FJ_StoragePool()
|
||||||
|
|
||||||
if pooltype == 'RAID':
|
if pooltype == 'RAID':
|
||||||
pool['InstanceID'] = 'FUJITSU:RSP0004'
|
pool['InstanceID'] = 'FUJITSU:RSP0004'
|
||||||
pool['CreationClassName'] = 'FUJITSU_RAIDStoragePool'
|
pool['CreationClassName'] = 'FUJITSU_RAIDStoragePool'
|
||||||
pool['ElementName'] = 'abcd1234_OSVD'
|
pool['ElementName'] = 'abcd1234_OSVD'
|
||||||
pool['TotalManagedSpace'] = 1170368102400
|
pool['TotalManagedSpace'] = 1170368102400
|
||||||
pool['RemainingManagedSpace'] = 1170368102400
|
pool['RemainingManagedSpace'] = 1170368102400 - 1 * units.Gi
|
||||||
pool.path = pool
|
pool.path = FJ_StoragePool()
|
||||||
|
pool.path['InstanceID'] = 'FUJITSU:RSP0004'
|
||||||
pool.path.classname = 'FUJITSU_RAIDStoragePool'
|
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:
|
else:
|
||||||
pool = FJ_StoragePool()
|
|
||||||
pool['InstanceID'] = 'FUJITSU:TPP0004'
|
pool['InstanceID'] = 'FUJITSU:TPP0004'
|
||||||
pool['CreationClassName'] = 'FUJITSU_ThinProvisioningPool'
|
pool['CreationClassName'] = 'FUJITSU_ThinProvisioningPool'
|
||||||
pool['ElementName'] = 'abcd1234_TPP'
|
pool['ElementName'] = 'abcd1234_TPP'
|
||||||
pool['TotalManagedSpace'] = 1170368102400
|
pool['TotalManagedSpace'] = 1170368102400
|
||||||
pool['RemainingManagedSpace'] = 1170368102400
|
pool['RemainingManagedSpace'] = 1170368102400 - 2 * units.Gi
|
||||||
pool.path = pool
|
pool.path = FJ_StoragePool()
|
||||||
|
pool.path['InstanceID'] = 'FUJITSU:TPP0004'
|
||||||
pool.path.classname = 'FUJITSU_ThinProvisioningPool'
|
pool.path.classname = 'FUJITSU_ThinProvisioningPool'
|
||||||
|
|
||||||
pools.append(pool)
|
pools.append(pool)
|
||||||
|
|
||||||
return pools
|
return pools
|
||||||
|
|
||||||
def _enum_volumes(self):
|
def _enum_volumes(self, force=False):
|
||||||
volumes = []
|
volumes = []
|
||||||
if VOL_STAT == '0':
|
if VOL_STAT == '0' and not force:
|
||||||
return volumes
|
return volumes
|
||||||
volume = FJ_StorageVolume()
|
volume = FJ_StorageVolume()
|
||||||
volume['name'] = TEST_VOLUME['name']
|
volume['name'] = TEST_VOLUME['name']
|
||||||
|
volume['poolpath'] = 'FUJITSU:TPP0004'
|
||||||
volume['CreationClassName'] = 'FUJITSU_StorageVolume'
|
volume['CreationClassName'] = 'FUJITSU_StorageVolume'
|
||||||
volume['Name'] = FAKE_LUN_ID1
|
volume['Name'] = FAKE_LUN_ID1
|
||||||
volume['DeviceID'] = FAKE_LUN_ID1
|
volume['DeviceID'] = FAKE_LUN_ID1
|
||||||
@ -558,20 +697,46 @@ class FakeEternusConnection(object):
|
|||||||
volume.path = volume
|
volume.path = volume
|
||||||
volume.path.classname = volume['CreationClassName']
|
volume.path.classname = volume['CreationClassName']
|
||||||
|
|
||||||
name = {}
|
name = {
|
||||||
name['classname'] = 'FUJITSU_StorageVolume'
|
'classname': 'FUJITSU_StorageVolume',
|
||||||
keys = {}
|
'keybindings': {
|
||||||
keys['CreationClassName'] = 'FUJITSU_StorageVolume'
|
'CreationClassName': 'FUJITSU_StorageVolume',
|
||||||
keys['SystemName'] = STORAGE_SYSTEM
|
'SystemName': STORAGE_SYSTEM,
|
||||||
keys['DeviceID'] = volume['DeviceID']
|
'DeviceID': volume['DeviceID'],
|
||||||
keys['SystemCreationClassName'] = 'FUJITSU_StorageComputerSystem'
|
'SystemCreationClassName': 'FUJITSU_StorageComputerSystem',
|
||||||
name['keybindings'] = keys
|
},
|
||||||
|
}
|
||||||
volume['provider_location'] = str(name)
|
volume['provider_location'] = str(name)
|
||||||
|
|
||||||
volumes.append(volume)
|
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 = FJ_StorageVolume()
|
||||||
snap_vol['name'] = TEST_SNAP['name']
|
snap_vol['name'] = TEST_SNAP['name']
|
||||||
|
snap_vol['poolpath'] = 'FUJITSU:RSP0004'
|
||||||
snap_vol['CreationClassName'] = 'FUJITSU_StorageVolume'
|
snap_vol['CreationClassName'] = 'FUJITSU_StorageVolume'
|
||||||
snap_vol['Name'] = FAKE_LUN_ID2
|
snap_vol['Name'] = FAKE_LUN_ID2
|
||||||
snap_vol['DeviceID'] = FAKE_LUN_ID2
|
snap_vol['DeviceID'] = FAKE_LUN_ID2
|
||||||
@ -581,20 +746,21 @@ class FakeEternusConnection(object):
|
|||||||
snap_vol.path = snap_vol
|
snap_vol.path = snap_vol
|
||||||
snap_vol.path.classname = snap_vol['CreationClassName']
|
snap_vol.path.classname = snap_vol['CreationClassName']
|
||||||
|
|
||||||
name2 = {}
|
name2 = {
|
||||||
name2['classname'] = 'FUJITSU_StorageVolume'
|
'classname': 'FUJITSU_StorageVolume',
|
||||||
keys2 = {}
|
'keybindings': {
|
||||||
keys2['CreationClassName'] = 'FUJITSU_StorageVolume'
|
'CreationClassName': 'FUJITSU_StorageVolume',
|
||||||
keys2['SystemName'] = STORAGE_SYSTEM
|
'SystemName': STORAGE_SYSTEM,
|
||||||
keys2['DeviceID'] = snap_vol['DeviceID']
|
'DeviceID': snap_vol['DeviceID'],
|
||||||
keys2['SystemCreationClassName'] = 'FUJITSU_StorageComputerSystem'
|
'SystemCreationClassName': 'FUJITSU_StorageComputerSystem',
|
||||||
name2['keybindings'] = keys2
|
},
|
||||||
|
}
|
||||||
snap_vol['provider_location'] = str(name2)
|
snap_vol['provider_location'] = str(name2)
|
||||||
|
|
||||||
volumes.append(snap_vol)
|
volumes.append(snap_vol)
|
||||||
|
|
||||||
clone_vol = FJ_StorageVolume()
|
clone_vol = FJ_StorageVolume()
|
||||||
clone_vol['name'] = TEST_CLONE['name']
|
clone_vol['name'] = TEST_CLONE['name']
|
||||||
|
clone_vol['poolpath'] = 'FUJITSU:TPP0004'
|
||||||
clone_vol['CreationClassName'] = 'FUJITSU_StorageVolume'
|
clone_vol['CreationClassName'] = 'FUJITSU_StorageVolume'
|
||||||
clone_vol['ElementName'] = TEST_CLONE['name']
|
clone_vol['ElementName'] = TEST_CLONE['name']
|
||||||
clone_vol['DeviceID'] = FAKE_LUN_ID2
|
clone_vol['DeviceID'] = FAKE_LUN_ID2
|
||||||
@ -678,7 +844,6 @@ class FakeEternusConnection(object):
|
|||||||
return targetlist
|
return targetlist
|
||||||
|
|
||||||
def _getinstance_storagevolume(self, objpath):
|
def _getinstance_storagevolume(self, objpath):
|
||||||
foundinstance = None
|
|
||||||
instance = FJ_StorageVolume()
|
instance = FJ_StorageVolume()
|
||||||
volumes = self._enum_volumes()
|
volumes = self._enum_volumes()
|
||||||
for volume in volumes:
|
for volume in volumes:
|
||||||
@ -713,6 +878,8 @@ class FJFCDriverTestCase(test.TestCase):
|
|||||||
# Make fake Object by using mock as configuration object.
|
# Make fake Object by using mock as configuration object.
|
||||||
self.configuration = mock.Mock(spec=conf.Configuration)
|
self.configuration = mock.Mock(spec=conf.Configuration)
|
||||||
self.configuration.cinder_eternus_config_file = self.config_file.name
|
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.mock_object(dx_common.FJDXCommon, '_get_eternus_connection',
|
||||||
self.fake_eternus_connection)
|
self.fake_eternus_connection)
|
||||||
@ -725,22 +892,27 @@ class FJFCDriverTestCase(test.TestCase):
|
|||||||
driver = dx_fc.FJDXFCDriver(configuration=self.configuration)
|
driver = dx_fc.FJDXFCDriver(configuration=self.configuration)
|
||||||
self.driver = driver
|
self.driver = driver
|
||||||
|
|
||||||
|
def fake_safe_get(self, str=None):
|
||||||
|
return str
|
||||||
|
|
||||||
def fake_eternus_connection(self):
|
def fake_eternus_connection(self):
|
||||||
conn = FakeEternusConnection()
|
conn = FakeEternusConnection()
|
||||||
return conn
|
return conn
|
||||||
|
|
||||||
def test_get_volume_stats(self):
|
def test_get_volume_stats(self):
|
||||||
ret = self.driver.get_volume_stats(True)
|
ret = self.driver.get_volume_stats(True)
|
||||||
stats = {'vendor_name': ret['vendor_name'],
|
|
||||||
'total_capacity_gb': ret['total_capacity_gb'],
|
self.assertEqual(FAKE_STATS2, ret)
|
||||||
'free_capacity_gb': ret['free_capacity_gb']}
|
|
||||||
self.assertEqual(FAKE_STATS, stats)
|
|
||||||
|
|
||||||
def test_create_and_delete_volume(self):
|
def test_create_and_delete_volume(self):
|
||||||
model_info = self.driver.create_volume(TEST_VOLUME)
|
model_info = self.driver.create_volume(TEST_VOLUME)
|
||||||
self.assertEqual(FAKE_MODEL_INFO1, model_info)
|
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_VOLUME)
|
||||||
|
self.driver.delete_volume(TEST_VOLUME2)
|
||||||
|
|
||||||
@mock.patch.object(dx_common.FJDXCommon, '_get_mapdata')
|
@mock.patch.object(dx_common.FJDXCommon, '_get_mapdata')
|
||||||
def test_map_unmap(self, mock_mapdata):
|
def test_map_unmap(self, mock_mapdata):
|
||||||
@ -816,7 +988,16 @@ class FJFCDriverTestCase(test.TestCase):
|
|||||||
model_info = self.driver.create_volume(TEST_VOLUME)
|
model_info = self.driver.create_volume(TEST_VOLUME)
|
||||||
self.assertEqual(FAKE_MODEL_INFO1, model_info)
|
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):
|
class FJISCSIDriverTestCase(test.TestCase):
|
||||||
@ -835,6 +1016,8 @@ class FJISCSIDriverTestCase(test.TestCase):
|
|||||||
# Make fake Object by using mock as configuration object.
|
# Make fake Object by using mock as configuration object.
|
||||||
self.configuration = mock.Mock(spec=conf.Configuration)
|
self.configuration = mock.Mock(spec=conf.Configuration)
|
||||||
self.configuration.cinder_eternus_config_file = self.config_file.name
|
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.mock_object(dx_common.FJDXCommon, '_get_eternus_connection',
|
||||||
self.fake_eternus_connection)
|
self.fake_eternus_connection)
|
||||||
@ -850,6 +1033,9 @@ class FJISCSIDriverTestCase(test.TestCase):
|
|||||||
driver = dx_iscsi.FJDXISCSIDriver(configuration=self.configuration)
|
driver = dx_iscsi.FJDXISCSIDriver(configuration=self.configuration)
|
||||||
self.driver = driver
|
self.driver = driver
|
||||||
|
|
||||||
|
def fake_safe_get(self, str=None):
|
||||||
|
return str
|
||||||
|
|
||||||
def fake_eternus_connection(self):
|
def fake_eternus_connection(self):
|
||||||
conn = FakeEternusConnection()
|
conn = FakeEternusConnection()
|
||||||
return conn
|
return conn
|
||||||
@ -867,16 +1053,18 @@ class FJISCSIDriverTestCase(test.TestCase):
|
|||||||
|
|
||||||
def test_get_volume_stats(self):
|
def test_get_volume_stats(self):
|
||||||
ret = self.driver.get_volume_stats(True)
|
ret = self.driver.get_volume_stats(True)
|
||||||
stats = {'vendor_name': ret['vendor_name'],
|
|
||||||
'total_capacity_gb': ret['total_capacity_gb'],
|
self.assertEqual(FAKE_STATS, ret)
|
||||||
'free_capacity_gb': ret['free_capacity_gb']}
|
|
||||||
self.assertEqual(FAKE_STATS, stats)
|
|
||||||
|
|
||||||
def test_create_and_delete_volume(self):
|
def test_create_and_delete_volume(self):
|
||||||
model_info = self.driver.create_volume(TEST_VOLUME)
|
model_info = self.driver.create_volume(TEST_VOLUME)
|
||||||
self.assertEqual(FAKE_MODEL_INFO1, model_info)
|
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_VOLUME)
|
||||||
|
self.driver.delete_volume(TEST_VOLUME2)
|
||||||
|
|
||||||
def test_map_unmap(self):
|
def test_map_unmap(self):
|
||||||
fake_mapdata = self.fake_get_mapdata(None, {}, None)
|
fake_mapdata = self.fake_get_mapdata(None, {}, None)
|
||||||
@ -942,4 +1130,13 @@ class FJISCSIDriverTestCase(test.TestCase):
|
|||||||
model_info = self.driver.create_volume(TEST_VOLUME)
|
model_info = self.driver.create_volume(TEST_VOLUME)
|
||||||
self.assertEqual(FAKE_MODEL_INFO1, model_info)
|
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',
|
RAIDGROUP: 'RAID_GROUP',
|
||||||
TPPOOL: 'Thinporvisioning_POOL',
|
TPPOOL: 'Thinporvisioning_POOL',
|
||||||
}
|
}
|
||||||
|
POOL_TYPE_list = [
|
||||||
|
'RAID',
|
||||||
|
'TPP'
|
||||||
|
]
|
||||||
OPERATION_dic = {
|
OPERATION_dic = {
|
||||||
SNAPOPC: RETURN_TO_RESOURCEPOOL,
|
SNAPOPC: RETURN_TO_RESOURCEPOOL,
|
||||||
OPC: DETACH,
|
OPC: DETACH,
|
||||||
|
@ -36,6 +36,7 @@ from cinder.i18n import _
|
|||||||
from cinder import utils
|
from cinder import utils
|
||||||
from cinder.volume import configuration as conf
|
from cinder.volume import configuration as conf
|
||||||
from cinder.volume.drivers.fujitsu.eternus_dx import constants as CONSTANTS
|
from cinder.volume.drivers.fujitsu.eternus_dx import constants as CONSTANTS
|
||||||
|
from cinder.volume import volume_utils
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
CONF = cfg.CONF
|
CONF = cfg.CONF
|
||||||
@ -61,10 +62,7 @@ class FJDXCommon(object):
|
|||||||
VERSION = "1.3.0"
|
VERSION = "1.3.0"
|
||||||
stats = {
|
stats = {
|
||||||
'driver_version': VERSION,
|
'driver_version': VERSION,
|
||||||
'free_capacity_gb': 0,
|
|
||||||
'reserved_percentage': 0,
|
|
||||||
'storage_protocol': None,
|
'storage_protocol': None,
|
||||||
'total_capacity_gb': 0,
|
|
||||||
'vendor_name': 'FUJITSU',
|
'vendor_name': 'FUJITSU',
|
||||||
'QoS_support': False,
|
'QoS_support': False,
|
||||||
'volume_backend_name': None,
|
'volume_backend_name': None,
|
||||||
@ -82,6 +80,7 @@ class FJDXCommon(object):
|
|||||||
# Get iSCSI ipaddress from driver configuration file.
|
# Get iSCSI ipaddress from driver configuration file.
|
||||||
self.configuration.iscsi_ip_address = (
|
self.configuration.iscsi_ip_address = (
|
||||||
self._get_drvcfg('EternusISCSIIP'))
|
self._get_drvcfg('EternusISCSIIP'))
|
||||||
|
self.conn = None
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_driver_options():
|
def get_driver_options():
|
||||||
@ -103,7 +102,7 @@ class FJDXCommon(object):
|
|||||||
'volumesize': volumesize})
|
'volumesize': volumesize})
|
||||||
|
|
||||||
# get poolname from driver configuration file
|
# 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
|
# Existence check the pool
|
||||||
pool = self._find_pool(eternus_pool)
|
pool = self._find_pool(eternus_pool)
|
||||||
|
|
||||||
@ -216,6 +215,47 @@ class FJDXCommon(object):
|
|||||||
|
|
||||||
return (element_path, metadata)
|
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):
|
def create_volume_from_snapshot(self, volume, snapshot):
|
||||||
"""Creates a volume from a snapshot."""
|
"""Creates a volume from a snapshot."""
|
||||||
LOG.debug('create_volume_from_snapshot, '
|
LOG.debug('create_volume_from_snapshot, '
|
||||||
@ -716,14 +756,12 @@ class FJDXCommon(object):
|
|||||||
'vol_instance': vol_instance.path})
|
'vol_instance': vol_instance.path})
|
||||||
|
|
||||||
# Get poolname from driver configuration file.
|
# Get poolname from driver configuration file.
|
||||||
eternus_pool = self._get_drvcfg('EternusPool')
|
pool_name, pool = self._find_pool_from_volume(vol_instance)
|
||||||
# Check the existence of volume.
|
|
||||||
pool = self._find_pool(eternus_pool)
|
|
||||||
if pool is None:
|
if pool is None:
|
||||||
msg = (_('extend_volume, '
|
msg = (_('extend_volume, '
|
||||||
'eternus_pool: %(eternus_pool)s, '
|
'eternus_pool: %(eternus_pool)s, '
|
||||||
'pool not found.')
|
'pool not found.')
|
||||||
% {'eternus_pool': eternus_pool})
|
% {'eternus_pool': pool_name})
|
||||||
LOG.error(msg)
|
LOG.error(msg)
|
||||||
raise exception.VolumeBackendAPIException(data=msg)
|
raise exception.VolumeBackendAPIException(data=msg)
|
||||||
|
|
||||||
@ -734,14 +772,14 @@ class FJDXCommon(object):
|
|||||||
pooltype = CONSTANTS.TPPOOL
|
pooltype = CONSTANTS.TPPOOL
|
||||||
|
|
||||||
configservice = self._find_eternus_service(CONSTANTS.STOR_CONF)
|
configservice = self._find_eternus_service(CONSTANTS.STOR_CONF)
|
||||||
if configservice is None:
|
if not configservice:
|
||||||
msg = (_('extend_volume, volume: %(volume)s, '
|
msg = (_('extend_volume, volume: %(volume)s, '
|
||||||
'volumename: %(volumename)s, '
|
'volumename: %(volumename)s, '
|
||||||
'eternus_pool: %(eternus_pool)s, '
|
'eternus_pool: %(eternus_pool)s, '
|
||||||
'Storage Configuration Service not found.')
|
'Storage Configuration Service not found.')
|
||||||
% {'volume': volume,
|
% {'volume': volume,
|
||||||
'volumename': volumename,
|
'volumename': volumename,
|
||||||
'eternus_pool': eternus_pool})
|
'eternus_pool': pool_name})
|
||||||
LOG.error(msg)
|
LOG.error(msg)
|
||||||
raise exception.VolumeBackendAPIException(data=msg)
|
raise exception.VolumeBackendAPIException(data=msg)
|
||||||
|
|
||||||
@ -755,7 +793,7 @@ class FJDXCommon(object):
|
|||||||
'TheElement: %(vol_instance)s.',
|
'TheElement: %(vol_instance)s.',
|
||||||
{'service': configservice,
|
{'service': configservice,
|
||||||
'volumename': volumename,
|
'volumename': volumename,
|
||||||
'eternus_pool': eternus_pool,
|
'eternus_pool': pool_name,
|
||||||
'pooltype': pooltype,
|
'pooltype': pooltype,
|
||||||
'volumesize': volumesize,
|
'volumesize': volumesize,
|
||||||
'vol_instance': vol_instance.path})
|
'vol_instance': vol_instance.path})
|
||||||
@ -793,48 +831,21 @@ class FJDXCommon(object):
|
|||||||
{'volumename': volumename,
|
{'volumename': volumename,
|
||||||
'rc': rc,
|
'rc': rc,
|
||||||
'errordesc': errordesc,
|
'errordesc': errordesc,
|
||||||
'eternus_pool': eternus_pool,
|
'eternus_pool': pool_name,
|
||||||
'pooltype': CONSTANTS.POOL_TYPE_dic[pooltype]})
|
'pooltype': CONSTANTS.POOL_TYPE_dic[pooltype]})
|
||||||
|
|
||||||
return eternus_pool
|
return pool_name
|
||||||
|
|
||||||
@lockutils.synchronized('ETERNUS-update', 'cinder-', True)
|
@lockutils.synchronized('ETERNUS-update', 'cinder-', True)
|
||||||
def update_volume_stats(self):
|
def update_volume_stats(self):
|
||||||
"""get pool capacity."""
|
"""Get pool capacity."""
|
||||||
|
|
||||||
self.conn = self._get_eternus_connection()
|
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)
|
return (self.stats, poolname_list)
|
||||||
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)
|
|
||||||
|
|
||||||
def _get_mapdata(self, vol_instance, connector, target_portlist):
|
def _get_mapdata(self, vol_instance, connector, target_portlist):
|
||||||
"""return mapping information."""
|
"""return mapping information."""
|
||||||
@ -1012,9 +1023,9 @@ class FJDXCommon(object):
|
|||||||
return mapdata
|
return mapdata
|
||||||
|
|
||||||
def _get_drvcfg(self, tagname, filename=None, multiple=False):
|
def _get_drvcfg(self, tagname, filename=None, multiple=False):
|
||||||
"""read from driver configuration file."""
|
"""Read from driver configuration file."""
|
||||||
if filename is None:
|
if not filename:
|
||||||
# set default configuration file name
|
# Set default configuration file name.
|
||||||
filename = self.configuration.cinder_eternus_config_file
|
filename = self.configuration.cinder_eternus_config_file
|
||||||
|
|
||||||
LOG.debug("_get_drvcfg, input[%(filename)s][%(tagname)s].",
|
LOG.debug("_get_drvcfg, input[%(filename)s][%(tagname)s].",
|
||||||
@ -1023,7 +1034,6 @@ class FJDXCommon(object):
|
|||||||
tree = ET.parse(filename)
|
tree = ET.parse(filename)
|
||||||
elem = tree.getroot()
|
elem = tree.getroot()
|
||||||
|
|
||||||
ret = None
|
|
||||||
if not multiple:
|
if not multiple:
|
||||||
ret = elem.findtext(".//" + tagname)
|
ret = elem.findtext(".//" + tagname)
|
||||||
else:
|
else:
|
||||||
@ -1141,6 +1151,116 @@ class FJDXCommon(object):
|
|||||||
LOG.debug('_find_pool, pool: %s.', ret)
|
LOG.debug('_find_pool, pool: %s.', ret)
|
||||||
return 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):
|
def _find_eternus_service(self, classname):
|
||||||
"""find CIM instance about service information."""
|
"""find CIM instance about service information."""
|
||||||
LOG.debug('_find_eternus_service, '
|
LOG.debug('_find_eternus_service, '
|
||||||
@ -1176,7 +1296,8 @@ class FJDXCommon(object):
|
|||||||
{'a': classname,
|
{'a': classname,
|
||||||
'b': instanceNameList,
|
'b': instanceNameList,
|
||||||
'c': param_dict})
|
'c': param_dict})
|
||||||
|
rc = None
|
||||||
|
retdata = None
|
||||||
# Use InvokeMethod.
|
# Use InvokeMethod.
|
||||||
try:
|
try:
|
||||||
rc, retdata = self.conn.InvokeMethod(
|
rc, retdata = self.conn.InvokeMethod(
|
||||||
@ -1223,11 +1344,14 @@ class FJDXCommon(object):
|
|||||||
|
|
||||||
@lockutils.synchronized('ETERNUS-SMIS-other', 'cinder-', True)
|
@lockutils.synchronized('ETERNUS-SMIS-other', 'cinder-', True)
|
||||||
@utils.retry(exception.VolumeBackendAPIException)
|
@utils.retry(exception.VolumeBackendAPIException)
|
||||||
def _enum_eternus_instances(self, classname):
|
def _enum_eternus_instances(self, classname, conn=None, **param_dict):
|
||||||
"""Enumerate Instances."""
|
"""Enumerate Instances."""
|
||||||
LOG.debug('_enum_eternus_instances, classname: %s.', classname)
|
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))
|
LOG.debug('_enum_eternus_instances, enum %d instances.', len(ret))
|
||||||
return ret
|
return ret
|
||||||
@ -1258,26 +1382,32 @@ class FJDXCommon(object):
|
|||||||
|
|
||||||
@lockutils.synchronized('ETERNUS-SMIS-other', 'cinder-', True)
|
@lockutils.synchronized('ETERNUS-SMIS-other', 'cinder-', True)
|
||||||
@utils.retry(exception.VolumeBackendAPIException)
|
@utils.retry(exception.VolumeBackendAPIException)
|
||||||
def _assoc_eternus(self, classname, **param_dict):
|
def _assoc_eternus(self, classname, conn=None, **param_dict):
|
||||||
"""Associator."""
|
"""Associator."""
|
||||||
LOG.debug('_assoc_eternus, '
|
LOG.debug('_assoc_eternus, '
|
||||||
'classname: %(cls)s, param: %(param)s.',
|
'classname: %(cls)s, param: %(param)s.',
|
||||||
{'cls': classname, 'param': param_dict})
|
{'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))
|
LOG.debug('_assoc_eternus, enum %d instances.', len(ret))
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
@lockutils.synchronized('ETERNUS-SMIS-other', 'cinder-', True)
|
@lockutils.synchronized('ETERNUS-SMIS-other', 'cinder-', True)
|
||||||
@utils.retry(exception.VolumeBackendAPIException)
|
@utils.retry(exception.VolumeBackendAPIException)
|
||||||
def _assoc_eternus_names(self, classname, **param_dict):
|
def _assoc_eternus_names(self, classname, conn=None, **param_dict):
|
||||||
"""Associator Names."""
|
"""Associator Names."""
|
||||||
LOG.debug('_assoc_eternus_names, '
|
LOG.debug('_assoc_eternus_names, '
|
||||||
'classname: %(cls)s, param: %(param)s.',
|
'classname: %(cls)s, param: %(param)s.',
|
||||||
{'cls': classname, 'param': param_dict})
|
{'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))
|
LOG.debug('_assoc_eternus_names, enum %d names.', len(ret))
|
||||||
return ret
|
return ret
|
||||||
@ -2103,3 +2233,66 @@ class FJDXCommon(object):
|
|||||||
result = num
|
result = num
|
||||||
|
|
||||||
return result
|
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>
|
<EternusUser>smisuser</EternusUser>
|
||||||
<EternusPassword>smispassword</EternusPassword>
|
<EternusPassword>smispassword</EternusPassword>
|
||||||
<EternusPool>raid5_0001</EternusPool>
|
<EternusPool>raid5_0001</EternusPool>
|
||||||
|
<EternusPool>tpp_0001</EternusPool>
|
||||||
|
<EternusPool>raid_0002</EternusPool>
|
||||||
<EternusSnapPool>raid5_0001</EternusSnapPool>
|
<EternusSnapPool>raid5_0001</EternusSnapPool>
|
||||||
</FUJITSU>
|
</FUJITSU>
|
||||||
|
|
||||||
@ -146,6 +148,8 @@ Configuration
|
|||||||
<EternusUser>smisuser</EternusUser>
|
<EternusUser>smisuser</EternusUser>
|
||||||
<EternusPassword>smispassword</EternusPassword>
|
<EternusPassword>smispassword</EternusPassword>
|
||||||
<EternusPool>raid5_0001</EternusPool>
|
<EternusPool>raid5_0001</EternusPool>
|
||||||
|
<EternusPool>tpp_0001</EternusPool>
|
||||||
|
<EternusPool>raid_0002</EternusPool>
|
||||||
<EternusSnapPool>raid5_0001</EternusSnapPool>
|
<EternusSnapPool>raid5_0001</EternusSnapPool>
|
||||||
<EternusISCSIIP>1.1.1.1</EternusISCSIIP>
|
<EternusISCSIIP>1.1.1.1</EternusISCSIIP>
|
||||||
<EternusISCSIIP>1.1.1.2</EternusISCSIIP>
|
<EternusISCSIIP>1.1.1.2</EternusISCSIIP>
|
||||||
@ -169,7 +173,7 @@ Configuration
|
|||||||
``EternusPassword``
|
``EternusPassword``
|
||||||
Password for the SMI-S connection of the ETERNUS DX.
|
Password for the SMI-S connection of the ETERNUS DX.
|
||||||
|
|
||||||
``EternusPool``
|
``EternusPool`` (Multiple setting allowed)
|
||||||
Storage pool name for volumes.
|
Storage pool name for volumes.
|
||||||
|
|
||||||
Enter RAID Group name or TPP name in the ETERNUS DX.
|
Enter RAID Group name or TPP name in the ETERNUS DX.
|
||||||
@ -188,6 +192,8 @@ Configuration
|
|||||||
and cannot specify TPP name.
|
and cannot specify TPP name.
|
||||||
* You can specify the same RAID Group name for ``EternusPool`` and ``EternusSnapPool``
|
* You can specify the same RAID Group name for ``EternusPool`` and ``EternusSnapPool``
|
||||||
if you create volumes and snapshots on a same storage pool.
|
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
|
Configuration example
|
||||||
~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
@ -0,0 +1,4 @@
|
|||||||
|
---
|
||||||
|
features:
|
||||||
|
- |
|
||||||
|
Fujitsu Driver: Added multiple pools support.
|
Loading…
Reference in New Issue
Block a user