/* * Copyright 2016 Dell Inc. * * 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. * */ package com.emc.storageos.driver.dellsc; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.emc.storageos.driver.dellsc.scapi.SizeUtil; import com.emc.storageos.driver.dellsc.scapi.StorageCenterAPI; import com.emc.storageos.driver.dellsc.scapi.StorageCenterAPIException; import com.emc.storageos.driver.dellsc.scapi.objects.ScControllerPort; import com.emc.storageos.driver.dellsc.scapi.objects.ScControllerPortFibreChannelConfiguration; import com.emc.storageos.driver.dellsc.scapi.objects.ScControllerPortIscsiConfiguration; import com.emc.storageos.driver.dellsc.scapi.objects.ScFaultDomain; import com.emc.storageos.driver.dellsc.scapi.objects.ScReplay; import com.emc.storageos.driver.dellsc.scapi.objects.ScReplayProfile; import com.emc.storageos.driver.dellsc.scapi.objects.ScStorageType; import com.emc.storageos.driver.dellsc.scapi.objects.ScStorageTypeStorageUsage; import com.emc.storageos.driver.dellsc.scapi.objects.ScVolume; import com.emc.storageos.driver.dellsc.scapi.objects.ScVolumeStorageUsage; import com.emc.storageos.driver.dellsc.scapi.objects.StorageCenter; import com.emc.storageos.storagedriver.model.Initiator.Protocol; import com.emc.storageos.storagedriver.model.StorageObject.AccessStatus; import com.emc.storageos.storagedriver.model.StoragePool; import com.emc.storageos.storagedriver.model.StoragePool.Protocols; import com.emc.storageos.storagedriver.model.StoragePort; import com.emc.storageos.storagedriver.model.StorageSystem; import com.emc.storageos.storagedriver.model.StorageSystem.SupportedProvisioningType; import com.emc.storageos.storagedriver.model.StorageSystem.SupportedReplication; import com.emc.storageos.storagedriver.model.StorageVolume; import com.emc.storageos.storagedriver.model.VolumeConsistencyGroup; import com.emc.storageos.storagedriver.model.VolumeSnapshot; /** * Utility methods for the driver. */ public class DellSCUtil { private static final Logger LOG = LoggerFactory.getLogger(DellSCUtil.class); private static DellSCUtil instance; /** * Private constructor. */ private DellSCUtil() { } /** * Gets the util instance. * * @return The util instance. */ public static DellSCUtil getInstance() { if (instance == null) { instance = new DellSCUtil(); } return instance; } /** * Gets a VolumeSnapshot object for a given replay. * * @param replay The Storage Center snapshot. * @param snapshot The VolumeSnapshot object to populate or null. * @return The VolumeSnapshot object. */ public VolumeSnapshot getVolumeSnapshotFromReplay(ScReplay replay, VolumeSnapshot snapshot) { VolumeSnapshot snap = snapshot; if (snap == null) { snap = new VolumeSnapshot(); } snap.setNativeId(replay.instanceId); snap.setDeviceLabel(replay.instanceName); snap.setDisplayName(replay.instanceName); snap.setStorageSystemId(String.valueOf(replay.scSerialNumber)); snap.setWwn(replay.globalIndex); snap.setParentId(replay.createVolume.instanceId); snap.setAllocatedCapacity(SizeUtil.sizeStrToBytes(replay.size)); snap.setProvisionedCapacity(SizeUtil.sizeStrToBytes(replay.size)); return snap; } /** * Populates a StorageVolume instance with Storage Center volume data. * * @param api The API connection. * @param volume The Storage Center volume. * @param cgInfo Consistency group information or null. * @return The StorageVolume. * @throws StorageCenterAPIException */ public StorageVolume getStorageVolumeFromScVolume(StorageCenterAPI api, ScVolume volume, Map<ScReplayProfile, List<String>> cgInfo) throws StorageCenterAPIException { ScVolumeStorageUsage storageUsage = api.getVolumeStorageUsage(volume.instanceId); StorageVolume driverVol = new StorageVolume(); driverVol.setStorageSystemId(volume.scSerialNumber); driverVol.setStoragePoolId(volume.storageType.instanceId); driverVol.setNativeId(volume.instanceId); driverVol.setThinlyProvisioned(true); driverVol.setProvisionedCapacity(SizeUtil.sizeStrToBytes(volume.configuredSize)); driverVol.setAllocatedCapacity(SizeUtil.sizeStrToBytes(storageUsage.totalDiskSpace)); driverVol.setWwn(volume.deviceId); driverVol.setDeviceLabel(volume.name); // Check consistency group membership if (cgInfo != null) { for (ScReplayProfile cg : cgInfo.keySet()) { if (cgInfo.get(cg).contains(volume.instanceId)) { // Found our volume in a consistency group driverVol.setConsistencyGroup(cg.instanceId); break; } } } return driverVol; } /** * Gets consistency groups and volumes from a given Storage Center. * * @param api The API connection. * @param ssn The Storage Center serial number. * @return The consistency groups and their volumes. */ public Map<ScReplayProfile, List<String>> getGCInfo(StorageCenterAPI api, String ssn) { Map<ScReplayProfile, List<String>> result = new HashMap<>(); ScReplayProfile[] cgs = api.getConsistencyGroups(ssn); for (ScReplayProfile cg : cgs) { result.put(cg, new ArrayList<>()); try { ScVolume[] vols = api.getReplayProfileVolumes(cg.instanceId); for (ScVolume vol : vols) { result.get(cg).add(vol.instanceId); } } catch (StorageCenterAPIException e) { LOG.warn(String.format("Error getting volumes for consistency group %s: %s", cg.instanceId, e)); } } return result; } /** * Gets a consistency group object from an SC replay profile. * * @param cg The ScReplayProfile. * @param volumeConsistencyGroup The consistency group object or null. * @return The consistency group object. */ public VolumeConsistencyGroup getVolumeConsistencyGroupFromReplayProfile(ScReplayProfile cg, VolumeConsistencyGroup volumeConsistencyGroup) { if (volumeConsistencyGroup == null) { volumeConsistencyGroup = new VolumeConsistencyGroup(); } volumeConsistencyGroup.setAccessStatus(AccessStatus.READ_WRITE); volumeConsistencyGroup.setNativeId(cg.instanceId); volumeConsistencyGroup.setDeviceLabel(cg.name); volumeConsistencyGroup.setStorageSystemId(cg.scSerialNumber); return volumeConsistencyGroup; } /** * Gets a StoragePort object for an ScControllerPort. * * @param api The API connection. * @param scPort The ScControllerPort. * @return The StoragePort. */ public StoragePort getStoragePortForControllerPort(StorageCenterAPI api, ScControllerPort scPort) { return getStoragePortForControllerPort(api, scPort, null); } /** * Gets a StoragePort object for an ScControllerPort. * * @param api The API connection. * @param scPort The ScControllerPort. * @param haZone The fault domain name. * @return The StoragePort. */ public StoragePort getStoragePortForControllerPort(StorageCenterAPI api, ScControllerPort scPort, String haZone) { StoragePort port = new StoragePort(); port.setNativeId(scPort.instanceId); port.setStorageSystemId(scPort.scSerialNumber); // Get the port configuration port.setPortHAZone(getHaZone(api, scPort, haZone)); if (ScControllerPort.FC_TRANSPORT_TYPE.equals(scPort.transportType)) { setFCPortInfo(api, scPort, port); } else if (ScControllerPort.ISCSI_TRANSPORT_TYPE.equals(scPort.transportType)) { setISCSIPortInfo(api, scPort, port); } StoragePort.OperationalStatus status = StoragePort.OperationalStatus.OK; if (!ScControllerPort.PORT_STATUS_UP.equals(scPort.status)) { status = StoragePort.OperationalStatus.NOT_OK; } port.setOperationalStatus(status); port.setPortName(port.getDeviceLabel()); port.setEndPointID(port.getPortNetworkId()); port.setAccessStatus(AccessStatus.READ_WRITE); return port; } /** * Gets the HA zone name. * * @param api The API connection. * @param scPort The ScControllerPort. * @param hazone The zone name if known. * @return The zone name. */ private String getHaZone(StorageCenterAPI api, ScControllerPort scPort, String hazone) { if (hazone != null && !hazone.isEmpty()) { return hazone; } String haZone = ""; ScFaultDomain[] faultDomains = api.getControllerPortFaultDomains(scPort.instanceId); if (faultDomains.length > 0) { // API returns list, but currently only one fault domain per port allowed haZone = faultDomains[0].name; } return haZone; } /** * Sets FC specific info for a port. * * @param api The API connection. * @param scPort The Storage Center port. * @param port The storage port object to populate. */ private void setFCPortInfo(StorageCenterAPI api, ScControllerPort scPort, StoragePort port) { port.setDeviceLabel(scPort.wwn); port.setTransportType(StoragePort.TransportType.FC); ScControllerPortFibreChannelConfiguration portConfig = api.getControllerPortFCConfig( scPort.instanceId); port.setPortNetworkId(formatWwn(scPort.wwn)); port.setEndPointID(port.getPortNetworkId()); port.setPortSpeed(SizeUtil.speedStrToGigabits(portConfig.speed)); port.setPortGroup(String.format("%s", portConfig.homeControllerIndex)); port.setPortSubGroup(String.format("%s", portConfig.slot)); port.setTcpPortNumber(0L); } /** * Sets iSCSI specific info for a port. * * @param api The API connection. * @param scPort The Storage Center port. * @param port The storage port object to populate. */ private void setISCSIPortInfo(StorageCenterAPI api, ScControllerPort scPort, StoragePort port) { port.setDeviceLabel(scPort.iscsiName); port.setTransportType(StoragePort.TransportType.IP); ScControllerPortIscsiConfiguration portConfig = api.getControllerPortIscsiConfig( scPort.instanceId); port.setEndPointID(scPort.iscsiName); port.setIpAddress(scPort.iscsiIpAddress); port.setPortNetworkId(scPort.iscsiName); port.setNetworkId(scPort.getNetwork()); port.setPortSpeed(SizeUtil.speedStrToGigabits(portConfig.speed)); port.setPortGroup(String.format("%s", portConfig.homeControllerIndex)); port.setPortSubGroup(String.format("%s", portConfig.slot)); port.setTcpPortNumber(portConfig.portNumber); } /** * Gets a StoragePool object for a storage type. * * @param api The Storage Center API connection. * @param storageType The storage type. * @param pool The StoragePool object to populate or null. * @return The StoragePool. */ public StoragePool getStoragePoolFromStorageType(StorageCenterAPI api, ScStorageType storageType, StoragePool pool) { if (pool == null) { pool = new StoragePool(); } pool.setNativeId(storageType.instanceId); pool.setStorageSystemId(storageType.scSerialNumber); LOG.info("Discovered Pool {}, storageSystem {}", pool.getNativeId(), pool.getStorageSystemId()); pool.setDeviceLabel(storageType.name); pool.setDisplayName(storageType.name); pool.setPoolName(storageType.name); pool.setCapabilities(new ArrayList<>(0)); // Get the supported transport protocols Set<StoragePool.Protocols> protocols = new HashSet<>(); List<String> transportProtocols = getSupportedProtocols(api, storageType.scSerialNumber); if (transportProtocols.contains(Protocol.FC.toString())) { protocols.add(StoragePool.Protocols.FC); } if (transportProtocols.contains(Protocol.iSCSI.toString())) { protocols.add(StoragePool.Protocols.iSCSI); } pool.setProtocols(protocols); pool.setPoolServiceType(StoragePool.PoolServiceType.block); pool.setMaximumThickVolumeSize(0L); pool.setMinimumThickVolumeSize(0L); pool.setMaximumThinVolumeSize(549755813888L); // Max 512 TB pool.setMinimumThinVolumeSize(1048576L); // Min 1 GB pool.setSupportedResourceType(StoragePool.SupportedResourceType.THIN_ONLY); ScStorageTypeStorageUsage su = api.getStorageTypeStorageUsage(storageType.instanceId); LOG.info("Space info: {} {} {}", su.allocatedSpace, su.freeSpace, su.usedSpace); pool.setSubscribedCapacity(SizeUtil.sizeStrToKBytes(su.usedSpace)); pool.setFreeCapacity(SizeUtil.sizeStrToKBytes(su.freeSpace)); pool.setTotalCapacity(SizeUtil.sizeStrToKBytes(su.allocatedSpace)); pool.setOperationalStatus(StoragePool.PoolOperationalStatus.READY); return pool; } /** * Gets the transport protocols supported by an array. * * @param api The SC API connection. * @param scSerialNumber The Storage Center serial number to check. * @return The supported protocols. */ private List<String> getSupportedProtocols(StorageCenterAPI api, String scSerialNumber) { List<String> protocols = new ArrayList<>(); boolean hasIScsi = false; boolean hasFC = false; ScControllerPort[] controllerPorts = api.getTargetPorts(scSerialNumber, null); for (ScControllerPort scPort : controllerPorts) { if (ScControllerPort.FC_TRANSPORT_TYPE.equals(scPort.transportType)) { hasFC = true; } else if (ScControllerPort.ISCSI_TRANSPORT_TYPE.equals(scPort.transportType)) { hasIScsi = true; } } if (hasIScsi) { protocols.add(Protocols.iSCSI.toString()); } if (hasFC) { protocols.add(Protocols.FC.toString()); } return protocols; } /** * Populate a StorageSystem object with Storage Center info. * * @param api The SC API connection. * @param sc The Storage Center. * @param storageSystemOrNull The StorageSystem to populate or null. * @return The StorageSystem. */ public StorageSystem getStorageSystemFromStorageCenter(StorageCenterAPI api, StorageCenter sc, StorageSystem storageSystemOrNull) { StorageSystem storageSystem = storageSystemOrNull; if (storageSystem == null) { storageSystem = new StorageSystem(); } storageSystem.setSerialNumber(sc.scSerialNumber); storageSystem.setAccessStatus(AccessStatus.READ_WRITE); storageSystem.setModel(sc.modelSeries); storageSystem.setProvisioningType(SupportedProvisioningType.THIN); storageSystem.setNativeId(sc.scSerialNumber); // Parse out version information String[] version = sc.version.split("\\."); storageSystem.setMajorVersion(version[0]); storageSystem.setMinorVersion(version[1]); storageSystem.setFirmwareVersion(sc.version); storageSystem.setIsSupportedVersion(true); storageSystem.setDeviceLabel(sc.scName); storageSystem.setDisplayName(sc.name); // Make sure it's reported that we support CGs Set<SupportedReplication> supportedReplications = new HashSet<>(); supportedReplications.add(SupportedReplication.elementReplica); supportedReplications.add(SupportedReplication.groupReplica); storageSystem.setSupportedReplications(supportedReplications); // Check the supported protocols for this array List<String> protocols = getSupportedProtocols(api, sc.scSerialNumber); storageSystem.setProtocols(protocols); return storageSystem; } /** * Gets a formatted WWN. * * @param wwn The raw WWN. * @return The formatted WWN. */ public String formatWwn(String wwn) { if (wwn == null || wwn.length() != 16) { return wwn; } List<String> parts = new ArrayList<>(); for (int i = 0; i < wwn.length(); i += 2) { parts.add(wwn.substring(i, i + 2).toUpperCase()); } return String.join(":", parts); } }