org.apache.falcon.regression.core.util.OozieUtil.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.falcon.regression.core.util.OozieUtil.java

Source

/**
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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 org.apache.falcon.regression.core.util;

import org.apache.falcon.entity.v0.EntityType;
import org.apache.falcon.regression.core.helpers.ColoHelper;
import org.apache.falcon.regression.core.helpers.entity.AbstractEntityHelper;
import org.apache.hadoop.conf.Configuration;
import org.apache.oozie.client.AuthOozieClient;
import org.apache.oozie.client.BundleJob;
import org.apache.oozie.client.OozieClient;
import org.apache.oozie.client.OozieClientException;
import org.apache.oozie.client.Job;
import org.apache.oozie.client.CoordinatorAction;
import org.apache.oozie.client.CoordinatorJob;
import org.apache.oozie.client.WorkflowAction;
import org.apache.oozie.client.WorkflowJob;
import org.joda.time.DateTime;
import org.apache.log4j.Logger;
import org.joda.time.DateTimeZone;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;
import org.json.JSONException;
import org.testng.Assert;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;

/**
 * helper methods for oozie .
 */
public final class OozieUtil {

    public static final String FAIL_MSG = "NO_such_workflow_exists";

    private OozieUtil() {
        throw new AssertionError("Instantiating utility class...");
    }

    private static final Logger LOGGER = Logger.getLogger(OozieUtil.class);

    public static AuthOozieClient getClient(String url) {
        return new AuthOozieClient(url);
    }

    public static List<BundleJob> getBundles(OozieClient client, String filter, int start, int len)
            throws OozieClientException {
        LOGGER.info("Connecting to oozie: " + client.getOozieUrl());
        return client.getBundleJobsInfo(filter, start, len);
    }

    public static List<String> getBundleIds(List<BundleJob> bundles) {
        List<String> ids = new ArrayList<>();
        for (BundleJob bundle : bundles) {
            LOGGER.info("Bundle Id: " + bundle.getId());
            ids.add(bundle.getId());
        }
        return ids;
    }

    public static List<Job.Status> getBundleStatuses(List<BundleJob> bundles) {
        List<Job.Status> statuses = new ArrayList<>();
        for (BundleJob bundle : bundles) {
            LOGGER.info("bundle: " + bundle);
            statuses.add(bundle.getStatus());
        }
        return statuses;
    }

    public static String getMaxId(List<String> ids) {
        String oozieId = ids.get(0);
        int maxInt = Integer.valueOf(oozieId.split("-")[0]);
        for (int i = 1; i < ids.size(); i++) {
            String currentId = ids.get(i);
            int currInt = Integer.valueOf(currentId.split("-")[0]);
            if (currInt > maxInt) {
                oozieId = currentId;
            }
        }
        return oozieId;
    }

    public static String getMinId(List<String> ids) {
        String oozieId = ids.get(0);
        int minInt = Integer.valueOf(oozieId.split("-")[0]);
        for (int i = 1; i < ids.size(); i++) {
            String currentId = ids.get(i);
            int currInt = Integer.valueOf(currentId.split("-")[0]);
            if (currInt < minInt) {
                oozieId = currentId;
            }
        }
        return oozieId;
    }

    /**
     * @param bundleID bundle number
     * @param oozieClient oozie client
     * @return list of action ids of the succeeded retention workflow
     * @throws OozieClientException
     */
    public static List<String> waitForRetentionWorkflowToSucceed(String bundleID, OozieClient oozieClient)
            throws OozieClientException {
        LOGGER.info("Connecting to oozie: " + oozieClient.getOozieUrl());
        List<String> jobIds = new ArrayList<>();
        LOGGER.info("using bundleId:" + bundleID);
        waitForCoordinatorJobCreation(oozieClient, bundleID);
        final String coordinatorId = oozieClient.getBundleJobInfo(bundleID).getCoordinators().get(0).getId();
        LOGGER.info("using coordinatorId: " + coordinatorId);

        for (int i = 0; i < 120 && oozieClient.getCoordJobInfo(coordinatorId).getActions().isEmpty(); ++i) {
            TimeUtil.sleepSeconds(4);
        }
        Assert.assertFalse(oozieClient.getCoordJobInfo(coordinatorId).getActions().isEmpty(),
                "Coordinator actions should have got created by now.");

        CoordinatorAction action = oozieClient.getCoordJobInfo(coordinatorId).getActions().get(0);
        for (int i = 0; i < 180; ++i) {
            CoordinatorAction actionInfo = oozieClient.getCoordActionInfo(action.getId());
            LOGGER.info("actionInfo: " + actionInfo);
            if (EnumSet.of(CoordinatorAction.Status.SUCCEEDED, CoordinatorAction.Status.KILLED,
                    CoordinatorAction.Status.FAILED).contains(actionInfo.getStatus())) {
                break;
            }
            TimeUtil.sleepSeconds(10);
        }
        Assert.assertEquals(oozieClient.getCoordActionInfo(action.getId()).getStatus(),
                CoordinatorAction.Status.SUCCEEDED, "Action did not succeed.");
        jobIds.add(action.getId());

        return jobIds;
    }

