/* * Copyright 2016 EMC Corporation * All Rights Reserved */ package com.emc.storageos.hp3par.impl; import java.net.URI; import java.text.MessageFormat; import java.util.ArrayList; import java.util.List; import javax.ws.rs.core.MultivaluedMap; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.codehaus.jettison.json.JSONObject; import com.emc.storageos.hp3par.command.CPGCommandResult; import com.emc.storageos.hp3par.command.CPGMember; import com.emc.storageos.hp3par.command.ConsistencyGroupResult; import com.emc.storageos.hp3par.command.ConsistencyGroupsListResult; import com.emc.storageos.hp3par.command.FcPath; import com.emc.storageos.hp3par.command.HostCommandResult; import com.emc.storageos.hp3par.command.HostMember; import com.emc.storageos.hp3par.command.HostSetDetailsCommandResult; import com.emc.storageos.hp3par.command.ISCSIPath; import com.emc.storageos.hp3par.command.PortCommandResult; import com.emc.storageos.hp3par.command.PortStatisticsCommandResult; import com.emc.storageos.hp3par.command.Privileges; import com.emc.storageos.hp3par.command.SystemCommandResult; import com.emc.storageos.hp3par.command.UserRoleCommandResult; import com.emc.storageos.hp3par.command.VVSetCloneList; import com.emc.storageos.hp3par.command.VVSetCloneList.VVSetVolumeClone; import com.emc.storageos.hp3par.command.VirtualLunsList; import com.emc.storageos.hp3par.command.VlunResult; import com.emc.storageos.hp3par.command.VolumeDetailsCommandResult; import com.emc.storageos.hp3par.connection.RESTClient; import com.emc.storageos.hp3par.utils.CompleteError; import com.emc.storageos.hp3par.utils.HP3PARConstants; import com.emc.storageos.hp3par.utils.SanUtils; import com.emc.storageos.storagedriver.model.VolumeClone; import com.google.gson.Gson; import com.google.json.JsonSanitizer; import com.sun.jersey.api.client.ClientResponse; import com.emc.storageos.hp3par.command.VolumesCommandResult; import static com.google.json.JsonSanitizer.*; /** * Implements communication with 3PAR storage */ public class HP3PARApi { private final URI _baseUrl; private final RESTClient _client; private Logger _log = LoggerFactory.getLogger(HP3PARApi.class); private String _authToken; private String _user; private String _password; // Discovery related private static final URI URI_LOGIN = URI.create("/api/v1/credentials"); private static final String URI_SYSTEM = "/api/v1/system"; private static final String URI_USER_ROLE = "/api/v1/users/{0}"; private static final String URI_CPGS = "/api/v1/cpgs"; private static final String URI_CPG_DETAILS = "/api/v1/cpgs/{0}"; private static final String URI_PORTS = "/api/v1/ports"; private static final String URI_PORT_STATISTICS = "/api/v1/systemreporter/attime/portstatistics/daily"; private static final String URI_PORT_STATISTICS_SPECIFIC = "/api/v1/systemreporter/attime/portstatistics/daily?query=%22portPos%20EQ%20{0}%22"; // Base volume , snap and clone volume related private static final String URI_CREATE_VOLUME = "/api/v1/volumes"; private static final String URI_VOLUME_DETAILS = "/api/v1/volumes/{0}"; private static final String URI_EXPAND_VOLUME = "/api/v1/volumes/{0}"; private static final String URI_DELETE_VOLUME = "/api/v1/volumes/{0}"; private static final String URI_STORAGE_VOLUMES = "/api/v1/volumes"; // snapshot / virtual copy private static final String URI_CREATE_VOLUME_SNAPSHOT = "/api/v1/volumes/{0}"; private static final String URI_DELETE_VOLUME_SNAPSHOT = "/api/v1/volumes/{0}"; private static final String URI_RESTORE_VOLUME_SNAPSHOT = "/api/v1/volumes/{0}"; // clone / physical copy private static final String URI_CREATE_VOLUME_CLONE = "/api/v1/volumes/{0}"; private static final String URI_DELETE_VOLUME_CLONE = "/api/v1/volumes/{0}"; private static final String URI_RESTORE_VOLUME_CLONE = "/api/v1/volumes/{0}"; // CG related private static final String URI_CREATE_CG = "/api/v1/volumesets"; private static final String URI_DELETE_CG = "/api/v1/volumesets/{0}"; private static final String URI_SNAPSHOT_CG = "/api/v1/volumesets/{0}"; private static final String URI_CLONE_CG = "/api/v1/volumesets/{0}"; private static final String URI_UPDATE_CG = "/api/v1/volumesets/{0}"; private static final String URI_CG_DETAILS = "/api/v1/volumesets/{0}"; private static final String URI_CG_LIST_DETAILS = "/api/v1/volumesets"; // For ingestion private static final String URI_VLUNS_OF_VOLUME = "/api/v1/vluns?query=%22volumeWWN=={0}%22"; private static final String URI_VLUNS_BY_VOlUME_NAME = "/api/v1/vluns?query=%22volumeName=={0}%22"; private static final String URI_SNAPSHOTS_OF_VOLUME = "/api/v1/volumes?query=%22copyOf=={0}%22"; // For export private static final String URI_CREATE_VLUN = "/api/v1/vluns"; private static final String URI_HOSTS = "/api/v1/hosts"; private static final String URI_HOSTSETS = "/api/v1/hostsets"; private static final String URI_HOSTSET_DETAILS = "/api/v1/hostsets/{0}"; private static final String URI_HOST_DETAILS = "/api/v1/hosts/{0}"; private static final String URI_VLUNS = "/api/v1/vluns"; private static final String URI_DELETE_VLUN = "/api/v1/vluns/{0},{1},{2}"; public HP3PARApi(URI endpoint, RESTClient client, String userName, String pass) { _baseUrl = endpoint; _client = client; _user = userName; _password = pass; } /** * Close client resources */ public void close() { _client.close(); } /** * Get authentication token from the storage * @param user user name * @param password password * @return authentication token * @throws Exception */ public String getAuthToken(String user, String password) throws Exception { _log.info("3PARDriver:getAuthToken enter"); String authToken = null; ClientResponse clientResp = null; String body= "{\"user\":\"" + user + "\", \"password\":\"" + password + "\"}"; try { clientResp = _client.post_json(_baseUrl.resolve(URI_LOGIN), body); if (clientResp == null) { _log.error("3PARDriver:There is no response from 3PAR"); throw new HP3PARException("There is no response from 3PAR"); } else if (clientResp.getStatus() != 201) { String errResp = getResponseDetails(clientResp); throw new HP3PARException(errResp); } else { JSONObject jObj = clientResp.getEntity(JSONObject.class); authToken = jObj.getString("key"); this._authToken = authToken; this._user = user; this._password = password; _log.info("3PARDriver:getAuthToken set"); } return authToken; } catch (Exception e) { throw e; } finally { if (clientResp != null) { clientResp.close(); } _log.info("3PARDriver:getAuthToken leave"); } //end try/catch/finally } /** * Get authentication token from the storage * @return authentication token * @throws Exception */ public String getAuthToken() throws Exception { _log.info("3PARDriver:getAuthToken enter, after expiry"); String authToken = null; ClientResponse clientResp = null; String body= "{\"user\":\"" + _user + "\", \"password\":\"" + _password + "\"}"; try { clientResp = _client.post_json(_baseUrl.resolve(URI_LOGIN), body); if (clientResp == null) { _log.error("3PARDriver:There is no response from 3PAR"); throw new HP3PARException("There is no response from 3PAR"); } else if (clientResp.getStatus() != 201) { String errResp = getResponseDetails(clientResp); throw new HP3PARException(errResp); } else { JSONObject jObj = clientResp.getEntity(JSONObject.class); authToken = jObj.getString("key"); } this._authToken = authToken; return authToken; } catch (Exception e) { throw e; } finally { if (clientResp != null) { clientResp.close(); } _log.info("3PARDriver:getAuthToken leave, after expiry"); } //end try/catch/finally } public void verifyUserRole(String name) throws Exception { _log.info("3PARDriver:verifyUserRole enter"); ClientResponse clientResp = null; final String path = MessageFormat.format(URI_USER_ROLE, name); _log.info("3PARDriver: verifyUserRole path is {}", path); try { clientResp = get(path); if (clientResp == null) { _log.error("3PARDriver:There is no response from 3PAR"); throw new HP3PARException("There is no response from 3PAR"); } else if (clientResp.getStatus() != 200) { String errResp = getResponseDetails(clientResp); throw new HP3PARException(errResp); } else { String responseString = clientResp.getEntity(String.class); _log.info("3PARDriver:getSystemDetails 3PAR response is {}", responseString); UserRoleCommandResult roleRes = new Gson().fromJson(sanitize(responseString), UserRoleCommandResult.class); boolean superUser = false; for (Privileges currPriv:roleRes.getPrivileges()) { if ( (currPriv.getDomain().compareToIgnoreCase("all") == 0) && (currPriv.getRole().compareToIgnoreCase("super") == 0)) { superUser = true; } } if (superUser == false) { _log.error("3PARDriver:User does not have sufficient privilege to discover"); throw new HP3PARException("User does not have sufficient privilege"); } else { _log.info("3PARDriver:User is super user"); } } } catch (Exception e) { throw e; } finally { if (clientResp != null) { clientResp.close(); } _log.info("3PARDriver:verifyUserRole leave"); } //end try/catch/finally } /** * Gets the storage array information * @return array details * @throws Exception */ public SystemCommandResult getSystemDetails() throws Exception { _log.info("3PARDriver:getSystemDetails enter"); ClientResponse clientResp = null; try { clientResp = get(URI_SYSTEM); if (clientResp == null) { _log.error("3PARDriver:There is no response from 3PAR"); throw new HP3PARException("There is no response from 3PAR"); } else if (clientResp.getStatus() != 200) { String errResp = getResponseDetails(clientResp); throw new HP3PARException(errResp); } else { String responseString = clientResp.getEntity(String.class); _log.info("3PARDriver:getSystemDetails 3PAR response is {}", responseString); SystemCommandResult systemRes = new Gson().fromJson(sanitize(responseString), SystemCommandResult.class); return systemRes; } } catch (Exception e) { throw e; } finally { if (clientResp != null) { clientResp.close(); } _log.info("3PARDriver:getSystemDetails leave"); } //end try/catch/finally } /** * Gets 3PAR CPG attributes * @return CPG details * @throws Exception */ public CPGCommandResult getAllCPGDetails() throws Exception { _log.info("3PARDriver:getAllCPGDetails enter"); ClientResponse clientResp = null; try { clientResp = get(URI_CPGS); if (clientResp == null) { _log.error("3PARDriver:There is no response from 3PAR"); throw new HP3PARException("There is no response from 3PAR"); } else if (clientResp.getStatus() != 200) { String errResp = getResponseDetails(clientResp); throw new HP3PARException(errResp); } else { String responseString = clientResp.getEntity(String.class); _log.info("3PARDriver:getAllCPGDetails 3PAR response is {}", responseString); CPGCommandResult cpgResult = new Gson().fromJson(sanitize(responseString), CPGCommandResult.class); return cpgResult; } } catch (Exception e) { throw e; } finally { if (clientResp != null) { clientResp.close(); } _log.info("3PARDriver:getSystemDetails leave"); } //end try/catch/finally } public CPGMember getCPGDetails(String name) throws Exception { _log.info("3PARDriver:getCPGDetails enter"); ClientResponse clientResp = null; final String path = MessageFormat.format(URI_CPG_DETAILS, name); _log.info("3PARDriver: getCPGDetails path is {}", path); try { clientResp = get(path); if (clientResp == null) { _log.error("3PARDriver:There is no response from 3PAR"); throw new HP3PARException("There is no response from 3PAR"); } else if (clientResp.getStatus() != 200) { String errResp = getResponseDetails(clientResp); throw new HP3PARException(errResp); } else { String responseString = clientResp.getEntity(String.class); _log.info("3PARDriver:getCPGDetails 3PAR response is {}", responseString); CPGMember cpgResult = new Gson().fromJson(sanitize(responseString), CPGMember.class); return cpgResult; } } catch (Exception e) { throw e; } finally { if (clientResp != null) { clientResp.close(); } _log.info("3PARDriver:getCPGDetails leave"); } //end try/catch/finally } /** * Gets host port information * @return port details * @throws Exception */ public PortCommandResult getPortDetails() throws Exception { _log.info("3PARDriver:getPortDetails enter"); ClientResponse clientResp = null; try { clientResp = get(URI_PORTS); if (clientResp == null) { _log.error("3PARDriver:There is no response from 3PAR"); throw new HP3PARException("There is no response from 3PAR"); } else if (clientResp.getStatus() != 200) { String errResp = getResponseDetails(clientResp); throw new HP3PARException(errResp); } else { String responseString = clientResp.getEntity(String.class); _log.info("3PARDriver:getPortDetails 3PAR response is {}", responseString); PortCommandResult portResult = new Gson().fromJson(sanitize(responseString), PortCommandResult.class); return portResult; } } catch (Exception e) { throw e; } finally { if (clientResp != null) { clientResp.close(); } _log.info("3PARDriver:getPortDetails leave"); } //end try/catch/finally } /** * Get port statistics information * @return port details * @throws Exception */ public PortStatisticsCommandResult getPortStatisticsDetail() throws Exception { _log.info("3PARDriver:getPortStatisticsDetail enter"); ClientResponse clientResp = null; try { clientResp = get(URI_PORT_STATISTICS); if (clientResp == null) { _log.error("3PARDriver:There is no response from 3PAR"); throw new HP3PARException("There is no response from 3PAR"); } else if (clientResp.getStatus() != 200) { String errResp = getResponseDetails(clientResp); throw new HP3PARException(errResp); } else { String responseString = clientResp.getEntity(String.class); _log.info("3PARDriveri:getPortStatisticsDetail 3PAR response is {}", responseString); PortStatisticsCommandResult portStatResult = new Gson().fromJson(sanitize(responseString), PortStatisticsCommandResult.class); return portStatResult; } } catch (Exception e) { throw e; } finally { if (clientResp != null) { clientResp.close(); } _log.info("3PARDriver:getPortStatisticsDetail leave"); } //end try/catch/finally } public PortStatisticsCommandResult getPortStatistics(String portId) throws Exception { _log.info("3PARDriver: getPortStatistics enter"); ClientResponse clientResp = null; final String path = MessageFormat.format(URI_PORT_STATISTICS_SPECIFIC, portId); PortStatisticsCommandResult portStatResult = null; _log.info("3PARDriver: getPortStatistics path is {}", path); try { clientResp = get(path); if (clientResp == null) { _log.error("3PARDriver: getPortStatistics There is no response from 3PAR"); throw new HP3PARException("There is no response from 3PAR"); } else if (clientResp.getStatus() != 200) { String errResp = getResponseDetails(clientResp); _log.error("3PARDriver: getPortStatistics There is error response from 3PAR = {}" , errResp); throw new HP3PARException(errResp); } else { String responseString = clientResp.getEntity(String.class); _log.info("3PARDriveri:getPortStatistics 3PAR response is {}", responseString); portStatResult = new Gson().fromJson(sanitize(responseString), PortStatisticsCommandResult.class); } } catch (Exception e) { throw e; } finally { if (clientResp != null) { clientResp.close(); } _log.info("3PARDriver: getPortStatistics leave"); } //end try/catch/finally return portStatResult; } public HostCommandResult getAllHostDetails() throws Exception { _log.info("3PARDriver:getAllHostDetails enter"); ClientResponse clientResp = null; try { clientResp = get(URI_HOSTS); if (clientResp == null) { _log.error("3PARDriver:There is no response from 3PAR"); throw new HP3PARException("There is no response from 3PAR"); } else if (clientResp.getStatus() != 200) { String errResp = getResponseDetails(clientResp); throw new HP3PARException(errResp); } else { String responseString = clientResp.getEntity(String.class); _log.info("3PARDriver:getAllHostDetails 3PAR response is {}", responseString); HostCommandResult hostResult = new Gson().fromJson(sanitize(responseString), HostCommandResult.class); return hostResult; } } catch (Exception e) { throw e; } finally { if (clientResp != null) { clientResp.close(); } _log.info("3PARDriver:getAllHostDetails leave"); } //end try/catch/finally } public void createVolume(String name, String cpg, Boolean thin, Boolean dedup, Long size) throws Exception { _log.info("3PARDriver:createVolume enter"); ClientResponse clientResp = null; String body = "{\"name\":\"" + name + "\", \"cpg\":\"" + cpg + "\", \"tpvv\":" + thin.toString() + ", \"tdvv\":" + dedup.toString() + ", \"sizeMiB\":" + size.toString() + ", \"snapCPG\":\"" + cpg + "\"}"; _log.info("3PARDriver: createVolume body is {}", body); try { clientResp = post(URI_CREATE_VOLUME, body); if (clientResp == null) { _log.error("3PARDriver:There is no response from 3PAR"); throw new HP3PARException("There is no response from 3PAR"); } else if (clientResp.getStatus() != 201) { String errResp = getResponseDetails(clientResp); throw new HP3PARException(errResp); } else { String responseString = getHeaderFieldValue(clientResp, "Location"); _log.info("3PARDriver:createVolume 3PAR response is Location: {}", responseString); } } catch (Exception e) { throw e; } finally { if (clientResp != null) { clientResp.close(); } _log.info("3PARDriver:createVolume leave"); } //end try/catch/finally } public void createVirtualCopy(String baseVolumeName, String snapName, Boolean readOnly) throws Exception { _log.info("3PARDriver:createVolumeVirtualCopy enter"); ClientResponse clientResp = null; // for snapshot creation String payload = "{\"action\":\"createSnapshot\", \"parameters\": { \"name\": \"" + snapName +"\" , \"readOnly\": " + readOnly +"} }"; final String path = MessageFormat.format(URI_CREATE_VOLUME_SNAPSHOT, baseVolumeName); _log.info(" 3PARDriver:createVolumeVirtualCopy uri = {} payload {} ",path,payload); try { clientResp = post(path, payload); if (clientResp == null) { _log.error("3PARDriver:There is no response from 3PAR"); throw new HP3PARException("There is no response from 3PAR"); } else if (clientResp.getStatus() != 201) { String errResp = getResponseDetails(clientResp); throw new HP3PARException(errResp); } else { _log.info("3PARDriver:createVolumeSnapshot success"); // below needed for multiple volume snashot creation /* String responseString = clientResp.getEntity(String.class); _log.info("3PARDriver:getVolumeDetails 3PAR response is {}", responseString); VolumeDetailsCommandResult volResult = new Gson().fromJson(sanitize(responseString), VolumeDetailsCommandResult.class); return volResult; */ } } catch (Exception e) { throw e; } finally { if (clientResp != null) { clientResp.close(); } _log.info("3PARDriver:createVolumeVirtualCopy leave"); } //end try/catch/finally } /** * * Vipr UI doesn't provide offline (Attached volume) / online options while creating clone, so * below logic will be followed: * First, to check destination volume exists we execute create clone creation as offline volume. * if error, destination clone volume doesn't exist, so create a new volume with clone name by * using its base volume parameters like TPVV,CPG. Then create a offline * volume clone using newly created volume clone * Note: A offline clone creates a attached clone, which actually creates * a intermediate snapshot which can be utilized for restore/update. * We are not using online option, as it creates detached volume with no way to restore/update. * */ public void createPhysicalCopy(String baseVolumeName, String cloneName, String cloneCPG) throws Exception { _log.info("3PARDriver: createPhysicalCopy enter"); String baseVolumeSnapCPG = cloneCPG; String baseVolumeUserCPG = cloneCPG; ClientResponse clientResp = null; String payload = null; String secondPayload = null; // clone creation, check if destination volume exists as expected by this API payload = "{\"action\":\"createPhysicalCopy\", \"parameters\": { \"destVolume\": \"" + cloneName + "\" , \"saveSnapshot\": " + true + "} }"; final String path = MessageFormat.format(URI_CREATE_VOLUME_CLONE, baseVolumeName); _log.info(" 3PARDriver: createPhysicalCopy uri = {} payload {} secondPayload {}", path, payload, secondPayload); try { // create clone considering destination volume already created clientResp = post(path, payload); if (clientResp == null || clientResp.getStatus() != 201) { if (clientResp != null) { String errResp = getResponseDetails(clientResp); _log.info(" 3PARDriver: createPhysicalCopy destination clone volume absent, hence creating new volume for clone. Error Info : {}",errResp); } VolumeDetailsCommandResult volResult = null; try { volResult = getVolumeDetails(baseVolumeName); } catch (Exception e) { _log.info("3PARDriver: createVolumeClone the specified volume {} for clone creation not found, its parent {}; continue with clone creation: {}.\n", baseVolumeName, cloneName, e.getMessage()); } if (volResult != null) { // UserCPG will be absent for clones, hence using snapCPG here. We might need to re-look CPG selection later baseVolumeUserCPG = volResult.getUserCPG(); baseVolumeSnapCPG = volResult.getSnapCPG(); Boolean tpvv = true; Boolean tdvv = false; if (volResult.getProvisioningType() == 6) { tdvv = true; tpvv = false; } _log.info("3PARDriver: createVolumeClone base volume exists, id {}, baseVolumeSnapCPG {} , baseVolumeUserCPG {} , copyOf {}, copyType {} , name {}, volume type {} - ", baseVolumeName, baseVolumeSnapCPG, baseVolumeUserCPG,volResult.getCopyOf(), volResult.getCopyType(), volResult.getName(), volResult.getProvisioningType()); createVolume(cloneName, baseVolumeSnapCPG, tpvv, tdvv, volResult.getSizeMiB()); // sleep for some milliseconds required ? try { volResult = getVolumeDetails(baseVolumeName); } catch (Exception e) { _log.info("3PARDriver: createVolumeClone the specified clone volume {} not created successfully yet. error {}", cloneName, e.getMessage()); } if (volResult != null) { clientResp = post(path, payload); } else { _log.info("3PARDriver: createVolumeClone unable to find the newly created volume, volResult is null"); } } else { _log.info("3PARDriver: createVolumeClone base volume not found, volResult is null"); } } if (clientResp == null) { _log.error("3PARDriver:There is no response from 3PAR"); throw new HP3PARException("There is no response from 3PAR"); } else if (clientResp.getStatus() != 201) { String errResp = getResponseDetails(clientResp); _log.info("3PARDriver: createPhysicalCopy error resopnse : {} ", errResp); throw new HP3PARException(errResp); } else { _log.info("3PARDriver: createPhysicalCopy success"); } } catch (Exception e) { throw e; } finally { if (clientResp != null) { clientResp.close(); } _log.info("3PARDriver: createPhysicalCopy leave"); } // end try/catch/finally } public VolumeDetailsCommandResult getVolumeDetails(String name) throws Exception { _log.info("3PARDriver:getVolumeDetails enter"); ClientResponse clientResp = null; final String path = MessageFormat.format(URI_VOLUME_DETAILS, name); _log.info("3PARDriver: getVolumeDetails path is {}", path); try { clientResp = get(path); if (clientResp == null) { _log.error("3PARDriver:There is no response from 3PAR"); throw new HP3PARException("There is no response from 3PAR"); } else if (clientResp.getStatus() != 200) { String errResp = getResponseDetails(clientResp); throw new HP3PARException(errResp); } else { String responseString = clientResp.getEntity(String.class); _log.info("3PARDriver:getVolumeDetails 3PAR response is {}", responseString); VolumeDetailsCommandResult volResult = new Gson().fromJson(sanitize(responseString), VolumeDetailsCommandResult.class); return volResult; } } catch (Exception e) { throw e; } finally { if (clientResp != null) { clientResp.close(); } _log.info("3PARDriver:getVolumeDetails leave"); } //end try/catch/finally } public VolumesCommandResult getStorageVolumes() throws Exception { _log.info("3PARDriver:getVolumeDetails enter"); ClientResponse clientResp = null; final String path = URI_STORAGE_VOLUMES; try { clientResp = get(path); if (clientResp == null) { _log.error("3PARDriver:There is no response from 3PAR"); throw new HP3PARException("There is no response from 3PAR"); } else if (clientResp.getStatus() != 200) { String errResp = getResponseDetails(clientResp); throw new HP3PARException(errResp); } else { String responseString = clientResp.getEntity(String.class); _log.info("3PARDriver:getVolumeDetails 3PAR response is {}", responseString); VolumesCommandResult storageVolsResult = new Gson().fromJson(sanitize(responseString), VolumesCommandResult.class); return storageVolsResult; } } catch (Exception e) { throw e; } finally { if (clientResp != null) { clientResp.close(); } _log.info("3PARDriver:getVolumeDetails leave"); } //end try/catch/finally } public void expandVolume(String name, Long additionalSize) throws Exception { _log.info("3PARDriver:expandVolume enter"); ClientResponse clientResp = null; final String path = MessageFormat.format(URI_EXPAND_VOLUME, name); String body = "{\"action\":3, \"sizeMiB\":" + additionalSize.toString() + "}"; _log.info("3PARDriver: expandVolume path is {}, body is {}", path, body); try { clientResp = put(path, body); if (clientResp == null) { _log.error("3PARDriver:There is no response from 3PAR"); throw new HP3PARException("There is no response from 3PAR"); } else if (clientResp.getStatus() != 200) { String errResp = getResponseDetails(clientResp); throw new HP3PARException(errResp); } else { String responseString = getHeaderFieldValue(clientResp, "Location"); _log.info("3PARDriver:createVolume 3PAR response is Location: {}", responseString); } } catch (Exception e) { throw e; } finally { if (clientResp != null) { clientResp.close(); } _log.info("3PARDriver:expandVolume leave"); } //end try/catch/finally } public void deleteVolume(String name) throws Exception { _log.info("3PARDriver:deleteVolume enter"); ClientResponse clientResp = null; final String path = MessageFormat.format(URI_DELETE_VOLUME, name); _log.info("3PARDriver: deleteVolume path is {}", path); try { clientResp = delete(path); if (clientResp == null) { _log.error("3PARDriver:There is no response from 3PAR"); throw new HP3PARException("There is no response from 3PAR"); } else if (clientResp.getStatus() != 200) { String errResp = getResponseDetails(clientResp); throw new HP3PARException(errResp); } else { _log.info("3PARDriver:deleteVolume success ", name); } } catch (Exception e) { throw e; } finally { if (clientResp != null) { clientResp.close(); } _log.info("3PARDriver:deleteVolume leave"); } //end try/catch/finally } public void deleteVirtualCopy(String name) throws Exception { _log.info("3PARDriver: deleteVolumeVirtualCOpy enter"); ClientResponse clientResp = null; final String path = MessageFormat.format(URI_DELETE_VOLUME_SNAPSHOT, name); try { clientResp = delete(path); if (clientResp == null) { _log.error("3PARDriver:There is no response from 3PAR"); throw new HP3PARException("There is no response from 3PAR"); } else if (clientResp.getStatus() != 200) { String errResp = getResponseDetails(clientResp); throw new HP3PARException(errResp); } else { _log.info("3PARDriver: deleteVolumeVirtualCOpy success"); } } catch (Exception e) { throw e; } finally { if (clientResp != null) { clientResp.close(); } _log.info("3PARDriver: deleteVolumeVirtualCOpy leave"); } //end try/catch/finally } public void deletePhysicalCopy(String name) throws Exception { _log.info("3PARDriver: deletePhysicalCopy enter"); ClientResponse clientResp = null; final String path = MessageFormat.format(URI_DELETE_VOLUME_CLONE, name); try { clientResp = delete(path); if (clientResp == null) { _log.error("3PARDriver:There is no response from 3PAR"); throw new HP3PARException("There is no response from 3PAR"); } else if (clientResp.getStatus() != 200) { String errResp = getResponseDetails(clientResp); throw new HP3PARException(errResp); } else { _log.info("3PARDriver: deletePhysicalCopy success"); } } catch (Exception e) { throw e; } finally { if (clientResp != null) { clientResp.close(); } _log.info("3PARDriver: deletePhysicalCopy leave"); } //end try/catch/finally } public void restoreVirtualCopy(String name) throws Exception { _log.info("3PARDriver: restoreVirtualCopy enter"); ClientResponse clientResp = null; final String path = MessageFormat.format(URI_RESTORE_VOLUME_SNAPSHOT, name); Boolean online = true; Boolean offline = false; // for snapshot restoration {"action":4, "online":true} String onlinePayload = "{\"action\":4, \"online\": " + online +" }"; // Offline restore is faster String offlinePayload = "{\"action\":4, \"online\": " + offline +" }"; try { // trying offline restore clientResp = put(path,offlinePayload); // if related volume is exported, we will get error here. Trying online restore if (clientResp.getStatus() != 200) { String errResp = getResponseDetails(clientResp); _log.error("3PARDriver: restoreVirtualCopy trying online restore option, offline restore failed with error " + errResp); clientResp = put(path,onlinePayload); } if (clientResp == null) { _log.error("3PARDriver: restoreVirtualCopy There is no response from 3PAR"); throw new HP3PARException("There is no response from 3PAR"); } else if (clientResp.getStatus() != 200) { String errResp = getResponseDetails(clientResp); throw new HP3PARException(errResp); } else { _log.info("3PARDriver: restoreVirtualCopy success"); } } catch (Exception e) { throw e; } finally { if (clientResp != null) { clientResp.close(); } _log.info("3PARDriver: restoreVirtualCopy leave"); } //end try/catch/finally } public void restorePhysicalCopy(String name) throws Exception { _log.info("3PARDriver: restorePhysicalCopy enter"); ClientResponse clientResp = null; Boolean offline = false; String intermediateSnapshot = name; // Offline restore is performed on intermediate snapshot String offlinePayload = "{\"action\":4, \"online\": " + offline +" }"; try { VolumeDetailsCommandResult volResult = null; volResult = getVolumeDetails(name); if (volResult != null && volResult.getProvisioningType() != 3) { // get intermediate snapshot of clone/physical copy intermediateSnapshot = volResult.getCopyOf(); } final String path = MessageFormat.format(URI_RESTORE_VOLUME_CLONE, intermediateSnapshot); // trying offline restore clientResp = put(path,offlinePayload); if (clientResp == null) { _log.error("3PARDriver: restorePhysicalCopy There is no response from 3PAR"); throw new HP3PARException("There is no response from 3PAR"); } else if (clientResp.getStatus() != 200) { String errResp = getResponseDetails(clientResp); _log.info("3PARDriver: restorePhysicalCopy error {} ", errResp); throw new HP3PARException(errResp); } else { _log.info("3PARDriver: restorePhysicalCopy success"); } } catch (Exception e) { _log.info("3PARDriver: restorePhysicalCopy exception info {} ",e.getMessage()); throw e; } finally { if (clientResp != null) { clientResp.close(); } _log.info("3PARDriver: restorePhysicalCopy leave"); } //end try/catch/finally } public VlunResult createVlun(String volumeName, int hlu, String hostName, String portId) throws Exception { _log.info("3PARDriver:createVlun enter"); ClientResponse clientResp = null; Integer lun = (hlu == -1) ? 0 : hlu; boolean autoLun = (lun == 0) ? true : false; String body = "{\"volumeName\":\"" + volumeName + "\", \"lun\":" + lun.toString() + ", " + "\"hostname\":\"" + hostName + "\"" + ", \"autoLun\":" + autoLun + ", \"maxAutoLun\": 0"; // port is specified for matched set; not for host set // only matched-set export is supported if (portId != null) { String[] pos = portId.split(":"); String portPos = String.format(", \"portPos\":{\"node\":%s, \"slot\":%s, \"cardPort\":%s}", pos[0], pos[1], pos[2]); body = body.concat(portPos); } body = body.concat("}"); _log.info("3PARDriver: createVlun body is {}", body); try { clientResp = post(URI_CREATE_VLUN, body); if (clientResp == null) { _log.error("3PARDriver:There is no response from 3PAR"); throw new HP3PARException("There is no response from 3PAR"); } else if (clientResp.getStatus() != 201) { String errResp = getResponseDetails(clientResp); throw new HP3PARException(errResp); } else { String responseString = getHeaderFieldValue(clientResp, "Location"); _log.info("3PARDriver:createVlun 3PAR response is Location: {}", responseString); String[] resp = responseString.split(","); VlunResult result = new VlunResult(); result.setAssignedLun(resp[1]); result.setStatus(true); return result; } } catch (Exception e) { _log.error(e.getMessage()); return null; } finally { if (clientResp != null) { clientResp.close(); } _log.info("3PARDriver:createVlun leave"); } //end try/catch/finally } public HostSetDetailsCommandResult getHostSetDetails(String name) throws Exception { _log.info("3PARDriver:getHostSetDetails enter"); ClientResponse clientResp = null; final String path = MessageFormat.format(URI_HOSTSET_DETAILS, name); _log.info("3PARDriver: getHostSetDetails path is {}", path); try { clientResp = get(path); if (clientResp == null) { _log.error("3PARDriver:There is no response from 3PAR"); throw new HP3PARException("There is no response from 3PAR"); } else if (clientResp.getStatus() != 200) { String errResp = getResponseDetails(clientResp); if (clientResp.getStatus() == 404 && errResp.contains("code: 102")) { return null; //Host set does not exists } else { throw new HP3PARException(errResp); } } else { String responseString = clientResp.getEntity(String.class); _log.info("3PARDriver:getHostSetDetails 3PAR response is {}", responseString); HostSetDetailsCommandResult hostsetResult = new Gson().fromJson(sanitize(responseString), HostSetDetailsCommandResult.class); return hostsetResult; } } catch (Exception e) { throw e; } finally { if (clientResp != null) { clientResp.close(); } _log.info("3PARDriver:getHostSetDetails leave"); } //end try/catch/finally } // Request is for creating the cluster with only one host public void createHostSet(String clustName, String hostName) throws Exception { _log.info("3PARDriver:createHostSet enter"); ClientResponse clientResp = null; String body = "{\"name\": \"" + clustName + "\", \"setmembers\": [\"" + hostName + "\"]}"; _log.info("3PARDriver: createHostSet body is {}", body); try { clientResp = post(URI_HOSTSETS, body); if (clientResp == null) { _log.error("3PARDriver:There is no response from 3PAR"); throw new HP3PARException("There is no response from 3PAR"); } else if (clientResp.getStatus() != 201) { String errResp = getResponseDetails(clientResp); throw new HP3PARException(errResp); } else { String responseString = getHeaderFieldValue(clientResp, "Location"); _log.info("3PARDriver:createHostSet 3PAR response is {}", responseString); } } catch (Exception e) { throw e; } finally { if (clientResp != null) { clientResp.close(); } _log.info("3PARDriver:createHostSet leave"); } //end try/catch/finally } public HostSetDetailsCommandResult updateHostSet(String clustName, String hostName) throws Exception { _log.info("3PARDriver:updateHostSet enter"); ClientResponse clientResp = null; String body = "{\"action\": 1, \"setmembers\": [\"" + hostName + "\"]}"; final String path = MessageFormat.format(URI_HOSTSET_DETAILS, clustName); _log.info("3PARDriver: updateHostSet path is {}, body is {}", path, body); try { clientResp = put(path, body); if (clientResp == null) { _log.error("3PARDriver:There is no response from 3PAR"); throw new HP3PARException("There is no response from 3PAR"); } else if (clientResp.getStatus() != 200) { String errResp = getResponseDetails(clientResp); throw new HP3PARException(errResp); } else { String responseString = clientResp.getEntity(String.class); _log.info("3PARDriver:updateHostSet 3PAR response is {}", responseString); HostSetDetailsCommandResult hostsetResult = new Gson().fromJson(sanitize(responseString), HostSetDetailsCommandResult.class); return hostsetResult; } } catch (Exception e) { throw e; } finally { if (clientResp != null) { clientResp.close(); } _log.info("3PARDriver:updateHostSet leave"); } //end try/catch/finally } public HostMember getHostDetails(String name) throws Exception { _log.info("3PARDriver:getHostDetails enter"); ClientResponse clientResp = null; final String path = MessageFormat.format(URI_HOST_DETAILS, name); _log.info("3PARDriver: getHostDetails path is {}", path); try { clientResp = get(path); if (clientResp == null) { _log.error("3PARDriver:There is no response from 3PAR"); throw new HP3PARException("There is no response from 3PAR"); } else if (clientResp.getStatus() != 200) { String errResp = getResponseDetails(clientResp); throw new HP3PARException(errResp); } else { String responseString = clientResp.getEntity(String.class); _log.info("3PARDriver:getHostDetails 3PAR response is {}", responseString); HostMember hostResult = new Gson().fromJson(sanitize(responseString), HostMember.class); return hostResult; } } catch (Exception e) { throw e; } finally { if (clientResp != null) { clientResp.close(); } _log.info("3PARDriver:getHostDetails leave"); } //end try/catch/finally } public VirtualLunsList getAllVlunDetails() throws Exception { _log.info("3PARDriver:getAllVlunDetails enter"); ClientResponse clientResp = null; try { clientResp = get(URI_VLUNS); if (clientResp == null) { _log.error("3PARDriver:There is no response from 3PAR"); throw new HP3PARException("There is no response from 3PAR"); } else if (clientResp.getStatus() != 200) { String errResp = getResponseDetails(clientResp); throw new HP3PARException(errResp); } else { String responseString = clientResp.getEntity(String.class); _log.info("3PARDriver:getAllVlunDetails 3PAR response is {}", responseString); VirtualLunsList vlunResult = new Gson().fromJson(sanitize(responseString), VirtualLunsList.class); return vlunResult; } } catch (Exception e) { throw e; } finally { if (clientResp != null) { clientResp.close(); } _log.info("3PARDriver:getAllVlunDetails leave"); } //end try/catch/finally } public void deleteVlun(String volName, String lun, String hostName, String pos) throws Exception { _log.info("3PARDriver:deleteVlun enter"); ClientResponse clientResp = null; String path = MessageFormat.format(URI_DELETE_VLUN, volName, lun, hostName); if (pos != null) { path = path.concat(","+pos); } _log.info("3PARDriver: deleteVlun path is {}", path); try { clientResp = delete(path); if (clientResp == null) { _log.error("3PARDriver:There is no response from 3PAR"); throw new HP3PARException("There is no response from 3PAR"); } else if (clientResp.getStatus() != 200) { String errResp = getResponseDetails(clientResp); throw new HP3PARException(errResp); } else { _log.info("3PARDriver:deleteVlun success " + volName); } } catch (Exception e) { throw e; } finally { if (clientResp != null) { clientResp.close(); } _log.info("3PARDriver:deleteVlun leave"); } //end try/catch/finally } public void createHost(String name, ArrayList<String> portIds, Integer persona) throws Exception { _log.info("3PARDriver:createHost enter"); ClientResponse clientResp = null; String portIdstr = "["; String body = null; for (String Id:portIds) { if (portIdstr.length() > 1 ) { portIdstr = portIdstr.concat(","); } portIdstr = portIdstr.concat("\"" + Id + "\""); } portIdstr = portIdstr.concat("]"); if (portIds.get(0).startsWith("iqn") == false) { body = "{\"name\":\"" + name + "\", \"FCWWNs\":" + portIdstr + ", \"persona\":" + persona.toString() + "}"; } else { body = "{\"name\":\"" + name + "\", \"iSCSINames\":" + portIdstr + ", \"persona\":" + persona.toString() + "}"; } _log.info("3PARDriver: createHost body is {}", body); try { clientResp = post(URI_HOSTS, body); if (clientResp == null) { _log.error("3PARDriver:There is no response from 3PAR"); throw new HP3PARException("There is no response from 3PAR"); } else if (clientResp.getStatus() != 201) { String errResp = getResponseDetails(clientResp); if (clientResp.getStatus() == 409 && errResp.contains("code: 16") == true) { // host exists; modify host by adding new initiators updateHost(name, portIds); } else { throw new HP3PARException(errResp); } } else { String responseString = getHeaderFieldValue(clientResp, "Location"); _log.info("3PARDriver:createHost 3PAR response is Location: {}", responseString); } } catch (Exception e) { throw e; } finally { if (clientResp != null) { clientResp.close(); } _log.info("3PARDriver:createHost leave"); } //end try/catch/finally } public void updateHost(String name, ArrayList<String> portIdsNew) throws Exception { _log.info("3PARDriver:updateHost enter"); ClientResponse clientResp = null; String portIdstr = "["; String body = null; final String path = MessageFormat.format(URI_HOST_DETAILS, name); ArrayList<String> portIdsNewFiltered = new ArrayList<>(); ArrayList<String> existingFc = new ArrayList<>(); try { // get existing host wwn/iqn; HostMember hostMemb = getHostDetails(name); if (portIdsNew.get(0).startsWith("iqn") == false) { for (FcPath fcPath:hostMemb.getFCPaths()) { existingFc.add(SanUtils.cleanWWN(fcPath.getWwn())); } // remove existing wwn/iqns in the list to be added for (String portId:portIdsNew) { if (existingFc.contains(SanUtils.cleanWWN(portId)) == false) { portIdsNewFiltered.add(SanUtils.cleanWWN(portId)); } } } else { for (ISCSIPath scPath:hostMemb.getiSCSIPaths()) { existingFc.add(scPath.getName()); } // remove existing wwn/iqns in the list to be added for (String portId:portIdsNew) { if (existingFc.contains(portId) == false) { portIdsNewFiltered.add(portId); } } } for (String Id:portIdsNewFiltered) { if (portIdstr.length() > 1 ) { portIdstr = portIdstr.concat(","); } portIdstr = portIdstr.concat("\"" + Id + "\""); } portIdstr = portIdstr.concat("]"); if (portIdsNewFiltered.get(0).startsWith("iqn") == false) { body = "{\"FCWWNs\":" + portIdstr + ", \"pathOperation\":1}"; } else { body = "{\"iSCSINames\":" + portIdstr + ", \"pathOperation\":1\"}"; } _log.info("3PARDriver: updateHost path is {}, body is {}", path, body); clientResp = put(path, body); if (clientResp == null) { _log.error("3PARDriver:There is no response from 3PAR"); throw new HP3PARException("There is no response from 3PAR"); } else if (clientResp.getStatus() != 200) { String errResp = getResponseDetails(clientResp); throw new HP3PARException(errResp); } else { String responseString = getHeaderFieldValue(clientResp, "Location"); _log.info("3PARDriver:updateHost 3PAR response is Location: {}", responseString); } } catch (Exception e) { throw e; } finally { if (clientResp != null) { clientResp.close(); } _log.info("3PARDriver:updateHost leave"); } //end try/catch/finally } private CompleteError getCompleteResponseDetails(ClientResponse clientResp) { String detailedResponse = null, ref=null; CompleteError compError = null; try { JSONObject jObj = clientResp.getEntity(JSONObject.class); String errorCode = jObj.getString("code"); detailedResponse = String.format("3PAR error code: %s, Description: %s", errorCode, jObj.getString("desc")); if (jObj.has("ref")) { ref = String.format(", refer:%s", jObj.getString("ref")); detailedResponse = detailedResponse + ref; } int httpCode = clientResp.getStatus(); _log.error(String.format("3PARDriver:HTTP error code: %d, Complete 3PAR error response: %s", httpCode, jObj.toString())); compError = new CompleteError(); compError.setHp3parCode(errorCode); compError.setHttpCode(httpCode); compError.setErrorResp(detailedResponse); } catch (Exception e) { _log.error("3PARDriver:Unable to get 3PAR error details"); detailedResponse = String.format("%1$s", (clientResp == null) ? "" : clientResp); } return compError; } private String getResponseDetails(ClientResponse clientResp) { String detailedResponse = null, ref=null;; try { JSONObject jObj = clientResp.getEntity(JSONObject.class); detailedResponse = String.format("3PAR error code: %s, Description: %s", jObj.getString("code"), jObj.getString("desc")); if (jObj.has("ref")) { ref = String.format(", refer:%s", jObj.getString("ref")); detailedResponse = detailedResponse + ref; } _log.error(String.format("3PARDriver:HTTP error code: %d, Complete 3PAR error response: %s", clientResp.getStatus(), jObj.toString())); } catch (Exception e) { _log.error("3PARDriver:Unable to get 3PAR error details"); detailedResponse = String.format("%1$s", (clientResp == null) ? "" : clientResp); } return detailedResponse; } private String getHeaderFieldValue(ClientResponse clientResp, String field) { List<String> valueList = null; String value = null; try { MultivaluedMap<String, String> headers = clientResp.getHeaders(); valueList = headers.get(field); value = valueList.get(0); } catch (Exception e) { _log.error("3PARDriver:Unable to get value for field in header: %s", field); } return value; } private String getResponseFieldValue(ClientResponse clientResp, String field) { String value = null; field = "privileges"; try { JSONObject jObj = clientResp.getEntity(JSONObject.class); _log.info("3PARDriver:getResponseFieldValue 3PAR response is : {}", jObj.toString()); value = jObj.getString(field); } catch (Exception e) { _log.error("3PARDriver:Unable to get field value in response: %s", field); } return value; } private ClientResponse get(final String uri) throws Exception { ClientResponse clientResp = _client.get_json(_baseUrl.resolve(uri), _authToken); if (clientResp.getStatus() == 403) { getAuthToken(); clientResp = _client.get_json(_baseUrl.resolve(uri), _authToken); } return clientResp; } private ClientResponse post(final String uri, String body) throws Exception { ClientResponse clientResp = _client.post_json(_baseUrl.resolve(uri), _authToken, body); if (clientResp.getStatus() == 403) { getAuthToken(); clientResp = _client.post_json(_baseUrl.resolve(uri), _authToken, body); } return clientResp; } private ClientResponse put(final String uri, String body) throws Exception { ClientResponse clientResp = _client.put_json(_baseUrl.resolve(uri), _authToken, body); if (clientResp.getStatus() == 403) { getAuthToken(); clientResp = _client.put_json(_baseUrl.resolve(uri), _authToken, body); } return clientResp; } private ClientResponse delete(final String uri) throws Exception { ClientResponse clientResp = _client.delete_json(_baseUrl.resolve(uri), _authToken); if (clientResp.getStatus() == 403) { getAuthToken(); clientResp = _client.delete_json(_baseUrl.resolve(uri), _authToken); } return clientResp; } /** * VV Set is a HP3PAR term for Consistency Group. * This deals with creation of CG, volumes will be added to the CG in create volume * * @param displayName * @throws Exception */ public void createVVset(String displayName) throws Exception { _log.info("createVVset enter"); ClientResponse clientResp = null; // for VV set creation String payload = "{\"name\": \"" + displayName +"\" }"; //final String path = MessageFormat.format(URI_CREATE_CG); _log.info(" 3PARDriver: createVVset uri = {} payload {} ",URI_CREATE_CG.toString(),payload); try { clientResp = post(URI_CREATE_CG, payload); if (clientResp == null) { _log.error("3PARDriver: createVVset There is no response from 3PAR"); throw new HP3PARException("There is no response from 3PAR"); } else if (clientResp.getStatus() != 201) { String errResp = getResponseDetails(clientResp); _log.error("3PARDriver: createVVset There is error response from 3PAR = {}" , errResp); throw new HP3PARException(errResp); } else { _log.info("3PARDriver: createVVset success"); } } catch (Exception e) { throw e; } finally { if (clientResp != null) { clientResp.close(); } _log.info("3PARDriver: createVVset leave"); } //end try/catch/finally } /** * Get Consistency Group details * * @param displayName * @return * @throws Exception */ public ConsistencyGroupResult getVVsetDetails(String displayName) throws Exception { _log.info("3PARDriver: getVVsetDetails enter"); ClientResponse clientResp = null; final String path = MessageFormat.format(URI_CG_DETAILS, displayName); try { clientResp = get(path); if (clientResp == null) { _log.error("3PARDriver: getVVsetDetails There is no response from 3PAR"); throw new HP3PARException("There is no response from 3PAR"); } else if (clientResp.getStatus() != 200) { String errResp = getResponseDetails(clientResp); _log.error("3PARDriver: getVVsetDetails There is error response from 3PAR = {}" , errResp); throw new HP3PARException(errResp); } else { String responseString = clientResp.getEntity(String.class); _log.info("3PARDriver: getVVsetDetails 3PAR response is {}", responseString); ConsistencyGroupResult cgResult = new Gson().fromJson(sanitize(responseString), ConsistencyGroupResult.class); return cgResult; } } catch (Exception e) { throw e; } finally { if (clientResp != null) { clientResp.close(); } _log.info("3PARDriver: getVVsetDetails leave"); } //end try/catch/finally } /** * delete a VV Set or Consistency Group * * @param nativeId * @throws Exception */ public void deleteVVset(String nativeId) throws Exception { _log.info("3PARDriver: deleteVVset enter"); ClientResponse clientResp = null; final String path = MessageFormat.format(URI_DELETE_CG, nativeId); _log.info("3PARDriver:deleteVVset running delete VV Set " + path); try { clientResp = delete(path); if (clientResp == null) { _log.error("3PARDriver:deleteVVset There is no response from 3PAR"); throw new HP3PARException("There is no response from 3PAR"); } else if (clientResp.getStatus() != 200) { String errResp = getResponseDetails(clientResp); throw new HP3PARException(errResp); } else { _log.info("3PARDriver: deleteVVset success"); } } catch (Exception e) { throw e; } finally { if (clientResp != null) { clientResp.close(); } _log.info("3PARDriver:deleteVVset leave"); } //end try/catch/finally } /** * Add or remove volume from an existing consistency group or VV Set * * @param volumeCGName * @param volName * @param actionValue * @throws Exception */ public void updateVVset(String volumeCGName, String volName, int actionValue) throws Exception { _log.info("3PARDriver: updateVVset enter"); ClientResponse clientResp = null; final String path = MessageFormat.format(URI_UPDATE_CG, volumeCGName); // for VV set addition {"action":1,"setmembers":["vol-name","vol-name2"]} String payload = "{\"action\": " + actionValue +", \"setmembers\": [ \"" + volName + "\" ] }"; _log.info("3PARDriver:updateVVset running update VV Set with URI {} and payload {} ", path, payload); try { clientResp = put(path,payload); if (clientResp == null) { _log.error("3PARDriver:updateVVset There is no response from 3PAR"); throw new HP3PARException("There is no response from 3PAR"); } else if (clientResp.getStatus() != 200) { String errResp = getResponseDetails(clientResp); throw new HP3PARException(errResp); } else { _log.info("3PARDriver: updateVVset success"); } } catch (Exception e) { throw e; } finally { if (clientResp != null) { clientResp.close(); } _log.info("3PARDriver:updateVVset leave"); } //end try/catch/finally } /** * Get Consistency Groups List * * @param displayName * @return * @throws Exception */ public ConsistencyGroupsListResult getVVsetsList() throws Exception { _log.info("3PARDriver: getVVsetsList enter"); ClientResponse clientResp = null; final String path = URI_CG_LIST_DETAILS; try { clientResp = get(path); if (clientResp == null) { _log.error("3PARDriver: getVVsetsList There is no response from 3PAR"); throw new HP3PARException("There is no response from 3PAR"); } else if (clientResp.getStatus() != 200) { String errResp = getResponseDetails(clientResp); _log.error("3PARDriver: getVVsetsList There is error response from 3PAR = {}" , errResp); throw new HP3PARException(errResp); } else { String responseString = clientResp.getEntity(String.class); _log.info("3PARDriver: getVVsetsList 3PAR response is {}", responseString); ConsistencyGroupsListResult cgListResult = new Gson().fromJson(sanitize(responseString), ConsistencyGroupsListResult.class); return cgListResult; } } catch (Exception e) { throw e; } finally { if (clientResp != null) { clientResp.close(); } _log.info("3PARDriver: getVVsetsList leave"); } //end try/catch/finally } /** * Get all vluns, which are associated with a volume, snapshot or a clone. * * @param displayName * @return * @throws Exception */ public VirtualLunsList getVLunsOfVolume(String volumeWWN) throws Exception { _log.info("3PARDriver: getVLunsOfVolume enter"); ClientResponse clientResp = null; final String path = MessageFormat.format(URI_VLUNS_OF_VOLUME, volumeWWN); _log.info("getVLunsOfVolume path is {}", path); try { clientResp = get(path); if (clientResp == null) { _log.error("3PARDriver: getVLunsOfVolume There is no response from 3PAR"); throw new HP3PARException("There is no response from 3PAR"); } else if (clientResp.getStatus() != 200) { String errResp = getResponseDetails(clientResp); _log.error("3PARDriver: getVLunsOfVolume There is error response from 3PAR = {}" , errResp); throw new HP3PARException(errResp); } else { String responseString = clientResp.getEntity(String.class); _log.info("3PARDriver: getVLunsOfVolume 3PAR response is {}", responseString); VirtualLunsList vlunsListResult = new Gson().fromJson(sanitize(responseString), VirtualLunsList.class); return vlunsListResult; } } catch (Exception e) { _log.info("getVLunsOfVolume exception is {}", e.getMessage()); throw e; } finally { if (clientResp != null) { clientResp.close(); } _log.info("3PARDriver: getVLunsOfVolume leave"); } //end try/catch/finally } /** * Get all vluns, which are associated with a volume, snapshot or a clone. * * @param displayName * @return * @throws Exception */ public VirtualLunsList getVLunsByVolumeName(String volumeName) throws Exception { _log.info("3PARDriver: getVLunsOfVolume enter"); ClientResponse clientResp = null; final String path = MessageFormat.format(URI_VLUNS_BY_VOlUME_NAME, volumeName); _log.info("getVLunsOfVolume path is {}", path); try { clientResp = get(path); if (clientResp == null) { _log.error("3PARDriver: getVLunsOfVolume There is no response from 3PAR"); throw new HP3PARException("There is no response from 3PAR"); } else if (clientResp.getStatus() != 200) { String errResp = getResponseDetails(clientResp); _log.error("3PARDriver: getVLunsOfVolume There is error response from 3PAR = {}" , errResp); throw new HP3PARException(errResp); } else { String responseString = clientResp.getEntity(String.class); _log.info("3PARDriver: getVLunsOfVolume 3PAR response is {}", responseString); VirtualLunsList vlunsListResult = new Gson().fromJson(sanitize(responseString), VirtualLunsList.class); return vlunsListResult; } } catch (Exception e) { _log.info("getVLunsOfVolume exception is {}", e.getMessage()); throw e; } finally { if (clientResp != null) { clientResp.close(); } _log.info("3PARDriver: getVLunsOfVolume leave"); } //end try/catch/finally } public void createVVsetVirtualCopy(String nativeId, String snapshotName, Boolean readOnly) throws Exception { _log.info("3PARDriver:createVVsetVirtualCopy enter"); _log.info(" 3PARDriver:createVVsetVirtualCopy CG name {} , CG snapshot name given {} ", nativeId,snapshotName); ClientResponse clientResp = null; String cgSnapshotString = snapshotName + "@count@"; // for snapshot creation String payload = "{\"action\":\"createSnapshot\", \"parameters\": { \"name\": \"" + cgSnapshotString + "\" , \"readOnly\": " + readOnly +"} }"; final String path = MessageFormat.format(URI_SNAPSHOT_CG, nativeId); _log.info(" 3PARDriver:createVVsetVirtualCopy uri = {} payload {} ",path,payload); try { clientResp = post(path, payload); if (clientResp == null) { _log.error("3PARDriver:There is no response from 3PAR"); throw new HP3PARException("There is no response from 3PAR"); } else if (clientResp.getStatus() != 201) { String errResp = getResponseDetails(clientResp); throw new HP3PARException(errResp); } else { _log.info("3PARDriver:createVVsetVirtualCopy success"); } } catch (Exception e) { throw e; } finally { if (clientResp != null) { clientResp.close(); } _log.info("3PARDriver:createVVsetVirtualCopy leave"); } //end try/catch/finally } public VVSetVolumeClone[] createVVsetPhysicalCopy(String nativeId, String vVsetNameForClone, List<VolumeClone> clones, Boolean saveSnapshot) throws Exception { _log.info("3PARDriver:createVVsetPhysicalCopy enter"); _log.info(" 3PARDriver:createVVsetPhysicalCopy CG name {} for cloning , corresponding CG clone name {} ", nativeId,vVsetNameForClone); ClientResponse clientResp = null; String vvSetClones = ""; // for snapshot creation String payload = "{\"action\":\"createPhysicalCopy\", \"parameters\": { \"destVolume\": \"" + vVsetNameForClone + "\" , \"saveSnapshot\": " + saveSnapshot +"} }"; final String path = MessageFormat.format(URI_CLONE_CG, nativeId); _log.info(" 3PARDriver: createVVsetPhysicalCopy uri = {} payload {} ",path,payload); try { // get Vipr generated clone name and create corresponding volumes for (VolumeClone clone : clones) { _log.info("3PARDriver: createVVsetPhysicalCopy generated clone native id {}, display name {} - start", clone.getParentId(), clone.getDisplayName()); String generatedCloneName = clone.getDisplayName(); String baseVolumeName = clone.getParentId(); // create new volume , CG clone will fail if already exists VolumeDetailsCommandResult volResult = null; volResult = getVolumeDetails(baseVolumeName); if (volResult != null) { // UserCPG will be absent for clones, hence using snapCPG here. We might need to re-look CPG selection later String baseVolumeUserCPG = volResult.getUserCPG(); String baseVolumeSnapCPG = volResult.getSnapCPG(); Boolean tpvv = true; Boolean tdvv = false; if (volResult.getProvisioningType() == 6) { tdvv = true; tpvv = false; } _log.info("3PARDriver: createVolumeClone base volume exists, id {}, baseVolumeSnapCPG {} , baseVolumeUserCPG {} , copyOf {}, copyType {} , name {}, volume type {} - ", baseVolumeName, baseVolumeSnapCPG, baseVolumeUserCPG,volResult.getCopyOf(), volResult.getCopyType(), volResult.getName(), volResult.getProvisioningType()); createVolume(generatedCloneName, baseVolumeSnapCPG, tpvv,tdvv, volResult.getSizeMiB()); vvSetClones = vvSetClones+"\""+generatedCloneName+"\","; } } if (vvSetClones != "") { vvSetClones = vvSetClones.substring(0,vvSetClones.lastIndexOf(",")); // for VV set addition {"action":1,"setmembers":["vol-name","vol-name2"]} String vvsetPayload = "{\"name\": \"" + vVsetNameForClone +"\", \"setmembers\": [ \"" + vvSetClones + "\" ] }"; //final String path = MessageFormat.format(URI_CREATE_CG); _log.info(" 3PARDriver: createVVsetPhysicalCopy uri = {} vvsetPayload {} ",URI_CREATE_CG.toString(),vvsetPayload); // Create and update Clone CG object and volumes try { clientResp = post(URI_CREATE_CG, vvsetPayload); if (clientResp == null) { _log.error("3PARDriver: createVVsetPhysicalCopy There is no response from 3PAR"); throw new HP3PARException("There is no response from 3PAR"); } else if (clientResp.getStatus() != 201) { String errResp = getResponseDetails(clientResp); _log.error("3PARDriver: createVVsetPhysicalCopy There is error response from 3PAR = {}" , errResp); throw new HP3PARException(errResp); } else { _log.info("3PARDriver: createVVsetPhysicalCopy vvset created"); } } catch (Exception e) { throw e; } finally { if (clientResp != null) { clientResp.close(); } _log.info("3PARDriver: createVVsetPhysicalCopy execute vvset"); } //end try/catch/finally // Executing CG clone try { clientResp = post(path, payload); if (clientResp == null) { _log.error("3PARDriver:There is no response from 3PAR"); throw new HP3PARException("There is no response from 3PAR"); } else if (clientResp.getStatus() != 201) { String errResp = getResponseDetails(clientResp); throw new HP3PARException(errResp); } else { String responseString = clientResp.getEntity(String.class); //String customerResponseString = "{\"Altered\":"+responseString+"}"; _log.info("3PARDriver:createVVsetVirtualCopy success , response ",responseString); VVSetVolumeClone[] output = new Gson().fromJson(sanitize(responseString), VVSetVolumeClone[].class); return output; } } catch (Exception e) { throw e; } finally { if (clientResp != null) { clientResp.close(); } _log.info("3PARDriver:createVVsetVirtualCopy leave"); } //end try/catch/finally } } catch (Exception e) { _log.info("3PARDriver:createVVsetVirtualCopy ERROR "); throw e; } return null; } }