Java tutorial
/** * Copyright (c) 2015 by Delphix. All rights reserved. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.delphix.delphix; import java.io.File; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; import org.apache.commons.collections.IteratorUtils; import org.apache.commons.io.FileUtils; import org.apache.http.HttpHeaders; import org.apache.http.HttpResponse; import org.apache.http.client.ClientProtocolException; import org.apache.http.client.HttpClient; import org.apache.http.client.config.RequestConfig; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpPost; import org.apache.http.entity.ByteArrayEntity; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.util.EntityUtils; import org.kohsuke.stapler.DataBoundConstructor; import com.delphix.delphix.DelphixContainer.ContainerType; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ArrayNode; import com.fasterxml.jackson.databind.node.ObjectNode; /** * Used for interacting with a Delphix Engine */ public class DelphixEngine { private static final Logger LOGGER = Logger.getLogger(DelphixEngine.class.getName()); public enum ContainerOperationType { REFRESH, ROLLBACK, SYNC, PROVISIONVDB, DELETECONTAINER } public enum EnvironmentOperationType { CREATE, REFRESH, DELETE } /* * Miscellaneous constants */ private static final String PROTOCOL = "http://"; private static final String ENCODING = "UTF-8"; private static final String CONTENT_TYPE = "application/json"; private static final String OK_STATUS = "OK"; /* * Paths to endpoints on Delphix Engine */ private static final String PATH_SESSION = "/resources/json/delphix/session"; private static final String PATH_LOGIN = "/resources/json/delphix/login"; private static final String PATH_DATABASE = "/resources/json/delphix/database"; private static final String PATH_SOURCE = "/resources/json/delphix/source"; private static final String PATH_HOOK_OPERATION = "/resources/json/delphix/source/%s"; private static final String PATH_TIMEFLOW = "/resources/json/delphix/timeflow"; private static final String PATH_REFRESH = "/resources/json/delphix/database/%s/refresh"; private static final String PATH_ROLLBACK = "/resources/json/delphix/database/%s/rollback"; private static final String PATH_SYNC = "/resources/json/delphix/database/%s/sync"; private static final String PATH_CANCEL_JOB = "/resources/json/delphix/job/%s/cancel"; private static final String PATH_CONTAINER = "/resources/json/delphix/database/%s"; private static final String PATH_JOB = "/resources/json/delphix/job/%s"; private static final String PATH_PROVISION_DEFAULTS = "/resources/json/delphix/database/provision/defaults"; private static final String PATH_PROVISION = "/resources/json/delphix/database/provision"; private static final String PATH_GROUPS = "/resources/json/delphix/group"; private static final String PATH_DELETE_CONTAINER = "/resources/json/delphix/database/%s/delete"; private static final String PATH_REFRESH_ENVIRONMENT = "/resources/json/delphix/environment/%s/refresh"; private static final String PATH_ENVIRONMENT = "/resources/json/delphix/environment"; private static final String PATH_DELETE_ENVIRONMENT = "/resources/json/delphix/environment/%s/delete"; private static final String PATH_SNAPSHOT = "/resources/json/delphix/snapshot"; private static final String PATH_SYSTEM_INFO = "/resources/json/delphix/system"; private static final String PATH_COMPATIBLE_REPOSITORIES = "/resources/json/delphix/repository/compatibleRepositories"; private static final String PATH_REPOSITORY = "/resources/json/delphix/repository/%s"; private static final String PATH_CLUSTER_NODES = "/resources/json/delphix/environment/oracle/clusternode"; /* * Content for POST requests to Delphix Engine */ private static final String CONTENT_SESSION = "{\"type\": \"APISession\",\"version\": " + "{\"type\": \"APIVersion\",\"major\": %s,\"minor\": %s,\"micro\": %s}}"; private static final String CONTENT_LOGIN = "{\"type\": \"LoginRequest\",\"username\": \"%s\",\"password\": \"%s\"}"; private static final String CONTENT_REFRESH_SEMANTIC = "{\"type\": \"%s\", \"timeflowPointParameters\": {" + "\"type\": \"TimeflowPointSemantic\",\"container\": \"%s\", \"location\": \"%s\"}}"; private static final String CONTENT_REFRESH_POINT = "{\"type\": \"%s\", \"timeflowPointParameters\": {" + "\"type\": \"TimeflowPointTimestamp\", \"timeflow\": \"%s\", \"timestamp\":\"%s\"}}"; private static final String CONTENT_ROLLBACK_SEMANTIC = CONTENT_REFRESH_SEMANTIC; private static final String CONTENT_ROLLBACK_POINT = CONTENT_REFRESH_POINT; private static final String CONTENT_SYNC = "{\"type\": \"%s\"}"; private static final String CONTENT_PROVISION_DEFAULTS_CONTAINER = "{\"type\": \"TimeflowPointSemantic\", \"container\": \"%s\", \"location\": \"%s\"}"; private static final String CONTENT_PROVISION_DEFAULTS_TIMESTAMP = "{\"type\": \"TimeflowPointTimestamp\", \"timeflow\": \"%s\",\"timestamp\":\"%s\"}"; private static final String CONTENT_DELETE_CONTAINER = "{\"type\": \"DeleteParameters\"}"; private static final String CONTENT_ORACLE_DELETE_CONTAINER = "{\"type\": \"OracleDeleteParameters\"}"; private static final String CONTENT_REFRESH_ENVIRONMENT = "{}"; private static final String CONTENT_ADD_UNIX_ENVIRONMENT = "{\"type\": \"HostEnvironmentCreateParameters\",\"primaryUser\": {\"type\": \"EnvironmentUser\"," + "\"name\": \"%s\",\"credential\": {\"type\": \"PasswordCredential\",\"password\": \"%s\"}}," + "\"hostEnvironment\": {\"type\": \"UnixHostEnvironment\"},\"hostParameters\": {\"type\": " + "\"UnixHostCreateParameters\",\"host\": {\"type\": \"UnixHost\",\"address\": " + "\"%s\",\"toolkitPath\": \"%s\"}}}"; private static final String CONTENT_DELETE_ENVIRONMENT = "{}"; public static final String CONTENT_LATEST_POINT = "LATEST_POINT"; public static final String CONTENT_LATEST_SNAPSHOT = "LATEST_SNAPSHOT"; public static final String CONTENT_SYNC_HOOK = "{\"operations\":{\"preSync\":%s,\"postSync\":%s, \"type\": \"%s\"}," + "\"type\":\"%s\"}"; public static final String CONTENT_REFRESH_HOOK = "{\"operations\":{\"preRefresh\":%s,\"postRefresh\":%s, \"type\": \"%s\"}," + "\"type\":\"%s\"}"; public static final String CONTENT_ROLLBACK_HOOK = "{\"operations\":{\"preRollback\":%s,\"postRollback\":%s, \"type\": \"%s\"}," + "\"type\":\"%s\"}"; public static final String CONTENT_COMPATIBLE_REPOSITORIES = "{\"environment\": \"%s\", \"timeflowPointParameters\":%s," + "\"type\":\"ProvisionCompatibilityParameters\"}"; /* * Fields used in JSON requests and responses */ private static final String FIELD_EVENTS = "events"; private static final String FIELD_JOB_STATE = "jobState"; private static final String FIELD_RESULT = "result"; private static final String FIELD_PROVISION_CONTAINER = "provisionContainer"; private static final String FIELD_TYPE = "type"; private static final String FIELD_JOB = "job"; private static final String FIELD_NAME = "name"; private static final String FIELD_REFERENCE = "reference"; private static final String FIELD_TARGET = "target"; private static final String FIELD_TARGET_NAME = "targetName"; private static final String FIELD_ACTION_TYPE = "actionType"; private static final String FIELD_TIMESTAMP = "timestamp"; private static final String FIELD_LATEST_CHANGE_POINT = "latestChangePoint"; private static final String FIELD_MESSAGE_DETAILS = "messageDetails"; private static final String FIELD_GROUP = "group"; private static final String FIELD_STATUS = "status"; private static final String FIELD_CONTAINER = "container"; private static final String FIELD_TIMEFLOW = "timeflow"; private static final String FIELD_PARENT_POINT = "parentPoint"; private static final String FIELD_CURRENT_TIMEFLOW = "currentTimeflow"; private static final String FIELD_RUNTIME = "runtime"; private static final String FIELD_API_VERSION = "apiVersion"; private static final String FIELD_MAJOR = "major"; private static final String FIELD_MINOR = "minor"; private static final String FIELD_MICRO = "micro"; private static final String FIELD_REPOSITORIES = "repositories"; private static final String FIELD_ENVIRONMENT = "environment"; private static final String FIELD_RAC = "rac"; private static final String FIELD_CLUSTER = "cluster"; /** * Address of the Delphix Engine */ private final String engineAddress; /** * Username for logging into engine */ private final String engineUsername; /** * Password of user */ private final String enginePassword; /* * Http client used for sending requests to engine */ private final HttpClient client; private static final ObjectMapper MAPPER = new ObjectMapper(); @DataBoundConstructor public DelphixEngine(String engineAddress, String engineUsername, String enginePassword) { this.engineAddress = engineAddress; this.engineUsername = engineUsername; this.enginePassword = enginePassword; RequestConfig.Builder requestBuilder = RequestConfig.custom(); requestBuilder = requestBuilder.setConnectTimeout(60 * 1000); requestBuilder = requestBuilder.setConnectionRequestTimeout(60 * 1000); requestBuilder = requestBuilder.setSocketTimeout(60 * 1000); HttpClientBuilder builder = HttpClientBuilder.create(); builder.setDefaultRequestConfig(requestBuilder.build()); client = builder.build(); } public DelphixEngine(DelphixEngine engine) { this.engineAddress = engine.engineAddress; this.engineUsername = engine.engineUsername; this.enginePassword = engine.enginePassword; RequestConfig.Builder requestBuilder = RequestConfig.custom(); requestBuilder = requestBuilder.setConnectTimeout(60 * 1000); requestBuilder = requestBuilder.setConnectionRequestTimeout(60 * 1000); requestBuilder = requestBuilder.setSocketTimeout(60 * 1000); HttpClientBuilder builder = HttpClientBuilder.create(); builder.setDefaultRequestConfig(requestBuilder.build()); client = builder.build(); } /** * Send POST to Delphix Engine and return the result */ private JsonNode enginePOST(final String path, final String content) throws IOException, DelphixEngineException { // Log requests if (!content.contains("LoginRequest")) { LOGGER.log(Level.WARNING, path + ":" + content); } // Build and send request HttpPost request = new HttpPost(PROTOCOL + engineAddress + path); try { request.setEntity(new ByteArrayEntity(content.getBytes(ENCODING))); } catch (UnsupportedEncodingException e) { throw new IllegalStateException(e); } request.setHeader(HttpHeaders.CONTENT_TYPE, CONTENT_TYPE); HttpResponse response = client.execute(request); // Get result of request String result = EntityUtils.toString(response.getEntity()); JsonNode jsonResult = MAPPER.readTree(result); EntityUtils.consume(response.getEntity()); if (!jsonResult.get(FIELD_STATUS).asText().equals(OK_STATUS)) { throw new DelphixEngineException(jsonResult.get("error").get("details").toString()); } // Log result if (!content.contains("LoginRequest")) { LOGGER.log(Level.WARNING, jsonResult.toString()); } return jsonResult; } /** * Send GET to Delphix Engine and return the result */ private JsonNode engineGET(final String path) throws IOException, DelphixEngineException { // Log requests LOGGER.log(Level.WARNING, path); // Build and send request HttpGet request = new HttpGet(PROTOCOL + engineAddress + path); request.setHeader(HttpHeaders.CONTENT_TYPE, CONTENT_TYPE); HttpResponse response = client.execute(request); // Get result of request String result = EntityUtils.toString(response.getEntity()); JsonNode jsonResult = MAPPER.readTree(result); EntityUtils.consume(response.getEntity()); if (!jsonResult.get(FIELD_STATUS).asText().equals(OK_STATUS)) { throw new DelphixEngineException(jsonResult.get("error").get("details").asText()); } // Log result LOGGER.log(Level.WARNING, jsonResult.toString()); return jsonResult; } /** * Login to Delphix Engine Will throw a DelphixEngineException if the login * fails due to bad username or password */ public void login() throws IOException, DelphixEngineException { // Get session with 1.0.0 enginePOST(PATH_SESSION, String.format(CONTENT_SESSION, "1", "0", "0")); // Login enginePOST(PATH_LOGIN, String.format(CONTENT_LOGIN, engineUsername, enginePassword)); // Find the most recent API session for this engine JsonNode version = engineGET(PATH_SYSTEM_INFO).get(FIELD_RESULT).get(FIELD_API_VERSION); // Get session with most recent API session enginePOST(PATH_SESSION, String.format(CONTENT_SESSION, version.get(FIELD_MAJOR), version.get(FIELD_MINOR), version.get(FIELD_MICRO))); // Login with most recent API session enginePOST(PATH_LOGIN, String.format(CONTENT_LOGIN, engineUsername, enginePassword)); } /** * Get a single container */ public DelphixContainer getContainer(String reference) throws ClientProtocolException, IOException, DelphixEngineException { LinkedHashMap<String, DelphixContainer> containers = listContainers(); return containers.get(reference); } /** * List containers in the Delphix Engine */ public LinkedHashMap<String, DelphixContainer> listContainers() throws ClientProtocolException, IOException, DelphixEngineException { // Get containers LinkedHashMap<String, DelphixContainer> containers = new LinkedHashMap<String, DelphixContainer>(); JsonNode containersJSON = engineGET(PATH_DATABASE).get(FIELD_RESULT); // Loop through container list for (int i = 0; i < containersJSON.size(); i++) { JsonNode containerJSON = containersJSON.get(i); ContainerType type; /* * Set the type of the container. Versions of Delphix before 4.4 * classify transformation containers and restoration datasets as * VDBs. They are differentiated in 4.4. */ if (containerJSON.get(FIELD_PROVISION_CONTAINER).asText().equals("null")) { type = ContainerType.SOURCE; } else { type = ContainerType.VDB; } // Create container object from JSON result DelphixContainer container = new DelphixContainer(engineAddress, containerJSON.get(FIELD_NAME).asText(), containerJSON.get(FIELD_REFERENCE).asText(), type, containerJSON.get(FIELD_GROUP).asText(), containerJSON.get(FIELD_CURRENT_TIMEFLOW).asText(), containerJSON.get(FIELD_TYPE).asText()); containers.put(container.getReference(), container); } return containers; } public ArrayList<DelphixGroup> listGroups() throws IOException, DelphixEngineException { // Get containers ArrayList<DelphixGroup> groups = new ArrayList<DelphixGroup>(); JsonNode groupsJSON = engineGET(PATH_GROUPS).get(FIELD_RESULT); // Loop through group list for (int i = 0; i < groupsJSON.size(); i++) { JsonNode groupJSON = groupsJSON.get(i); // Create group object from JSON result DelphixGroup group = new DelphixGroup(groupJSON.get(FIELD_REFERENCE).asText(), groupJSON.get(FIELD_NAME).asText()); groups.add(group); } return groups; } /** * List sources in the Delphix Engine */ public LinkedHashMap<String, DelphixSource> listSources() throws ClientProtocolException, IOException, DelphixEngineException { // Get containers LinkedHashMap<String, DelphixSource> sources = new LinkedHashMap<String, DelphixSource>(); JsonNode sourcesJSON = engineGET(PATH_SOURCE).get(FIELD_RESULT); // Loop through container list for (int i = 0; i < sourcesJSON.size(); i++) { JsonNode sourceJSON = sourcesJSON.get(i); // Create container object from JSON result DelphixSource source = new DelphixSource(sourceJSON.get(FIELD_REFERENCE).asText(), sourceJSON.get(FIELD_NAME).asText(), sourceJSON.get(FIELD_CONTAINER).asText(), sourceJSON.get(FIELD_RUNTIME).get(FIELD_STATUS).asText(), sourceJSON.get(FIELD_TYPE).asText()); sources.put(source.getContainer(), source); } return sources; } /** * List timeflows in the Delphix Engine */ public LinkedHashMap<String, DelphixTimeflow> listTimeflows() throws ClientProtocolException, IOException, DelphixEngineException { // Get containers LinkedHashMap<String, DelphixTimeflow> timeflows = new LinkedHashMap<String, DelphixTimeflow>(); JsonNode timeflowsJSON = engineGET(PATH_TIMEFLOW).get(FIELD_RESULT); // Loop through container list for (int i = 0; i < timeflowsJSON.size(); i++) { JsonNode timeflowJSON = timeflowsJSON.get(i); // Create container object from JSON result JsonNode parentPoint = timeflowJSON.get(FIELD_PARENT_POINT); String timestamp = "N/A"; if (!parentPoint.isNull()) { timestamp = parentPoint.get(FIELD_TIMESTAMP).asText(); } DelphixTimeflow timeflow = new DelphixTimeflow(timeflowJSON.get(FIELD_REFERENCE).asText(), timeflowJSON.get(FIELD_NAME).asText(), timestamp, timeflowJSON.get(FIELD_CONTAINER).asText()); timeflows.put(timeflow.getReference(), timeflow); } return timeflows; } /** * List environments in the Delphix Engine */ public LinkedHashMap<String, DelphixEnvironment> listEnvironments() throws ClientProtocolException, IOException, DelphixEngineException { // Get containers LinkedHashMap<String, DelphixEnvironment> environments = new LinkedHashMap<String, DelphixEnvironment>(); JsonNode environmentsJSON = engineGET(PATH_ENVIRONMENT).get(FIELD_RESULT); // Loop through container list for (int i = 0; i < environmentsJSON.size(); i++) { JsonNode environmentJSON = environmentsJSON.get(i); DelphixEnvironment environment = new DelphixEnvironment(environmentJSON.get(FIELD_REFERENCE).asText(), environmentJSON.get(FIELD_NAME).asText()); environments.put(environment.getReference(), environment); } return environments; } public DelphixSnapshot getSnapshot(String reference) throws ClientProtocolException, IOException, DelphixEngineException { LinkedHashMap<String, DelphixSnapshot> snapshots = listSnapshots(); return snapshots.get(reference); } /** * List snapshots in the Delphix Engine */ public LinkedHashMap<String, DelphixSnapshot> listSnapshots() throws ClientProtocolException, IOException, DelphixEngineException { // Get snapshots LinkedHashMap<String, DelphixSnapshot> snapshots = new LinkedHashMap<String, DelphixSnapshot>(); JsonNode snapshotsJSON = engineGET(PATH_SNAPSHOT).get(FIELD_RESULT); // Loop through snapshot list for (int i = 0; i < snapshotsJSON.size(); i++) { JsonNode snapshotJSON = snapshotsJSON.get(i); DelphixSnapshot snapshot = new DelphixSnapshot(snapshotJSON.get(FIELD_REFERENCE).asText(), snapshotJSON.get(FIELD_NAME).asText(), snapshotJSON.get(FIELD_CONTAINER).asText(), snapshotJSON.get(FIELD_TIMEFLOW).asText(), snapshotJSON.get(FIELD_LATEST_CHANGE_POINT).get(FIELD_TIMESTAMP).asText()); snapshots.put(snapshot.getReference(), snapshot); } return snapshots; } /** * Cancel a job running on the Delphix Engine */ public void cancelJob(String jobRef) throws ClientProtocolException, IOException, DelphixEngineException { enginePOST(String.format(PATH_CANCEL_JOB, jobRef), ""); } /** * Get the status of a job running on the Delphix Engine */ public JobStatus getJobStatus(String job) throws ClientProtocolException, IOException, DelphixEngineException { // Get job status JsonNode result = engineGET(String.format(PATH_JOB, job)); // Parse JSON to construct object JsonNode jobStatus = result.get(FIELD_RESULT); JsonNode events = jobStatus.get(FIELD_EVENTS); JsonNode recentEvent = events.get(events.size() - 1); JobStatus.StatusEnum status = JobStatus.StatusEnum.valueOf(jobStatus.get(FIELD_JOB_STATE).asText()); String summary = recentEvent.get(FIELD_TIMESTAMP).asText() + " - " + recentEvent.get(FIELD_MESSAGE_DETAILS).asText(); String target = jobStatus.get(FIELD_TARGET).asText(); String targetName = jobStatus.get(FIELD_TARGET_NAME).asText(); String actionType = jobStatus.get(FIELD_ACTION_TYPE).asText(); return new JobStatus(status, summary, target, targetName, actionType); } /** * Refresh a virtual database on the Delphix Engine using semantic location */ public String refreshContainer(String vdbRef, String location) throws IOException, DelphixEngineException { // Construct parameters to send to engine String type; if (getContainerType(vdbRef).equals("OracleDatabaseContainer")) { type = "OracleRefreshParameters"; } else { type = "RefreshParameters"; } JsonNode result; // Do refresh by either semantic point or by snapshot point if (location.equals(CONTENT_LATEST_POINT) || location.equals(CONTENT_LATEST_SNAPSHOT)) { result = enginePOST(String.format(PATH_REFRESH, vdbRef), String.format(CONTENT_REFRESH_SEMANTIC, type, getParentContainer(vdbRef), location)); } else { DelphixSnapshot snapshot = getSnapshot(location); result = enginePOST(String.format(PATH_REFRESH, vdbRef), String.format(CONTENT_REFRESH_POINT, type, snapshot.getTimeflowRef(), snapshot.getLatestChangePoint())); } return result.get(FIELD_JOB).asText(); } /** * Rollback a virtual database on the Delphix Engine using semantic location */ public String rollbackContainer(String vdbRef, String location) throws IOException, DelphixEngineException { // Construct parameters to send to engine String type; if (getContainerType(vdbRef).equals("OracleDatabaseContainer")) { type = "OracleRollbackParameters"; } else { type = "RollbackParameters"; } JsonNode result; // Do refresh by either semantic point or by snapshot point if (location.equals(CONTENT_LATEST_POINT) || location.equals(CONTENT_LATEST_SNAPSHOT)) { result = enginePOST(String.format(PATH_ROLLBACK, vdbRef), String.format(CONTENT_ROLLBACK_SEMANTIC, type, vdbRef, location)); } else { DelphixSnapshot snapshot = getSnapshot(location); result = enginePOST(String.format(PATH_ROLLBACK, vdbRef), String.format(CONTENT_ROLLBACK_POINT, type, snapshot.getTimeflowRef(), snapshot.getLatestChangePoint())); } return result.get(FIELD_JOB).asText(); } /** * Get the parent of a virtual database on the Delphix Engine */ public String getParentContainer(String vdbRef) throws IOException, DelphixEngineException { JsonNode result = engineGET(String.format(PATH_CONTAINER, vdbRef)); JsonNode container = result.get(FIELD_RESULT); return container.get(FIELD_PROVISION_CONTAINER).asText(); } /** * Get the type of a container on the Delphix Engine */ private String getContainerType(String containerRef) throws IOException, DelphixEngineException { JsonNode result = engineGET(String.format(PATH_CONTAINER, containerRef)); JsonNode container = result.get(FIELD_RESULT); return container.get(FIELD_TYPE).asText(); } /** * Run a sync operation for a source on the Delphix Engine */ public String sync(String sourceRef) throws IOException, DelphixEngineException { // Construct parameters to send to engine String type = getContainerType(sourceRef); type = type.replace("Container", ""); type = type.replace("Database", ""); type = type + "SyncParameters"; // Do sync JsonNode result = enginePOST(String.format(PATH_SYNC, sourceRef), String.format(CONTENT_SYNC, type)); return result.get(FIELD_JOB).asText(); } /** * Gets a repository */ public DelphixRepository getRepository(String repositoryRef) throws IOException, DelphixEngineException { JsonNode result = engineGET(String.format(PATH_REPOSITORY, repositoryRef)); JsonNode jsonRepository = result.get(FIELD_RESULT); DelphixRepository repository = new DelphixRepository(jsonRepository.get(FIELD_NAME).asText(), jsonRepository.get(FIELD_REFERENCE).asText(), jsonRepository.get(FIELD_ENVIRONMENT).asText(), jsonRepository.get(FIELD_RAC).asBoolean()); return repository; } /** * Get cluster nodes */ public ArrayList<DelphixClusterNode> listClusterNodes() throws IOException, DelphixEngineException { JsonNode result = engineGET(PATH_CLUSTER_NODES); JsonNode jsonClusterNodes = result.get(FIELD_RESULT); ArrayList<DelphixClusterNode> clusterNodes = new ArrayList<DelphixClusterNode>(); for (int i = 0; i < jsonClusterNodes.size(); i++) { DelphixClusterNode clusterNode = new DelphixClusterNode( jsonClusterNodes.get(i).get(FIELD_NAME).asText(), jsonClusterNodes.get(i).get(FIELD_REFERENCE).asText(), jsonClusterNodes.get(i).get(FIELD_CLUSTER).asText()); clusterNodes.add(clusterNode); } return clusterNodes; } /** * Get the compatible provision repositories */ private ArrayList<DelphixRepository> getCompatibleRepositories(String environmentRef, String provisionParameters) throws IOException, DelphixEngineException { JsonNode result = enginePOST(PATH_COMPATIBLE_REPOSITORIES, String.format(CONTENT_COMPATIBLE_REPOSITORIES, environmentRef, provisionParameters)); JsonNode jsonRepositories = result.get(FIELD_RESULT).get(FIELD_REPOSITORIES); ArrayList<DelphixRepository> repositories = new ArrayList<DelphixRepository>(); for (int i = 0; i < jsonRepositories.size(); i++) { DelphixRepository repository = new DelphixRepository(jsonRepositories.get(i).get(FIELD_NAME).asText(), jsonRepositories.get(i).get(FIELD_REFERENCE).asText(), jsonRepositories.get(i).get(FIELD_ENVIRONMENT).asText(), jsonRepositories.get(i).get(FIELD_RAC).asBoolean()); repositories.add(repository); } return repositories; } /** * Get the compatible provision repositories from a snapshot */ public ArrayList<DelphixRepository> getCompatibleRepositoriesSnapshot(String environmentRef, String snapshotRef) throws ClientProtocolException, IOException, DelphixEngineException { DelphixSnapshot snapshot = getSnapshot(snapshotRef); String parameters = String.format(CONTENT_PROVISION_DEFAULTS_TIMESTAMP, snapshot.getTimeflowRef(), snapshot.getLatestChangePoint()); return getCompatibleRepositories(environmentRef, parameters); } /** * Get the compatible provision repositories from a semantic point on a container */ public ArrayList<DelphixRepository> getCompatibleRepositoriesContainer(String environmentRef, String containerRef, String location) throws IOException, DelphixEngineException { String parameters = String.format(CONTENT_PROVISION_DEFAULTS_CONTAINER, containerRef, location); return getCompatibleRepositories(environmentRef, parameters); } /** * Get the provision defaults for provisioning from a semantic point on a container */ private String getProvisionDefaultsContainer(String containerRef, String location) throws IOException, DelphixEngineException { JsonNode result = enginePOST(PATH_PROVISION_DEFAULTS, String.format(CONTENT_PROVISION_DEFAULTS_CONTAINER, containerRef, location)); return result.get(FIELD_RESULT).toString(); } /** * Get the provision defaults for provisioning from a snapshot */ private String getProvisionDefaultsSnapshot(String snapshotRef) throws IOException, DelphixEngineException { DelphixSnapshot snapshot = getSnapshot(snapshotRef); JsonNode result = enginePOST(PATH_PROVISION_DEFAULTS, String.format(CONTENT_PROVISION_DEFAULTS_TIMESTAMP, snapshot.getTimeflowRef(), snapshot.getLatestChangePoint())); ObjectNode node = (ObjectNode) result.get(FIELD_RESULT); return node.toString(); } /** * Provision a VDB either a semantic point or a snapshot with the name of the new VDB being optional */ @SuppressWarnings("unchecked") public String provisionVDB(String containerRef, String snapshotRef, String containerName, String repositoryRef, String mountBase) throws IOException, DelphixEngineException { String defaultParams = ""; if (snapshotRef.equals(CONTENT_LATEST_POINT) || snapshotRef.equals(CONTENT_LATEST_SNAPSHOT)) { defaultParams = getProvisionDefaultsContainer(containerRef, snapshotRef); } else { defaultParams = getProvisionDefaultsSnapshot(snapshotRef); } // Strip out null values from provision parameters defaultParams = defaultParams.replaceAll("(\"[^\"]+\":null,?|,?\"[^\"]+\":null)", ""); JsonNode params = MAPPER.readTree(defaultParams); // Set new VDB name if it is passed if (!containerName.isEmpty()) { ObjectNode containerNode = (ObjectNode) params.get("container"); containerNode.put("name", containerName); ObjectNode sourceConfigNode = (ObjectNode) params.get("sourceConfig"); sourceConfigNode.put("databaseName", containerName); sourceConfigNode.put("uniqueName", containerName); ObjectNode instanceNode = (ObjectNode) sourceConfigNode.get("instance"); instanceNode.put("instanceName", containerName); } // Set target repository if (!repositoryRef.isEmpty() && !repositoryRef.equals("default")) { ObjectNode sourceConfig = (ObjectNode) params.get("sourceConfig"); sourceConfig.put("repository", repositoryRef); DelphixRepository repository = getRepository(repositoryRef); // Handle provisioning to RAC if (repository.getRAC()) { sourceConfig.put("type", "OracleRACConfig"); if (sourceConfig.has("instance")) { sourceConfig.remove("instance"); } ArrayNode instances = sourceConfig.putArray("instances"); ArrayList<DelphixClusterNode> clusterNodes = listClusterNodes(); int i = 1; for (DelphixClusterNode node : clusterNodes) { ObjectNode instance = MAPPER.createObjectNode(); instance.put("type", "OracleRACInstance"); instance.put("instanceNumber", i); ObjectNode containerNode = (ObjectNode) params.get("container"); instance.put("instanceName", containerNode.get("name").asText() + i); instance.put("node", node.getReference()); instances.add(instance); i++; } } } // Set the base mount point if (!mountBase.isEmpty()) { ObjectNode sourceConfig = (ObjectNode) params.get("source"); sourceConfig.put("mountBase", mountBase); } JsonNode result; ObjectNode sourceNode = (ObjectNode) params.get("source"); // Hack for RAC support if (sourceNode.has("redoLogSizeInMB")) { sourceNode.remove("redoLogSizeInMB"); } try { result = enginePOST(PATH_PROVISION, params.toString()); } catch (DelphixEngineException e) { // Handle the case where some of the fields in the defaults are read // only by removing those fields if (e.getMessage().contains("This field is read-only")) { JsonNode errors = MAPPER.readTree(e.getMessage()); List<String> list1 = IteratorUtils.toList(errors.fieldNames()); for (String field1 : list1) { List<String> list2 = IteratorUtils.toList(errors.get(field1).fieldNames()); for (String field2 : list2) { // Field1 is the outer field and field2 is the inner // field ObjectNode node = (ObjectNode) params.get(field1); // Remove the inner field node.remove(field2); } } result = enginePOST(PATH_PROVISION, params.toString()); } else { throw e; } } return result.get(FIELD_JOB).asText(); } /** * Create and discover an environment */ public String createEnvironment(String address, String user, String password, String toolkit) throws IOException, DelphixEngineException { JsonNode result = enginePOST(PATH_ENVIRONMENT, String.format(CONTENT_ADD_UNIX_ENVIRONMENT, user, password, address, toolkit)); return result.get(FIELD_JOB).asText(); } /** * Delete a container */ public String deleteContainer(String containerRef) throws IOException, DelphixEngineException { DelphixContainer container = getContainer(containerRef); String content = CONTENT_DELETE_CONTAINER; if (container.getPlatform().contains("Oracle")) { content = CONTENT_ORACLE_DELETE_CONTAINER; } JsonNode result = enginePOST(String.format(PATH_DELETE_CONTAINER, containerRef), content); return result.get(FIELD_JOB).asText(); } /** * Refresh and discover an environment */ public String refreshEnvironment(String environmentRef) throws IOException, DelphixEngineException { JsonNode result = enginePOST(String.format(PATH_REFRESH_ENVIRONMENT, environmentRef), CONTENT_REFRESH_ENVIRONMENT); return result.get(FIELD_JOB).asText(); } /** * Delete an environment */ public String deleteEnvironment(String environmentRef) throws IOException, DelphixEngineException { JsonNode result = enginePOST(String.format(PATH_DELETE_ENVIRONMENT, environmentRef), CONTENT_DELETE_ENVIRONMENT); return result.get(FIELD_JOB).asText(); } /** * Update the pre and post hooks for a specific type of operation on a container */ public void updateHooks(ContainerOperationType targetOperation, String containerRef, ArrayList<HookOperation> preOperations, ArrayList<HookOperation> postOperations) throws IOException, DelphixEngineException { // Figure out the operation type to set on JSON payload DelphixSource source = listSources().get(containerRef); String operationType = ""; if (source.getType().contains("VirtualSource")) { operationType = "VirtualSourceOperations"; } else if (source.getType().contains("LinkedSource")) { operationType = "LinkedSourceOperations"; } // Read the operation content for pre hooks and set it on payload ArrayNode preHooks = MAPPER.createArrayNode(); for (HookOperation operation : preOperations) { ObjectNode node = MAPPER.createObjectNode(); String operationContent = FileUtils.readFileToString(new File(operation.getPath())); node.put("command", operationContent); node.put("type", "RunCommandOnSourceOperation"); preHooks.add(node); } // Read the operation content for post hooks and set it on payload ArrayNode postHooks = MAPPER.createArrayNode(); for (HookOperation operation : postOperations) { ObjectNode node = MAPPER.createObjectNode(); String operationContent = FileUtils.readFileToString(new File(operation.getPath())); node.put("command", operationContent); node.put("type", "RunCommandOnSourceOperation"); postHooks.add(node); } // Choose the appropriate payload based on target operation type to update String postData = ""; if (targetOperation.equals(ContainerOperationType.REFRESH)) { postData = String.format(CONTENT_REFRESH_HOOK, preHooks.toString(), postHooks.toString(), operationType, source.getType()); } else if (targetOperation.equals(ContainerOperationType.ROLLBACK)) { postData = String.format(CONTENT_ROLLBACK_HOOK, preHooks.toString(), postHooks.toString(), operationType, source.getType()); } else if (targetOperation.equals(ContainerOperationType.SYNC)) { postData = String.format(CONTENT_SYNC_HOOK, preHooks.toString(), postHooks.toString(), operationType, source.getType()); } enginePOST(String.format(PATH_HOOK_OPERATION, source.getReference()), postData); } public String getEngineAddress() { return engineAddress; } public String getEngineUsername() { return engineUsername; } public String getEnginePassword() { return enginePassword; } }