    public static void waitForCoordinatorJobCreation(OozieClient oozieClient, String bundleID)
            throws OozieClientException {
        LOGGER.info("Connecting to oozie: " + oozieClient.getOozieUrl());
        for (int i = 0; i < 60 && oozieClient.getBundleJobInfo(bundleID).getCoordinators().isEmpty(); ++i) {
            TimeUtil.sleepSeconds(2);
        }
        Assert.assertFalse(oozieClient.getBundleJobInfo(bundleID).getCoordinators().isEmpty(),
                "Coordinator job should have got created by now.");
    }

    public static Job.Status getOozieJobStatus(OozieClient client, String processName, EntityType entityType)
            throws OozieClientException {
        String filter = String.format("name=FALCON_%s_%s", entityType, processName);
        List<Job.Status> statuses = getBundleStatuses(getBundles(client, filter, 0, 10));
        if (statuses.isEmpty()) {
            return null;
        } else {
            return statuses.get(0);
        }
    }

    public static List<String> getBundles(OozieClient client, String entityName, EntityType entityType)
            throws OozieClientException {
        String filter = "name=FALCON_" + entityType + "_" + entityName;
        return getBundleIds(getBundles(client, filter, 0, 10));
    }

    public static List<DateTime> getStartTimeForRunningCoordinators(ColoHelper prismHelper, String bundleID)
            throws OozieClientException {
        List<DateTime> startTimes = new ArrayList<>();

        OozieClient oozieClient = prismHelper.getClusterHelper().getOozieClient();
        BundleJob bundleJob = oozieClient.getBundleJobInfo(bundleID);
        CoordinatorJob jobInfo;

        for (CoordinatorJob job : bundleJob.getCoordinators()) {

            if (job.getAppName().contains("DEFAULT")) {
                jobInfo = oozieClient.getCoordJobInfo(job.getId());
                for (CoordinatorAction action : jobInfo.getActions()) {
                    DateTime temp = new DateTime(action.getCreatedTime(), DateTimeZone.UTC);
                    LOGGER.info(temp);
                    startTimes.add(temp);
                }
            }

            Collections.sort(startTimes);

            if (!(startTimes.isEmpty())) {
                return startTimes;
            }
        }

        return null;
    }

    public static boolean verifyOozieJobStatus(OozieClient client, String processName, EntityType entityType,
            Job.Status expectedStatus) throws OozieClientException {
        for (int seconds = 0; seconds < 100; seconds += 5) {
            Job.Status status = getOozieJobStatus(client, processName, entityType);
            LOGGER.info("Current status: " + status);
            if (status == expectedStatus) {
                return true;
            }
            TimeUtil.sleepSeconds(5);
        }
        return false;
    }

