/** * Copyright (c) 2015 EMC Corporation * All Rights Reserved */ package com.emc.storageos.scaleio.api.restapi; import java.net.URI; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.ws.rs.core.MediaType; import org.codehaus.jettison.json.JSONArray; import org.codehaus.jettison.json.JSONException; import org.codehaus.jettison.json.JSONObject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.emc.storageos.scaleio.ScaleIOException; import com.emc.storageos.services.restutil.StandardRestClient; import com.sun.jersey.api.client.Client; import com.sun.jersey.api.client.ClientResponse; import com.sun.jersey.api.client.WebResource; import com.sun.jersey.api.client.WebResource.Builder; import com.sun.jersey.api.client.filter.HTTPBasicAuthFilter; import com.sun.jersey.api.client.filter.LoggingFilter; import com.emc.storageos.scaleio.api.ScaleIOConstants; import com.emc.storageos.scaleio.api.restapi.request.ScaleIOCreateVolume; import com.emc.storageos.scaleio.api.restapi.request.ScaleIOMapVolumeToSDC; import com.emc.storageos.scaleio.api.restapi.request.ScaleIOMapVolumeToScsiInitiator; import com.emc.storageos.scaleio.api.restapi.request.ScaleIOModifyVolumeSize; import com.emc.storageos.scaleio.api.restapi.request.ScaleIORemoveConsistencyGroupSnapshots; import com.emc.storageos.scaleio.api.restapi.request.ScaleIORemoveVolume; import com.emc.storageos.scaleio.api.restapi.request.ScaleIOSnapshotVolumes; import com.emc.storageos.scaleio.api.restapi.request.ScaleIOUnmapVolumeToSDC; import com.emc.storageos.scaleio.api.restapi.request.ScaleIOUnmapVolumeToScsiInitiator; import com.emc.storageos.scaleio.api.restapi.request.ScaleIOVolumeList; import com.emc.storageos.scaleio.api.restapi.response.ScaleIOProtectionDomain; import com.emc.storageos.scaleio.api.restapi.response.ScaleIOScsiInitiator; import com.emc.storageos.scaleio.api.restapi.response.ScaleIOSDC; import com.emc.storageos.scaleio.api.restapi.response.ScaleIOSDS; import com.emc.storageos.scaleio.api.restapi.response.ScaleIOSnapshotVolumeResponse; import com.emc.storageos.scaleio.api.restapi.response.ScaleIOStoragePool; import com.emc.storageos.scaleio.api.restapi.response.ScaleIOSystem; import com.emc.storageos.scaleio.api.restapi.response.ScaleIOVolume; import com.google.gson.Gson; /** * This class implements methods for calling ScaleIO REST API to do operations. */ public class ScaleIORestClient extends StandardRestClient { private static Logger log = LoggerFactory.getLogger(ScaleIORestClient.class); /** * Constructor * * @param factory A reference to the ScaleIORestClientFactory * @param baseURI the base URI to connect to ScaleIO Gateway * @param client A reference to a Jersey Apache HTTP client. * @param username The MDM usernam. * @param password The MDM user password. */ public ScaleIORestClient(URI baseURI, String username, String password, Client client) { _client = client; _base = baseURI; _username = username; _password = password; _authToken = ""; } public void setUsername(String username) { _username = username; } public void setPassword(String password) { _password = password; } public String init() throws Exception { return getVersion(); } /** * Query all SDCs * * @return The list of ScaleIOSDC * @throws JSONException */ public List<ScaleIOSDC> queryAllSDC() throws JSONException { ClientResponse response = get(URI.create(ScaleIOConstants.GET_SDC_URI)); return getResponseObjects(ScaleIOSDC.class, response); } /** * Query all SDS * * @return The list of ScaleIOSDS * @throws Exception */ public List<ScaleIOSDS> queryAllSDS() throws Exception { log.info("Discoverying all SDS."); ClientResponse response = get(URI.create(ScaleIOConstants.GET_SDS_URI)); return getResponseObjects(ScaleIOSDS.class, response); } /** * Get the detail information for a pool * * @param poolId The storage pool native Id * @return The storage pool information * @throws Exception */ public ScaleIOStoragePool queryStoragePool(String poolId) throws Exception { ClientResponse response = get(URI.create(ScaleIOConstants.getStoragePoolStatsURI(poolId))); ScaleIOStoragePool pool = getResponseObject(ScaleIOStoragePool.class, response); ScaleIOStoragePool poolStats = getStoragePoolStats(poolId); pool.setCapacityAvailableForVolumeAllocationInKb(poolStats.getCapacityAvailableForVolumeAllocationInKb()); pool.setMaxCapacityInKb(poolStats.getMaxCapacityInKb()); return pool; } /** * Create a volume * * @param protectionDomainId The protection domain ID * @param storagePoolId The storage pool ID * @param volumeName The volume name * @param volumeSize The volume Size * @return The detail of the created volume * @throws Exception */ public ScaleIOVolume addVolume(String protectionDomainId, String storagePoolId, String volumeName, String volumeSize) throws Exception { ScaleIOCreateVolume volume = new ScaleIOCreateVolume(); Long sizeInKb = Long.parseLong(volumeSize) / 1024L; volume.setVolumeSizeInKb(sizeInKb.toString()); volume.setStoragePoolId(storagePoolId); volume.setName(volumeName); volume.setProtectionDomainId(protectionDomainId); ClientResponse response = post(URI.create(ScaleIOConstants.VOLUMES_URI), getJsonForEntity(volume)); ScaleIOVolume createdVol = getResponseObject(ScaleIOVolume.class, response); return queryVolume(createdVol.getId()); } /** * Create a volume * * @param protectionDomainId The protection domain ID * @param storagePoolId The storage pool ID * @param volumeName The volume name * @param volumeSize The volume Size * @param thinProvisioned true for thinProvisioned, false for thickProvisioned * @return * @throws Exception */ public ScaleIOVolume addVolume(String protectionDomainId, String storagePoolId, String volumeName, String volumeSize, boolean thinProvisioned) throws Exception { ScaleIOCreateVolume volume = new ScaleIOCreateVolume(); Long sizeInKb = Long.parseLong(volumeSize) / 1024L; volume.setVolumeSizeInKb(sizeInKb.toString()); volume.setStoragePoolId(storagePoolId); volume.setName(volumeName); volume.setProtectionDomainId(protectionDomainId); if (thinProvisioned) { volume.setVolumeType(ScaleIOConstants.THIN_PROVISIONED); } else { volume.setVolumeType(ScaleIOConstants.THICK_PROVISIONED); } ClientResponse response = post(URI.create(ScaleIOConstants.VOLUMES_URI), getJsonForEntity(volume)); ScaleIOVolume createdVol = getResponseObject(ScaleIOVolume.class, response); return queryVolume(createdVol.getId()); } /** * Remove the volume * * @param volumeId The volume ID * @throws Exception */ public void removeVolume(String volumeId) throws Exception { String uri = ScaleIOConstants.getRemoveVolumeURI(volumeId); ScaleIORemoveVolume removeParm = new ScaleIORemoveVolume(); post(URI.create(uri), getJsonForEntity(removeParm)); } /** * Expand the volume * * @param volumeId The volume ID * @param newSizeGB The new size of the volume in GB * @throws Exception */ public ScaleIOVolume modifyVolumeCapacity(String volumeId, String newSizeGB) throws Exception { String uri = ScaleIOConstants.getModifyVolumeSizeURI(volumeId); ScaleIOModifyVolumeSize modifyParm = new ScaleIOModifyVolumeSize(); modifyParm.setSizeInGB(newSizeGB); post(URI.create(uri), getJsonForEntity(modifyParm)); return queryVolume(volumeId); } /** * Create a snapshot of the volume * * @param volId The volume ID * @param snapshotName The snapshot name * @param systemId * @return * @throws Exception */ public ScaleIOSnapshotVolumeResponse snapshotVolume(String volId, String snapshotName, String systemId) throws Exception { String uri = ScaleIOConstants.getSnapshotVolumesURI(systemId); ScaleIOSnapshotVolumes spVol = new ScaleIOSnapshotVolumes(); spVol.addSnapshot(volId, snapshotName); ClientResponse response = post(URI.create(uri), getJsonForEntity(spVol)); return getResponseObject(ScaleIOSnapshotVolumeResponse.class, response); } /** * Create multiple snapshots in a consistency group * * @param id2snapshot Volume ID to snapshotName map * @param systemId The system ID * @return The result of the snapshots created * @throws Exception */ public ScaleIOSnapshotVolumeResponse snapshotMultiVolume(Map<String, String> id2snapshot, String systemId) throws Exception { String uri = ScaleIOConstants.getSnapshotVolumesURI(systemId); ScaleIOSnapshotVolumes spVol = new ScaleIOSnapshotVolumes(); for (Map.Entry<String, String> entry : id2snapshot.entrySet()) { spVol.addSnapshot(entry.getKey(), entry.getValue()); } ClientResponse response = post(URI.create(uri), getJsonForEntity(spVol)); return getResponseObject(ScaleIOSnapshotVolumeResponse.class, response); } /** * Map volume to SDC * * @param volumeId The volume ID * @param sdcId The SDC ID * @throws Exception */ public void mapVolumeToSDC(String volumeId, String sdcId) throws Exception { log.info("mapping to sdc"); String uri = ScaleIOConstants.getMapVolumeToSDCURI(volumeId); ScaleIOMapVolumeToSDC mapParm = new ScaleIOMapVolumeToSDC(); mapParm.setSdcId(sdcId); mapParm.setAllowMultipleMappings("TRUE"); post(URI.create(uri), getJsonForEntity(mapParm)); } /** * Unmap the volume * * @param volumeId The Volume ID * @param sdcId The SDC ID * @throws Exception */ public void unMapVolumeToSDC(String volumeId, String sdcId) throws Exception { String uri = ScaleIOConstants.getUnmapVolumeToSDCURI(volumeId); ScaleIOUnmapVolumeToSDC unmapParm = new ScaleIOUnmapVolumeToSDC(); unmapParm.setSdcId(sdcId); unmapParm.setIgnoreScsiInitiators("TRUE"); post(URI.create(uri), getJsonForEntity(unmapParm)); } /** * Remove snapshots, which are in a consistency group * * @param consistencyGroupId The consistency group ID * @throws Exception */ public void removeConsistencyGroupSnapshot( String consistencyGroupId) throws Exception { String systemId = getSystemId(); String uri = ScaleIOConstants.getRemoveConsistencyGroupSnapshotsURI(systemId); ScaleIORemoveConsistencyGroupSnapshots parm = new ScaleIORemoveConsistencyGroupSnapshots(); parm.setSnapGroupId(consistencyGroupId); post(URI.create(uri), getJsonForEntity(parm)); } /** * Query all SCSI Initiators * * @return The list of the ScaleIOScsiInitiator * @throws JSONException */ public List<ScaleIOScsiInitiator> queryAllSCSIInitiators() throws JSONException { log.info("Discovery all SCSI Initiators"); List<ScaleIOScsiInitiator> scsiInits = Collections.emptyList(); try { ClientResponse response = get(URI.create(ScaleIOConstants.GET_SCSI_INITIATOR_URI)); scsiInits = getResponseObjects(ScaleIOScsiInitiator.class, response); } catch (Exception e) { // 1.32 and later does not support get ScsiInitiators log.debug("This exception is expected for ScaleIO 1.32 and later, since iSCSI supported is removed. Exception:", e); } return scsiInits; } /** * Map the volume to SCSI Initiator * * @param volumeId the volume ID * @param initiatorId The ScsiInitiator ID * @throws Exception */ public void mapVolumeToSCSIInitiator( String volumeId, String initiatorId) throws Exception { log.info("mapping to scsi"); String uri = ScaleIOConstants.getMapVolumeToScsiInitiatorURI(volumeId); ScaleIOMapVolumeToScsiInitiator mapParm = new ScaleIOMapVolumeToScsiInitiator(); mapParm.setScsiInitiatorId(initiatorId); mapParm.setAllowMultipleMapp("TRUE"); post(URI.create(uri), getJsonForEntity(mapParm)); } /** * Unmap the volume from SCSI Initiator * * @param volumeId The volume ID * @param initiatorId The ScsiInitiator ID * @throws Exception */ public void unMapVolumeFromSCSIInitiator(String volumeId, String initiatorId) throws Exception { String uri = ScaleIOConstants.getUnmapVolumeToScsiInitiatorURI(volumeId); ScaleIOUnmapVolumeToScsiInitiator unmapParm = new ScaleIOUnmapVolumeToScsiInitiator(); unmapParm.setScsiInitiatorId(initiatorId); post(URI.create(uri), getJsonForEntity(unmapParm)); } /** * Get the system version * * @return The version */ public String getVersion() { String result = null; try { ScaleIOSystem system = getSystem(); result = system.getVersion(); } catch (Exception e) { log.error("Exception while getting version", e); } return result; } @Override protected Builder setResourceHeaders(WebResource resource) { return resource.getRequestBuilder(); } @Override protected void authenticate() { log.info("Authenticating"); _client.removeAllFilters(); _client.addFilter(new HTTPBasicAuthFilter(_username, _password)); if (log.isDebugEnabled()) { _client.addFilter(new LoggingFilter(System.out)); } URI requestURI = _base.resolve(URI.create(ScaleIOConstants.API_LOGIN)); ClientResponse response = _client.resource(requestURI).type(MediaType.APPLICATION_JSON) .get(ClientResponse.class); if (response.getClientResponseStatus() != ClientResponse.Status.OK && response.getClientResponseStatus() != ClientResponse.Status.CREATED) { throw ScaleIOException.exceptions.authenticationFailure(_base.toString()); } _authToken = response.getEntity(String.class).replace("\"", ""); _client.removeAllFilters(); _client.addFilter(new HTTPBasicAuthFilter(_username, _authToken)); if (log.isDebugEnabled()) { _client.addFilter(new LoggingFilter(System.out)); } } @Override protected int checkResponse(URI uri, ClientResponse response) { ClientResponse.Status status = response.getClientResponseStatus(); int errorCode = status.getStatusCode(); if (errorCode >= 300) { JSONObject obj = null; int code = 0; try { obj = response.getEntity(JSONObject.class); code = obj.getInt(ScaleIOConstants.ERROR_CODE); } catch (Exception e) { log.error("Parsing the failure response object failed", e); } if (code == 404 || code == 410) { throw ScaleIOException.exceptions.resourceNotFound(uri.toString()); } else if (code == 401) { throw ScaleIOException.exceptions.authenticationFailure(uri.toString()); } else { throw ScaleIOException.exceptions.internalError(uri.toString(), obj.toString()); } } else { return errorCode; } } private <T> List<T> getResponseObjects(Class<T> clazz, ClientResponse response) throws JSONException { JSONArray resp = response.getEntity(JSONArray.class); List<T> result = null; if (resp != null && resp.length() > 0) { result = new ArrayList<T>(); for (int i = 0; i < resp.length(); i++) { JSONObject entry = resp.getJSONObject(i); T respObject = new Gson().fromJson(entry.toString(), clazz); result.add(respObject); } } return result; } /** * Get the system * * @return The details of the system * @throws Exception */ public ScaleIOSystem getSystem() throws Exception { ClientResponse response = get(URI.create(ScaleIOConstants.GET_SYSTEMS_URI)); List<ScaleIOSystem> systemsInfo = getResponseObjects(ScaleIOSystem.class, response); return systemsInfo.get(0); } /** * Get protection domains in the system * * @return The List of protection domains * @throws JSONException */ public List<ScaleIOProtectionDomain> getProtectionDomains() throws JSONException { ClientResponse response = get(URI.create(ScaleIOConstants.GET_PROTECTION_DOMAIN_URI)); return getResponseObjects(ScaleIOProtectionDomain.class, response); } /** * Get the storage pools in the protection domain * * @param pdId The protection domain ID * @return The List of storage pools * @throws JSONException */ public List<ScaleIOStoragePool> getProtectionDomainStoragePools(String pdId) throws Exception { ClientResponse response = get(URI.create(ScaleIOConstants.getProtectionDomainStoragePoolURI(pdId))); List<ScaleIOStoragePool> pools = getResponseObjects(ScaleIOStoragePool.class, response); for (ScaleIOStoragePool pool : pools) { ScaleIOStoragePool poolResult = getStoragePoolStats(pool.getId()); pool.setCapacityAvailableForVolumeAllocationInKb(poolResult.getCapacityAvailableForVolumeAllocationInKb()); pool.setMaxCapacityInKb(poolResult.getMaxCapacityInKb()); } return pools; } /** * Get the Storage Pool detail data * * @param poolId The storage pool ID * @return The details of the storage pool * @throws Exception */ public ScaleIOStoragePool getStoragePoolStats(String poolId) throws Exception { ClientResponse response = get(URI.create(ScaleIOConstants.getStoragePoolStatsURI(poolId))); ScaleIOStoragePool pool = getResponseObject(ScaleIOStoragePool.class, response); pool.setId(poolId); return pool; } /** * Get the system ID * * @return The System ID * @throws Exception */ public String getSystemId() throws Exception { ClientResponse response = get(URI.create(ScaleIOConstants.GET_SYSTEMS_URI)); List<ScaleIOSystem> systemsInfo = getResponseObjects(ScaleIOSystem.class, response); return systemsInfo.get(0).getId(); } /** * Query the volume details * * @param volId The Volume ID * @return The details of the volume * @throws Exception */ public ScaleIOVolume queryVolume(String volId) throws Exception { ClientResponse response = get(URI.create(ScaleIOConstants.getVolumeURI(volId))); return getResponseObject(ScaleIOVolume.class, response); } /** * Get the volume names for the given volume Ids * * @param volumeIds The list of volume IDs * @return The map of the volume name and volume ID * @throws Exception */ public Map<String, String> getVolumes(List<String> volumeIds) throws Exception { Map<String, String> result = new HashMap<String, String>(); ScaleIOVolumeList parm = new ScaleIOVolumeList(); parm.setIds(volumeIds); ClientResponse response = post(URI.create(ScaleIOConstants.GET_VOLUMES_BYIDS_URI), getJsonForEntity(parm)); List<ScaleIOVolume> volumes = getResponseObjects(ScaleIOVolume.class, response); for (ScaleIOVolume volume : volumes) { result.put(volume.getName(), volume.getId()); } return result; } /** * Get the volumes for the given volume Ids * * @param volumeIds The list of volume IDs * @return The map of the ScaleIO volume and volume ID * @throws Exception */ public Map<String, ScaleIOVolume> getVolumeNameMap(List<String> volumeIds) throws Exception { Map<String, ScaleIOVolume> result = new HashMap<String, ScaleIOVolume>(); ScaleIOVolumeList parm = new ScaleIOVolumeList(); parm.setIds(volumeIds); ClientResponse response = post(URI.create(ScaleIOConstants.GET_VOLUMES_BYIDS_URI), getJsonForEntity(parm)); List<ScaleIOVolume> volumes = getResponseObjects(ScaleIOVolume.class, response); for (ScaleIOVolume volume : volumes) { result.put(volume.getName(), volume); } return result; } }