/* * Copyright (c) 2013 EMC Corporation * All Rights Reserved */ package com.emc.storageos.hds.api; import java.io.IOException; import java.io.InputStream; import java.net.URI; import java.util.List; import java.util.Set; import org.apache.http.HttpStatus; import org.milyn.payload.JavaResult; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.emc.storageos.hds.HDSConstants; import com.emc.storageos.hds.HDSException; import com.emc.storageos.hds.model.EchoCommand; import com.emc.storageos.hds.model.Error; import com.emc.storageos.hds.model.HDSHost; import com.emc.storageos.hds.model.LogicalUnit; import com.emc.storageos.hds.model.ObjectLabel; import com.emc.storageos.hds.model.Pool; import com.emc.storageos.hds.model.StorageArray; import com.emc.storageos.hds.util.SmooksUtil; import com.sun.jersey.api.client.ClientResponse; /** * The HDS API Client is used to get information from the HighCommand Device Manager and to execute * configuration commands on a Hitachi Devices. A HDSApiClient instance represents a * connection to a single HighCommand Device Manager server. Use the {@link HDSApiFactory} to get a HdsApiClient instance for a given * HighCommand * management server. */ public class HDSApiClient { // Logger reference. private static Logger log = LoggerFactory.getLogger(HDSApiClient.class); private static final String STATUS_QUERY = "<?xml version=\"1.0\"?><HiCommandServerMessage><APIInfo version=\"7.4.1\"/><Request><SessionManager><Get target=\"RequestStatus\"><RequestStatus messageID=\"%1$s\"/></Get></SessionManager></Request></HiCommandServerMessage>"; // The base URI of the HDS Management Station. private URI baseURI; // The REST client for executing requests to the HDS Management Station. private RESTClient client; private HDSApiVolumeManager hdsApiVolumeManager; private HDSApiExportManager hdsApiExportManager; private HDSApiDiscoveryManager hdsApiDiscoveryManager; private HDSApiProtectionManager hdsApiProtectionManager; private HDSBatchApiExportManager hdsBatchApiExportManager; /** * Constructor * * @param endpoint The URI of the HighCommand Device Manager. * @param client A reference to the REST client for making requests. */ HDSApiClient(URI endpoint, RESTClient client) { this.baseURI = endpoint; this.client = client; this.hdsApiVolumeManager = new HDSApiVolumeManager(this); this.hdsApiExportManager = new HDSApiExportManager(this); this.hdsBatchApiExportManager = new HDSBatchApiExportManager(this); this.hdsApiDiscoveryManager = new HDSApiDiscoveryManager(this); this.hdsApiProtectionManager = new HDSApiProtectionManager(this); } /** * Client call to create Thick volumes. * * @param systemObjectId * @param arrayGroupId * @param luCapacity * @param noOfLus * @param label * @return * @throws Exception */ public String createThickVolumes(String systemObjectId, String arrayGroupId, Long luCapacity, int noOfLus, String label, String formatType, String model, Integer devNum) throws Exception { return hdsApiVolumeManager.createThickVolumes(systemObjectId, arrayGroupId, luCapacity, noOfLus, label, formatType, model, devNum); } /** * Client call to create Thick volumes. * * @param systemObjectId * @param arrayGroupId * @param luCapacity * @param noOfLus * @param label * @return * @throws Exception */ public String createThinVolumes(String systemObjectId, String arrayGroupId, Long luCapacity, int noOfLus, String label, String formatType, String model) throws Exception { return hdsApiVolumeManager.createThinVolumes(systemObjectId, arrayGroupId, luCapacity, noOfLus, label, formatType, model); } public String createSnapshotVolume(String systemObjectId, Long luCapacity, String model) throws Exception { return hdsApiVolumeManager.createSnapshotVolume(systemObjectId, luCapacity, model); } /** * Client call to delete snapshot volume * * @param storageSystemObjId * @param logicalUnitObjId * @param model * @return * @throws Exception */ public String deleteSnapshotVolume(String storageSystemObjId, String logicalUnitObjId, String model) throws Exception { return hdsApiVolumeManager.deleteSnapshotVolume(storageSystemObjId, logicalUnitObjId, model); } /** * Client API call to add label to * * @param luObjectId * @param name * @throws Exception */ public ObjectLabel addVolumeLabel(String luObjectId, String name) throws Exception { return hdsApiVolumeManager.addLabelToObject(luObjectId, name); } /** * Formats the given logicalunit. * * @param systemObjectId * @param luObjectId * @return * @throws Exception */ public String formatLogicalUnit(String systemObjectId, String luObjectId) throws Exception { return hdsApiVolumeManager.formatLogicalUnit(systemObjectId, luObjectId); } /** * Client call to create Logical units. * * @param systemObjectId * @param arrayGroupId * @param luCapacity * @param noOfLus * @param label * @param isThinVolume * @return * @throws Exception */ public LogicalUnit createLUSEVolume(String systemObjectId, String metaHead, List<String> ldevIds) throws Exception { return hdsApiVolumeManager.addLUSE(systemObjectId, metaHead, ldevIds); } /** * Client call to create Logical units. * * @param systemObjectId * @param arrayGroupId * @param luCapacity * @param noOfLus * @param label * @param isThinVolume * @return * @throws Exception */ public String modifyThinVolume(String systemObjectId, String luObjectId, Long newLUCapacityInBytes, String model) throws Exception { return hdsApiVolumeManager.modifyVirtualVolume(systemObjectId, luObjectId, newLUCapacityInBytes, model); } /** * * @param systemObjectId * @param logicalUnitObjectId * @return */ public LogicalUnit getLogicalUnitInfo(String systemObjectId, String logicalUnitObjectId) throws Exception { return hdsApiVolumeManager.getLogicalUnitInfo(systemObjectId, logicalUnitObjectId); } /** * Client call to delete logicalunits. * * @param systemObjectId * @param logicalUnitIdList * @throws Exception */ public String deleteThickLogicalUnits(String systemObjectId, Set<String> logicalUnitIdList, String model) throws Exception { return hdsApiVolumeManager.deleteThickLogicalUnits(systemObjectId, logicalUnitIdList, model); } /** * Client call to delete logicalunits. * * @param systemObjectId * @param logicalUnitIdList * @throws Exception */ public String deleteThinLogicalUnits(String systemObjectId, Set<String> logicalUnitIdList, String model) throws Exception { return hdsApiVolumeManager.deleteThinLogicalUnits(systemObjectId, logicalUnitIdList, model); } /** * Client call to get StoragePool Information * * @param systemObjectId * @param poolObjectId * @return * @throws Exception */ public Pool getStoragePoolInfo(String systemObjectId, String poolObjectId) throws Exception { return hdsApiVolumeManager.getStoragePoolInfo(systemObjectId, poolObjectId); } /** * Client call to get all Storage systems info managed by the HiCommand Device Manager. * * @return * @throws Exception */ public List<StorageArray> getStorageSystemsInfo() throws Exception { return hdsApiDiscoveryManager.getStorageSystemsInfo(); } /** * Returns HiCommand Device Manager's API version. * * @return apiVersion * @throws Exception */ public String getProviderAPIVersion() throws Exception { return hdsApiDiscoveryManager.getProviderAPIVersion(); } /** * Client call to get all Storage systems info managed by the HiCommand Device Manager. * * @return * @throws Exception */ public StorageArray getStorageSystemDetails(String systemObjectID) throws Exception { return hdsApiDiscoveryManager.getStorageSystemDetails(systemObjectID); } /** * Returns tiering policies of a given storage system. * * @param systemObjectID * @return * @throws Exception */ public StorageArray getStorageSystemTieringPolicies(String systemObjectID) throws Exception { return hdsApiDiscoveryManager.getStorageSystemTieringPolicyDetails(systemObjectID); } /** * Returns pool tier info of a given storage system. * * @param systemObjectID * @return * @throws Exception */ public Pool getStoragePoolTierInfo(String systemObjectID, String poolObjectID) throws Exception { return hdsApiDiscoveryManager.getStoragePoolTierInfo(systemObjectID, poolObjectID); } /** * Returns all pools tier info of a given storage system. * * @param systemObjectID * @return * @throws Exception */ public List<Pool> getStoragePoolsTierInfo(String systemObjectID) throws Exception { return hdsApiDiscoveryManager.getStoragePoolsTierInfo(systemObjectID); } /** * * @return */ public HDSApiVolumeManager getHDSApiVolumeManager() { return hdsApiVolumeManager; } /** * * @return */ public HDSApiExportManager getHDSApiExportManager() { return hdsApiExportManager; } /** * * @return */ public HDSBatchApiExportManager getHDSBatchApiExportManager() { return hdsBatchApiExportManager; } /** * * @return */ public HDSApiDiscoveryManager getHDSApiDiscoveryManager() { return hdsApiDiscoveryManager; } /** * Package protected getter for the base URI for the client. * * @return The base URI for the client. */ public URI getBaseURI() { return baseURI; } /** * Package protected method for executing a GET request. * * @param resourceURI The resource URI. * * @return The client response. */ public ClientResponse get(URI resourceURI) { return client.get(resourceURI); } /** * Package protected method for executing a POST request. * * @param resourceURI The resource URI. * @param postData The POST data. * * @return The client response. */ public ClientResponse post(URI resourceURI, String postData) { return client.post(resourceURI, postData); } /** * Package protected method for executing a PUT request. * * @param resourceURI The resource URI. * * @return The client response. */ public ClientResponse put(URI resourceURI) { return client.put(resourceURI); } /** * This method is responsible to check the async task status for every 3 seconds * and if we don't get status in 60 retries, throw exception. * * @param messageID : Task status for messageID * @return Parsed java result of response stream. */ public JavaResult waitForCompletion(String messageID) throws Exception { log.info("Verifying the Async task status for message {}", messageID); InputStream responseStream = null; EchoCommand command = null; JavaResult result = null; String statusQueryWithParams = String.format(STATUS_QUERY, messageID); int retries = 0; do { try { log.info("retrying {}th time", retries); ClientResponse response = client.post(getBaseURI(), statusQueryWithParams); if (HttpStatus.SC_OK == response.getStatus()) { responseStream = response.getEntityInputStream(); result = SmooksUtil.getParsedXMLJavaResult(responseStream, HDSConstants.SMOOKS_CONFIG_FILE); command = result.getBean(EchoCommand.class); // Sleep for some time if the Async task is still // processing state. if (HDSConstants.PROCESSING_STR.equalsIgnoreCase(command .getStatus())) { log.info("Async task is still in processing state. Hence sleeping..."); Thread.sleep(HDSConstants.TASK_PENDING_WAIT_TIME); } } else { throw HDSException.exceptions .asyncTaskInvalidResponse(response.getStatus()); } } finally { try { if (null != responseStream) { responseStream.close(); } } catch (IOException ioEx) { log.warn( "Ignoring io exception that occurred during stream closing for async status check for messageID {}", messageID); } } } while (HDSConstants.PROCESSING_STR.equalsIgnoreCase(command.getStatus()) && retries++ < HDSConstants.MAX_RETRIES); if (retries >= HDSConstants.MAX_RETRIES) { log.error("Async task exceeded the maximum number of retries"); throw HDSException.exceptions .asyncTaskMaximumRetriesExceed(messageID); // handle carefully for the generated task. is it possible to cancel the task? } if (HDSConstants.FAILED_STR.equalsIgnoreCase(command.getStatus())) { Error error = result.getBean(Error.class); String errorMessage = String .format("Async task failed for messageID %s due to %s with error code: %d", messageID, error.getDescription(), error.getCode()); log.error(errorMessage); HDSException.exceptions.asyncTaskFailedWithErrorResponse(messageID, error.getDescription(), error.getCode()); throw new Exception(errorMessage); } log.info("Async task completed for messageID {}", messageID); return result; } /** * This method is responsible to check the async task status. * * @param messageID : Task status for messageID * @return Parsed java result of response stream. */ public JavaResult checkAsyncTaskStatus(String messageID) throws Exception { InputStream responseStream = null; JavaResult result = null; try { String statusQueryWithParams = String.format(STATUS_QUERY, messageID); ClientResponse response = client.post(getBaseURI(), statusQueryWithParams); if (HttpStatus.SC_OK == response.getStatus()) { responseStream = response.getEntityInputStream(); result = SmooksUtil.getParsedXMLJavaResult(responseStream, HDSConstants.SMOOKS_CONFIG_FILE); } else { throw HDSException.exceptions .asyncTaskInvalidResponse(response.getStatus()); } } finally { try { if (null != responseStream) { responseStream.close(); } } catch (IOException ioEx) { log.warn( "Ignoring io exception that occurred during stream closing for async status check for messageID {}", messageID); } } return result; } /** * Utility method to check if there are any errors or not. * * @param javaResult * @throws Exception */ public void verifyErrorPayload(JavaResult javaResult) throws Exception { EchoCommand command = javaResult.getBean(EchoCommand.class); if (null == command || null == command.getStatus() || HDSConstants.FAILED_STR.equalsIgnoreCase(command.getStatus())) { Error error = javaResult.getBean(Error.class); // log.info("Error response received from Hitachi server for messageID :{}", command.getMessageID()); log.info("Hitachi command failed with error code:{} with message:{} for request:{}", new Object[] { error.getCode().toString(), error.getDescription(), error.getSource() }); throw HDSException.exceptions.errorResponseReceived( error.getCode(), error.getDescription()); } } public HDSApiProtectionManager getHdsApiProtectionManager() { return hdsApiProtectionManager; } public String modifyThinVolumeTieringPolicy(String systemObjectID, String luObjectID, String ldevObjectID, String tieringPolicyName, String model) { return hdsApiVolumeManager.modifyThinVolumeTieringPolicy(systemObjectID, luObjectID, ldevObjectID, tieringPolicyName, model); } public HDSHost getSnapshotGroupPairManagementServer(String serialNumber) throws Exception { return hdsApiProtectionManager.getSnapshotGroupPairManagementServer(serialNumber); } public List<Pool> getThinImagePoolList(String systemObjectId) throws Exception { return hdsApiDiscoveryManager.getThinImagePoolList(systemObjectId); } public boolean createThinImagePair(String snapshotGroupObjId, String hostObjId, String sourNativeId, String sanpNativeId, String thinImagePoolId, String model) throws Exception { return hdsApiProtectionManager.createThinImagePair(snapshotGroupObjId, hostObjId, sourNativeId, sanpNativeId, thinImagePoolId, model); } public boolean restoreThinImagePair(String pairMgmtServerHostObjId, String snapshotGroupObjId, String replicationInfoObjId, String model) throws Exception { return hdsApiProtectionManager.restoreThinImagePair(pairMgmtServerHostObjId, snapshotGroupObjId, replicationInfoObjId, model); } public void deleteThinImagePair(String pairMgmtServerHostObjId, String snapShotGrpId, String replicationInfoObjId, String model) throws Exception { hdsApiProtectionManager.deleteThinImagePair(pairMgmtServerHostObjId, snapShotGrpId, replicationInfoObjId, model); } }