    public static List<String> getMissingDependencies(OozieClient oozieClient, String bundleID)
            throws OozieClientException {
        CoordinatorJob jobInfo;
        jobInfo = null;
        BundleJob bundleJob = oozieClient.getBundleJobInfo(bundleID);
        List<CoordinatorJob> coordinatorJobList = bundleJob.getCoordinators();
        if (coordinatorJobList.size() > 1) {

            for (CoordinatorJob coord : bundleJob.getCoordinators()) {
                LOGGER.info("Appname is : " + coord.getAppName());
                if ((coord.getAppName().contains("DEFAULT") && coord.getAppName().contains("PROCESS"))
                        || (coord.getAppName().contains("REPLICATION") && coord.getAppName().contains("FEED"))) {
                    jobInfo = oozieClient.getCoordJobInfo(coord.getId());
                } else {
                    LOGGER.info("Desired coord does not exists on " + oozieClient.getOozieUrl());
                }
            }

        } else {
            jobInfo = oozieClient.getCoordJobInfo(bundleJob.getCoordinators().get(0).getId());
        }

        LOGGER.info("Coordinator id : " + jobInfo);
        List<CoordinatorAction> actions = null;
        if (jobInfo != null) {
            actions = jobInfo.getActions();
        }

        if (actions != null) {
            if (actions.size() < 1) {
                return null;
            }
        }
        if (actions != null) {
            LOGGER.info("conf from event: " + actions.get(0).getMissingDependencies());
        }

        String[] missingDependencies = new String[0];
        if (actions != null) {
            missingDependencies = actions.get(0).getMissingDependencies().split("#");
        }
        return new ArrayList<>(Arrays.asList(missingDependencies));
    }

    public static List<String> getWorkflowJobs(OozieClient oozieClient, String bundleID)
            throws OozieClientException {
        waitForCoordinatorJobCreation(oozieClient, bundleID);
        List<String> workflowIds = new ArrayList<>();
        List<CoordinatorJob> coordJobs = oozieClient.getBundleJobInfo(bundleID).getCoordinators();
        CoordinatorJob coordJobInfo = oozieClient.getCoordJobInfo(coordJobs.get(0).getId());

        for (CoordinatorAction action : coordJobInfo.getActions()) {
            workflowIds.add(action.getExternalId());
        }
        return workflowIds;
    }

    public static List<String> getWorkflow(OozieClient oozieClient, String bundleID) throws OozieClientException {
        waitForCoordinatorJobCreation(oozieClient, bundleID);
        List<String> workflowIds = new ArrayList<>();
        String coordId = getDefaultCoordIDFromBundle(oozieClient, bundleID);
        CoordinatorJob coordJobInfo = oozieClient.getCoordJobInfo(coordId);
        for (CoordinatorAction action : coordJobInfo.getActions()) {
            if (action.getStatus().name().equals("RUNNING") || action.getStatus().name().equals("SUCCEEDED")) {
                workflowIds.add(action.getExternalId());
            }
            if (action.getStatus().name().equals("KILLED") || action.getStatus().name().equals("WAITING")) {
                Assert.assertNull(action.getExternalId());
            }
        }
        return workflowIds;
    }

    public static Date getNominalTime(OozieClient oozieClient, String bundleID) throws OozieClientException {
        BundleJob bundleJob = oozieClient.getBundleJobInfo(bundleID);
        CoordinatorJob jobInfo = oozieClient.getCoordJobInfo(bundleJob.getCoordinators().get(0).getId());
        List<CoordinatorAction> actions = jobInfo.getActions();
        return actions.get(0).getNominalTime();
    }

    public static CoordinatorJob getDefaultOozieCoord(OozieClient oozieClient, String bundleId, EntityType type)
            throws OozieClientException {
        BundleJob bundlejob = oozieClient.getBundleJobInfo(bundleId);
        for (CoordinatorJob coord : bundlejob.getCoordinators()) {
            if ((coord.getAppName().contains("DEFAULT") && EntityType.PROCESS == type)
                    || (coord.getAppName().contains("REPLICATION") && EntityType.FEED == type)) {
                return oozieClient.getCoordJobInfo(coord.getId());
            } else {
                LOGGER.info("Desired coord does not exists on " + oozieClient.getOozieUrl());
            }
        }
        return null;
    }

    public static int getNumberOfWorkflowInstances(OozieClient oozieClient, String bundleId)
            throws OozieClientException {
        return getDefaultOozieCoord(oozieClient, bundleId, EntityType.PROCESS).getActions().size();
    }

    public static List<String> getActionsNominalTime(OozieClient oozieClient, String bundleId, EntityType type)
            throws OozieClientException {
        Map<Date, CoordinatorAction.Status> actions = getActionsNominalTimeAndStatus(oozieClient, bundleId, type);
        List<String> nominalTime = new ArrayList<>();
        for (Date date : actions.keySet()) {
            nominalTime.add(date.toString());
        }
        return nominalTime;
    }

