1460 lines
65 KiB
Python
1460 lines
65 KiB
Python
# Copyright (c) 2012 - 2014 EMC Corporation.
|
|
# All Rights Reserved.
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
# not use this file except in compliance with the License. You may obtain
|
|
# a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
# License for the specific language governing permissions and limitations
|
|
# under the License.
|
|
import six
|
|
|
|
from cinder import exception
|
|
from cinder.i18n import _
|
|
from cinder.openstack.common import log as logging
|
|
from cinder.volume.drivers.emc import emc_vmax_fast
|
|
from cinder.volume.drivers.emc import emc_vmax_provision
|
|
from cinder.volume.drivers.emc import emc_vmax_utils
|
|
|
|
LOG = logging.getLogger(__name__)
|
|
|
|
STORAGEGROUPTYPE = 4
|
|
POSTGROUPTYPE = 3
|
|
INITIATORGROUPTYPE = 2
|
|
|
|
ISCSI = 'iscsi'
|
|
FC = 'fc'
|
|
|
|
EMC_ROOT = 'root/emc'
|
|
|
|
|
|
class EMCVMAXMasking(object):
|
|
"""Masking class for SMI-S based EMC volume drivers.
|
|
|
|
Masking code to dynamically create a masking view
|
|
This masking class is for EMC volume drivers based on SMI-S.
|
|
It supports VMAX arrays.
|
|
"""
|
|
def __init__(self, prtcl):
|
|
self.protocol = prtcl
|
|
self.utils = emc_vmax_utils.EMCVMAXUtils(prtcl)
|
|
self.fast = emc_vmax_fast.EMCVMAXFast(prtcl)
|
|
self.provision = emc_vmax_provision.EMCVMAXProvision(prtcl)
|
|
|
|
def get_or_create_masking_view_and_map_lun(self, conn, maskingViewDict):
|
|
"""Get or Create a masking view.
|
|
|
|
Given a masking view tuple either get or create a masking view and add
|
|
the volume to the associated storage group
|
|
|
|
:param conn: the connection to ecom
|
|
:para maskingViewDict: the masking view tuple
|
|
:returns: dict rollbackDict
|
|
"""
|
|
rollbackDict = {}
|
|
|
|
controllerConfigService = maskingViewDict['controllerConfigService']
|
|
sgGroupName = maskingViewDict['sgGroupName']
|
|
volumeInstance = maskingViewDict['volumeInstance']
|
|
igGroupName = maskingViewDict['igGroupName']
|
|
connector = maskingViewDict['connector']
|
|
storageSystemName = maskingViewDict['storageSystemName']
|
|
maskingViewName = maskingViewDict['maskingViewName']
|
|
volumeName = maskingViewDict['volumeName']
|
|
pgGroupName = maskingViewDict['pgGroupName']
|
|
|
|
fastPolicyName = maskingViewDict['fastPolicy']
|
|
defaultStorageGroupInstanceName = None
|
|
|
|
# we need a rollback scenario for FAST.
|
|
# We must make sure that volume is returned to default storage
|
|
# group if anything goes wrong
|
|
if fastPolicyName is not None:
|
|
defaultStorageGroupInstanceName = (
|
|
self.fast.get_and_verify_default_storage_group(
|
|
conn, controllerConfigService, volumeInstance.path,
|
|
volumeName, fastPolicyName))
|
|
if defaultStorageGroupInstanceName is None:
|
|
exceptionMessage = (_(
|
|
"Cannot get the default storage group for FAST policy: "
|
|
"%(fastPolicyName)s. ")
|
|
% {'fastPolicyName': fastPolicyName})
|
|
LOG.error(exceptionMessage)
|
|
raise exception.VolumeBackendAPIException(
|
|
data=exceptionMessage)
|
|
|
|
retStorageGroupInstanceName = (
|
|
self.remove_device_from_default_storage_group(
|
|
conn, controllerConfigService, volumeInstance.path,
|
|
volumeName, fastPolicyName))
|
|
if retStorageGroupInstanceName is None:
|
|
exceptionMessage = (_(
|
|
"Failed to remove volume %(volumeName)s from default SG: "
|
|
"%(volumeName)s. ")
|
|
% {'volumeName': volumeName})
|
|
LOG.error(exceptionMessage)
|
|
raise exception.VolumeBackendAPIException(
|
|
data=exceptionMessage)
|
|
|
|
try:
|
|
maskingViewInstanceName = self._find_masking_view(
|
|
conn, maskingViewName, storageSystemName)
|
|
if maskingViewInstanceName is None:
|
|
storageGroupInstanceName = (
|
|
self._get_storage_group_instance_name(
|
|
conn, controllerConfigService, volumeInstance,
|
|
volumeName, sgGroupName, fastPolicyName,
|
|
storageSystemName, defaultStorageGroupInstanceName))
|
|
if storageGroupInstanceName is None:
|
|
exceptionMessage = (_(
|
|
"Cannot get or create a storage group: %(sgGroupName)s"
|
|
" for volume %(volumeName)s ")
|
|
% {'sgGroupName': sgGroupName,
|
|
'volumeName': volumeName})
|
|
LOG.error(exceptionMessage)
|
|
raise
|
|
|
|
portGroupInstanceName = self._get_port_group_instance_name(
|
|
conn, controllerConfigService, pgGroupName)
|
|
if portGroupInstanceName is None:
|
|
exceptionMessage = (_(
|
|
"Cannot get port group: %(pgGroupName)s. ")
|
|
% {'pgGroupName': pgGroupName})
|
|
LOG.error(exceptionMessage)
|
|
raise
|
|
|
|
initiatorGroupInstanceName = (
|
|
self._get_initiator_group_instance_name(
|
|
conn, controllerConfigService, igGroupName, connector,
|
|
storageSystemName))
|
|
if initiatorGroupInstanceName is None:
|
|
exceptionMessage = (_(
|
|
"Cannot get or create initiator group: "
|
|
"%(igGroupName)s. ")
|
|
% {'igGroupName': igGroupName})
|
|
LOG.error(exceptionMessage)
|
|
raise
|
|
|
|
maskingViewInstanceName = (
|
|
self._get_masking_view_instance_name(
|
|
conn, controllerConfigService, maskingViewName,
|
|
storageGroupInstanceName, portGroupInstanceName,
|
|
initiatorGroupInstanceName))
|
|
if maskingViewInstanceName is None:
|
|
exceptionMessage = (_(
|
|
"Cannot create masking view: %(maskingViewName)s. ")
|
|
% {'maskingViewName': maskingViewName})
|
|
LOG.error(exceptionMessage)
|
|
raise
|
|
|
|
else:
|
|
# first verify that the initiator group matches the initiators
|
|
if not self._verify_initiator_group_from_masking_view(
|
|
conn, controllerConfigService, maskingViewName,
|
|
connector, storageSystemName, igGroupName):
|
|
exceptionMessage = (_(
|
|
"Unable to verify initiator group: %(igGroupName)s"
|
|
"in masking view %(maskingViewName)s ")
|
|
% {'igGroupName': igGroupName,
|
|
'maskingViewName': maskingViewName})
|
|
LOG.error(exceptionMessage)
|
|
raise
|
|
|
|
# get the storage from the masking view and add the
|
|
# volume to it.
|
|
storageGroupInstanceName = (
|
|
self._get_storage_group_from_masking_view(
|
|
conn, maskingViewName, storageSystemName))
|
|
|
|
if storageGroupInstanceName is None:
|
|
exceptionMessage = (_(
|
|
"Cannot get storage group from masking view: "
|
|
"%(maskingViewName)s. ")
|
|
% {'maskingViewName': maskingViewName})
|
|
LOG.error(exceptionMessage)
|
|
raise
|
|
|
|
if self._is_volume_in_storage_group(
|
|
conn, storageGroupInstanceName,
|
|
volumeInstance):
|
|
LOG.warn(_(
|
|
"Volume: %(volumeName)s is already part "
|
|
"of storage group %(sgGroupName)s ")
|
|
% {'volumeName': volumeName,
|
|
'sgGroupName': sgGroupName})
|
|
else:
|
|
self.add_volume_to_storage_group(
|
|
conn, controllerConfigService,
|
|
storageGroupInstanceName, volumeInstance, volumeName,
|
|
sgGroupName, fastPolicyName, storageSystemName)
|
|
|
|
except Exception as e:
|
|
# rollback code if we cannot complete any of the steps above
|
|
# successfully then we must roll back by adding the volume back to
|
|
# the default storage group for that fast policy
|
|
if (fastPolicyName is not None and
|
|
defaultStorageGroupInstanceName is not None):
|
|
# if the exception happened before the volume was removed from
|
|
# the default storage group no action
|
|
self._check_if_rollback_action_for_masking_required(
|
|
conn, controllerConfigService, volumeInstance, volumeName,
|
|
fastPolicyName, defaultStorageGroupInstanceName)
|
|
|
|
LOG.error(_("Exception: %s") % six.text_type(e))
|
|
errorMessage = (_(
|
|
"Failed to get or create masking view %(maskingViewName)s ")
|
|
% {'maskingViewName': maskingViewName})
|
|
LOG.error(errorMessage)
|
|
exception.VolumeBackendAPIException(data=errorMessage)
|
|
|
|
rollbackDict['controllerConfigService'] = controllerConfigService
|
|
rollbackDict['defaultStorageGroupInstanceName'] = (
|
|
defaultStorageGroupInstanceName)
|
|
rollbackDict['volumeInstance'] = volumeInstance
|
|
rollbackDict['volumeName'] = volumeName
|
|
rollbackDict['fastPolicyName'] = fastPolicyName
|
|
return rollbackDict
|
|
|
|
def _is_volume_in_storage_group(
|
|
self, conn, storageGroupInstanceName, volumeInstance):
|
|
"""Check if the volume is already part of the storage group.
|
|
|
|
Check if the volume is already part of the storage group,
|
|
if it is no need to re-add it.
|
|
|
|
:param conn: the connection to ecom
|
|
:param storageGroupInstanceName: the storage group instance name
|
|
:param volumeInstance: the volume instance
|
|
:returns: boolean True/False
|
|
"""
|
|
foundStorageGroupInstanceName = (
|
|
self.utils.get_storage_group_from_volume(
|
|
conn, volumeInstance.path))
|
|
|
|
storageGroupInstance = conn.GetInstance(
|
|
storageGroupInstanceName, LocalOnly=False)
|
|
|
|
LOG.debug(
|
|
"The existing storage group instance element name is: "
|
|
"%(existingElement)s. "
|
|
% {'existingElement': storageGroupInstance['ElementName']})
|
|
|
|
if foundStorageGroupInstanceName is not None:
|
|
foundStorageGroupInstance = conn.GetInstance(
|
|
foundStorageGroupInstanceName, LocalOnly=False)
|
|
LOG.debug(
|
|
"The found storage group instance element name is: "
|
|
"%(foundElement)s. "
|
|
% {'foundElement': foundStorageGroupInstance['ElementName']})
|
|
if (foundStorageGroupInstance['ElementName'] == (
|
|
storageGroupInstance['ElementName'])):
|
|
LOG.warn(_(
|
|
"The volume is already part of storage group: "
|
|
"%(storageGroupInstanceName)s. ")
|
|
% {'storageGroupInstanceName': storageGroupInstanceName})
|
|
return True
|
|
|
|
return False
|
|
|
|
def _find_masking_view(self, conn, maskingViewName, storageSystemName):
|
|
"""Given the masking view name get the masking view instance.
|
|
|
|
:param conn: connection to the ecom server
|
|
:param maskingViewName: the masking view name
|
|
:param storageSystemName: the storage system name(String)
|
|
:returns: foundMaskingViewInstanceName masking view instance name
|
|
"""
|
|
foundMaskingViewInstanceName = None
|
|
maskingViewInstanceNames = conn.EnumerateInstanceNames(
|
|
'EMC_LunMaskingSCSIProtocolController')
|
|
|
|
for maskingViewInstanceName in maskingViewInstanceNames:
|
|
if storageSystemName == maskingViewInstanceName['SystemName']:
|
|
instance = conn.GetInstance(
|
|
maskingViewInstanceName, LocalOnly=False)
|
|
if maskingViewName == instance['ElementName']:
|
|
foundMaskingViewInstanceName = maskingViewInstanceName
|
|
break
|
|
|
|
if foundMaskingViewInstanceName is not None:
|
|
infoMessage = (_(
|
|
"Found existing masking view: %(maskingViewName)s ")
|
|
% {'maskingViewName': maskingViewName})
|
|
LOG.info(infoMessage)
|
|
return foundMaskingViewInstanceName
|
|
|
|
def _create_storage_group(
|
|
self, conn, controllerConfigService, storageGroupName,
|
|
volumeInstance, fastPolicyName, volumeName, storageSystemName,
|
|
defaultStorageGroupInstanceName):
|
|
"""Create a new storage group that doesn't already exist.
|
|
|
|
If fastPolicyName is not none we attempt to remove it from the
|
|
default storage group of that policy and associate to the new storage
|
|
group that will be part of the masking view.
|
|
Will not handle any exception in this method it will be handled
|
|
up the stack
|
|
|
|
:param conn: connection the ecom server
|
|
:param controllerConfigService: the controller configuration service
|
|
:param storageGroupName: the proposed group name (String)
|
|
:param volumeInstance: useful information on the volume
|
|
:param fastPolicyName: the fast policy name (String) can be None
|
|
:param volumeName: the volume name (String)
|
|
:param storageSystemName: the storage system name (String)
|
|
:param defaultStorageGroupInstanceName: the default storage group
|
|
instance name (Can be None)
|
|
:returns: foundStorageGroupInstanceName the instance Name of the
|
|
storage group
|
|
"""
|
|
failedRet = None
|
|
foundStorageGroupInstanceName = (
|
|
self.provision.create_and_get_storage_group(
|
|
conn, controllerConfigService, storageGroupName,
|
|
volumeInstance.path))
|
|
if foundStorageGroupInstanceName is None:
|
|
LOG.error(_(
|
|
"Cannot get storage Group from job : %(storageGroupName)s. ")
|
|
% {'storageGroupName': storageGroupName})
|
|
return failedRet
|
|
else:
|
|
LOG.info(_(
|
|
"Created new storage group: %(storageGroupName)s ")
|
|
% {'storageGroupName': storageGroupName})
|
|
|
|
if (fastPolicyName is not None and
|
|
defaultStorageGroupInstanceName is not None):
|
|
assocTierPolicyInstanceName = (
|
|
self.fast.add_storage_group_and_verify_tier_policy_assoc(
|
|
conn, controllerConfigService,
|
|
foundStorageGroupInstanceName,
|
|
storageGroupName, fastPolicyName))
|
|
if assocTierPolicyInstanceName is None:
|
|
LOG.error(_(
|
|
"Cannot add and verify tier policy association for storage"
|
|
" group : %(storageGroupName)s to FAST policy : "
|
|
"%(fastPolicyName)s. ")
|
|
% {'storageGroupName': storageGroupName,
|
|
'fastPolicyName': fastPolicyName})
|
|
return failedRet
|
|
|
|
return foundStorageGroupInstanceName
|
|
|
|
def _find_port_group(self, conn, controllerConfigService, portGroupName):
|
|
"""Given the port Group name get the port group instance name.
|
|
|
|
:param conn: connection to the ecom server
|
|
:param controllerConfigService: the controller configuration service
|
|
:param portGroupName: the name of the port group you are getting
|
|
:returns: foundPortGroup storage group instance name
|
|
"""
|
|
foundPortGroupInstanceName = None
|
|
portMaskingGroupInstanceNames = conn.AssociatorNames(
|
|
controllerConfigService, resultClass='CIM_TargetMaskingGroup')
|
|
|
|
for portMaskingGroupInstanceName in portMaskingGroupInstanceNames:
|
|
instance = conn.GetInstance(
|
|
portMaskingGroupInstanceName, LocalOnly=False)
|
|
if portGroupName == instance['ElementName']:
|
|
foundPortGroupInstanceName = portMaskingGroupInstanceName
|
|
break
|
|
|
|
if foundPortGroupInstanceName is None:
|
|
LOG.error(_(
|
|
"Could not find port group : %(portGroupName)s. Check that the"
|
|
" EMC configuration file has the correct port group name. ")
|
|
% {'portGroupName': portGroupName})
|
|
|
|
return foundPortGroupInstanceName
|
|
|
|
def _create_or_get_initiator_group(
|
|
self, conn, controllerConfigService, igGroupName,
|
|
connector, storageSystemName):
|
|
"""Attempt to create a initiatorGroup.
|
|
|
|
If one already exists with the same Initiator/wwns then get it
|
|
|
|
Check to see if an initiatorGroup already exists, that matches the
|
|
connector information
|
|
NOTE: An initiator/wwn can only belong to one initiatorGroup.
|
|
If we were to attempt to create one with an initiator/wwn that
|
|
is already belong to another initiatorGroup, it would fail
|
|
|
|
:param conn: connection to the ecom server
|
|
:param controllerConfigService: the controller config Servicer
|
|
:param igGroupName: the proposed name of the initiator group
|
|
:param connector: the connector information to the host
|
|
:param storageSystemName: the storage system name (String)
|
|
:returns: foundInitiatorGroupInstanceName
|
|
"""
|
|
failedRet = None
|
|
initiatorNames = self._find_initiator_names(conn, connector)
|
|
LOG.debug("The initiator name(s) are: %(initiatorNames)s "
|
|
% {'initiatorNames': initiatorNames})
|
|
|
|
foundInitiatorGroupInstanceName = self._find_initiator_masking_group(
|
|
conn, controllerConfigService, initiatorNames)
|
|
|
|
# If you cannot find an initiatorGroup that matches the connector
|
|
# info create a new initiatorGroup
|
|
if foundInitiatorGroupInstanceName is None:
|
|
# check that our connector information matches the
|
|
# hardwareId(s) on the symm
|
|
storageHardwareIDInstanceNames = (
|
|
self._get_storage_hardware_id_instance_names(
|
|
conn, initiatorNames, storageSystemName))
|
|
if not storageHardwareIDInstanceNames:
|
|
LOG.error(_(
|
|
"Initiator Name(s) %(initiatorNames)s are not on array "
|
|
"%(storageSystemName)s ")
|
|
% {'initiatorNames': initiatorNames,
|
|
'storageSystemName': storageSystemName})
|
|
return failedRet
|
|
|
|
foundInitiatorGroupInstanceName = self._create_initiator_Group(
|
|
conn, controllerConfigService, igGroupName,
|
|
storageHardwareIDInstanceNames)
|
|
|
|
LOG.info("Created new initiator group name: %(igGroupName)s "
|
|
% {'igGroupName': igGroupName})
|
|
else:
|
|
LOG.info("Using existing initiator group name: %(igGroupName)s "
|
|
% {'igGroupName': igGroupName})
|
|
|
|
return foundInitiatorGroupInstanceName
|
|
|
|
def _find_initiator_names(self, conn, connector):
|
|
"""check the connector object for initiators(ISCSI) or wwpns(FC).
|
|
|
|
:param conn: the connection to the ecom
|
|
:param connector: the connector object
|
|
:returns list foundinitiatornames list of string initiator names
|
|
"""
|
|
foundinitiatornames = []
|
|
name = 'initiator name'
|
|
if (self.protocol.lower() == ISCSI and connector['initiator']):
|
|
foundinitiatornames.append(connector['initiator'])
|
|
elif (self.protocol.lower() == FC and connector['wwpns']):
|
|
for wwn in connector['wwpns']:
|
|
foundinitiatornames.append(wwn)
|
|
name = 'world wide port names'
|
|
|
|
if (foundinitiatornames is None or len(foundinitiatornames) == 0):
|
|
msg = (_('Error finding %s.') % name)
|
|
LOG.error(msg)
|
|
raise exception.VolumeBackendAPIException(data=msg)
|
|
|
|
LOG.debug("Found %(name)s: %(initiator)s."
|
|
% {'name': name,
|
|
'initiator': foundinitiatornames})
|
|
|
|
return foundinitiatornames
|
|
|
|
def _find_initiator_masking_group(
|
|
self, conn, controllerConfigService, initiatorNames):
|
|
"""Check to see if an initiatorGroup already exists.
|
|
|
|
NOTE: An initiator/wwn can only belong to one initiatorGroup.
|
|
If we were to attempt to create one with an initiator/wwn that is
|
|
already belong to another initiatorGroup, it would fail
|
|
|
|
:param conn: the connection to the ecom server
|
|
:param controllerConfigService: the controller configuration service
|
|
:param initiatorName: the list of initiator names
|
|
:returns: foundInitiatorMaskingGroup
|
|
"""
|
|
foundInitiatorMaskingGroupName = None
|
|
|
|
initiatorMaskingGroupNames = (
|
|
conn.AssociatorNames(controllerConfigService,
|
|
ResultClass='CIM_InitiatorMaskingGroup'))
|
|
|
|
for initiatorMaskingGroupName in initiatorMaskingGroupNames:
|
|
initiatorMaskingGroup = conn.GetInstance(
|
|
initiatorMaskingGroupName, LocalOnly=False)
|
|
associators = (
|
|
conn.Associators(initiatorMaskingGroup.path,
|
|
ResultClass='EMC_StorageHardwareID'))
|
|
for assoc in associators:
|
|
# if EMC_StorageHardwareID matches the initiator,
|
|
# we found the existing EMC_LunMaskingSCSIProtocolController
|
|
# (Storage Group for VNX)
|
|
# we can use for masking a new LUN
|
|
hardwareid = assoc['StorageID']
|
|
for initiator in initiatorNames:
|
|
if six.text_type(hardwareid).lower() == \
|
|
six.text_type(initiator).lower():
|
|
foundInitiatorMaskingGroupName = (
|
|
initiatorMaskingGroupName)
|
|
break
|
|
|
|
if foundInitiatorMaskingGroupName is not None:
|
|
break
|
|
|
|
if foundInitiatorMaskingGroupName is not None:
|
|
break
|
|
return foundInitiatorMaskingGroupName
|
|
|
|
def _get_storage_hardware_id_instance_names(
|
|
self, conn, initiatorNames, storageSystemName):
|
|
"""Given a list of initiator names find CIM_StorageHardwareID instance.
|
|
|
|
:param conn: the connection to the ecom server
|
|
:param initiatorName: the list of initiator names
|
|
:param storageSystemName: the storage system name
|
|
:returns: foundHardwardIDsInstanceNames
|
|
"""
|
|
foundHardwardIDsInstanceNames = []
|
|
|
|
hardwareIdManagementService = (
|
|
self.utils.find_storage_hardwareid_service(
|
|
conn, storageSystemName))
|
|
|
|
hardwareIdInstanceNames = (
|
|
self.utils.get_hardware_id_instance_names_from_array(
|
|
conn, hardwareIdManagementService))
|
|
|
|
for hardwareIdInstanceName in hardwareIdInstanceNames:
|
|
hardwareIdInstance = conn.GetInstance(hardwareIdInstanceName)
|
|
storageId = hardwareIdInstance['StorageID']
|
|
for initiatorName in initiatorNames:
|
|
LOG.debug("The storage Id is : %(storageId)s "
|
|
% {'storageId': storageId.lower()})
|
|
LOG.debug("The initiatorName is : %(initiatorName)s "
|
|
% {'initiatorName': initiatorName.lower()})
|
|
if storageId.lower() == initiatorName.lower():
|
|
foundHardwardIDsInstanceNames.append(
|
|
hardwareIdInstanceName)
|
|
break
|
|
|
|
LOG.debug(
|
|
"The found hardware IDs are : %(foundHardwardIDsInstanceNames)s "
|
|
% {'foundHardwardIDsInstanceNames': foundHardwardIDsInstanceNames})
|
|
|
|
return foundHardwardIDsInstanceNames
|
|
|
|
def _get_initiator_group_from_job(self, conn, job):
|
|
"""After creating an new initiator group find it and return it
|
|
|
|
:param conn: the connection to the ecom server
|
|
:param job: the create initiator group job
|
|
:returns: dict initiatorDict
|
|
"""
|
|
associators = conn.Associators(
|
|
job['Job'],
|
|
ResultClass='CIM_InitiatorMaskingGroup')
|
|
volpath = associators[0].path
|
|
initiatorDict = {}
|
|
initiatorDict['classname'] = volpath.classname
|
|
keys = {}
|
|
keys['CreationClassName'] = volpath['CreationClassName']
|
|
keys['SystemName'] = volpath['SystemName']
|
|
keys['DeviceID'] = volpath['DeviceID']
|
|
keys['SystemCreationClassName'] = volpath['SystemCreationClassName']
|
|
initiatorDict['keybindings'] = keys
|
|
return initiatorDict
|
|
|
|
def _create_masking_view(
|
|
self, conn, configService, maskingViewName, deviceMaskingGroup,
|
|
targetMaskingGroup, initiatorMaskingGroup):
|
|
"""After creating an new initiator group find it and return it.
|
|
|
|
:param conn: the connection to the ecom server
|
|
:param configService: the create initiator group job
|
|
:param maskingViewName: the masking view name string
|
|
:param deviceMaskingGroup: device(storage) masking group (instanceName)
|
|
:param targetMaskingGroup: target(port) masking group (instanceName)
|
|
:param initiatorMaskingGroup: initiator masking group (instanceName)
|
|
:returns: int rc return code
|
|
:returns: dict job
|
|
"""
|
|
rc, job = conn.InvokeMethod(
|
|
'CreateMaskingView', configService, ElementName=maskingViewName,
|
|
InitiatorMaskingGroup=initiatorMaskingGroup,
|
|
DeviceMaskingGroup=deviceMaskingGroup,
|
|
TargetMaskingGroup=targetMaskingGroup)
|
|
|
|
if rc != 0L:
|
|
rc, errordesc = self.utils.wait_for_job_complete(conn, job)
|
|
if rc != 0L:
|
|
exceptionMessage = (_(
|
|
"Error Create Masking View: %(groupName)s. "
|
|
"Return code: %(rc)lu. Error: %(error)s")
|
|
% {'groupName': maskingViewName,
|
|
'rc': rc,
|
|
'error': errordesc})
|
|
LOG.error(exceptionMessage)
|
|
raise exception.VolumeBackendAPIException(
|
|
data=exceptionMessage)
|
|
|
|
LOG.info(_("Created new masking view : %(maskingViewName)s ")
|
|
% {'maskingViewName': maskingViewName})
|
|
return rc, job
|
|
|
|
def find_new_masking_view(self, conn, jobDict):
|
|
"""Find the newly created volume
|
|
|
|
:param conn: the connection to the ecom server
|
|
:param jobDict: the job tuple
|
|
:returns: instance maskingViewInstance
|
|
"""
|
|
associators = conn.Associators(
|
|
jobDict['Job'],
|
|
ResultClass='Symm_LunMaskingView')
|
|
mvpath = associators[0].path
|
|
maskingViewInstance = {}
|
|
maskingViewInstance['classname'] = mvpath.classname
|
|
keys = {}
|
|
keys['CreationClassName'] = mvpath['CreationClassName']
|
|
keys['SystemName'] = mvpath['SystemName']
|
|
keys['DeviceID'] = mvpath['DeviceID']
|
|
keys['SystemCreationClassName'] = mvpath['SystemCreationClassName']
|
|
maskingViewInstance['keybindings'] = keys
|
|
return maskingViewInstance
|
|
|
|
def _get_storage_group_from_masking_view(
|
|
self, conn, maskingViewName, storageSystemName):
|
|
"""Gets the Device Masking Group from masking view.
|
|
|
|
:param conn: the connection to the ecom server
|
|
:param maskingViewName: the masking view name (String)
|
|
:param storageSystemName: storage system name (String)
|
|
:returns: instance name foundStorageGroupInstanceName
|
|
"""
|
|
foundStorageGroupInstanceName = None
|
|
maskingviews = conn.EnumerateInstanceNames(
|
|
'EMC_LunMaskingSCSIProtocolController')
|
|
for view in maskingviews:
|
|
if storageSystemName == view['SystemName']:
|
|
instance = conn.GetInstance(view, LocalOnly=False)
|
|
if maskingViewName == instance['ElementName']:
|
|
foundView = view
|
|
break
|
|
|
|
groups = conn.AssociatorNames(
|
|
foundView,
|
|
ResultClass='CIM_DeviceMaskingGroup')
|
|
if groups[0] > 0:
|
|
foundStorageGroupInstanceName = groups[0]
|
|
|
|
LOG.debug("Masking view: %(view)s DeviceMaskingGroup: %(masking)s."
|
|
% {'view': maskingViewName,
|
|
'masking': foundStorageGroupInstanceName})
|
|
|
|
return foundStorageGroupInstanceName
|
|
|
|
def _get_storage_group_instance_name(
|
|
self, conn, controllerConfigService, volumeInstance, volumeName,
|
|
sgGroupName, fastPolicyName, storageSystemName,
|
|
defaultStorageGroupInstanceName):
|
|
"""Gets the storage group instance name.
|
|
|
|
If fastPolicy name is None
|
|
then NON FAST is assumed. If it is a valid fastPolicy name
|
|
then associate the new storage group with the fast policy.
|
|
If we are using an existing storage group then we must check that
|
|
it is associated with the correct fast policy
|
|
|
|
:param conn: the connection to the ecom server
|
|
:param controllerConfigService: the controller configuration server
|
|
:param volumeInstance: the volume instance
|
|
:param volumeName: the volume name (String)
|
|
:param sgGroupName: the storage group name (String)
|
|
:param fastPolicyName: the fast policy name (String): can be None
|
|
:param storageSystemName: the storage system name (String)
|
|
:param defaultStorageGroupInstanceName: default storage group instance
|
|
name (can be None for Non FAST)
|
|
:returns: instance name storageGroupInstanceName
|
|
"""
|
|
storageGroupInstanceName = self.utils.find_storage_masking_group(
|
|
conn, controllerConfigService, sgGroupName)
|
|
|
|
if storageGroupInstanceName is None:
|
|
storageGroupInstanceName = self._create_storage_group(
|
|
conn, controllerConfigService, sgGroupName, volumeInstance,
|
|
fastPolicyName, volumeName, storageSystemName,
|
|
defaultStorageGroupInstanceName)
|
|
if storageGroupInstanceName is None:
|
|
errorMessage = (_(
|
|
"Cannot create or find an storage group with name "
|
|
"%(sgGroupName)s")
|
|
% {'sgGroupName': sgGroupName})
|
|
LOG.error(errorMessage)
|
|
raise exception.VolumeBackendAPIException(data=errorMessage)
|
|
else:
|
|
if self._is_volume_in_storage_group(
|
|
conn, storageGroupInstanceName, volumeInstance):
|
|
LOG.warn(_("Volume: %(volumeName)s is already "
|
|
"part of storage group %(sgGroupName)s ")
|
|
% {'volumeName': volumeName,
|
|
'sgGroupName': sgGroupName})
|
|
else:
|
|
self.add_volume_to_storage_group(
|
|
conn, controllerConfigService, storageGroupInstanceName,
|
|
volumeInstance, volumeName, sgGroupName, fastPolicyName,
|
|
storageSystemName)
|
|
|
|
return storageGroupInstanceName
|
|
|
|
def _get_port_group_instance_name(
|
|
self, conn, controllerConfigService, pgGroupName):
|
|
"""Gets the port group instance name.
|
|
|
|
The portGroup name has been defined in the EMC Config file if it
|
|
does not exist the operation should fail
|
|
|
|
:param conn: the connection to the ecom server
|
|
:param controllerConfigService: the controller configuration server
|
|
:param pgGroupName: the port group name
|
|
:returns: instance name foundPortGroupInstanceName
|
|
"""
|
|
foundPortGroupInstanceName = self._find_port_group(
|
|
conn, controllerConfigService, pgGroupName)
|
|
if foundPortGroupInstanceName is None:
|
|
errorMessage = (_(
|
|
"Cannot find a portGroup with name %(pgGroupName)s. "
|
|
"The port group for a masking view must be pre-defined")
|
|
% {'pgGroupName': pgGroupName})
|
|
LOG.error(errorMessage)
|
|
return foundPortGroupInstanceName
|
|
|
|
LOG.info(_(
|
|
"Port group instance name is %(foundPortGroupInstanceName)s")
|
|
% {'foundPortGroupInstanceName': foundPortGroupInstanceName})
|
|
|
|
return foundPortGroupInstanceName
|
|
|
|
def _get_initiator_group_instance_name(
|
|
self, conn, controllerConfigService, igGroupName, connector,
|
|
storageSystemName):
|
|
"""Gets the initiator group instance name.
|
|
|
|
:param conn: the connection to the ecom server
|
|
:param controllerConfigService: the controller configuration server
|
|
:param igGroupName: the port group name
|
|
:param connector: the connector object
|
|
:param storageSystemName = the storage system name
|
|
:returns: instance name foundInitiatorGroupInstanceName
|
|
"""
|
|
foundInitiatorGroupInstanceName = (self._create_or_get_initiator_group(
|
|
conn, controllerConfigService, igGroupName, connector,
|
|
storageSystemName))
|
|
if foundInitiatorGroupInstanceName is None:
|
|
errorMessage = (_(
|
|
"Cannot create or find an initiator group with "
|
|
"name %(igGroupName)s")
|
|
% {'igGroupName': igGroupName})
|
|
LOG.error(errorMessage)
|
|
|
|
return foundInitiatorGroupInstanceName
|
|
|
|
def _get_masking_view_instance_name(
|
|
self, conn, controllerConfigService, maskingViewName,
|
|
storageGroupInstanceName, portGroupInstanceName,
|
|
initiatorGroupInstanceName):
|
|
"""Gets the masking view instance name
|
|
|
|
:param conn: the connection to the ecom server
|
|
:param controllerConfigService: the controller configuration server
|
|
:param maskingViewName: the masking view name (String)
|
|
:param storageGroupInstanceName: the storage group instance name
|
|
:param portGroupInstanceName: the port group instance name
|
|
:param initiatorGroupInstanceName: the initiator group instance name
|
|
:returns: instance name foundMaskingViewInstanceName
|
|
"""
|
|
rc, job = self._create_masking_view(
|
|
conn, controllerConfigService, maskingViewName,
|
|
storageGroupInstanceName, portGroupInstanceName,
|
|
initiatorGroupInstanceName)
|
|
foundMaskingViewInstanceName = self.find_new_masking_view(conn, job)
|
|
if foundMaskingViewInstanceName is None:
|
|
errorMessage = (_(
|
|
"Cannot find the new masking view just created with name "
|
|
"%(maskingViewName)s")
|
|
% {'maskingViewName': maskingViewName})
|
|
LOG.error(errorMessage)
|
|
|
|
return foundMaskingViewInstanceName
|
|
|
|
def _check_if_rollback_action_for_masking_required(
|
|
self, conn, controllerConfigService, volumeInstance,
|
|
volumeName, fastPolicyName, defaultStorageGroupInstanceName):
|
|
"""This is a rollback action for FAST.
|
|
|
|
We need to be able to return the volume to the default storage group
|
|
if anything has gone wrong. The volume can also potentially belong to
|
|
a storage group that is not the default depending on where
|
|
the exception occurred.
|
|
|
|
:param conn: the connection to the ecom server
|
|
:param controllerConfigService: the controller config service
|
|
:param volumeInstanceName: the volume instance name
|
|
:param volumeName: the volume name (String)
|
|
:param fastPolicyName: the fast policy name (String)
|
|
:param defaultStorageGroupInstanceName: the default storage group
|
|
instance name
|
|
"""
|
|
try:
|
|
foundStorageGroupInstanceName = (
|
|
self.utils.get_storage_group_from_volume(
|
|
conn, volumeInstance.path))
|
|
# volume is not associated with any storage group so add it back
|
|
# to the default
|
|
if len(foundStorageGroupInstanceName) == 0:
|
|
infoMessage = (_(
|
|
"Performing rollback on Volume: %(volumeName)s "
|
|
"To return it to the default storage group for FAST policy"
|
|
" %(fastPolicyName)s. ")
|
|
% {'volumeName': volumeName,
|
|
'fastPolicyName': fastPolicyName})
|
|
LOG.warn("No storage group found. " + infoMessage)
|
|
assocDefaultStorageGroupName = (
|
|
self.fast
|
|
.add_volume_to_default_storage_group_for_fast_policy(
|
|
conn, controllerConfigService, volumeInstance,
|
|
volumeName, fastPolicyName))
|
|
if assocDefaultStorageGroupName is None:
|
|
errorMsg = (_(
|
|
"Failed to Roll back to re-add volume %(volumeName)s "
|
|
"to default storage group for fast policy "
|
|
"%(fastPolicyName)s: Please contact your sys admin to "
|
|
"get the volume re-added manually ")
|
|
% {'volumeName': volumeName,
|
|
'fastPolicyName': fastPolicyName})
|
|
LOG.error(errorMsg)
|
|
if len(foundStorageGroupInstanceName) > 0:
|
|
errorMsg = (_(
|
|
"The storage group found is "
|
|
"%(foundStorageGroupInstanceName)s: ")
|
|
% {'foundStorageGroupInstanceName':
|
|
foundStorageGroupInstanceName})
|
|
LOG.info(errorMsg)
|
|
|
|
# check the name see is it the default storage group or another
|
|
if (foundStorageGroupInstanceName !=
|
|
defaultStorageGroupInstanceName):
|
|
# remove it from its current masking view and return it
|
|
# to its default masking view if fast is enabled
|
|
self.remove_and_reset_members(
|
|
conn, controllerConfigService, volumeInstance,
|
|
fastPolicyName, volumeName)
|
|
except Exception as e:
|
|
LOG.error(_("Exception: %s") % six.text_type(e))
|
|
errorMessage = (_(
|
|
"Rollback for Volume: %(volumeName)s has failed. "
|
|
"Please contact your system administrator to manually return "
|
|
"your volume to the default storage group for fast policy "
|
|
"%(fastPolicyName)s failed ")
|
|
% {'volumeName': volumeName,
|
|
'fastPolicyName': fastPolicyName})
|
|
LOG.error(errorMessage)
|
|
raise exception.VolumeBackendAPIException(data=errorMessage)
|
|
|
|
def _find_new_initiator_group(self, conn, maskingGroupDict):
|
|
"""After creating an new initiator group find it and return it.
|
|
|
|
:param conn: connection the ecom server
|
|
:param maskingGroupDict: the maskingGroupDict dict
|
|
:param storageGroupName: storage group name (String)
|
|
:returns: instance name foundInitiatorGroupInstanceName
|
|
"""
|
|
foundInitiatorGroupInstanceName = None
|
|
|
|
if 'MaskingGroup' in maskingGroupDict:
|
|
foundInitiatorGroupInstanceName = maskingGroupDict['MaskingGroup']
|
|
|
|
return foundInitiatorGroupInstanceName
|
|
|
|
def _get_initiator_group_from_masking_view(
|
|
self, conn, maskingViewName, storageSystemName):
|
|
"""Given the masking view name get the initiator group from it.
|
|
|
|
:param conn: connection the the ecom server
|
|
:param maskingViewName: the name of the masking view
|
|
:param storageSystemName: the storage system name
|
|
:returns: instance name foundInitiatorMaskingGroupInstanceName
|
|
"""
|
|
foundInitiatorMaskingGroupInstanceName = None
|
|
|
|
maskingviews = conn.EnumerateInstanceNames(
|
|
'EMC_LunMaskingSCSIProtocolController')
|
|
for view in maskingviews:
|
|
if storageSystemName == view['SystemName']:
|
|
instance = conn.GetInstance(view, LocalOnly=False)
|
|
if maskingViewName == instance['ElementName']:
|
|
foundView = view
|
|
break
|
|
|
|
groups = conn.AssociatorNames(
|
|
foundView,
|
|
ResultClass='CIM_InitiatorMaskingGroup')
|
|
if len(groups):
|
|
foundInitiatorMaskingGroupInstanceName = groups[0]
|
|
|
|
LOG.debug(
|
|
"Masking view: %(view)s InitiatorMaskingGroup: %(masking)s."
|
|
% {'view': maskingViewName,
|
|
'masking': foundInitiatorMaskingGroupInstanceName})
|
|
|
|
return foundInitiatorMaskingGroupInstanceName
|
|
|
|
def _verify_initiator_group_from_masking_view(
|
|
self, conn, controllerConfigService, maskingViewName, connector,
|
|
storageSystemName, igGroupName):
|
|
"""Check that the initiator group contains the correct initiators.
|
|
|
|
If using an existing masking view check that the initiator group
|
|
contains the correct initiators. If it does not contain the correct
|
|
initiators then we delete the initiator group from the masking view,
|
|
re-create it with the correct initiators and add it to the masking view
|
|
NOTE: EMC does not support ModifyMaskingView so we must first
|
|
delete the masking view and recreate it.
|
|
|
|
:param conn: connection the ecom server
|
|
:param controllerConfigService: the controller configuration service
|
|
:param maskingViewName: maskingview name (String)
|
|
:param connector: the connector dict
|
|
:param storageSystemName: the storage System Name (string)
|
|
:param igGroupName: the initiator group name (String)
|
|
"""
|
|
initiatorNames = self._find_initiator_names(conn, connector)
|
|
foundInitiatorGroupFromConnector = self._find_initiator_masking_group(
|
|
conn, controllerConfigService, initiatorNames)
|
|
|
|
foundInitiatorGroupFromMaskingView = (
|
|
self._get_initiator_group_from_masking_view(
|
|
conn, maskingViewName, storageSystemName))
|
|
|
|
if (foundInitiatorGroupFromConnector !=
|
|
foundInitiatorGroupFromMaskingView):
|
|
if foundInitiatorGroupFromMaskingView is not None:
|
|
maskingViewInstanceName = self._find_masking_view(
|
|
conn, maskingViewName, storageSystemName)
|
|
if foundInitiatorGroupFromConnector is None:
|
|
storageHardwareIDInstanceNames = (
|
|
self._get_storage_hardware_id_instance_names(
|
|
conn, initiatorNames, storageSystemName))
|
|
if not storageHardwareIDInstanceNames:
|
|
LOG.error(_(
|
|
"Initiator Name(s) %(initiatorNames)s are not on "
|
|
"array %(storageSystemName)s ")
|
|
% {'initiatorNames': initiatorNames,
|
|
'storageSystemName': storageSystemName})
|
|
return False
|
|
|
|
foundInitiatorGroupFromConnector = (
|
|
self._create_initiator_Group(
|
|
conn, controllerConfigService, igGroupName,
|
|
storageHardwareIDInstanceNames))
|
|
storageGroupInstanceName = (
|
|
self._get_storage_group_from_masking_view(
|
|
conn, maskingViewName, storageSystemName))
|
|
portGroupInstanceName = self._get_port_group_from_masking_view(
|
|
conn, maskingViewName, storageSystemName)
|
|
if (foundInitiatorGroupFromConnector is not None and
|
|
storageGroupInstanceName is not None and
|
|
portGroupInstanceName is not None):
|
|
self._delete_masking_view(
|
|
conn, controllerConfigService, maskingViewName,
|
|
maskingViewInstanceName)
|
|
newMaskingViewInstanceName = (
|
|
self._get_masking_view_instance_name(
|
|
conn, controllerConfigService, maskingViewName,
|
|
storageGroupInstanceName, portGroupInstanceName,
|
|
foundInitiatorGroupFromConnector))
|
|
if newMaskingViewInstanceName is not None:
|
|
LOG.debug(
|
|
"The old masking view has been replaced: "
|
|
"%(maskingViewName)s. "
|
|
% {'maskingViewName': maskingViewName})
|
|
else:
|
|
LOG.error(_(
|
|
"One of the components of the original masking view "
|
|
"%(maskingViewName)s cannot be retrieved so "
|
|
"please contact your system administrator to check "
|
|
"that the correct initiator(s) are part of masking ")
|
|
% {'maskingViewName': maskingViewName})
|
|
return False
|
|
return True
|
|
|
|
def _create_initiator_Group(
|
|
self, conn, controllerConfigService, igGroupName,
|
|
hardwareIdinstanceNames):
|
|
"""Create a new initiator group
|
|
|
|
Given a list of hardwareId Instance name create a new
|
|
initiator group
|
|
|
|
:param conn: connection the ecom server
|
|
:param controllerConfigService: the controller configuration service
|
|
:param igGroupName: the initiator group name (String)
|
|
:param hardwareIdinstanceNames: one or more hardware id instance names
|
|
"""
|
|
rc, job = conn.InvokeMethod(
|
|
'CreateGroup', controllerConfigService, GroupName=igGroupName,
|
|
Type=self.utils.get_num(INITIATORGROUPTYPE, '16'),
|
|
Members=[hardwareIdinstanceNames[0]])
|
|
|
|
if rc != 0L:
|
|
rc, errordesc = self.utils.wait_for_job_complete(conn, job)
|
|
if rc != 0L:
|
|
exceptionMessage = (_(
|
|
"Error Create Group: %(groupName)s. "
|
|
"Return code: %(rc)lu. Error: %(error)s")
|
|
% {'groupName': igGroupName,
|
|
'rc': rc,
|
|
'error': errordesc})
|
|
LOG.error(exceptionMessage)
|
|
raise exception.VolumeBackendAPIException(
|
|
data=exceptionMessage)
|
|
foundInitiatorGroupInstanceName = self._find_new_initiator_group(
|
|
conn, job)
|
|
|
|
numHardwareIDInstanceNames = len(hardwareIdinstanceNames)
|
|
if numHardwareIDInstanceNames > 1:
|
|
for j in range(1, numHardwareIDInstanceNames):
|
|
rc, job = conn.InvokeMethod(
|
|
'AddMembers', controllerConfigService,
|
|
MaskingGroup=foundInitiatorGroupInstanceName,
|
|
Members=[hardwareIdinstanceNames[j]])
|
|
|
|
if rc != 0L:
|
|
rc, errordesc = self.utils.wait_for_job_complete(conn, job)
|
|
if rc != 0L:
|
|
exceptionMessage = (_(
|
|
"Error adding initiator to group : %(groupName)s. "
|
|
"Return code: %(rc)lu. Error: %(error)s")
|
|
% {'groupName': igGroupName,
|
|
'rc': rc,
|
|
'error': errordesc})
|
|
LOG.error(exceptionMessage)
|
|
raise exception.VolumeBackendAPIException(
|
|
data=exceptionMessage)
|
|
j = j + 1
|
|
|
|
return foundInitiatorGroupInstanceName
|
|
|
|
def _get_port_group_from_masking_view(
|
|
self, conn, maskingViewName, storageSystemName):
|
|
"""Given the masking view name get the port group from it
|
|
|
|
:param conn: connection the the ecom server
|
|
:param maskingViewName: the name of the masking view
|
|
:param storageSystemName: the storage system name
|
|
:returns: instance name foundPortMaskingGroupInstanceName
|
|
"""
|
|
foundPortMaskingGroupInstanceName = None
|
|
|
|
maskingviews = conn.EnumerateInstanceNames(
|
|
'EMC_LunMaskingSCSIProtocolController')
|
|
for view in maskingviews:
|
|
if storageSystemName == view['SystemName']:
|
|
instance = conn.GetInstance(view, LocalOnly=False)
|
|
if maskingViewName == instance['ElementName']:
|
|
foundView = view
|
|
break
|
|
|
|
groups = conn.AssociatorNames(
|
|
foundView,
|
|
ResultClass='CIM_TargetMaskingGroup')
|
|
if len(groups) > 0:
|
|
foundPortMaskingGroupInstanceName = groups[0]
|
|
|
|
LOG.debug(
|
|
"Masking view: %(view)s InitiatorMaskingGroup: %(masking)s."
|
|
% {'view': maskingViewName,
|
|
'masking': foundPortMaskingGroupInstanceName})
|
|
|
|
return foundPortMaskingGroupInstanceName
|
|
|
|
def _delete_masking_view(
|
|
self, conn, controllerConfigService, maskingViewName,
|
|
maskingViewInstanceName):
|
|
"""Delete a masking view
|
|
|
|
:param conn: connection the ecom server
|
|
:param controllerConfigService: the controller configuration service
|
|
:param maskingViewName: maskingview name (String)
|
|
:param maskingViewInstanceName: the masking view instance name
|
|
"""
|
|
rc, job = conn.InvokeMethod('DeleteMaskingView',
|
|
controllerConfigService,
|
|
ProtocolController=maskingViewInstanceName)
|
|
|
|
if rc != 0L:
|
|
rc, errordesc = self.utils.wait_for_job_complete(conn, job)
|
|
if rc != 0L:
|
|
exceptionMessage = (_(
|
|
"Error Modifying masking view : %(groupName)s. "
|
|
"Return code: %(rc)lu. Error: %(error)s")
|
|
% {'groupName': maskingViewName,
|
|
'rc': rc,
|
|
'error': errordesc})
|
|
LOG.error(exceptionMessage)
|
|
raise exception.VolumeBackendAPIException(
|
|
data=exceptionMessage)
|
|
|
|
def get_masking_view_from_storage_group(
|
|
self, conn, storageGroupInstanceName):
|
|
"""Get the associated maskingview instance name
|
|
|
|
Given storage group instance name, get the associated masking
|
|
view instance name
|
|
|
|
:param conn: connection the ecom server
|
|
:param storageGroupInstanceName: the storage group instance name
|
|
:returns: instance name foundMaskingViewInstanceName
|
|
"""
|
|
foundMaskingViewInstanceName = None
|
|
maskingViews = conn.AssociatorNames(
|
|
storageGroupInstanceName,
|
|
ResultClass='Symm_LunMaskingView')
|
|
if len(maskingViews) > 0:
|
|
foundMaskingViewInstanceName = maskingViews[0]
|
|
|
|
return foundMaskingViewInstanceName
|
|
|
|
def add_volume_to_storage_group(
|
|
self, conn, controllerConfigService, storageGroupInstanceName,
|
|
volumeInstance, volumeName, sgGroupName, fastPolicyName,
|
|
storageSystemName=None):
|
|
"""Add a volume to an existing storage group
|
|
|
|
:param conn: connection to ecom server
|
|
:param controllerConfigService: the controller configuration service
|
|
:param storageGroup: storage group instance
|
|
:param volumeInstance: the volume instance
|
|
:param volumeName: the name of the volume (String)
|
|
:param sgGroupName: the name of the storage group (String)
|
|
:param fastPolicyName: the fast policy name (String) can be None
|
|
:param storageSystemName: the storage system name (Optional Parameter),
|
|
if None plain operation assumed
|
|
:returns: int rc the return code of the job
|
|
:returns: dict the job dict
|
|
"""
|
|
self.provision.add_members_to_masking_group(
|
|
conn, controllerConfigService, storageGroupInstanceName,
|
|
volumeInstance.path, volumeName)
|
|
|
|
infoMessage = (_(
|
|
"Added volume: %(volumeName)s to existing storage group "
|
|
"%(sgGroupName)s. ")
|
|
% {'volumeName': volumeName,
|
|
'sgGroupName': sgGroupName})
|
|
LOG.info(infoMessage)
|
|
|
|
def remove_device_from_default_storage_group(
|
|
self, conn, controllerConfigService, volumeInstanceName,
|
|
volumeName, fastPolicyName):
|
|
"""Remove the volume from the default storage group.
|
|
|
|
Remove the volume from the default storage group for the FAST
|
|
policy and return the default storage group instance name
|
|
|
|
:param conn: the connection to the ecom server
|
|
:param controllerConfigService: the controller config service
|
|
:param volumeInstanceName: the volume instance name
|
|
:param volumeName: the volume name (String)
|
|
:param fastPolicyName: the fast policy name (String)
|
|
:returns: instance name defaultStorageGroupInstanceName
|
|
"""
|
|
failedRet = None
|
|
defaultStorageGroupInstanceName = (
|
|
self.fast.get_and_verify_default_storage_group(
|
|
conn, controllerConfigService, volumeInstanceName,
|
|
volumeName, fastPolicyName))
|
|
|
|
if defaultStorageGroupInstanceName is None:
|
|
errorMessage = (_(
|
|
"Volume %(volumeName)s was not first part of the default "
|
|
"storage group for the FAST Policy")
|
|
% {'volumeName': volumeName})
|
|
LOG.warn(errorMessage)
|
|
return failedRet
|
|
|
|
assocVolumeInstanceNames = self.get_devices_from_storage_group(
|
|
conn, defaultStorageGroupInstanceName)
|
|
|
|
LOG.debug(
|
|
"There are %(length)lu associated with the default storage group "
|
|
"for fast before removing volume %(volumeName)s"
|
|
% {'length': len(assocVolumeInstanceNames),
|
|
'volumeName': volumeName})
|
|
|
|
self.provision.remove_device_from_storage_group(
|
|
conn, controllerConfigService, defaultStorageGroupInstanceName,
|
|
volumeInstanceName, volumeName)
|
|
|
|
assocVolumeInstanceNames = self.get_devices_from_storage_group(
|
|
conn, defaultStorageGroupInstanceName)
|
|
LOG.debug(
|
|
"There are %(length)lu associated with the default storage group "
|
|
"for fast after removing volume %(volumeName)s"
|
|
% {'length': len(assocVolumeInstanceNames),
|
|
'volumeName': volumeName})
|
|
|
|
# required for unit tests
|
|
emptyStorageGroupInstanceName = (
|
|
self._wrap_get_storage_group_from_volume(conn, volumeInstanceName))
|
|
|
|
if emptyStorageGroupInstanceName is not None:
|
|
errorMessage = (_(
|
|
"Failed to remove %(volumeName)s from the default storage "
|
|
"group for the FAST Policy")
|
|
% {'volumeName': volumeName})
|
|
LOG.error(errorMessage)
|
|
return failedRet
|
|
|
|
return defaultStorageGroupInstanceName
|
|
|
|
def _wrap_get_storage_group_from_volume(self, conn, volumeInstanceName):
|
|
|
|
"""Wrapper for get_storage_group_from_volume.
|
|
|
|
Needed for override in tests
|
|
|
|
:param conn: the connection to the ecom server
|
|
:param volumeInstanceName: the volume instance name
|
|
:returns: emptyStorageGroupInstanceName
|
|
"""
|
|
return self.utils.get_storage_group_from_volume(
|
|
conn, volumeInstanceName)
|
|
|
|
def get_devices_from_storage_group(
|
|
self, conn, storageGroupInstanceName):
|
|
"""Get the associated volume Instance names
|
|
|
|
Given the storage group instance name get the associated volume
|
|
Instance names
|
|
|
|
:param conn: connection the the ecom server
|
|
:param storageGroupInstanceName: the storage group instance name
|
|
:returns: list volumeInstanceNames list of volume instance names
|
|
"""
|
|
volumeInstanceNames = conn.AssociatorNames(
|
|
storageGroupInstanceName,
|
|
ResultClass='EMC_StorageVolume')
|
|
|
|
return volumeInstanceNames
|
|
|
|
def get_associated_masking_group_from_device(
|
|
self, conn, volumeInstanceName):
|
|
maskingGroupInstanceNames = conn.AssociatorNames(
|
|
volumeInstanceName,
|
|
ResultClass='CIM_DeviceMaskingGroup',
|
|
AssocClass='CIM_OrderedMemberOfCollection')
|
|
if len(maskingGroupInstanceNames) > 0:
|
|
return maskingGroupInstanceNames[0]
|
|
else:
|
|
return None
|
|
|
|
def remove_and_reset_members(
|
|
self, conn, controllerConfigService, volumeInstance,
|
|
fastPolicyName, volumeName):
|
|
"""Part of unmap device or rollback.
|
|
|
|
Removes volume from the Device Masking Group that belongs to a
|
|
Masking View. Check if fast policy is in the extra specs, if it isn't
|
|
we do not need to do any thing for FAST. Assume that
|
|
isTieringPolicySupported is False unless the FAST policy is in
|
|
the extra specs and tiering is enabled on the array
|
|
|
|
:param conn: connection the the ecom server
|
|
:param controllerConfigService: the controller configuration service
|
|
:param volumeInstance: the volume Instance
|
|
:param fastPolicyName: the fast policy name (if it exists)
|
|
:param volumeName: the volume name
|
|
:returns: list volumeInstanceNames list of volume instance names
|
|
"""
|
|
rc = -1
|
|
maskingGroupInstanceName = (
|
|
self.get_associated_masking_group_from_device(
|
|
conn, volumeInstance.path))
|
|
|
|
volumeInstanceNames = self.get_devices_from_storage_group(
|
|
conn, maskingGroupInstanceName)
|
|
storageSystemInstanceName = self.utils.find_storage_system(
|
|
conn, controllerConfigService)
|
|
|
|
isTieringPolicySupported = False
|
|
if fastPolicyName is not None:
|
|
tierPolicyServiceInstanceName = self.utils.get_tier_policy_service(
|
|
conn, storageSystemInstanceName)
|
|
|
|
isTieringPolicySupported = self.fast.is_tiering_policy_enabled(
|
|
conn, tierPolicyServiceInstanceName)
|
|
LOG.debug(
|
|
"FAST policy enabled on %(storageSystem)s: %(isSupported)s"
|
|
% {'storageSystem': storageSystemInstanceName,
|
|
'isSupported': isTieringPolicySupported})
|
|
|
|
numVolInMaskingView = len(volumeInstanceNames)
|
|
LOG.debug(
|
|
"There are %(numVol)d volumes in the masking view %(maskingGroup)s"
|
|
% {'numVol': numVolInMaskingView,
|
|
'maskingGroup': maskingGroupInstanceName})
|
|
|
|
if numVolInMaskingView == 1: # last volume in the storage group
|
|
# delete masking view
|
|
mvInstanceName = self.get_masking_view_from_storage_group(
|
|
conn, maskingGroupInstanceName)
|
|
LOG.debug(
|
|
"Last volume in the storage group, deleting masking view "
|
|
"%(mvInstanceName)s"
|
|
% {'mvInstanceName': mvInstanceName})
|
|
conn.DeleteInstance(mvInstanceName)
|
|
|
|
# disassociate storage group from FAST policy
|
|
if fastPolicyName is not None and isTieringPolicySupported is True:
|
|
tierPolicyInstanceName = self.fast.get_tier_policy_by_name(
|
|
conn, storageSystemInstanceName['Name'], fastPolicyName)
|
|
|
|
LOG.info(_(
|
|
"policy:%(policy)s, policy service:%(service)s, "
|
|
"masking group=%(maskingGroup)s")
|
|
% {'policy': tierPolicyInstanceName,
|
|
'service': tierPolicyServiceInstanceName,
|
|
'maskingGroup': maskingGroupInstanceName})
|
|
|
|
self.fast.delete_storage_group_from_tier_policy_rule(
|
|
conn, tierPolicyServiceInstanceName,
|
|
maskingGroupInstanceName, tierPolicyInstanceName)
|
|
|
|
rc = self.provision.remove_device_from_storage_group(
|
|
conn, controllerConfigService, maskingGroupInstanceName,
|
|
volumeInstance.path, volumeName)
|
|
|
|
LOG.debug(
|
|
"Remove the last volume %(volumeName)s completed successfully."
|
|
% {'volumeName': volumeName})
|
|
|
|
# Delete storage group
|
|
conn.DeleteInstance(maskingGroupInstanceName)
|
|
|
|
if isTieringPolicySupported:
|
|
self._cleanup_tiering(
|
|
conn, controllerConfigService, fastPolicyName,
|
|
volumeInstance, volumeName)
|
|
else:
|
|
# not the last volume
|
|
LOG.debug("start: number of volumes in masking storage group: "
|
|
"%(numVol)d" % {'numVol': len(volumeInstanceNames)})
|
|
rc = self.provision.remove_device_from_storage_group(
|
|
conn, controllerConfigService, maskingGroupInstanceName,
|
|
volumeInstance.path, volumeName)
|
|
|
|
LOG.debug(
|
|
"RemoveMembers for volume %(volumeName)s completed "
|
|
"successfully." % {'volumeName': volumeName})
|
|
|
|
# if FAST POLICY enabled, move the volume to the default SG
|
|
if fastPolicyName is not None and isTieringPolicySupported:
|
|
self._cleanup_tiering(
|
|
conn, controllerConfigService, fastPolicyName,
|
|
volumeInstance, volumeName)
|
|
|
|
# validation
|
|
volumeInstanceNames = self.get_devices_from_storage_group(
|
|
conn, maskingGroupInstanceName)
|
|
LOG.debug(
|
|
"end: number of volumes in masking storage group: %(numVol)d"
|
|
% {'numVol': len(volumeInstanceNames)})
|
|
|
|
return rc
|
|
|
|
def _cleanup_tiering(
|
|
self, conn, controllerConfigService, fastPolicyName,
|
|
volumeInstance, volumeName):
|
|
"""Cleanup tiering
|
|
|
|
:param conn: the ecom connection
|
|
:param controllerConfigService: the controller configuration service
|
|
:param fastPolicyName: the fast policy name
|
|
:param volumeInstance: volume instance
|
|
:param volumeName: the volume name
|
|
"""
|
|
defaultStorageGroupInstanceName = (
|
|
self.fast.get_policy_default_storage_group(
|
|
conn, controllerConfigService, fastPolicyName))
|
|
volumeInstanceNames = self.get_devices_from_storage_group(
|
|
conn, defaultStorageGroupInstanceName)
|
|
LOG.debug(
|
|
"start: number of volumes in default storage group: %(numVol)d"
|
|
% {'numVol': len(volumeInstanceNames)})
|
|
defaultStorageGroupInstanceName = (
|
|
self.fast.add_volume_to_default_storage_group_for_fast_policy(
|
|
conn, controllerConfigService, volumeInstance, volumeName,
|
|
fastPolicyName))
|
|
# check default storage group number of volumes
|
|
volumeInstanceNames = self.get_devices_from_storage_group(
|
|
conn, defaultStorageGroupInstanceName)
|
|
LOG.debug(
|
|
"end: number of volumes in default storage group: %(numVol)d"
|
|
% {'numVol': len(volumeInstanceNames)})
|
|
|
|
def get_target_wwns(self, conn, mvInstanceName):
|
|
"""Get the DA ports' wwns.
|
|
|
|
:param conn: the ecom connection
|
|
:param mvInstanceName: masking view instance name
|
|
"""
|
|
targetWwns = []
|
|
targetPortInstanceNames = conn.AssociatorNames(
|
|
mvInstanceName,
|
|
ResultClass='Symm_FCSCSIProtocolEndpoint')
|
|
numberOfPorts = len(targetPortInstanceNames)
|
|
if numberOfPorts <= 0:
|
|
LOG.warn("No target ports found in "
|
|
"masking view %(maskingView)s"
|
|
% {'numPorts': len(targetPortInstanceNames),
|
|
'maskingView': mvInstanceName})
|
|
for targetPortInstanceName in targetPortInstanceNames:
|
|
targetWwns.append(targetPortInstanceName['Name'])
|
|
return targetWwns
|
|
|
|
def get_masking_view_by_volume(self, conn, volumeInstance):
|
|
"""Given volume, retrieve the masking view instance name.
|
|
|
|
:param volume: the volume instance
|
|
:param mvInstanceName: masking view instance name
|
|
"""
|
|
sgInstanceName = self.get_associated_masking_group_from_device(
|
|
conn, volumeInstance.path)
|
|
mvInstanceName = self.get_masking_view_from_storage_group(
|
|
conn, sgInstanceName)
|
|
LOG.debug("Found Masking View %(mv)s: " % {'mv': mvInstanceName})
|
|
return mvInstanceName
|
|
|
|
def get_masking_views_by_port_group(self, conn, portGroupInstanceName):
|
|
"""Given port group, retrieve the masking view instance name.
|
|
|
|
:param : the volume
|
|
:param mvInstanceName: masking view instance name
|
|
:returns: maksingViewInstanceNames
|
|
"""
|
|
mvInstanceNames = conn.AssociatorNames(
|
|
portGroupInstanceName, ResultClass='Symm_LunMaskingView')
|
|
return mvInstanceNames
|
|
|
|
def get_port_group_from_masking_view(self, conn, maskingViewInstanceName):
|
|
"""Get the port group in a masking view.
|
|
|
|
:param maskingViewInstanceName: masking view instance name
|
|
:returns: portGroupInstanceName
|
|
"""
|
|
portGroupInstanceNames = conn.AssociatorNames(
|
|
maskingViewInstanceName, ResultClass='SE_TargetMaskingGroup')
|
|
if len(portGroupInstanceNames) > 0:
|
|
LOG.debug("Found port group %(pg)s in masking view %(mv)s"
|
|
% {'pg': portGroupInstanceNames[0],
|
|
'mv': maskingViewInstanceName})
|
|
return portGroupInstanceNames[0]
|
|
else:
|
|
LOG.warn("No port group found in masking view %(mv)s"
|
|
% {'mv': maskingViewInstanceName})
|