* Almost rewrite pool_create(). Introduced many duplicate codes for LSI
vendor specific pool_create(), but that make work flow much clearer and
easier to maintain.
* As new pool_create() and pool_delete() required, returned Pool will contain
full optional_data.
* Interop supported. Tested on:
1. EMC VNX # Interop with ASYNC support
2. IBM XIV # Interop without ASYNC
3. LSI MegaRAID # No interop, no ASYNC
* Since SNIA does not have good way to handle 'Size' and 'InExtents' using
at the same, we add Smis._NEW_POOL_SIZE_OVERHAD for disk choosing.
(SNIA SMI-S 1.6.1 draft 2 introduced disk type in CIM_StorageSetting,
which will fix this problem, we will code it out once it released)
Signed-off-by: Gris Ge <***@redhat.com>
---
lsm/lsm/smis.py | 736 +++++++++++++++++++++++++++++++++++++++++++-------------
1 file changed, 574 insertions(+), 162 deletions(-)
diff --git a/lsm/lsm/smis.py b/lsm/lsm/smis.py
index 4ad515c..4f8a9c6 100644
--- a/lsm/lsm/smis.py
+++ b/lsm/lsm/smis.py
@@ -23,7 +23,8 @@ import traceback
from pywbem import CIMError
from iplugin import IStorageAreaNetwork
-from common import Error, uri_parse, LsmError, ErrorNumber, JobStatus, md5
+from common import Error, uri_parse, LsmError, ErrorNumber, JobStatus, md5, \
+ size_human_2_size_bytes, size_bytes_2_size_human
from data import Pool, Initiator, Volume, AccessGroup, System, Capabilities,\
Disk, OptionalData, txt_a
from version import VERSION
@@ -273,6 +274,14 @@ class Smis(IStorageAreaNetwork):
LSI_DISK_MEDIA_TYPE_SSD = 1
LSI_DISK_MEDIA_TYPE_SSD_FLASH = 2
+ LSI_DEFAULT_RAID_TYPE = Pool.RAID_TYPE_RAID0
+ # this is define by us not LSI. We just want to make thing simple
+ # when user creating new pool without defining raid type
+
+ _NEW_POOL_SIZE_OVERHAD = size_human_2_size_bytes("100MiB")
+ # This constant is used by _auto_choose_disks(), check its comments for
+ # detail
+
class RepSvc(object):
class Action(object):
@@ -1239,13 +1248,17 @@ class Smis(IStorageAreaNetwork):
For SYNC CreateOrModifyElementFromStoragePool action.
The new CIM_StoragePool is stored in out['Pool']
"""
- pool_pros = self._new_pool_cim_pool_pros()
+ pool_pros = self._new_pool_cim_pool_pros(flag_full_info=True)
if 'Pool' in out:
cim_new_pool = self._c.GetInstance(
out['Pool'],
PropertyList=pool_pros)
- return self._new_pool(cim_new_pool)
+ pool = self._new_pool(cim_new_pool)
+ opt_pro_dict = self._pool_opt_data(cim_new_pool)
+ for key, value in opt_pro_dict.items():
+ pool.optional_data.set(key, value)
+ return pool
else:
raise LsmError(ErrorNumber.INTERNAL_ERROR,
"Got not new Pool from out of InvokeMethod" +
@@ -1286,12 +1299,16 @@ class Smis(IStorageAreaNetwork):
"""
Given a CIMInstance of CIM_ConcreteJob, return a LSM Pool
"""
- pool_pros = self._new_pool_cim_pool_pros()
+ pool_pros = self._new_pool_cim_pool_pros(flag_full_info=True)
cim_pools = self._c.Associators(cim_job.path,
AssocClass='CIM_AffectedJobElement',
ResultClass='CIM_StoragePool',
PropertyList=pool_pros)
- return self._new_pool(cim_pools[0])
+ pool = self._new_pool(cim_pools[0])
+ opt_pro_dict = self._pool_opt_data(cim_pools[0])
+ for key, value in opt_pro_dict.items():
+ pool.optional_data.set(key, value)
+ return pool
@handle_cim_errors
def volumes(self, flags=0):
@@ -1326,7 +1343,7 @@ class Smis(IStorageAreaNetwork):
for cim_sys in cim_syss:
sys_id = self._sys_id(cim_sys)
pool_pros = self._property_list_of_id('Pool')
- for cim_pool in self._pools(cim_sys, pool_pros):
+ for cim_pool in self._cim_pools(cim_sys.path, pool_pros):
pool_id = self._pool_id(cim_pool)
cim_vols = self._c.Associators(
cim_pool.path,
@@ -1390,7 +1407,7 @@ class Smis(IStorageAreaNetwork):
else:
return cim_syss
- def _pools(self, cim_sys, property_list=None):
+ def _cim_pools(self, cim_sys_path, property_list=None):
pros = []
if property_list is None:
pros = ['Primordial']
@@ -1399,13 +1416,29 @@ class Smis(IStorageAreaNetwork):
if 'Primordial' not in pros:
pros.extend(['Primordial'])
- cim_pools = self._c.Associators(cim_sys.path,
+ cim_pools = self._c.Associators(cim_sys_path,
AssocClass='CIM_HostedStoragePool',
ResultClass='CIM_StoragePool',
PropertyList=pros)
return [p for p in cim_pools if not p["Primordial"]]
+ def _cim_pool_of_id(self, cim_sys_path, pool_id, property_list=None):
+ if property_list is None:
+ property_list = self._property_list_of_id("Pool")
+ else:
+ for key in self._property_list_of_id("Pool"):
+ if key not in property_list:
+ property_list.extend([prop])
+
+ cim_pools = self._cim_pools(cim_sys_path, property_list)
+ for cim_pool in cim_pools:
+ if self._pool_id(cim_pool) == pool_id:
+ return cim_pool
+
+ raise LsmError(ErrorNumber.INVALID_POOL,
+ "Invalid pool: %s" % pool_id)
+
def _new_pool_cim_pool_pros(self, flag_full_info=False):
"""
Return a list of properties for creating new pool.
@@ -1448,7 +1481,7 @@ class Smis(IStorageAreaNetwork):
for cim_sys in cim_syss:
system_id = self._sys_id(cim_sys)
- for cim_pool in self._pools(cim_sys, cim_pool_pros):
+ for cim_pool in self._cim_pools(cim_sys.path, cim_pool_pros):
# Skip spare storage pool.
if 'Usage' in cim_pool and \
cim_pool['Usage'] == Smis.DMTF_POOL_USAGE_SPARE:
@@ -3181,10 +3214,10 @@ class Smis(IStorageAreaNetwork):
"""
Delete a Pool via CIM_StorageConfigurationService.DeleteStoragePool
"""
- cim_sys = self._get_cim_instance_by_id('System', pool.system_id, False)
+ cim_sys = self._cim_sys_of_id(pool.system_id)
cim_scs = self._cim_scs(cim_sys.path, [])
+ cim_pool = self._cim_pool_of_id(cim_sys.path, pool.id)
- cim_pool = self._get_cim_instance_by_id('Pool', pool.id, False)
in_params = {'Pool': cim_pool.path}
return self._pi("pool_delete", Smis.JOB_RETRIEVE_NONE,
@@ -3192,70 +3225,184 @@ class Smis(IStorageAreaNetwork):
cim_scs.path,
**in_params)))[0]
- @handle_cim_errors
- def pool_create(self, system_id, pool_name, raid_type, member_type,
- member_ids, member_count, size_bytes, thinp_type, flags=0):
+ def _pool_create_lsi(self, cim_sys, pool_name, size_bytes,
+ raid_type=Pool.RAID_TYPE_UNKNOWN,
+ member_type=Pool.MEMBER_TYPE_UNKNOWN, flags=0):
"""
- Creating pool via
- CIM_StorageConfigurationService.CreateOrModifyStoragePool()
- TODO: Each vendor are needing different parameters for
- CreateOrModifyStoragePool()
+ LSI Vendor specific way of pool creation.
"""
- cim_sys = self._get_cim_instance_by_id('System', system_id, False)
+ if raid_type == Pool.RAID_TYPE_UNKNOWN:
+ # LSI does not allow InExtents to be NULL.
+ # We have to chose disks for LSI.
+ # To make thing simple, we only check 1 raid type for
+ # possible disk combination
+ raid_type =Smis.LSI_DEFAULT_RAID_TYPE
+
cim_scs = self._cim_scs(cim_sys.path, [])
# we does not support defining thinp_type yet.
# just using whatever provider set.
in_params = {}
- if pool_name:
- in_params['ElementName'] = pool_name
-
- in_cim_exts_path = []
- if member_type == Pool.MEMBER_TYPE_DISK:
- if member_ids and len(member_ids) >= 1:
- cim_exts = self._pri_cim_ext_of_disk_ids(member_ids)
- system_id = None
- for cim_ext in cim_exts:
- tmp_system_id = self._sys_id_child(cim_ext)
- if not system_id:
- system_id = tmp_system_id
- elif system_id != tmp_system_id:
- raise LsmError(ErrorNumber.INVALID_DISK,
- "Specified member are belong to " +
- "two or more system: %s %s" %
- (system_id, tmp_system_id))
- if cim_exts:
- in_cim_exts_path = [x.path for x in cim_exts]
- in_params['InExtents'] = in_cim_exts_path
-
- elif member_type == Pool.MEMBER_TYPE_POOL:
- cim_member_pools_path = []
- for member_id in member_ids:
- cim_member_pools_path.extend(
- [self._get_cim_instance_by_id('Pool', member_id,
- False, None).path])
- in_params['InPools'] = cim_member_pools_path
-
- elif member_type == Pool.MEMBER_TYPE_VOLUME:
- raise LsmError(ErrorNumber.NO_SUPPORT,
- "Creating Pool against another Volume is not " +
- "supported by SMIS plugin")
- # TODO: support member_count
- if member_count and member_count > 0:
+ in_params['ElementName'] = pool_name
+ in_params['Size'] = pywbem.Uint64(1)
+ # LSI require InExtents define, so the 'Size' just set to 1 bytes
+ # would be OK.
+
+ raise_error = True
+
+ if member_type == Pool.MEMBER_TYPE_UNKNOWN:
+ raise_error = True # We only support disk pool, so raise error
+ # anyway.
+
+ if member_type != Pool.MEMBER_TYPE_UNKNOWN and \
+ not Pool.member_type_is_disk(member_type):
raise LsmError(ErrorNumber.NO_SUPPORT,
- "member_count is not supported by " +
- "SMI-S plugin yet.")
+ "Creating new pool with member type %s(%d) "
+ % (Pool.member_type_to_str(member_type),
+ member_type) + "is not supported")
+
+ # Find CIM_StorageSetting for 'Goal' parameter
+ cim_st_set = self._find_preset_st_set(cim_sys.path, raid_type)
+
+ if cim_st_set:
+ in_params['Goal'] = cim_st_set.path
+
+
+ disk_type = Disk.DISK_TYPE_UNKNOWN
+ if Pool.member_type_is_disk(member_type):
+ disk_type = Pool.member_type_to_disk_type(member_type)
+
+ cim_disks = self._auto_choose_disks(cim_sys.path, size_bytes,
+ raid_type, disk_type, raise_error)
+ if len(cim_disks) > 1:
+ in_params['InExtents'] = []
+ for cim_disk in cim_disks:
+ cim_ext = self._pri_cim_ext_of_cim_disk(cim_disk.path, None)
+ if cim_ext:
+ in_params['InExtents'].extend([cim_ext.path])
+ else:
+ return (None, None)
+
+ return self._pi("pool_create", Smis.JOB_RETRIEVE_POOL,
+ *(self._c.InvokeMethod(
+ 'CreateOrModifyStoragePool',
+ cim_scs.path, **in_params)))
+
+ @handle_cim_errors
+ def pool_create(self, system_id, pool_name, size_bytes,
+ raid_type=Pool.RAID_TYPE_UNKNOWN,
+ member_type=Pool.MEMBER_TYPE_UNKNOWN, flags=0):
+ """
+ Using SNIA SMI-S 1.4 or later 'Block Service Packpage' profile:
+
+ CIM_StorageConfigurationService.CreateOrModifyStoragePool(
+ InPools = [CIMInstanceName of CIM_StoragePool,],
+ InExtents = [
+ CIMInstanceName of CIM_StorageExtent or CIM_StoragePool, ],
+ Size = Uint64 size,
+ Goal = CIMInstanceName of CIM_StorageSetting,
+ )
+
+ Only support disk pool for now.
+ Basic procedure:
+ 1. If vendor specific procedure available, use vendor specific
+ way only.
+ 2. Use SNIA general procedure (based on Gris's understanding)
+ to setup parameters for CreateOrModifyStoragePool()
+ 3. Use CIM_StorageConfigurationCapabilities to check capability
+ before callout.
+ 4. Call CreateOrModifyStoragePool()
+
+ SNIA general procedure:
+ 1. If raid_type defined, we set 'Goal' by finding predefined
+ CIM_StorageSetting. If not, we leave 'Goal' as undefine.
+ 2. If member_type defeind, we find certain type of disks to setup
+ 'InExtents'. If not, we leave 'InExtents' as undefine.
+ 3. When 'InExtents' was set, we set 'Size' to 1B just in case
+ the requested size exceed the 'InExtents' capable size.
+ 4. In SNIA 1.6.1 draft, allowing Goal defined disk type via
+ properties: InterconnectType, RPM, and etc. I see no
+ vendor support it yet.
+
+ TODO: Check CIM_StoragePool.GetSupportedSizeRange() before
+ execute pool creation call.
+ """
+ cim_sys = self._cim_sys_of_id(system_id)
+
+ if cim_sys.classname == 'LSIESG_MegaRAIDHBA':
+ return self._pool_create_lsi(cim_sys, pool_name, size_bytes,
+ raid_type, member_type, flags)
+
+ cim_scs = self._cim_scs(cim_sys.path, [])
+
+ # we does not support defining thinp_type yet.
+ # just using whatever provider set.
+
+ in_params = {}
+ in_params['ElementName'] = pool_name
+ in_params['Size'] = pywbem.Uint64(size_bytes)
- if size_bytes > 0:
- in_params['Size'] = pywbem.Uint64(size_bytes)
+ raise_error = True
- if raid_type != Pool.RAID_TYPE_UNKNOWN and \
- raid_type != Pool.RAID_TYPE_NOT_APPLICABLE:
- in_params['Goal'] = self._cim_st_set_for_goal(
- raid_type, thinp_type, cim_sys.path)
+ if member_type == Pool.MEMBER_TYPE_UNKNOWN:
+ raise_error = True # We only support disk pool, so raise error
+ # anyway.
+
+ if member_type == Pool.MEMBER_TYPE_UNKNOWN:
+ if raid_type == Pool.RAID_TYPE_UNKNOWN:
+ # 'Size' and 'ElementName' is enough.
+ pass
+ else:
+ # Need use 'Goal' to define RAID type
+ cim_st_set = self._find_preset_st_set(cim_sys.path, raid_type)
+ if cim_st_set is None:
+ if raise_error:
+ raise LsmError(ErrorNumber.NO_SUPPORT,
+ "RAID type %s(%d) is not supported "
+ % (Pool.raid_type_to_str(raid_type),
+ raid_type) +
+ "by target array")
+ else:
+ return (None, None)
+ in_params['Goal'] = cim_st_set.path
+
+ elif Pool.member_type_is_disk(member_type):
+ if raid_type != Pool.RAID_TYPE_UNKNOWN:
+ # Need to set 'Goal' parameter for RAID type
+ cim_st_set = self._find_preset_st_set(cim_sys.path, raid_type)
+ if cim_st_set is None:
+ if raise_error:
+ raise LsmError(ErrorNumber.NO_SUPPORT,
+ "RAID type %s(%d) is not supported "
+ % (Pool.raid_type_to_str(raid_type),
+ raid_type) +
+ "by target array")
+ else:
+ return (None, None)
+ in_params['Goal'] = cim_st_set.path
+
+ disk_type = Pool.member_type_to_disk_type(member_type)
+ if disk_type != Disk.DISK_TYPE_UNKNOWN:
+ # Need to set 'InExtents' parameter for disks
+ cim_disks = self._auto_choose_disks(
+ cim_sys.path, size_bytes, raid_type, disk_type,
+ raise_error)
+ if len(cim_disks) > 1:
+ in_params['InExtents'] = []
+ for cim_disk in cim_disks:
+ cim_ext = self._pri_cim_ext_of_cim_disk(
+ cim_disk.path, None)
+ in_params['InExtents'].extend([cim_ext.path])
+ in_params['Size'] = pywbem.Uint64(1)
+ else:
+ raise LsmError(ErrorNumber.NO_SUPPORT,
+ "Creating new pool with member type %s(%d) "
+ % (Pool.member_type_to_str(member_type),
+ member_type) + "is not supported")
in_params = self._pool_chg_paras_check(in_params, cim_sys.path)
+
return self._pi("pool_create", Smis.JOB_RETRIEVE_POOL,
*(self._c.InvokeMethod(
'CreateOrModifyStoragePool',
@@ -3346,11 +3493,30 @@ class Smis(IStorageAreaNetwork):
cim_disk_path)
@handle_cim_errors
- def _find_preset_st_set(self, cim_cap_path, raid_type, thinp_type):
+ def _find_preset_st_set(self, cim_sys_path , raid_type):
"""
Usage:
Find first proper CIM_StorageSetting under speficied
CIM_StorageCapabilities by giving raid_type and thinp_type.
+ Using this associations:
+ CIM_ComputerSystem
+ |
+ | CIM_HostedStoragePool
+ |
+ v
+ CIM_StoragePool (Primordial)
+ |
+ | CIM_ElementCapabilities
+ |
+ CIM_StorageCapabilities
+ |
+ | CIM_StorageSettingsAssociatedToCapabilities
+ |
+ v
+ CIM_StorageSetting (Predefined)
+
+ TODO: Support SPEC 1.6.1 for disk type in CIM_StorageSetting
+ But no vendor support that yet.
Parameter:
cim_cap_path # CIMInstanceName of CIM_StorageCapabilities
raid_type # Pool.RAID_TYPE_XXX
@@ -3360,15 +3526,31 @@ class Smis(IStorageAreaNetwork):
or
None # No match found
"""
- DMTF_CHANGEABLE_TYPE_FIX = 0
- cim_fix_st_sets = self._c.Associators(
- cim_cap_path,
- AssocClass='CIM_StorageSettingsAssociatedToCapabilities',
- ResultClass='CIM_StorageSetting',
- PropertyList=['ElementName',
- 'ThinProvisionedPoolType'])
- if not cim_fix_st_sets:
- return None
+ all_cim_st_sets = []
+ # get Primordial StoragePool
+ cim_pools = self._c.Associators(
+ cim_sys_path,
+ AssocClass='CIM_HostedStoragePool',
+ ResultClass='CIM_StoragePool',
+ PropertyList=['Primordial'])
+
+ for cim_pool in cim_pools:
+ if not cim_pool['Primordial']:
+ continue
+ cim_caps_path = self._c.AssociatorNames(
+ cim_pool.path,
+ AssocClass='CIM_ElementCapabilities',
+ ResultClass='CIM_StorageCapabilities')
+
+ for cim_cap_path in cim_caps_path:
+ tmp_cim_st_sets = self._c.Associators(
+ cim_cap_path,
+ AssocClass='CIM_StorageSettingsAssociatedToCapabilities',
+ ResultClass='CIM_StorageSetting',
+ PropertyList=['ElementName', 'ThinProvisionedPoolType'])
+ all_cim_st_sets.extend(tmp_cim_st_sets)
+
+
raid_type_str = Pool.raid_type_to_str(raid_type)
# According to SNIA suggest, RAID6 can also be writen as RAID5DP
# and etc.
@@ -3378,96 +3560,21 @@ class Smis(IStorageAreaNetwork):
elif raid_type_str == 'RAID10':
possible_element_names.extend(['RAID1+0'])
- if thinp_type != Pool.THINP_TYPE_UNKNOWN:
- if thinp_type == Pool.THINP_TYPE_THIN:
- # searching for a thin CIM_StorageSetting
- dmtf_thinp_type = self._lsm_thinp_type_to_dmtf(thinp_type)
-
- for cim_st_set in cim_fix_st_sets:
- if 'ElementName' in cim_st_set \
- and cim_st_set['ElementName'] \
- in possible_element_names \
- and cim_st_set['ThinProvisionedPoolType'] \
- == dmtf_thinp_type:
- return cim_st_set.path
- else: # searching for a Thick CIM_StorageSetting
- for cim_st_set in cim_fix_st_sets:
- if 'ElementName' not in cim_st_set or \
- cim_st_set['ElementName'] not in possible_element_names:
- continue
- if 'ThinProvisionedPoolType' not in cim_st_set:
- return cim_st_set.path
- # EMC define Thick pools with ThinProvisionedPoolType
- # value.
- elif cim_st_set.classname == 'Clar_StoragePoolSetting' \
- and cim_st_set['ThinProvisionedPoolType'] \
- == Smis.EMC_THINP_POOL_TYPE_THICK:
- return cim_st_set.path
- else: # Searching a CIM_StorageSetting regardless Thin or Thick.
- for cim_st_set in cim_fix_st_sets:
- if 'ElementName' in cim_st_set and \
- cim_st_set['ElementName'] in possible_element_names:
- return cim_st_set.path
- return None
+ chose_cim_st_sets = []
+ for cim_st_set in all_cim_st_sets:
+ if cim_st_set['ElementName'] in possible_element_names:
+ chose_cim_st_sets.extend([cim_st_set])
- def _cim_st_set_for_goal(self, raid_type, thinp_type, cim_sys_path):
- """
- Usage:
- Find out the array pre-defined CIM_StorageSetting for certain RAID
- Level. Only check CIM_StorageSetting['ElementName'] for RAID type,
- and CIM_StorageSetting['ThinProvisionedPoolType'] for Thin
- Provision setting.
- Even SNIA defined a way to create new setting, but we find out
- that not a good way to follow.
- Parameter:
- raid_type # Tier.RAID_TYPE_XXX
- disk_count # how many disks will this RAID group hold.
- cim_sys_path # CIMInstanceName of CIM_ComputerSystem.
- Returns:
- cim_st_set_path # Found or created CIMInstanceName of
- # CIM_StorageSetting
- Exceptions:
- LsmError
- ErrorNumber.NO_SUPPORT # Failed to find out
- # suitable CIM_StorageSetting
- """
- cim_chose_st_set_path = None
- # We will try to find the existing CIM_StorageSetting
- # with ElementName equal to raid_type_str
- # potted(pre-defined) CIM_StorageSetting
- cim_pool_path = None
- cim_pools = self._c.Associators(cim_sys_path,
- ResultClass='CIM_StoragePool',
- PropertyList=['Primordial'])
- # Base on SNIA commanded, each array should provide a
- # Primordial pool.
- for cim_tmp_pool in cim_pools:
- if cim_tmp_pool['Primordial']:
- cim_pool_path = cim_tmp_pool.path
- break
- if not cim_pool_path:
- raise LsmError(ErrorNumber.NO_SUPPORT,
- "Target storage array does not have "
- "Primordial CIM_StoragePool")
- cim_caps = self._c.Associators(
- cim_pool_path,
- ResultClass='CIM_StorageCapabilities',
- PropertyList=['ElementType'])
- for cim_cap in cim_caps:
- cim_tmp_st_set = self._find_preset_st_set(
- cim_cap.path,
- raid_type,
- thinp_type)
- if cim_tmp_st_set:
- cim_chose_st_set_path = cim_tmp_st_set
- break
- if not cim_chose_st_set_path:
- raise LsmError(ErrorNumber.NO_SUPPORT,
- "Current array does not support RAID type: " +
- "%s and thinp_type %s" %
- (Pool.raid_type_to_str(raid_type),
- Pool.thinp_type_to_str(thinp_type)))
- return cim_chose_st_set_path
+ # we prefer thin provisioning CIM_StorageSetting
+ for cim_st_set in chose_cim_st_sets:
+ if ('ThinProvisionedPoolType' in cim_st_set and \
+ cim_st_set['ThinProvisionedPoolType'] == \
+ Smis.DMTF_THINP_POOL_TYPE_ALLOCATED):
+ return cim_st_set
+
+ if len(chose_cim_st_sets) > 0:
+ return chose_cim_st_sets[0]
+ return None
def _pool_chg_paras_check(self, in_params, cim_sys_path):
"""
@@ -3731,7 +3838,7 @@ class Smis(IStorageAreaNetwork):
Returns:
cim_syss # A list of CIMInstanceName of CIM_ComputerSystem
or
- None
+ []
"""
sys_id_pros = self._property_list_of_id('System')
flag_full_info = False
@@ -3795,3 +3902,308 @@ class Smis(IStorageAreaNetwork):
return needed_cim_syss
else:
return cim_syss
+
+ def _free_disks_list(self, cim_sys_path, disk_type=Disk.DISK_TYPE_UNKNOWN):
+ """
+ Usage:
+ Return a list of cim_disk which is free.
+ Using two ways to check free disks:
+ A. CIM_StoragePool.GetAvailableExtents(
+ Goal=CIMInstanceName of CIM_StorageSetting)
+ B. Check for those disks not used by any pool.
+
+ Method A) is optional SNIA SMI-S Block Service Package
+ (SPEC 1.4 to 1.6)
+
+ Since even LSI support method A(without profile registering),
+ we skip method B)
+
+ When disk_type define, will only contain defined type of disk
+ in return list.
+ The cim_disk will only contain properties defined in
+ Smis._new_disk_cim_ext_pros()
+ TODO: Need tests again IBM SVC which use remote volume as pool
+ member.
+ Parameter:
+ cim_sys_path # CIMInstanceName of CIM_ComputerSystem
+ disk_type # Disk.DISK_TYPE_XXX
+ Returns:
+ [cim_disk, ] # a list of items.
+ or
+ [] # Nothing found
+ Exceptions:
+ LsmError
+ ErrorNumber.INTERNAL_ERROR
+ * When no Primordial StoragePool found.
+ N/A
+ """
+ rc = []
+
+ if self._profile_is_supported(Smis.SNIA_BLK_SRVS_PROFILE,
+ Smis.SNIA_SMIS_SPEC_VER_1_4,
+ strict=False) and \
+ not self._profile_is_supported(Smis.SNIA_DISK_LITE_PROFILE,
+ Smis.SNIA_SMIS_SPEC_VER_1_4,
+ strict=False):
+ # Support 'Disk Drive Lite' 1.4+ is prerequisite.
+ return []
+
+ # get Primordial StoragePool
+ cim_pools = self._c.Associators(
+ cim_sys_path,
+ AssocClass='CIM_HostedStoragePool',
+ ResultClass='CIM_StoragePool',
+ PropertyList=['Primordial'])
+
+ cim_pools = [p for p in cim_pools if p["Primordial"]]
+ if len(cim_pools) == 0:
+ raise LsmError(ErrorNumber.INTERNAL_ERROR,
+ "No Primordial CIM_StoragePool found for "
+ "system %s, might be a provider's bug"
+ % cim_sys_path)
+ cim_disk_pros = self._new_disk_cim_disk_pros()
+ # LSI has more than 1 Primordial StoragePool :(
+ for cim_pool in cim_pools:
+ return_code = 0
+ out = dict()
+ # TODO: Use Goal parameter of
+ # CIM_StoragePool.GetAvailableExtents() to filter
+ # CIM_StorageExtent
+ try:
+ (return_code, out) = self._c.InvokeMethod(
+ 'GetAvailableExtents',
+ cim_pool.path)
+ except CIMError as e:
+ if e[0] == pywbem.CIM_ERR_NOT_SUPPORTED or \
+ e[0] == pywbem.CIM_ERR_NOT_FOUND:
+ break
+ else:
+ raise e
+
+ if return_code == Smis.INVOKE_OK and \
+ 'AvailableExtents' in out and \
+ out['AvailableExtents']:
+ for cim_pri_ext_path in out['AvailableExtents']:
+ rc.extend(
+ self._c.Associators(
+ cim_pri_ext_path,
+ AssocClass='CIM_MediaPresent',
+ ResultClass='CIM_DiskDrive',
+ PropertyList=cim_disk_pros))
+ if disk_type != Disk.DISK_TYPE_UNKNOWN:
+ cleanup_rc = []
+ for cim_disk in rc:
+ if self._disk_type_of(cim_disk) == disk_type:
+ cleanup_rc.extend([cim_disk])
+
+ rc = cleanup_rc
+ return rc
+
+ def _free_disks(self, cim_sys_path, disk_type=Disk.DISK_TYPE_UNKNOWN):
+ """
+ Usage:
+ Return a structure like this:
+ {
+ Disk.disk_type = {
+ Disk.total_space = [ cim_disk, ],
+ },
+ }
+ When disk_type define, will only contain defined type of disk
+ in return structure.
+ The cim_disk will only contain properties defined in
+ Smis._new_disk_cim_ext_pros()
+ Parameter:
+ cim_sys_path # CIMInstanceName of CIM_ComputerSystem
+ disk_type # Disk.DISK_TYPE_XXX
+ Returns:
+ free_disk_t # Check above
+ or
+ dcit() # Nothing found
+ Exceptions:
+ N/A
+ """
+ rc = dict()
+ free_cim_disks = self._free_disks_list(cim_sys_path, disk_type)
+ if len(free_cim_disks) == 0:
+ return rc
+
+ for cim_disk in free_cim_disks:
+ disk_type = self._disk_type_of(cim_disk)
+ disk_size = self._disk_total_space_of(cim_disk)
+ if disk_size == 0 or disk_type == Disk.DISK_TYPE_UNKNOWN:
+ continue
+ if disk_type not in rc.keys():
+ rc[disk_type] = dict()
+
+ if disk_size not in rc[disk_type].keys():
+ rc[disk_type][disk_size] = []
+ rc[disk_type][disk_size].extend([cim_disk])
+
+ return rc
+
+ def _size_of_disk_raid(self, cim_disks, raid_type):
+ member_sizes = []
+ for cim_disk in cim_disks:
+ disk_size = self._disk_total_space_of(cim_disk)
+ if disk_size > 1:
+ member_sizes.extend([disk_size])
+
+ if len(member_sizes) == 0:
+ return 0
+
+ all_size = 0
+ member_count = len(member_sizes)
+ for member_size in member_sizes:
+ all_size += member_size
+
+ # assuming all raid member is in the same size which was already
+ # checked by caller. (JBOD and NOT_APPLICABLE does not require that)
+ member_size = member_sizes[0]
+
+ if raid_type == Pool.RAID_TYPE_JBOD or \
+ raid_type == Pool.RAID_TYPE_NOT_APPLICABLE or \
+ raid_type == Pool.RAID_TYPE_RAID0:
+ return int(all_size)
+ elif (raid_type == Pool.RAID_TYPE_RAID1 or
+ raid_type == Pool.RAID_TYPE_RAID10):
+ if member_count % 2 == 1:
+ return 0
+ return int(all_size/2)
+ elif (raid_type == Pool.RAID_TYPE_RAID3 or
+ raid_type == Pool.RAID_TYPE_RAID4 or
+ raid_type == Pool.RAID_TYPE_RAID5):
+ if member_count < 3:
+ return 0
+ return int(all_size - member_size)
+ elif raid_type == Pool.RAID_TYPE_RAID50:
+ if member_count < 6 or member_count % 2 == 1:
+ return 0
+ return int(all_size - member_size*2)
+ elif raid_type == Pool.RAID_TYPE_RAID6:
+ if member_count < 4:
+ return 0
+ return int(all_size - member_size * 2)
+ elif raid_type == Pool.RAID_TYPE_RAID60:
+ if member_count < 8 or member_count % 2 == 1:
+ return 0
+ return int(all_size - member_size*4)
+ elif raid_type == Pool.RAID_TYPE_RAID51:
+ if member_count < 6 or member_count % 2 == 1:
+ return 0
+ return int(all_size/2 - member_size)
+ elif raid_type == Pool.RAID_TYPE_RAID61:
+ if member_count < 8 or member_count % 2 == 1:
+ return 0
+ return int(all_size/2 - member_size*2)
+ raise LsmError(ErrorNumber.INTERNAL_ERROR,
+ "_size_of_raid() got invalid raid type: " +
+ "%s(%d)" % (Pool.raid_type_to_str(raid_type),
+ raid_type))
+
+ def _auto_choose_disks(self, cim_sys_path, size_bytes, raid_type,
+ disk_type, raise_error=False):
+ """
+ Usage:
+ Automatically choose the correct disks to assemble defined
+ RAID.
+ Considering the pool/raid overhead, size_bytes will increase
+ Smis._NEW_POOL_SIZE_OVERHAD
+ Raise LsmError if nothing found when raise_error is True.
+ Return [] if nothing found when raise_error is False.
+ Parameter:
+ cim_sys_path # CIMInstanceName of CIM_ComputerSystem
+ size_bytes # int, requested size for new RAID.
+ raid_type # Pool.RAID_TYPE_XXX
+ disk_type # Disk.DISK_TYPE_XXX
+ raise_error # True or False, raise on error or not.
+ Returns:
+ [cim_disk, ] # a list of cim_disk
+ or
+ [] # Nothing found if raise_error is False
+ Exceptions:
+ LsmError
+ ErrorNumber.DISK_BUSY
+ # No free disks
+ ErrorNumber.SIZE_INSUFFICIENT_SPACE
+ # No enough disks or size meet request
+ """
+ new_size_bytes = size_bytes + Smis._NEW_POOL_SIZE_OVERHAD
+
+ disk_type_str = "disk"
+ if disk_type != Disk.DISK_TYPE_UNKNOWN:
+ disk_type_str = "disk(type: %s)" % Disk.disk_type_to_str(disk_type)
+
+ if raid_type == Pool.RAID_TYPE_NOT_APPLICABLE:
+ # NOT_APPLICABLE means pool will only contain one disk.
+ cim_disks = self._free_disks_list(cim_sys_path, disk_type)
+ if len(cim_disks) == 0:
+ if raise_error:
+ raise LsmError(ErrorNumber.DISK_BUSY,
+ "No free %s found" % disk_type_str)
+ else:
+ return []
+
+ for cim_disk in cim_disks:
+ if self._disk_total_space_of(cim_disk) >= new_size_bytes:
+ return [cim_disk]
+ if raise_error:
+ raise LsmError(ErrorNumber.SIZE_INSUFFICIENT_SPACE,
+ "No %s is bigger than " % disk_type_str +
+ "expected size: %s(%d)" %
+ (size_bytes_2_size_human(size_bytes),
+ size_bytes))
+ else:
+ return []
+
+ if raid_type == Pool.RAID_TYPE_JBOD:
+ # JBOD does not require all disks in the same size or the same type.
+ cim_disks = self._free_disks_list(cim_sys_path, disk_type)
+ if len(cim_disks) == 0:
+ if raise_error:
+ raise LsmError(ErrorNumber.DISK_BUSY,
+ "No free %s found" % disk_type_str)
+ else:
+ return []
+
+ chose_cim_disks = []
+ all_free_size = 0
+ for cim_disk in cim_disks:
+ chose_cim_disks.extend([cim_disk])
+ all_free_size += self._disk_total_space_of(cim_disk)
+ if all_free_size >= new_size_bytes:
+ return chose_cim_disks
+ if raise_error:
+ raise LsmError(ErrorNumber.SIZE_INSUFFICIENT_SPACE,
+ "No enough %s to provide size %s(%d)" %
+ (disk_type_str,
+ size_bytes_2_size_human(size_bytes),
+ size_bytes))
+ else:
+ return []
+
+ # All rest RAID type require member are in the same size and same
+ # type.
+ cim_disks_struct = self._free_disks(cim_sys_path, disk_type)
+ for cur_disk_type in cim_disks_struct.keys():
+ for cur_disk_size in cim_disks_struct[cur_disk_type].keys():
+ cur_cim_disks = cim_disks_struct[cur_disk_type][cur_disk_size]
+ if len(cur_cim_disks) == 0:
+ continue
+ chose_cim_disks = []
+ for member_count in range(1, len(cur_cim_disks) + 1):
+ partial_cim_disks = cur_cim_disks[0:member_count]
+ raid_actual_size = self._size_of_disk_raid(
+ partial_cim_disks, raid_type)
+ if new_size_bytes <= raid_actual_size:
+ return partial_cim_disks
+
+ if raise_error:
+ raise LsmError(ErrorNumber.SIZE_INSUFFICIENT_SPACE,
+ "No enough %s " % disk_type_str +
+ "to create %s providing size: %s(%d)" %
+ (Pool.raid_type_to_str(raid_type),
+ size_bytes_2_size_human(size_bytes),
+ size_bytes))
+ else:
+ return []
+
--
1.8.3.1