    public static Map<Date, CoordinatorAction.Status> getActionsNominalTimeAndStatus(OozieClient oozieClient,
            String bundleId, EntityType type) throws OozieClientException {
        Map<Date, CoordinatorAction.Status> result = new TreeMap<>();
        List<CoordinatorAction> actions = getDefaultOozieCoord(oozieClient, bundleId, type).getActions();
        for (CoordinatorAction action : actions) {
            result.put(action.getNominalTime(), action.getStatus());
        }
        return result;
    }

    public static boolean isBundleOver(ColoHelper coloHelper, String bundleId) throws OozieClientException {
        OozieClient client = coloHelper.getClusterHelper().getOozieClient();

        BundleJob bundleJob = client.getBundleJobInfo(bundleId);

        if (EnumSet.of(BundleJob.Status.DONEWITHERROR, BundleJob.Status.FAILED, BundleJob.Status.SUCCEEDED,
                BundleJob.Status.KILLED).contains(bundleJob.getStatus())) {
            return true;
        }
        TimeUtil.sleepSeconds(20);
        return false;
    }

    public static void verifyNewBundleCreation(OozieClient oozieClient, String originalBundleId,
            List<String> initialNominalTimes, String entity, boolean shouldBeCreated, boolean matchInstances)
            throws OozieClientException {
        String entityName = Util.readEntityName(entity);
        EntityType entityType = Util.getEntityType(entity);
        String newBundleId = getLatestBundleID(oozieClient, entityName, entityType);
        if (shouldBeCreated) {
            Assert.assertTrue(!newBundleId.equalsIgnoreCase(originalBundleId),
                    "eeks! new bundle is not getting created!!!!");
            LOGGER.info("old bundleId=" + originalBundleId + " on oozie: " + oozieClient);
            LOGGER.info("new bundleId=" + newBundleId + " on oozie: " + oozieClient);
            if (matchInstances) {
                validateNumberOfWorkflowInstances(oozieClient, initialNominalTimes, originalBundleId, newBundleId,
                        entityType);
            }
        } else {
            Assert.assertEquals(newBundleId, originalBundleId, "eeks! new bundle is getting created!!!!");
        }
    }

    private static void validateNumberOfWorkflowInstances(OozieClient oozieClient, List<String> initialNominalTimes,
            String originalBundleId, String newBundleId, EntityType type) throws OozieClientException {
        List<String> nominalTimesOriginalAndNew = getActionsNominalTime(oozieClient, originalBundleId, type);
        nominalTimesOriginalAndNew.addAll(getActionsNominalTime(oozieClient, newBundleId, type));
        initialNominalTimes.removeAll(nominalTimesOriginalAndNew);
        if (initialNominalTimes.size() != 0) {
            LOGGER.info("Missing instance are : " + initialNominalTimes);
            LOGGER.debug("Original Bundle ID   : " + originalBundleId);
            LOGGER.debug("New Bundle ID        : " + newBundleId);
            Assert.fail("some instances have gone missing after update");
        }
    }

    public static String getCoordStartTime(OozieClient oozieClient, String entity, int bundleNo)
            throws OozieClientException {
        String bundleID = getSequenceBundleID(oozieClient, Util.readEntityName(entity), Util.getEntityType(entity),
                bundleNo);
        CoordinatorJob coord = getDefaultOozieCoord(oozieClient, bundleID, Util.getEntityType(entity));
        return TimeUtil.dateToOozieDate(coord.getStartTime());
    }

    public static DateTimeFormatter getOozieDateTimeFormatter() {
        return DateTimeFormat.forPattern("yyyy'-'MM'-'dd'T'HH':'mm'Z'");
    }

    public static int getNumberOfBundle(OozieClient oozieClient, EntityType type, String entityName)
            throws OozieClientException {
        return OozieUtil.getBundles(oozieClient, entityName, type).size();
    }

    public static void createMissingDependencies(ColoHelper helper, EntityType type, String entityName,
            int bundleNumber, int instanceNumber) throws OozieClientException, IOException {
        final OozieClient oozieClient = helper.getClusterHelper().getOozieClient();
        String bundleID = getSequenceBundleID(oozieClient, entityName, type, bundleNumber);
        List<CoordinatorJob> coords = oozieClient.getBundleJobInfo(bundleID).getCoordinators();
        final List<String> missingDependencies = getMissingDependenciesForInstance(oozieClient, coords,
                instanceNumber);
        HadoopUtil.createFolders(helper.getClusterHelper().getHadoopFS(), helper.getPrefix(), missingDependencies);
    }

    private static List<String> getMissingDependenciesForInstance(OozieClient oozieClient,
            List<CoordinatorJob> coords, int instanceNumber) throws OozieClientException {
        ArrayList<String> missingPaths = new ArrayList<>();
        for (CoordinatorJob coord : coords) {
            CoordinatorJob temp = oozieClient.getCoordJobInfo(coord.getId());
            CoordinatorAction instance = temp.getActions().get(instanceNumber);
            missingPaths.addAll(Arrays.asList(instance.getMissingDependencies().split("#")));
        }
        return missingPaths;
    }

    public static List<List<String>> createMissingDependencies(ColoHelper helper, EntityType type,
            String entityName, int bundleNumber) throws OozieClientException, IOException {
        final OozieClient oozieClient = helper.getClusterHelper().getOozieClient();
        String bundleID = getSequenceBundleID(oozieClient, entityName, type, bundleNumber);
        return createMissingDependenciesForBundle(helper, bundleID);
    }

    public static List<List<String>> createMissingDependenciesForBundle(ColoHelper helper, String bundleId)
            throws OozieClientException, IOException {
        OozieClient oozieClient = helper.getClusterHelper().getOozieClient();
        List<CoordinatorJob> coords = oozieClient.getBundleJobInfo(bundleId).getCoordinators();
        List<List<String>> missingDependencies = getMissingDependenciesForBundle(oozieClient, coords);
        for (List<String> missingDependencyPerInstance : missingDependencies) {
            HadoopUtil.createFolders(helper.getClusterHelper().getHadoopFS(), helper.getPrefix(),
                    missingDependencyPerInstance);
        }
        return missingDependencies;
    }

    private static List<List<String>> getMissingDependenciesForBundle(OozieClient oozieClient,
            List<CoordinatorJob> coords) throws OozieClientException, IOException {
        List<List<String>> missingDependencies = new ArrayList<>();
        for (CoordinatorJob coord : coords) {
            CoordinatorJob temp = oozieClient.getCoordJobInfo(coord.getId());
            for (int instanceNumber = 0; instanceNumber < temp.getActions().size(); instanceNumber++) {
                CoordinatorAction instance = temp.getActions().get(instanceNumber);
                missingDependencies.add(Arrays.asList(instance.getMissingDependencies().split("#")));
            }
        }
        return missingDependencies;
    }

    public static void validateRetryAttempts(OozieClient oozieClient, String bundleId, EntityType type,
            int attempts) throws OozieClientException {
        CoordinatorJob coord = getDefaultOozieCoord(oozieClient, bundleId, type);
        int actualRun = oozieClient.getJobInfo(coord.getActions().get(0).getExternalId()).getRun();
        LOGGER.info("Actual run count: " + actualRun); // wrt 0
        Assert.assertEquals(actualRun, attempts, "Rerun attempts did not match");
    }

    /**
     * Try to find feed coordinators of given type.
     */
    public static int checkIfFeedCoordExist(OozieClient oozieClient, String feedName, String coordType)
            throws OozieClientException {
        return checkIfFeedCoordExist(oozieClient, feedName, coordType, 5);
    }

    /**
     * Try to find feed coordinators of given type given number of times.
     */
    public static int checkIfFeedCoordExist(OozieClient oozieClient, String feedName, String coordType,
            int numberOfRetries) throws OozieClientException {
        LOGGER.info("feedName: " + feedName);
        for (int retryAttempt = 0; retryAttempt < numberOfRetries; retryAttempt++) {
            int numberOfCoord = 0;
            List<String> bundleIds = getBundles(oozieClient, feedName, EntityType.FEED);
            if (bundleIds.size() == 0) {
                TimeUtil.sleepSeconds(4);
                continue;
            }
            LOGGER.info("bundleIds: " + bundleIds);
            for (String aBundleId : bundleIds) {
                LOGGER.info("aBundleId: " + aBundleId);
                waitForCoordinatorJobCreation(oozieClient, aBundleId);
                List<CoordinatorJob> coords = getBundleCoordinators(oozieClient, aBundleId);
                LOGGER.info("coords: " + coords);
                for (CoordinatorJob coord : coords) {
                    if (coord.getAppName().contains(coordType)) {
                        numberOfCoord++;
                    }
                }
            }
            if (numberOfCoord > 0) {
                return numberOfCoord;
            }
            TimeUtil.sleepSeconds(4);
        }
        return 0;
    }

    /**
     * Retrieves replication coordinatorID from bundle of coordinators.
     */
    public static List<String> getReplicationCoordID(String bundleId, AbstractEntityHelper helper)
            throws OozieClientException {
        final OozieClient oozieClient = helper.getOozieClient();
        List<CoordinatorJob> coords = getBundleCoordinators(oozieClient, bundleId);
        List<String> replicationCoordID = new ArrayList<>();
        for (CoordinatorJob coord : coords) {
            if (coord.getAppName().contains("FEED_REPLICATION")) {
                replicationCoordID.add(coord.getId());
            }
        }
        return replicationCoordID;
    }

    /**
     * Retrieves ID of bundle related to some process/feed using its ordinal number.
     *
     * @param entityName   - name of entity bundle is related to
     * @param entityType   - feed or process
     * @param bundleNumber - ordinal number of bundle
     * @return bundle ID
     * @throws org.apache.oozie.client.OozieClientException
     */
    public static String getSequenceBundleID(OozieClient oozieClient, String entityName, EntityType entityType,
            int bundleNumber) throws OozieClientException {
        //sequence start from 0
        List<String> bundleIds = getBundles(oozieClient, entityName, entityType);
        Collections.sort(bundleIds);
        if (bundleNumber < bundleIds.size()) {
            return bundleIds.get(bundleNumber);
        }
        return null;
    }

    /**
     * Retrieves the latest bundle ID.
     *
     * @param oozieClient where job is running
     * @param entityName  name of entity job is related to
     * @param entityType  type of entity - feed or process expected
     * @return latest bundle ID
     * @throws org.apache.oozie.client.OozieClientException
     */
    public static String getLatestBundleID(OozieClient oozieClient, String entityName, EntityType entityType)
            throws OozieClientException {
        List<String> bundleIds = getBundles(oozieClient, entityName, entityType);
        String max = "0";
        int maxID = -1;
        for (String strID : bundleIds) {
            if (maxID < Integer.parseInt(strID.substring(0, strID.indexOf('-')))) {
                maxID = Integer.parseInt(strID.substring(0, strID.indexOf('-')));
                max = strID;
            }
        }
        return max;
    }

    /**
     * Retrieves all coordinators of bundle.
     *
     * @param oozieClient Oozie client to use for fetching info.
     * @param bundleID specific bundle ID
     * @return list of bundle coordinators
     * @throws org.apache.oozie.client.OozieClientException
     */
    public static List<CoordinatorJob> getBundleCoordinators(OozieClient oozieClient, String bundleID)
            throws OozieClientException {
        BundleJob bundleInfo = oozieClient.getBundleJobInfo(bundleID);
        return bundleInfo.getCoordinators();
    }

    public static Job.Status getDefaultCoordinatorStatus(OozieClient oozieClient, String processName,
            int bundleNumber) throws OozieClientException {
        String bundleID = getSequenceBundleID(oozieClient, processName, EntityType.PROCESS, bundleNumber);
        String coordId = getDefaultCoordIDFromBundle(oozieClient, bundleID);
        return oozieClient.getCoordJobInfo(coordId).getStatus();
    }

    public static String getDefaultCoordIDFromBundle(OozieClient oozieClient, String bundleId)
            throws OozieClientException {
        waitForCoordinatorJobCreation(oozieClient, bundleId);
        BundleJob bundleInfo = oozieClient.getBundleJobInfo(bundleId);
        List<CoordinatorJob> coords = bundleInfo.getCoordinators();
        int min = 100000;
        String minString = "";
        for (CoordinatorJob coord : coords) {
            String strID = coord.getId();
            if (min > Integer.parseInt(strID.substring(0, strID.indexOf('-')))) {
                min = Integer.parseInt(strID.substring(0, strID.indexOf('-')));
                minString = coord.getId();
            }
        }
        LOGGER.info("function getDefaultCoordIDFromBundle: minString: " + minString);
        return minString;
    }

    public static String getLatestCoordinatorID(OozieClient oozieClient, String processName, EntityType entityType)
            throws OozieClientException {
        final String latestBundleID = getLatestBundleID(oozieClient, processName, entityType);
        return getDefaultCoordIDFromBundle(oozieClient, latestBundleID);
    }

    /**
     * Waits till bundle job will reach expected status.
     * Generates time according to expected status.
     *
     * @param oozieClient    oozieClient of cluster job is running on
     * @param processName    name of process which job is being analyzed
     * @param expectedStatus job status we are waiting for
     * @throws org.apache.oozie.client.OozieClientException
     */
    public static void waitForBundleToReachState(OozieClient oozieClient, String processName,
            Job.Status expectedStatus) throws OozieClientException {
        int totalMinutesToWait = getMinutesToWait(expectedStatus);
        waitForBundleToReachState(oozieClient, processName, expectedStatus, totalMinutesToWait);
    }

    /**
     * Waits till bundle job will reach expected status during specific time.
     * Use it directly in test cases when timeouts are different from trivial, in other cases use
     * waitForBundleToReachState(OozieClient, String, Status)
     *
     * @param oozieClient        oozie client of cluster job is running on
     * @param processName        name of process which job is being analyzed
     * @param expectedStatus     job status we are waiting for
     * @param totalMinutesToWait specific time to wait expected state
     * @throws org.apache.oozie.client.OozieClientException
     */
    public static void waitForBundleToReachState(OozieClient oozieClient, String processName,
            Job.Status expectedStatus, int totalMinutesToWait) throws OozieClientException {
        int sleep = totalMinutesToWait * 60 / 20;
        for (int sleepCount = 0; sleepCount < sleep; sleepCount++) {
            String bundleID = getLatestBundleID(oozieClient, processName, EntityType.PROCESS);
            BundleJob j = oozieClient.getBundleJobInfo(bundleID);
            LOGGER.info(sleepCount + ". Current status: " + j.getStatus() + "; expected: " + expectedStatus);
            if (j.getStatus() == expectedStatus) {
                return;
            }
            TimeUtil.sleepSeconds(20);
        }
        Assert.fail("State " + expectedStatus + " wasn't reached in " + totalMinutesToWait + " mins");
    }

    /**
     * Generates time which is presumably needed for bundle job to reach particular state.
     *
     * @param expectedStatus status which we are expect to get from bundle job
     * @return minutes to wait for expected status
     */
    private static int getMinutesToWait(Job.Status expectedStatus) {
        switch (expectedStatus) {
        case DONEWITHERROR:
        case SUCCEEDED:
            return OSUtil.IS_WINDOWS ? 40 : 20;
        case KILLED:
            return OSUtil.IS_WINDOWS ? 30 : 15;
        default:
            return OSUtil.IS_WINDOWS ? 60 : 30;
        }
    }

    public static String getActionStatus(OozieClient oozieClient, String workflowId, String actionName)
            throws OozieClientException {
        List<WorkflowAction> wfAction = oozieClient.getJobInfo(workflowId).getActions();
        for (WorkflowAction wf : wfAction) {
            if (wf.getName().contains(actionName)) {
                return wf.getExternalStatus();
            }
        }
        return "";
    }

    public static String getWorkflowActionStatus(OozieClient oozieClient, String bundleId, String actionName)
            throws OozieClientException {
        List<String> workflowIds = getWorkflowJobs(oozieClient, bundleId);
        if (workflowIds.get(0).isEmpty()) {
            return FAIL_MSG;
        }
        return getActionStatus(oozieClient, workflowIds.get(0), actionName);
    }

    public static String getSubWorkflowActionStatus(OozieClient oozieClient, String bundleId, String actionName,
            String subAction) throws OozieClientException {
        List<String> workflowIds = getWorkflowJobs(oozieClient, bundleId);
        if (workflowIds.get(0).isEmpty()) {
            return FAIL_MSG;
        }

        String wid = "";
        List<WorkflowAction> wfAction = oozieClient.getJobInfo(workflowIds.get(0)).getActions();
        for (WorkflowAction wf : wfAction) {
            if (wf.getName().contains(actionName)) {
                wid = wf.getExternalId();
            }
        }

        if (!wid.isEmpty()) {
            return getActionStatus(oozieClient, wid, subAction);
        }
        return FAIL_MSG;
    }

    /**
     * Returns configuration object of a given bundleID for a given instanceTime.
     *
     * @param oozieClient  oozie client of cluster job is running on
     * @param bundleID     bundleID of given cluster
     * @param time         instanceTime
     * @throws org.apache.oozie.client.OozieClientException
     * @throws org.json.JSONException
     */
    public static Configuration getProcessConf(OozieClient oozieClient, String bundleID, String time)
            throws OozieClientException, JSONException {
        waitForCoordinatorJobCreation(oozieClient, bundleID);
        List<CoordinatorJob> coordJobs = oozieClient.getBundleJobInfo(bundleID).getCoordinators();
        CoordinatorJob coordJobInfo = oozieClient.getCoordJobInfo(coordJobs.get(0).getId());

        Configuration conf = new Configuration();
        for (CoordinatorAction action : coordJobInfo.getActions()) {
            String dateStr = (new DateTime(action.getNominalTime(), DateTimeZone.UTC)).toString();
            if (!dateStr.isEmpty() && dateStr.contains(time.replace("Z", ""))) {
                conf.addResource(new ByteArrayInputStream(
                        oozieClient.getJobInfo(action.getExternalId()).getConf().getBytes()));
            }
        }
        return conf;
    }

    /**
     * Method retrieves and parses replication coordinator action workflow definition and checks whether specific
     * properties are present in list of workflow args or not.
     * @param workflowDefinition workflow definition
     * @param actionName action within workflow, e.g pre-processing, replication etc.
     * @param propMap specific properties which are expected to be in arg list
     * @return true if all keys and values are present, false otherwise
     */
    public static boolean propsArePresentInWorkflow(String workflowDefinition, String actionName,
            HashMap<String, String> propMap) {
        //get action definition
        Document definition = Util.convertStringToDocument(workflowDefinition);
        Assert.assertNotNull(definition, "Workflow definition shouldn't be null.");
        NodeList actions = definition.getElementsByTagName("action");
        Element action = null;
        for (int i = 0; i < actions.getLength(); i++) {
            Node node = actions.item(i);
            if (node.getNodeType() == Node.ELEMENT_NODE) {
                action = (Element) node;
                if (action.getAttribute("name").equals(actionName)) {
                    break;
                }
                action = null;
            }
        }
        Assert.assertNotNull(action, actionName + " action not found.");

        //retrieve and checks whether properties are present in workflow args
        Element javaElement = (Element) action.getElementsByTagName("java").item(0);
        NodeList args = javaElement.getElementsByTagName("arg");
        int counter = 0;
        String key = null;
        for (int i = 0; i < args.getLength(); i++) {
            Node node = args.item(i);
            if (node.getNodeType() == Node.ELEMENT_NODE) {
                String argKey = node.getTextContent().replace("-", "");
                if (key != null && propMap.get(key).equals(argKey)) {
                    counter++;
                    key = null;
                } else if (key == null && propMap.containsKey(argKey)) {
                    key = argKey;
                }
            }
        }
        return counter == propMap.size();
    }

    /**
     * Returns configuration object of a given bundleID for a given retentionFeed.
     *
     * @param oozieClient  oozie client of cluster job is running on
     * @param bundleID     bundleID of given cluster
     * @throws OozieClientException
     */
    public static Configuration getRetentionConfiguration(OozieClient oozieClient, String bundleID)
            throws OozieClientException {
        waitForCoordinatorJobCreation(oozieClient, bundleID);
        CoordinatorJob coord = null;
        List<CoordinatorJob> coordJobs = oozieClient.getBundleJobInfo(bundleID).getCoordinators();
        for (CoordinatorJob coordinatorJob : coordJobs) {
            if (coordinatorJob.getAppName().startsWith("FALCON_FEED_RETENTION")) {
                coord = oozieClient.getCoordJobInfo(coordinatorJob.getId());
            }
        }

        Configuration configuration = new Configuration();
        if (coord != null) {
            WorkflowJob wid = oozieClient.getJobInfo(coord.getActions().get(0).getExternalId());
            configuration.addResource(new ByteArrayInputStream(wid.getConf().getBytes()));
        } else {
            configuration = null;
        }

        return configuration;
    }
}