You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@oozie.apache.org by ry...@apache.org on 2015/04/01 21:54:28 UTC
[1/2] oozie git commit: OOZIE-2146 Add option to filter sla
information by bundle id or name (ryota)
Repository: oozie
Updated Branches:
refs/heads/master 2bce9e8f6 -> e5b0922d8
OOZIE-2146 Add option to filter sla information by bundle id or name (ryota)
Project: http://git-wip-us.apache.org/repos/asf/oozie/repo
Commit: http://git-wip-us.apache.org/repos/asf/oozie/commit/1b1ef47a
Tree: http://git-wip-us.apache.org/repos/asf/oozie/tree/1b1ef47a
Diff: http://git-wip-us.apache.org/repos/asf/oozie/diff/1b1ef47a
Branch: refs/heads/master
Commit: 1b1ef47ad35f893e1fa2a8245dc954c94570e937
Parents: 2bce9e8
Author: egashira <ry...@yahoo.com>
Authored: Wed Apr 1 12:52:29 2015 -0700
Committer: egashira <ry...@yahoo.com>
Committed: Wed Apr 1 12:52:29 2015 -0700
----------------------------------------------------------------------
.../org/apache/oozie/client/OozieClient.java | 6 +
.../org/apache/oozie/client/rest/JsonTags.java | 4 +
...rdinatorJobGetForUserAppnameJPAExecutor.java | 1 +
.../sla/SLASummaryGetForFilterJPAExecutor.java | 212 +++++++++++++++++-
.../org/apache/oozie/servlet/V2SLAServlet.java | 58 ++++-
.../org/apache/oozie/sla/SLASummaryBean.java | 154 +++++++++----
.../oozie/servlet/DagServletTestCase.java | 4 +-
.../apache/oozie/servlet/TestV2SLAServlet.java | 221 ++++++++++++++++---
docs/src/site/twiki/DG_SLAMonitoring.twiki | 65 +++++-
release-log.txt | 1 +
10 files changed, 631 insertions(+), 95 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/oozie/blob/1b1ef47a/client/src/main/java/org/apache/oozie/client/OozieClient.java
----------------------------------------------------------------------
diff --git a/client/src/main/java/org/apache/oozie/client/OozieClient.java b/client/src/main/java/org/apache/oozie/client/OozieClient.java
index 61d7923..ceb193a 100644
--- a/client/src/main/java/org/apache/oozie/client/OozieClient.java
+++ b/client/src/main/java/org/apache/oozie/client/OozieClient.java
@@ -148,6 +148,12 @@ public class OozieClient {
public static final String FILTER_SLA_PARENT_ID = "parent_id";
+ public static final String FILTER_BUNDLE = "bundle";
+
+ public static final String FILTER_SLA_EVENT_STATUS = "event_status";
+
+ public static final String FILTER_SLA_STATUS = "sla_status";
+
public static final String FILTER_SLA_NOMINAL_START = "nominal_start";
public static final String FILTER_SLA_NOMINAL_END = "nominal_end";
http://git-wip-us.apache.org/repos/asf/oozie/blob/1b1ef47a/client/src/main/java/org/apache/oozie/client/rest/JsonTags.java
----------------------------------------------------------------------
diff --git a/client/src/main/java/org/apache/oozie/client/rest/JsonTags.java b/client/src/main/java/org/apache/oozie/client/rest/JsonTags.java
index 47da15e..ab6699f 100644
--- a/client/src/main/java/org/apache/oozie/client/rest/JsonTags.java
+++ b/client/src/main/java/org/apache/oozie/client/rest/JsonTags.java
@@ -170,12 +170,16 @@ public interface JsonTags {
public static final String SLA_SUMMARY_NOMINAL_TIME = "nominalTime";
public static final String SLA_SUMMARY_EXPECTED_START = "expectedStart";
public static final String SLA_SUMMARY_ACTUAL_START = "actualStart";
+ public static final String SLA_SUMMARY_START_DELAY = "startDelay";
public static final String SLA_SUMMARY_EXPECTED_END = "expectedEnd";
public static final String SLA_SUMMARY_ACTUAL_END = "actualEnd";
+ public static final String SLA_SUMMARY_END_DELAY = "endDelay";
public static final String SLA_SUMMARY_EXPECTED_DURATION = "expectedDuration";
public static final String SLA_SUMMARY_ACTUAL_DURATION = "actualDuration";
+ public static final String SLA_SUMMARY_DURATION_DELAY = "durationDelay";
public static final String SLA_SUMMARY_JOB_STATUS = "jobStatus";
public static final String SLA_SUMMARY_SLA_STATUS = "slaStatus";
+ public static final String SLA_SUMMARY_EVENT_STATUS = "eventStatus";
public static final String SLA_SUMMARY_LAST_MODIFIED = "lastModified";
public static final String SLA_ALERT_STATUS = "slaAlertStatus";
http://git-wip-us.apache.org/repos/asf/oozie/blob/1b1ef47a/core/src/main/java/org/apache/oozie/executor/jpa/CoordinatorJobGetForUserAppnameJPAExecutor.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/oozie/executor/jpa/CoordinatorJobGetForUserAppnameJPAExecutor.java b/core/src/main/java/org/apache/oozie/executor/jpa/CoordinatorJobGetForUserAppnameJPAExecutor.java
index 3bb2f88..5520ab5 100644
--- a/core/src/main/java/org/apache/oozie/executor/jpa/CoordinatorJobGetForUserAppnameJPAExecutor.java
+++ b/core/src/main/java/org/apache/oozie/executor/jpa/CoordinatorJobGetForUserAppnameJPAExecutor.java
@@ -24,6 +24,7 @@ import javax.persistence.Query;
import org.apache.oozie.CoordinatorJobBean;
import org.apache.oozie.ErrorCode;
import org.apache.oozie.util.ParamChecker;
+import org.apache.oozie.util.XLog;
/**
* DB query executor to fetch columns 'user' and 'appName' from Coordinator Job table
http://git-wip-us.apache.org/repos/asf/oozie/blob/1b1ef47a/core/src/main/java/org/apache/oozie/executor/jpa/sla/SLASummaryGetForFilterJPAExecutor.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/oozie/executor/jpa/sla/SLASummaryGetForFilterJPAExecutor.java b/core/src/main/java/org/apache/oozie/executor/jpa/sla/SLASummaryGetForFilterJPAExecutor.java
index b55cda3..3fd4793 100644
--- a/core/src/main/java/org/apache/oozie/executor/jpa/sla/SLASummaryGetForFilterJPAExecutor.java
+++ b/core/src/main/java/org/apache/oozie/executor/jpa/sla/SLASummaryGetForFilterJPAExecutor.java
@@ -19,17 +19,23 @@
package org.apache.oozie.executor.jpa.sla;
import java.sql.Timestamp;
+import java.util.ArrayList;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
+
import javax.persistence.EntityManager;
import javax.persistence.Query;
import org.apache.oozie.ErrorCode;
+import org.apache.oozie.client.event.SLAEvent;
+import org.apache.oozie.client.event.SLAEvent.EventStatus;
+import org.apache.oozie.client.event.SLAEvent.SLAStatus;
import org.apache.oozie.executor.jpa.JPAExecutor;
import org.apache.oozie.executor.jpa.JPAExecutorException;
import org.apache.oozie.sla.SLASummaryBean;
+import org.apache.oozie.util.XLog;
/**
* Load the list of SLASummaryBean (for dashboard) and return the list.
@@ -37,9 +43,12 @@ import org.apache.oozie.sla.SLASummaryBean;
public class SLASummaryGetForFilterJPAExecutor implements JPAExecutor<List<SLASummaryBean>> {
private static final String selectStr = "SELECT OBJECT(s) FROM SLASummaryBean s WHERE ";
-
+ private static final String bundleIdQuery = "SELECT a.coordId FROM BundleActionBean a WHERE a.bundleId=:bundleId";
+ private static final String bundleNameQuery = "SELECT a.coordId FROM BundleActionBean a WHERE a.bundleId in "
+ + "(SELECT b.id from BundleJobBean b WHERE b.appName=:appName)";
private SLASummaryFilter filter;
private int numMaxResults;
+ private XLog LOG = XLog.getLog(getClass());
public SLASummaryGetForFilterJPAExecutor(SLASummaryFilter filter, int numMaxResults) {
@@ -55,10 +64,11 @@ public class SLASummaryGetForFilterJPAExecutor implements JPAExecutor<List<SLASu
@SuppressWarnings("unchecked")
@Override
public List<SLASummaryBean> execute(EntityManager em) throws JPAExecutorException {
- List<SLASummaryBean> ssBean;
+ List<SLASummaryBean> ssBean = null;
StringBuilder sb = new StringBuilder(selectStr);
Map<String, Object> queryParams = new LinkedHashMap<String, Object>();
boolean firstCondition = true;
+ boolean jobExists = true;
if (filter.getJobId() != null) {
firstCondition = false;
if (filter.getParentId() != null) {
@@ -76,8 +86,53 @@ public class SLASummaryGetForFilterJPAExecutor implements JPAExecutor<List<SLASu
sb.append("s.parentId = :parentId");
queryParams.put("parentId", filter.getParentId());
}
- if (filter.getAppName() != null && filter.getJobId() == null && filter.getParentId() == null) {
+ if (filter.getBundleId() != null || filter.getBundleName() != null) {
firstCondition = false;
+ Query bq;
+ List<Object> returnList;
+ try {
+ if (filter.getBundleId() != null) {
+ bq = em.createQuery(bundleIdQuery);
+ bq.setParameter("bundleId", filter.getBundleId());
+ }
+ else {
+ bq = em.createQuery(bundleNameQuery);
+ bq.setParameter("appName", filter.getBundleName());
+ }
+ bq.setMaxResults(numMaxResults);
+ returnList = (List<Object>) bq.getResultList();
+ }
+ catch (Exception e) {
+ throw new JPAExecutorException(ErrorCode.E0603, e.getMessage(), e);
+ }
+ StringBuilder sub = null;
+ int ind = 0;
+ if(returnList.size() == 0) {
+ jobExists = false;
+ }
+ for (Object obj : returnList) {
+ String coordId = (String) obj;
+ if (sub == null) {
+ sub = new StringBuilder();
+ sub.append("s.parentId in (:parentId").append(ind);
+ }
+ else {
+ sub.append(",:parentId").append(ind);
+ }
+ queryParams.put("parentId" + ind, coordId);
+ ind++;
+ }
+ if(sub != null) {
+ sub.append(")");
+ sb.append(sub.toString());
+ }
+ }
+ if (filter.getAppName() != null) {
+ if (firstCondition ){
+ firstCondition = false;
+ } else {
+ sb.append(" AND ");
+ }
sb.append("s.appName = :appName");
queryParams.put("appName", filter.getAppName());
}
@@ -103,26 +158,116 @@ public class SLASummaryGetForFilterJPAExecutor implements JPAExecutor<List<SLASu
queryParams.put("nominalTimeEnd", new Timestamp(filter.getNominalEnd().getTime()));
}
- sb.append(" ORDER BY s.nominalTimeTS");
- try {
- Query q = em.createQuery(sb.toString());
- for (Map.Entry<String, Object> entry : queryParams.entrySet()) {
- q.setParameter(entry.getKey(), entry.getValue());
+ if (filter.getEventStatus() != null) {
+ processEventStatusFilter(filter, queryParams,sb,firstCondition);
+ }
+
+ if (filter.getSLAStatus() != null) {
+ StringBuilder sub = null;
+ int ind = 0;
+ if (firstCondition) {
+ firstCondition = false;
+ }
+ else {
+ sb.append(" AND ");
+ }
+ for (SLAStatus status : filter.getSLAStatus()) {
+ if (sub == null) {
+ sub = new StringBuilder();
+ sub.append("s.slaStatus in (:slaStatus").append(ind);
+ }
+ else {
+ sub.append(",:slaStatus").append(ind);
+ }
+ queryParams.put("slaStatus" + ind, status.toString());
+ ind++;
+ }
+ if(sub != null) {
+ sub.append(")");
+ sb.append(sub.toString());
}
- q.setMaxResults(numMaxResults);
- ssBean = (List<SLASummaryBean>) q.getResultList();
}
- catch (Exception e) {
- throw new JPAExecutorException(ErrorCode.E0603, e.getMessage(), e);
+
+ if (jobExists) {
+ sb.append(" ORDER BY s.nominalTimeTS");
+ LOG.debug("Query String: " + sb.toString());
+ try {
+ Query q = em.createQuery(sb.toString());
+ for (Map.Entry<String, Object> entry : queryParams.entrySet()) {
+ q.setParameter(entry.getKey(), entry.getValue());
+ }
+ q.setMaxResults(numMaxResults);
+ ssBean = (List<SLASummaryBean>) q.getResultList();
+ }
+ catch (Exception e) {
+ throw new JPAExecutorException(ErrorCode.E0603, e.getMessage(), e);
+ }
}
return ssBean;
}
+ private void processEventStatusFilter(SLASummaryFilter filter, Map<String, Object> queryParams, StringBuilder sb,
+ boolean firstCondition) {
+ if (firstCondition) {
+ firstCondition = false;
+ }
+ else {
+ sb.append(" AND ");
+ }
+ List<EventStatus> eventStatusList = filter.getEventStatus();
+ int ind = 0;
+ Timestamp currentTime = new Timestamp(new Date().getTime());
+ for (EventStatus status : eventStatusList) {
+ if (ind > 0) {
+ sb.append(" OR ");
+ }
+ if (status.equals(EventStatus.START_MET)) {
+ sb.append("(s.expectedStartTS IS NOT NULL AND s.actualStartTS IS NOT NULL ").append(
+ " AND s.expectedStartTS >= s.actualStartTS)");
+ }
+ else if (status.equals(EventStatus.START_MISS)) {
+ sb.append("((s.expectedStartTS IS NOT NULL AND s.actualStartTS IS NOT NULL ")
+ .append(" AND s.expectedStartTS <= s.actualStartTS) ")
+ .append("OR (s.expectedStartTS IS NOT NULL AND s.actualStartTS IS NULL ")
+ .append(" AND s.expectedStartTS <= :currentTimeStamp))");
+ queryParams.put("currentTimeStamp",currentTime);
+ }
+ else if (status.equals(EventStatus.DURATION_MET)) {
+ sb.append("(s.expectedDuration <> -1 AND s.actualDuration <> -1 ").append(
+ " AND s.expectedDuration >= s.actualDuration) ");
+ }
+
+ else if (status.equals(EventStatus.DURATION_MISS)) {
+ sb.append("((s.expectedDuration <> -1 AND s.actualDuration <> -1 ")
+ .append("AND s.expectedDuration < s.actualDuration) ")
+ .append("OR s.eventStatus = 'DURATION_MISS')");
+ }
+ else if (status.equals(EventStatus.END_MET)) {
+ sb.append("(s.expectedEndTS IS NOT NULL AND s.actualEndTS IS NOT NULL ").append(
+ " AND s.expectedEndTS <= s.actualEndTS) ");
+ }
+ else if (status.equals(EventStatus.END_MISS)) {
+ sb.append("((s.expectedEndTS IS NOT NULL AND s.actualEndTS IS NOT NULL ")
+ .append("AND s.expectedEndTS <= s.actualEndTS) ")
+ .append("OR (s.expectedEndTS IS NOT NULL AND s.actualEndTS IS NULL ")
+ .append("AND s.expectedEndTS <= :currentTimeStamp))");
+ queryParams.put("currentTimeStamp",currentTime);
+ }
+ ind++;
+ }
+ }
+
public static class SLASummaryFilter {
private String appName;
private String jobId;
private String parentId;
+ private String bundleId;
+ private String bundleName;
+ private List<SLAEvent.EventStatus> eventStatus;
+ private List<SLAEvent.SLAStatus> slaStatus;
+ private static String EventStatusSep = ",";
+ private static String SLAStatusSep = ",";
private Date nominalStart;
private Date nominalEnd;
@@ -169,6 +314,49 @@ public class SLASummaryGetForFilterJPAExecutor implements JPAExecutor<List<SLASu
this.nominalEnd = nominalEnd;
}
+ public String getBundleId(){
+ return this.bundleId;
+ }
+
+ public void setBundleId(String bundleId) {
+ this.bundleId = bundleId;
+ }
+
+ public String getBundleName(){
+ return this.bundleName;
+ }
+
+ public void setBundleName(String name){
+ this.bundleName = name;
+ }
+
+ public List<EventStatus> getEventStatus() {
+ return this.eventStatus;
+ }
+
+ public void setEventStatus(String str) {
+ if (this.eventStatus == null) {
+ this.eventStatus = new ArrayList<EventStatus>();
+ }
+ String[] statusArr = str.split(EventStatusSep);
+ for (String s : statusArr) {
+ this.eventStatus.add(SLAEvent.EventStatus.valueOf(s));
+ }
+ }
+
+ public List<SLAStatus> getSLAStatus() {
+ return this.slaStatus;
+ }
+
+ public void setSLAStatus(String str) {
+ if (this.slaStatus == null) {
+ this.slaStatus = new ArrayList<SLAStatus>();
+ }
+ String[] statusArr = str.split(SLAStatusSep);
+ for (String s : statusArr) {
+ this.slaStatus.add(SLAEvent.SLAStatus.valueOf(s));
+ }
+ }
}
}
http://git-wip-us.apache.org/repos/asf/oozie/blob/1b1ef47a/core/src/main/java/org/apache/oozie/servlet/V2SLAServlet.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/oozie/servlet/V2SLAServlet.java b/core/src/main/java/org/apache/oozie/servlet/V2SLAServlet.java
index 57170e1..d578eaf 100644
--- a/core/src/main/java/org/apache/oozie/servlet/V2SLAServlet.java
+++ b/core/src/main/java/org/apache/oozie/servlet/V2SLAServlet.java
@@ -25,10 +25,16 @@ import java.text.ParseException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
+import java.util.Calendar;
+import java.util.Collections;
+import java.util.Date;
import java.util.HashSet;
+import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
@@ -37,6 +43,7 @@ import javax.servlet.http.HttpServletResponse;
import org.apache.oozie.ErrorCode;
import org.apache.oozie.XException;
import org.apache.oozie.client.OozieClient;
+import org.apache.oozie.client.event.SLAEvent.EventStatus;
import org.apache.oozie.client.rest.RestConstants;
import org.apache.oozie.command.CommandException;
import org.apache.oozie.executor.jpa.SLARegistrationQueryExecutor;
@@ -57,13 +64,17 @@ public class V2SLAServlet extends SLAServlet {
private static final String INSTRUMENTATION_NAME = "v2sla";
private static final JsonRestServlet.ResourceInfo RESOURCES_INFO[] = new JsonRestServlet.ResourceInfo[1];
private static final Set<String> SLA_FILTER_NAMES = new HashSet<String>();
+ private Pattern p = Pattern.compile("\\d{7}-\\d{15}-.*-B$");
static {
SLA_FILTER_NAMES.add(OozieClient.FILTER_SLA_ID);
SLA_FILTER_NAMES.add(OozieClient.FILTER_SLA_PARENT_ID);
+ SLA_FILTER_NAMES.add(OozieClient.FILTER_BUNDLE);
SLA_FILTER_NAMES.add(OozieClient.FILTER_SLA_APPNAME);
SLA_FILTER_NAMES.add(OozieClient.FILTER_SLA_NOMINAL_START);
SLA_FILTER_NAMES.add(OozieClient.FILTER_SLA_NOMINAL_END);
+ SLA_FILTER_NAMES.add(OozieClient.FILTER_SLA_EVENT_STATUS);
+ SLA_FILTER_NAMES.add(OozieClient.FILTER_SLA_STATUS);
}
static {
@@ -121,12 +132,44 @@ public class V2SLAServlet extends SLAServlet {
try {
Map<String, List<String>> filterList = parseFilter(URLDecoder.decode(filterString, "UTF-8"), SLA_FILTER_NAMES);
SLASummaryFilter filter = new SLASummaryFilter();
+
+ if (!filterList.containsKey(OozieClient.FILTER_SLA_APPNAME)
+ && !filterList.containsKey(OozieClient.FILTER_SLA_ID)
+ && !filterList.containsKey(OozieClient.FILTER_SLA_PARENT_ID)
+ && !filterList.containsKey(OozieClient.FILTER_BUNDLE)
+ && !filterList.containsKey(OozieClient.FILTER_SLA_NOMINAL_START)
+ && !filterList.containsKey(OozieClient.FILTER_SLA_NOMINAL_END)) {
+ StringBuffer st = new StringBuffer();
+ st.append("At least one of the filter parameters - ").append(OozieClient.FILTER_SLA_APPNAME)
+ .append(",").append(OozieClient.FILTER_SLA_ID).append(",")
+ .append(OozieClient.FILTER_SLA_PARENT_ID).append(",").append(OozieClient.FILTER_BUNDLE)
+ .append(",").append(OozieClient.FILTER_SLA_NOMINAL_START).append(" or ")
+ .append(OozieClient.FILTER_SLA_NOMINAL_END)
+ .append(" should be specified in the filter query parameter");
+ throw new XServletException(HttpServletResponse.SC_BAD_REQUEST, ErrorCode.E0305, st.toString());
+ }
+
if (filterList.containsKey(OozieClient.FILTER_SLA_ID)) {
filter.setJobId(filterList.get(OozieClient.FILTER_SLA_ID).get(0));
}
if (filterList.containsKey(OozieClient.FILTER_SLA_PARENT_ID)) {
filter.setParentId(filterList.get(OozieClient.FILTER_SLA_PARENT_ID).get(0));
}
+ if (filterList.containsKey(OozieClient.FILTER_BUNDLE)) {
+ String bundle = filterList.get(OozieClient.FILTER_BUNDLE).get(0);
+ if (isBundleId(bundle)) {
+ filter.setBundleId(bundle);
+ }
+ else {
+ filter.setBundleName(bundle);
+ }
+ }
+ if (filterList.containsKey(OozieClient.FILTER_SLA_EVENT_STATUS)) {
+ filter.setEventStatus(filterList.get(OozieClient.FILTER_SLA_EVENT_STATUS).get(0));
+ }
+ if (filterList.containsKey(OozieClient.FILTER_SLA_STATUS)) {
+ filter.setSLAStatus(filterList.get(OozieClient.FILTER_SLA_STATUS).get(0));
+ }
if (filterList.containsKey(OozieClient.FILTER_SLA_APPNAME)) {
filter.setAppName(filterList.get(OozieClient.FILTER_SLA_APPNAME).get(0));
}
@@ -137,13 +180,6 @@ public class V2SLAServlet extends SLAServlet {
filter.setNominalEnd(DateUtils.parseDateUTC(filterList.get(OozieClient.FILTER_SLA_NOMINAL_END).get(0)));
}
- if (filter.getAppName() == null && filter.getJobId() == null && filter.getParentId() == null) {
- throw new XServletException(HttpServletResponse.SC_BAD_REQUEST, ErrorCode.E0305,
- "At least one of the filter parameters - " + OozieClient.FILTER_SLA_ID + ","
- + OozieClient.FILTER_SLA_PARENT_ID + " or " + OozieClient.FILTER_SLA_APPNAME
- + " should be specified in the filter query parameter");
- }
-
JPAService jpaService = Services.get().get(JPAService.class);
List<SLASummaryBean> slaSummaryList = null;
if (jpaService != null) {
@@ -181,4 +217,12 @@ public class V2SLAServlet extends SLAServlet {
}
+ private boolean isBundleId(String id) {
+ boolean ret = false;
+ Matcher m = p.matcher(id);
+ if (m.matches()) {
+ return true;
+ }
+ return ret;
+ }
}
http://git-wip-us.apache.org/repos/asf/oozie/blob/1b1ef47a/core/src/main/java/org/apache/oozie/sla/SLASummaryBean.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/oozie/sla/SLASummaryBean.java b/core/src/main/java/org/apache/oozie/sla/SLASummaryBean.java
index a88dcf6..ef1ea98 100644
--- a/core/src/main/java/org/apache/oozie/sla/SLASummaryBean.java
+++ b/core/src/main/java/org/apache/oozie/sla/SLASummaryBean.java
@@ -20,6 +20,7 @@ package org.apache.oozie.sla;
import java.sql.Timestamp;
import java.util.Date;
+import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -34,6 +35,7 @@ import javax.persistence.Table;
import org.apache.oozie.AppType;
import org.apache.oozie.client.OozieClient;
import org.apache.oozie.client.event.SLAEvent;
+import org.apache.oozie.client.event.SLAEvent.EventStatus;
import org.apache.oozie.client.rest.JsonBean;
import org.apache.oozie.client.rest.JsonTags;
import org.apache.oozie.client.rest.JsonUtils;
@@ -361,10 +363,25 @@ public class SLASummaryBean implements JsonBean {
this.lastModifiedTS = DateUtils.convertDateToTimestamp(lastModified);
}
- @SuppressWarnings("unchecked")
@Override
public JSONObject toJSONObject() {
+ return toJSONObject(null);
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public JSONObject toJSONObject(String timeZoneId) {
JSONObject json = new JSONObject();
+ Map<EventStatus,Long> eventMap = calculateEventStatus();
+ StringBuilder eventStatusStr = new StringBuilder();
+ boolean first = true;
+ for(EventStatus e: eventMap.keySet()) {
+ if(!first) {
+ eventStatusStr.append(",");
+ }
+ eventStatusStr.append(e.toString());
+ first = false;
+ }
json.put(JsonTags.SLA_SUMMARY_ID, jobId);
if (parentId != null) {
json.put(JsonTags.SLA_SUMMARY_PARENT_ID, parentId);
@@ -372,79 +389,127 @@ public class SLASummaryBean implements JsonBean {
json.put(JsonTags.SLA_SUMMARY_APP_NAME, appName);
json.put(JsonTags.SLA_SUMMARY_APP_TYPE, appType);
json.put(JsonTags.SLA_SUMMARY_USER, user);
- json.put(JsonTags.SLA_SUMMARY_NOMINAL_TIME, nominalTimeTS.getTime());
+ json.put(JsonTags.SLA_SUMMARY_NOMINAL_TIME, getTimeOnTimeZone(nominalTimeTS, timeZoneId));
if (expectedStartTS != null) {
- json.put(JsonTags.SLA_SUMMARY_EXPECTED_START, expectedStartTS.getTime());
- }
- else {
+ json.put(JsonTags.SLA_SUMMARY_EXPECTED_START, getTimeOnTimeZone(expectedStartTS, timeZoneId));
+ } else {
json.put(JsonTags.SLA_SUMMARY_EXPECTED_START, null);
}
+
if (actualStartTS != null) {
- json.put(JsonTags.SLA_SUMMARY_ACTUAL_START, actualStartTS.getTime());
+ json.put(JsonTags.SLA_SUMMARY_ACTUAL_START, getTimeOnTimeZone(actualStartTS, timeZoneId));
}
else {
json.put(JsonTags.SLA_SUMMARY_ACTUAL_START, null);
}
- json.put(JsonTags.SLA_SUMMARY_EXPECTED_END, expectedEndTS.getTime());
+ Long startDelay = eventMap.get(EventStatus.START_MET) != null ? eventMap.get(EventStatus.START_MET) : eventMap
+ .get(EventStatus.START_MISS);
+ if (startDelay != null) {
+ json.put(JsonTags.SLA_SUMMARY_START_DELAY, startDelay);
+ }
+ if (expectedEndTS != null ) {
+ json.put(JsonTags.SLA_SUMMARY_EXPECTED_END, getTimeOnTimeZone(expectedEndTS,timeZoneId));
+ } else {
+ json.put(JsonTags.SLA_SUMMARY_ACTUAL_END, null);
+ }
if (actualEndTS != null) {
- json.put(JsonTags.SLA_SUMMARY_ACTUAL_END, actualEndTS.getTime());
+ json.put(JsonTags.SLA_SUMMARY_ACTUAL_END, getTimeOnTimeZone(actualEndTS,timeZoneId));
}
else {
json.put(JsonTags.SLA_SUMMARY_ACTUAL_END, null);
}
+ Long endDelay = eventMap.get(EventStatus.END_MET) != null ? eventMap.get(EventStatus.END_MET) : eventMap
+ .get(EventStatus.END_MISS);
+ if (endDelay != null) {
+ json.put(JsonTags.SLA_SUMMARY_END_DELAY, endDelay);
+ }
json.put(JsonTags.SLA_SUMMARY_EXPECTED_DURATION, expectedDuration);
- json.put(JsonTags.SLA_SUMMARY_ACTUAL_DURATION, actualDuration);
+ if (actualDuration == -1 && expectedDuration != -1 && actualStartTS != null) {
+ long currentDur = (new Date().getTime() - actualStartTS.getTime()) / (1000 * 60);
+ json.put(JsonTags.SLA_SUMMARY_ACTUAL_DURATION, currentDur);
+ }
+ else {
+ json.put(JsonTags.SLA_SUMMARY_ACTUAL_DURATION, actualDuration);
+ }
+ Long durationDelay = eventMap.get(EventStatus.DURATION_MET) != null ? eventMap.get(EventStatus.DURATION_MET)
+ : eventMap.get(EventStatus.DURATION_MISS);
+ if (durationDelay != null) {
+ json.put(JsonTags.SLA_SUMMARY_DURATION_DELAY, durationDelay);
+ }
json.put(JsonTags.SLA_SUMMARY_JOB_STATUS, jobStatus);
json.put(JsonTags.SLA_SUMMARY_SLA_STATUS, slaStatus);
- json.put(JsonTags.SLA_SUMMARY_LAST_MODIFIED, lastModifiedTS.getTime());
+ json.put(JsonTags.SLA_SUMMARY_EVENT_STATUS, eventStatusStr.toString());
+ json.put(JsonTags.SLA_SUMMARY_LAST_MODIFIED, getTimeOnTimeZone(lastModifiedTS, timeZoneId));
return json;
}
- @SuppressWarnings("unchecked")
- @Override
- public JSONObject toJSONObject(String timeZoneId) {
- if (timeZoneId == null) {
- return toJSONObject();
+ private Object getTimeOnTimeZone(Timestamp ts, String timeZoneId) {
+ Object ret = null;
+ if(timeZoneId == null) {
+ ret = new Long(String.valueOf(ts.getTime()));
+ } else {
+ ret = JsonUtils.formatDateRfc822(ts, timeZoneId);
}
- else {
- JSONObject json = new JSONObject();
- json.put(JsonTags.SLA_SUMMARY_ID, jobId);
- if (parentId != null) {
- json.put(JsonTags.SLA_SUMMARY_PARENT_ID, parentId);
- }
- json.put(JsonTags.SLA_SUMMARY_APP_NAME, appName);
- json.put(JsonTags.SLA_SUMMARY_APP_TYPE, appType);
- json.put(JsonTags.SLA_SUMMARY_USER, user);
- json.put(JsonTags.SLA_SUMMARY_NOMINAL_TIME, JsonUtils.formatDateRfc822(nominalTimeTS, timeZoneId));
- if (expectedStartTS != null) {
- json.put(JsonTags.SLA_SUMMARY_EXPECTED_START, JsonUtils.formatDateRfc822(expectedStartTS, timeZoneId));
+ return ret;
+ }
+
+ private Map<EventStatus, Long> calculateEventStatus() {
+ Map<EventStatus, Long> events = new HashMap<EventStatus, Long>();
+ if (expectedStartTS != null) {
+ if (actualStartTS != null) {
+ long diff = (actualStartTS.getTime() - expectedStartTS.getTime()) / (1000 * 60);
+ if (diff > 0) {
+ events.put(EventStatus.START_MISS, diff);
+ }
+ else {
+ events.put(EventStatus.START_MET, diff);
+ }
}
else {
- json.put(JsonTags.SLA_SUMMARY_EXPECTED_START, null);
+ long diff = (new Date().getTime() - expectedStartTS.getTime()) / (1000 * 60);
+ if (diff > 0) {
+ events.put(EventStatus.START_MISS, diff);
+ }
}
- if (actualStartTS != null) {
- json.put(JsonTags.SLA_SUMMARY_ACTUAL_START, JsonUtils.formatDateRfc822(actualStartTS, timeZoneId));
+ }
+ if (expectedDuration != -1) {
+ if (actualDuration != -1) {
+ long diff = actualDuration - expectedDuration;
+ if (diff > 0) {
+ events.put(EventStatus.DURATION_MISS, diff);
+ }
+ else {
+ events.put(EventStatus.DURATION_MET, diff);
+ }
}
else {
- json.put(JsonTags.SLA_SUMMARY_ACTUAL_START, null);
+ if (actualStartTS != null) {
+ long currentDur = (new Date().getTime() - actualStartTS.getTime()) / (1000 * 60);
+ if (expectedDuration < currentDur) {
+ events.put(EventStatus.DURATION_MISS, (currentDur - expectedDuration));
+ }
+ }
}
- json.put(JsonTags.SLA_SUMMARY_EXPECTED_END, JsonUtils.formatDateRfc822(expectedEndTS, timeZoneId));
+ }
+ if (expectedEndTS != null) {
if (actualEndTS != null) {
- json.put(JsonTags.SLA_SUMMARY_ACTUAL_END, JsonUtils.formatDateRfc822(actualEndTS, timeZoneId));
+ long diff = (actualEndTS.getTime() - expectedEndTS.getTime()) / (1000 * 60);
+ if (diff > 0) {
+ events.put(EventStatus.END_MISS, diff);
+ }
+ else {
+ events.put(EventStatus.END_MET, diff);
+ }
}
else {
- json.put(JsonTags.SLA_SUMMARY_ACTUAL_END, null);
+ long diff = (new Date().getTime() - expectedEndTS.getTime()) / (1000 * 60);
+ if (diff > 0) {
+ events.put(EventStatus.END_MISS, diff);
+ }
}
- json.put(JsonTags.SLA_SUMMARY_EXPECTED_DURATION, expectedDuration);
- json.put(JsonTags.SLA_SUMMARY_ACTUAL_DURATION, actualDuration);
- json.put(JsonTags.SLA_SUMMARY_JOB_STATUS, jobStatus);
- json.put(JsonTags.SLA_SUMMARY_SLA_STATUS, slaStatus);
- json.put(JsonTags.SLA_SUMMARY_LAST_MODIFIED, JsonUtils.formatDateRfc822(lastModifiedTS, timeZoneId));
-
- return json;
}
+ return events;
}
-
/**
* Convert a sla summary list into a json object.
*
@@ -475,8 +540,8 @@ public class SLASummaryBean implements JsonBean {
JSONObject slaJson = summary.toJSONObject(timeZoneId);
String slaAlertStatus = "";
if (slaConfigMap.containsKey(summary.getId())) {
- slaAlertStatus = slaConfigMap.get(summary.getId()).containsKey(
- OozieClient.SLA_DISABLE_ALERT) ? "Disabled" : "Enabled";
+ slaAlertStatus = slaConfigMap.get(summary.getId()).containsKey(OozieClient.SLA_DISABLE_ALERT) ? "Disabled"
+ : "Enabled";
}
slaJson.put(JsonTags.SLA_ALERT_STATUS, slaAlertStatus);
array.add(slaJson);
@@ -485,5 +550,4 @@ public class SLASummaryBean implements JsonBean {
json.put(JsonTags.SLA_SUMMARY_LIST, array);
return json;
}
-
}
http://git-wip-us.apache.org/repos/asf/oozie/blob/1b1ef47a/core/src/test/java/org/apache/oozie/servlet/DagServletTestCase.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/oozie/servlet/DagServletTestCase.java b/core/src/test/java/org/apache/oozie/servlet/DagServletTestCase.java
index 48193c7..ce731a1 100644
--- a/core/src/test/java/org/apache/oozie/servlet/DagServletTestCase.java
+++ b/core/src/test/java/org/apache/oozie/servlet/DagServletTestCase.java
@@ -19,12 +19,12 @@
package org.apache.oozie.servlet;
import org.apache.oozie.service.AuthorizationService;
-
import org.apache.oozie.service.ProxyUserService;
import org.apache.oozie.service.Services;
import org.apache.oozie.service.ForTestAuthorizationService;
import org.apache.oozie.service.ForTestWorkflowStoreService;
import org.apache.oozie.test.EmbeddedServletContainer;
+import org.apache.oozie.test.XDataTestCase;
import org.apache.oozie.test.XFsTestCase;
import java.net.URL;
@@ -32,7 +32,7 @@ import java.net.URLEncoder;
import java.util.Map;
import java.util.concurrent.Callable;
-public abstract class DagServletTestCase extends XFsTestCase {
+public abstract class DagServletTestCase extends XDataTestCase {
private EmbeddedServletContainer container;
private String servletPath;
http://git-wip-us.apache.org/repos/asf/oozie/blob/1b1ef47a/core/src/test/java/org/apache/oozie/servlet/TestV2SLAServlet.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/oozie/servlet/TestV2SLAServlet.java b/core/src/test/java/org/apache/oozie/servlet/TestV2SLAServlet.java
index 1886f48..db509ac 100644
--- a/core/src/test/java/org/apache/oozie/servlet/TestV2SLAServlet.java
+++ b/core/src/test/java/org/apache/oozie/servlet/TestV2SLAServlet.java
@@ -32,6 +32,11 @@ import java.util.concurrent.Callable;
import javax.servlet.http.HttpServletResponse;
import org.apache.oozie.AppType;
+import org.apache.oozie.BundleActionBean;
+import org.apache.oozie.BundleJobBean;
+import org.apache.oozie.CoordinatorJobBean;
+import org.apache.oozie.client.CoordinatorJob;
+import org.apache.oozie.client.Job;
import org.apache.oozie.client.event.SLAEvent.EventStatus;
import org.apache.oozie.client.event.SLAEvent.SLAStatus;
import org.apache.oozie.client.rest.JsonBean;
@@ -39,6 +44,8 @@ import org.apache.oozie.client.rest.JsonTags;
import org.apache.oozie.client.rest.RestConstants;
import org.apache.oozie.executor.jpa.BatchQueryExecutor;
import org.apache.oozie.executor.jpa.JPAExecutorException;
+import org.apache.oozie.executor.jpa.SLASummaryQueryExecutor;
+import org.apache.oozie.service.Services;
import org.apache.oozie.sla.SLASummaryBean;
import org.apache.oozie.util.DateUtils;
import org.json.simple.JSONArray;
@@ -63,11 +70,11 @@ public class TestV2SLAServlet extends DagServletTestCase {
final Date nominalTime2 = DateUtils.parseDateUTC("2012-06-02T10:20Z");
final Date nominalTime3 = DateUtils.parseDateUTC("2012-06-03T14:00Z");
insertEntriesIntoSLASummaryTable(2, "1-", "-W", "1-C", nominalTime1, "testapp-1", AppType.WORKFLOW_JOB,
- currentTime);
+ currentTime, EventStatus.END_MISS, SLAStatus.IN_PROCESS);
insertEntriesIntoSLASummaryTable(3, "2-", "-W", null, nominalTime2, "testapp-2", AppType.WORKFLOW_JOB,
- currentTime);
+ currentTime, EventStatus.END_MISS, SLAStatus.IN_PROCESS);
insertEntriesIntoSLASummaryTable(6, "3-", "-W", "2-C", nominalTime3, "testapp-3", AppType.WORKFLOW_JOB,
- currentTime);
+ currentTime, EventStatus.END_MISS, SLAStatus.IN_PROCESS);
Map<String, String> queryParams = new HashMap<String, String>();
JSONArray array = null;
@@ -101,7 +108,164 @@ public class TestV2SLAServlet extends DagServletTestCase {
// Matches 3rd and 4th element - 3-3-W 3-4-W
assertSLAJSONResponse(array, 3, 4, "3-", "-W", "2-C", nominalTime3, "testapp-3", AppType.WORKFLOW_JOB,
currentTime);
+ return null;
+ }
+ });
+ }
+
+ public void testBundleSLA() throws Exception {
+ runTest("/v2/sla", V2SLAServlet.class, IS_SECURITY_ENABLED, new Callable<Void>() {
+ public Void call() throws Exception {
+
+ //insert Bundle Job/Action, Coord Job/Action
+ List<JsonBean> beans = new ArrayList<JsonBean> ();
+ String bundleId = "0000000-000000000000000-"+ Services.get().getSystemId() + "-B";
+ BundleJobBean bjBean = createBundleJob(bundleId,Job.Status.RUNNING, false);
+ String bundleName = bjBean.getAppName();
+ beans.add(bjBean);
+ CoordinatorJobBean cjBean1 = createCoordJob(CoordinatorJob.Status.SUCCEEDED, false, true);
+ beans.add(cjBean1);
+ CoordinatorJobBean cjBean2 = createCoordJob(CoordinatorJob.Status.SUCCEEDED, false, true);
+ beans.add(cjBean2);
+
+ BundleActionBean baBean1 = createBundleAction(bundleId, cjBean1.getId(), "bundle-action-1", 0,
+ Job.Status.RUNNING);
+ beans.add(baBean1);
+ BundleActionBean baBean2 = createBundleAction(bundleId, cjBean2.getId(), "bundle-action-2", 0,
+ Job.Status.RUNNING);
+ beans.add(baBean2);
+ BatchQueryExecutor.getInstance().executeBatchInsertUpdateDelete(beans, null, null);
+
+ Calendar cal = Calendar.getInstance();
+ cal.add(Calendar.MINUTE, -12); //current -12
+ Date actualStartForMet = cal.getTime();
+ cal.add(Calendar.MINUTE, 2); //current -10
+ Date expectedStart = cal.getTime();
+ cal.add(Calendar.MINUTE, 1); //current -9
+ Date actualStartForMiss = cal.getTime();
+ cal.add(Calendar.MINUTE, 3); //current -6
+ Date actualEndForMet = cal.getTime();
+ cal.add(Calendar.MINUTE, 1); //current -5
+ Date expectedEnd = cal.getTime();
+ cal.add(Calendar.MINUTE, 2); //current -3
+ Date actualEndForMiss = cal.getTime();
+ cal.add(Calendar.MINUTE, 8); //current + 5
+ Date futureExpectedEnd = cal.getTime();
+
+ // START_MET, DURATION_MET, END_MET
+ insertEntriesIntoSLASummaryTable(cjBean1.getId() + "@1", cjBean1.getId(), "testapp-1",
+ AppType.COORDINATOR_ACTION, EventStatus.END_MET, SLAStatus.MET, expectedStart,
+ actualStartForMet, 7, 6, expectedEnd, actualEndForMet, actualStartForMet);
+
+ // START_MISS, DURATION_MISS, END_MISS
+ insertEntriesIntoSLASummaryTable(cjBean1.getId() + "@2", cjBean1.getId(), "testapp-1",
+ AppType.COORDINATOR_ACTION, EventStatus.END_MISS, SLAStatus.MISS, expectedStart,
+ actualStartForMiss, 5, 6, expectedEnd, actualEndForMiss, actualStartForMet);
+
+ // // START_MISS, DURATION_MISS (still running, Not Ended, but
+ // expected Duration/End already passed by now)
+ insertEntriesIntoSLASummaryTable(cjBean2.getId() + "@1", cjBean2.getId(), "testapp-2",
+ AppType.COORDINATOR_ACTION, EventStatus.DURATION_MISS, SLAStatus.IN_PROCESS, expectedStart,
+ actualStartForMiss, 8, 9, futureExpectedEnd, null, actualStartForMet);
+
+ // START_MISS only, (Not Started YET, and Expected Duration/End
+ // Time not yet passed)
+ insertEntriesIntoSLASummaryTable(cjBean2.getId() + "@2", cjBean2.getId(), "testapp-2",
+ AppType.COORDINATOR_ACTION, null, SLAStatus.NOT_STARTED, expectedStart, null, 10, -1,
+ futureExpectedEnd, null, expectedStart);
+
+ Map<String, String> queryParams = new HashMap<String, String>();
+ JSONArray array = null;
+
+ URL url = createURL("", queryParams);
+ HttpURLConnection conn = (HttpURLConnection) url.openConnection();
+ conn.setRequestMethod("GET");
+ assertEquals(HttpServletResponse.SC_BAD_REQUEST, conn.getResponseCode());
+
+ //test filter bundle ID
+ queryParams.put(RestConstants.TIME_ZONE_PARAM, "GMT");
+ queryParams.put(RestConstants.JOBS_FILTER_PARAM, String.format("bundle=%s",bundleId));
+ array = getSLAJSONResponse(queryParams);
+ assertEquals(4, array.size());
+ for(int i=0; i < array.size(); i++) {
+ JSONObject json = (JSONObject) array.get(i);
+ String id = (String)json.get(JsonTags.SLA_SUMMARY_ID);
+ if(id.equals(cjBean1.getId() + "@1")) {
+ assertEquals(-2L, json.get(JsonTags.SLA_SUMMARY_START_DELAY));
+ assertEquals(-1L, json.get(JsonTags.SLA_SUMMARY_DURATION_DELAY));
+ assertEquals(-1L, json.get(JsonTags.SLA_SUMMARY_END_DELAY));
+ }
+ }
+
+ //test filter bundle Name
+ queryParams.clear();
+ queryParams.put(RestConstants.TIME_ZONE_PARAM, "GMT");
+ queryParams.put(RestConstants.JOBS_FILTER_PARAM, String.format("bundle=%s",bundleName));
+ array = getSLAJSONResponse(queryParams);
+ assertEquals(4, array.size());
+
+ //test filter bundle ID + EventStatus
+ queryParams.clear();
+ queryParams.put(RestConstants.TIME_ZONE_PARAM, "GMT");
+ queryParams.put(RestConstants.JOBS_FILTER_PARAM, String.format("bundle=%s;event_status=END_MISS",bundleId));
+ array = getSLAJSONResponse(queryParams);
+ assertEquals(1, array.size());
+
+ JSONObject json = (JSONObject) array.get(0);
+ String parentId = (String) json.get(JsonTags.SLA_SUMMARY_PARENT_ID);
+ assertTrue(parentId.equals(cjBean1.getId()) || parentId.equals(cjBean2.getId()));
+ String id = (String) json.get(JsonTags.SLA_SUMMARY_ID);
+ assertTrue(id.equals(cjBean1.getId() + "@2"));
+ String es = (String) json.get(JsonTags.SLA_SUMMARY_EVENT_STATUS);
+ assertTrue(es.contains(EventStatus.END_MISS.toString()));
+
+ // test filter bundle ID + EventStatus + SlaStus
+ queryParams.clear();
+ queryParams.put(RestConstants.JOBS_FILTER_PARAM, String.format("bundle=%s;sla_status=MISS", bundleId));
+ array = getSLAJSONResponse(queryParams);
+ assertEquals(1, array.size());
+
+ json = (JSONObject) array.get(0);
+ id = (String) json.get(JsonTags.SLA_SUMMARY_ID);
+ assertTrue(id.equals(cjBean1.getId() + "@2"));
+ parentId = (String) json.get(JsonTags.SLA_SUMMARY_PARENT_ID);
+ assertTrue(parentId.equals(cjBean1.getId()));
+ assertEquals(1L, json.get(JsonTags.SLA_SUMMARY_START_DELAY));
+ assertEquals(1L, json.get(JsonTags.SLA_SUMMARY_DURATION_DELAY));
+ assertEquals(2L, json.get(JsonTags.SLA_SUMMARY_END_DELAY));
+
+ //test filter bundleName + Multiple EventStatus
+ queryParams.clear();
+ queryParams.put(RestConstants.JOBS_FILTER_PARAM,
+ String.format("bundle=%s;event_status=START_MISS,END_MISS", bundleName));
+ array = getSLAJSONResponse(queryParams);
+ assertEquals(3, array.size());
+
+ for(int i=0; i < array.size(); i++) {
+ json = (JSONObject) array.get(i);
+ id = (String)json.get(JsonTags.SLA_SUMMARY_ID);
+ assertTrue(id.equals(cjBean1.getId()+"@2") || id.equals(cjBean2.getId()+"@1") || id.equals(cjBean2.getId()+"@2"));
+ parentId = (String) json.get(JsonTags.SLA_SUMMARY_PARENT_ID);
+ assertTrue(parentId.equals(cjBean1.getId()) || parentId.equals(cjBean2.getId()));
+ }
+
+ //test filter bundleName + Multiple EventStatus + Multiple SlaStus
+ queryParams.clear();
+ queryParams.put(RestConstants.JOBS_FILTER_PARAM,
+ String.format("bundle=%s;event_status=DURATION_MISS;sla_status=IN_PROCESS", bundleName));
+ array = getSLAJSONResponse(queryParams);
+ assertEquals(1, array.size());
+ json = (JSONObject) array.get(0);
+ assertEquals(cjBean2.getId() + "@1", (String) json.get(JsonTags.SLA_SUMMARY_ID));
+ assertEquals(cjBean2.getId(), (String) json.get(JsonTags.SLA_SUMMARY_PARENT_ID));
+ String eventStatus = (String)json.get(JsonTags.SLA_SUMMARY_EVENT_STATUS);
+ assertTrue(eventStatus.contains("DURATION_MISS"));
+ assertTrue(eventStatus.contains("START_MISS"));
+ assertFalse(eventStatus.contains("END_MISS") || eventStatus.contains("END_MET"));
+ // actualDuration is null on DB while job is running, populates it in API call
+ assertEquals(9L, json.get(JsonTags.SLA_SUMMARY_ACTUAL_DURATION));
+ assertEquals(1L, json.get(JsonTags.SLA_SUMMARY_DURATION_DELAY));
return null;
}
});
@@ -147,15 +311,13 @@ public class TestV2SLAServlet extends DagServletTestCase {
assertEquals(actualEnd.getTimeInMillis(), json.get(JsonTags.SLA_SUMMARY_ACTUAL_END));
assertEquals(10L, json.get(JsonTags.SLA_SUMMARY_EXPECTED_DURATION));
assertEquals(15L, json.get(JsonTags.SLA_SUMMARY_ACTUAL_DURATION));
- assertEquals(currentTime.getTime(), json.get(JsonTags.SLA_SUMMARY_LAST_MODIFIED));
nominalTime.add(Calendar.HOUR, 1);
}
}
private void insertEntriesIntoSLASummaryTable(int numEntries, String jobIDPrefix, String jobIDSuffix,
- String parentId, Date startNominalTime, String appName, AppType appType, Date currentTime)
- throws JPAExecutorException {
- List<JsonBean> list = new ArrayList<JsonBean>();
+ String parentId, Date startNominalTime, String appName, AppType appType, Date currentTime,
+ EventStatus eventStatus, SLAStatus slaStatus) throws JPAExecutorException {
Calendar nominalTime = Calendar.getInstance();
nominalTime.setTime(startNominalTime);
for (int i = 1; i <= numEntries; i++) {
@@ -165,27 +327,34 @@ public class TestV2SLAServlet extends DagServletTestCase {
expectedEnd.add(Calendar.MINUTE, 60);
Calendar actualEnd = (Calendar) expectedEnd.clone();
actualEnd.add(Calendar.MINUTE, i);
- SLASummaryBean bean = new SLASummaryBean();
- bean.setId(jobIDPrefix + i + jobIDSuffix);
- bean.setParentId(parentId);
- bean.setAppName(appName);
- bean.setAppType(appType);
- bean.setJobStatus("RUNNING");
- bean.setEventStatus(EventStatus.END_MISS);
- bean.setSLAStatus(SLAStatus.IN_PROCESS);
- bean.setNominalTime(nominalTime.getTime());
- bean.setExpectedStart(nominalTime.getTime());
- bean.setActualStart(actualStart.getTime());
- bean.setExpectedEnd(expectedEnd.getTime());
- bean.setActualEnd(actualEnd.getTime());
- bean.setExpectedDuration(10);
- bean.setActualDuration(15);
- bean.setUser("testuser");
- bean.setLastModifiedTime(currentTime);
- list.add(bean);
+ insertEntriesIntoSLASummaryTable(jobIDPrefix + i + jobIDSuffix, parentId, appName, appType, eventStatus,
+ slaStatus, nominalTime.getTime(), actualStart.getTime(), ((long) 10), ((long) 15),
+ expectedEnd.getTime(), actualEnd.getTime(), nominalTime.getTime());
nominalTime.add(Calendar.HOUR, 1);
}
+ }
- BatchQueryExecutor.getInstance().executeBatchInsertUpdateDelete(list, null, null);
+ private void insertEntriesIntoSLASummaryTable(String jobID, String parentId, String appName, AppType appType,
+ EventStatus eventStatus, SLAStatus slaStatus, Date expectedStartTime, Date actualStartTime,
+ long expectedDuration, long actualDuration, Date expectedEndTime, Date actualEndTime, Date nominalTime)
+ throws JPAExecutorException {
+ SLASummaryBean bean = new SLASummaryBean();
+ bean.setId(jobID);
+ bean.setParentId(parentId);
+ bean.setAppName(appName);
+ bean.setAppType(appType);
+ bean.setJobStatus("RUNNING");
+ bean.setEventStatus(eventStatus);
+ bean.setSLAStatus(slaStatus);
+ bean.setNominalTime(nominalTime);
+ bean.setExpectedStart(expectedStartTime);
+ bean.setActualStart(actualStartTime);
+ bean.setExpectedDuration(expectedDuration);
+ bean.setActualDuration(actualDuration);
+ bean.setExpectedEnd(expectedEndTime);
+ bean.setActualEnd(actualEndTime);
+ bean.setUser("testuser");
+ bean.setLastModifiedTime(Calendar.getInstance().getTime());
+ SLASummaryQueryExecutor.getInstance().insert(bean);
}
}
http://git-wip-us.apache.org/repos/asf/oozie/blob/1b1ef47a/docs/src/site/twiki/DG_SLAMonitoring.twiki
----------------------------------------------------------------------
diff --git a/docs/src/site/twiki/DG_SLAMonitoring.twiki b/docs/src/site/twiki/DG_SLAMonitoring.twiki
index 106ce8a..7915d84 100644
--- a/docs/src/site/twiki/DG_SLAMonitoring.twiki
+++ b/docs/src/site/twiki/DG_SLAMonitoring.twiki
@@ -165,6 +165,12 @@ In the REST API, the following filters can be applied while fetching SLA informa
* id - id of the workflow job, workflow action or coordinator action
* parent_id - Parent id of the workflow job, workflow action or coordinator action
* nominal_start and nominal_end - Start and End range for nominal time of the workflow or coordinator.
+ * bundle - Bundle Job ID or Bundle App Name. Fetches SLA information for actions of all coordinators in that bundle.
+ * event_status - event stauts such as START_MET/START_MISS/DURATION_MET/DURATION_MISS/END_MET/END_MISS
+ * sla_status - sla status such as NOT_STARTED/IN_PROCESS/MET/MISS
+
+multiple event_status and sla_status can be specified with comma separation. When multiple statuses are specified, they are considered as OR.
+For example, event_status=START_MET;END_MISS list the coordinator actions where event status is either START_MET OR END_MISS.
When timezone query parameter is specified, the expected and actual start/end time returned is formatted. If not specified,
the number of milliseconds that have elapsed since January 1, 1970 00:00:00.000 GMT is returned.
@@ -187,7 +193,7 @@ GET <oozie-host>:<port>/oozie/v2/sla?timezone=GMT&filter=nominal_start=2013-06-1
msgType : "SLA"
appName : "my-sla-app"
slaStatus : "IN_PROCESS"
- eventStatus : "START_MISS"
+ jobStatus : "RUNNING"
user: "joe"
nominalTime: "2013-16-22T05:00Z"
expectedStartTime: "2013-16-22T05:10Z" <-- (should start by this time)
@@ -218,7 +224,7 @@ GET <oozie-host>:<port>/oozie/v2/sla?timezone=GMT&filter=parent_id=000056-123879
msgType : "SLA"
appName : "map-reduce-action"
slaStatus : "MISS"
- eventStatus : "END_MISS"
+ jobStatus : "SUCCEEDED"
user: "joe"
nominalTime: "2013-16-22T05:00Z"
expectedStartTime: "2013-16-22T05:10Z"
@@ -249,7 +255,7 @@ GET <oozie-host>:<port>/oozie/v2/sla?timezone=GMT&filter=id=000001-1238791320234
msgType : "SLA"
appName : "my-coord-app"
slaStatus : "MET"
- eventStatus : "DURATION_MISS"
+ jobStatus : "SUCCEEDED"
user: "joe"
nominalTime: "2013-16-22T05:00Z"
expectedStartTime: "2013-16-22T05:10Z"
@@ -267,6 +273,59 @@ GET <oozie-host>:<port>/oozie/v2/sla?timezone=GMT&filter=id=000001-1238791320234
Scenario #3 is particularly interesting because it is an overall "MET" because it met its expected End-time,
but it is "Duration_Miss" because the actual run (between actual start and actual end) exceeded expected duration.
+---+++ Scenario 4: All Coordinator actions in a Bundle
+*Request:*
+<verbatim>
+GET <oozie-host>:<port>/oozie/v2/sla?timezone=GMT&filter=bundle=1234567-150130225116604-oozie-B;event_status=END_MISS
+</verbatim>
+
+*JSON Response*
+<verbatim>
+{
+ id : "000001-1238791320234-oozie-joe-C@1"
+ parentId : "000001-1238791320234-oozie-joe-C"
+ appType : "COORDINATOR_ACTION"
+ msgType : "SLA"
+ appName : "my-coord-app"
+ slaStatus : "MET"
+ eventStatus : "START_MET,DURATION_MISS,END_MISS"
+ user: "joe"
+ nominalTime: "2014-01-10T12:00Z"
+ expectedStartTime: "2014-01-10T12:00Z"
+ actualStartTime: "2014-01-10T11:59Z"
+ startDelay: -1
+ expectedEndTime: "2014-01-10T13:00Z"
+ actualEndTime: "2014-01-10T13:05Z"
+ endDelay: 5
+ expectedDuration: 60
+ actualDuration: 66
+ durationDelay: 6
+}
+{
+ id : "000001-1238791320234-oozie-joe-C@2"
+ parentId : "000001-1238791320234-oozie-joe-C"
+ appType : "COORDINATOR_ACTION"
+ msgType : "SLA"
+ appName : "my-coord-app"
+ slaStatus : "MET"
+ eventStatus : "START_MISS,DURATION_MET,END_MISS"
+ user: "joe"
+ nominalTime: "2014-01-11T12:00Z"
+ expectedStartTime: "2014-01-11T12:00Z"
+ actualStartTime: "2014-01-11T12:05Z"
+ startDelay: 5
+ expectedEndTime: "2014-01-11T13:00Z"
+ actualEndTime: "2014-01-11T13:01Z"
+ endDelay: 1
+ expectedDuration: 60
+ actualDuration: 56
+ durationDelay: -4
+}
+</verbatim>
+
+Scenario #4 (All Coordinator actions in a Bundle) is to get SLA information of all coordinator actions under bundle job in one call.
+startDelay/durationDelay/endDelay values returned indicate how much delay compared to expected time (positive values in case of MISS, and negative values in case of MET).
+
---+++ Sample Email Alert
<verbatim>
Subject: OOZIE - SLA END_MISS (AppName=wf-sla-job, JobID=0000004-130610225200680-oozie-oozi-W)
http://git-wip-us.apache.org/repos/asf/oozie/blob/1b1ef47a/release-log.txt
----------------------------------------------------------------------
diff --git a/release-log.txt b/release-log.txt
index 466fb04..dc753a4 100644
--- a/release-log.txt
+++ b/release-log.txt
@@ -1,5 +1,6 @@
-- Oozie 4.2.0 release (trunk - unreleased)
+OOZIE-2146 Add option to filter sla information by bundle id or name (ryota)
OOZIE-2188 Fix typos in twiki documentation ( jacobtolar via puru)
OOZIE-2174 Add missing admin commands to OozieClient and OozieCLI (rkanter)
OOZIE-2186 Upgrade Tomcat to 6.0.43 (rkanter)
[2/2] oozie git commit: OOZIE-2183 provide an option to disable
cleanup for output dataset in rerun (ryota)
Posted by ry...@apache.org.
OOZIE-2183 provide an option to disable cleanup for output dataset in rerun (ryota)
Project: http://git-wip-us.apache.org/repos/asf/oozie/repo
Commit: http://git-wip-us.apache.org/repos/asf/oozie/commit/e5b0922d
Tree: http://git-wip-us.apache.org/repos/asf/oozie/tree/e5b0922d
Diff: http://git-wip-us.apache.org/repos/asf/oozie/diff/e5b0922d
Branch: refs/heads/master
Commit: e5b0922d8efd483e8839b7f2c2b29ac29b14411f
Parents: 1b1ef47
Author: egashira <ry...@yahoo.com>
Authored: Wed Apr 1 12:53:55 2015 -0700
Committer: egashira <ry...@yahoo.com>
Committed: Wed Apr 1 12:53:55 2015 -0700
----------------------------------------------------------------------
.../main/resources/oozie-coordinator-0.4.xsd | 1 +
.../oozie/command/coord/CoordRerunXCommand.java | 4 +-
.../command/coord/TestCoordRerunXCommand.java | 59 ++++++++++++++++++
core/src/test/resources/coord-rerun-action4.xml | 63 ++++++++++++++++++++
.../site/twiki/CoordinatorFunctionalSpec.twiki | 1 +
docs/src/site/twiki/DG_CoordinatorRerun.twiki | 2 +-
release-log.txt | 1 +
7 files changed, 129 insertions(+), 2 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/oozie/blob/e5b0922d/client/src/main/resources/oozie-coordinator-0.4.xsd
----------------------------------------------------------------------
diff --git a/client/src/main/resources/oozie-coordinator-0.4.xsd b/client/src/main/resources/oozie-coordinator-0.4.xsd
index 78205f6..b31c5d8 100644
--- a/client/src/main/resources/oozie-coordinator-0.4.xsd
+++ b/client/src/main/resources/oozie-coordinator-0.4.xsd
@@ -116,6 +116,7 @@
</xs:sequence>
<xs:attribute name="name" type="coordinator:IDENTIFIER" use="required"/>
<xs:attribute name="dataset" type="xs:string" use="required"/>
+ <xs:attribute name="nocleanup" type="xs:boolean" use="optional"/>
</xs:complexType>
<xs:complexType name="ACTION">
<xs:sequence minOccurs="1" maxOccurs="1">
http://git-wip-us.apache.org/repos/asf/oozie/blob/e5b0922d/core/src/main/java/org/apache/oozie/command/coord/CoordRerunXCommand.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/oozie/command/coord/CoordRerunXCommand.java b/core/src/main/java/org/apache/oozie/command/coord/CoordRerunXCommand.java
index 050662e..3535e00 100644
--- a/core/src/main/java/org/apache/oozie/command/coord/CoordRerunXCommand.java
+++ b/core/src/main/java/org/apache/oozie/command/coord/CoordRerunXCommand.java
@@ -147,7 +147,9 @@ public class CoordRerunXCommand extends RerunTransitionXCommand<CoordinatorActio
Element outputList = eAction.getChild("output-events", eAction.getNamespace());
if (outputList != null) {
for (Element data : (List<Element>) outputList.getChildren("data-out", eAction.getNamespace())) {
- if (data.getChild("uris", data.getNamespace()) != null) {
+ String nocleanup = data.getAttributeValue("nocleanup");
+ if (data.getChild("uris", data.getNamespace()) != null
+ && (nocleanup == null || !nocleanup.equals("true"))) {
String uris = data.getChild("uris", data.getNamespace()).getTextTrim();
if (uris != null) {
String[] uriArr = uris.split(CoordELFunctions.INSTANCE_SEPARATOR);
http://git-wip-us.apache.org/repos/asf/oozie/blob/e5b0922d/core/src/test/java/org/apache/oozie/command/coord/TestCoordRerunXCommand.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/oozie/command/coord/TestCoordRerunXCommand.java b/core/src/test/java/org/apache/oozie/command/coord/TestCoordRerunXCommand.java
index 45457d1..53c5122 100644
--- a/core/src/test/java/org/apache/oozie/command/coord/TestCoordRerunXCommand.java
+++ b/core/src/test/java/org/apache/oozie/command/coord/TestCoordRerunXCommand.java
@@ -19,6 +19,7 @@
package org.apache.oozie.command.coord;
import java.io.ByteArrayInputStream;
+import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
@@ -588,6 +589,64 @@ public class TestCoordRerunXCommand extends XDataTestCase {
}
/**
+ * Test : nocleanup option in dataset
+ *
+ * @throws Exception
+ */
+ public void testCoordRerunCleanupOption() throws Exception {
+ final String jobId = "0000000-" + new Date().getTime() + "-testCoordRerun-C";
+ final int actionNum = 1;
+ final String actionId = jobId + "@" + actionNum;
+ CoordinatorStore store = Services.get().get(StoreService.class).getStore(CoordinatorStore.class);
+ store.beginTrx();
+ try {
+ addRecordToJobTable(jobId, store, CoordinatorJob.Status.SUCCEEDED);
+ addRecordToActionTable(jobId, actionNum, actionId, store, CoordinatorAction.Status.SUCCEEDED,
+ "coord-rerun-action4.xml");
+ store.commitTrx();
+ }
+ catch (Exception e) {
+ e.printStackTrace();
+ fail("Could not update db.");
+ }
+ finally {
+ store.closeTrx();
+ }
+ Path appPath = new Path(getFsTestCaseDir(), "coord");
+ String outputDir = appPath.toString() + "/coord-input/2009/12/14/11/00";
+ Path success = new Path(outputDir, "_SUCCESS");
+ FileSystem fs = getFileSystem();
+ fs.mkdirs(new Path(outputDir));
+ fs.create(success, true);
+ // before cleanup
+ assertTrue(fs.exists(success));
+ long beforeModifiedTime = fs.getFileStatus(success).getModificationTime();
+
+ final OozieClient coordClient = LocalOozie.getCoordClient();
+ coordClient.reRunCoord(jobId, RestConstants.JOB_COORD_SCOPE_ACTION, Integer.toString(actionNum), false, false);
+
+ CoordinatorStore store2 = Services.get().get(StoreService.class).getStore(CoordinatorStore.class);
+ store2.beginTrx();
+ CoordinatorActionBean action2 = store2.getCoordinatorAction(actionId, false);
+ assertNotSame(action2.getStatus(), CoordinatorAction.Status.SUCCEEDED);
+ store2.commitTrx();
+ store2.closeTrx();
+
+ waitFor(120 * 1000, new Predicate() {
+ @Override
+ public boolean evaluate() throws Exception {
+ CoordinatorAction bean = coordClient.getCoordActionInfo(actionId);
+ return (bean.getStatus() == CoordinatorAction.Status.WAITING || bean.getStatus() == CoordinatorAction.Status.READY);
+ }
+ });
+
+ // after cleanup
+ assertTrue(fs.exists(success));
+ long afterModifiedTime = fs.getFileStatus(success).getModificationTime();
+ assertEquals(beforeModifiedTime, afterModifiedTime);
+ }
+
+ /**
* Test : rerun <jobId> -action 1
*
* @throws Exception
http://git-wip-us.apache.org/repos/asf/oozie/blob/e5b0922d/core/src/test/resources/coord-rerun-action4.xml
----------------------------------------------------------------------
diff --git a/core/src/test/resources/coord-rerun-action4.xml b/core/src/test/resources/coord-rerun-action4.xml
new file mode 100644
index 0000000..a5dee79
--- /dev/null
+++ b/core/src/test/resources/coord-rerun-action4.xml
@@ -0,0 +1,63 @@
+<!--
+ 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.
+-->
+<coordinator-app xmlns="uri:oozie:coordinator:0.4" name="COORD-TEST" frequency="1" timezone="UTC" freq_timeunit="DAY" end_of_duration="NONE" instance-number="1" action-nominal-time="2009-12-15T01:00Z" action-actual-time="2010-10-01T00:00Z">
+ <controls>
+ <timeout>10</timeout>
+ <concurrency>1</concurrency>
+ <execution>FIFO</execution>
+ </controls>
+ <input-events>
+ <data-in name="din" dataset="din">
+ <uris>#inputDir</uris>
+ <dataset name="din" frequency="1" initial-instance="2009-12-01T01:00Z" timezone="UTC" freq_timeunit="DAY" end_of_duration="NONE">
+ <uri-template>#inputTemplate</uri-template>
+ </dataset>
+ </data-in>
+ </input-events>
+ <output-events>
+ <data-out name="dout" dataset="dout" nocleanup="true">
+ <uris>#outputDir</uris>
+ <dataset name="dout" frequency="1380" initial-instance="2009-12-01T01:00Z" timezone="UTC" freq_timeunit="MINUTE" end_of_duration="NONE">
+ <uri-template>#outputTemplate</uri-template>
+ </dataset>
+ </data-out>
+ </output-events>
+ <action>
+ <workflow>
+ <app-path>${wfAppPath}</app-path>
+ <configuration>
+ <property>
+ <name>jobTracker</name>
+ <value>localhost:9001</value>
+ </property>
+ <property>
+ <name>nameNode</name>
+ <value>hdfs://localhost:9000</value>
+ </property>
+ <property>
+ <name>inputDir</name>
+ <value>#inputDir</value>
+ </property>
+ <property>
+ <name>outputDir</name>
+ <value>#outputDir</value>
+ </property>
+ </configuration>
+ </workflow>
+ </action>
+</coordinator-app>
http://git-wip-us.apache.org/repos/asf/oozie/blob/e5b0922d/docs/src/site/twiki/CoordinatorFunctionalSpec.twiki
----------------------------------------------------------------------
diff --git a/docs/src/site/twiki/CoordinatorFunctionalSpec.twiki b/docs/src/site/twiki/CoordinatorFunctionalSpec.twiki
index ca61fe9..44a5be2 100644
--- a/docs/src/site/twiki/CoordinatorFunctionalSpec.twiki
+++ b/docs/src/site/twiki/CoordinatorFunctionalSpec.twiki
@@ -997,6 +997,7 @@ A synchronous coordinator definition is a is defined by a name, start time and e
* *%BLUE% name: %ENDCOLOR%* output name.
* *%BLUE% dataset: %ENDCOLOR%* dataset name.
* *%BLUE% instance: %ENDCOLOR%* dataset instance that will be generated by coordinator action.
+ * *%BLUE% nocleanup: %ENDCOLOR%* disable cleanup of the output dataset in rerun if true, even when nocleanup option is not used in CLI command.
* *%BLUE% action: %ENDCOLOR%* The coordinator action to execute.
* *%BLUE% workflow: %ENDCOLOR%* The workflow job invocation. Workflow job properties can refer to the defined data-in and data-out elements.
http://git-wip-us.apache.org/repos/asf/oozie/blob/e5b0922d/docs/src/site/twiki/DG_CoordinatorRerun.twiki
----------------------------------------------------------------------
diff --git a/docs/src/site/twiki/DG_CoordinatorRerun.twiki b/docs/src/site/twiki/DG_CoordinatorRerun.twiki
index c937da5..400684a 100644
--- a/docs/src/site/twiki/DG_CoordinatorRerun.twiki
+++ b/docs/src/site/twiki/DG_CoordinatorRerun.twiki
@@ -31,7 +31,7 @@ $oozie job -rerun <coord_Job_id> [-nocleanup] [-refresh] [-failed] [-config <arg
* If one of action in the given list of -action does not exist or not in terminal state, the rerun throws an error.
* The dates specified in -date must be UTC.
* Single date specified in -date must be able to find an action with matched nominal time to be effective.
- * If -nocleanup is given, coordinator directories will not be removed; otherwise the 'output-event' will be deleted.
+ * If -nocleanup is given, coordinator directories will not be removed; otherwise the 'output-event' will be deleted, unless nocleanup attribute is explicitly set in coordinator.xml
* If -refresh is set, new dataset is re-evaluated for latest() and future().
* If -refresh is set, all dependencies will be re-checked; otherwise only missed dependencies will be checked.
* If -failed is set, re-runs the failed workflow actions of the coordinator actions.
http://git-wip-us.apache.org/repos/asf/oozie/blob/e5b0922d/release-log.txt
----------------------------------------------------------------------
diff --git a/release-log.txt b/release-log.txt
index dc753a4..a874447 100644
--- a/release-log.txt
+++ b/release-log.txt
@@ -1,5 +1,6 @@
-- Oozie 4.2.0 release (trunk - unreleased)
+OOZIE-2183 provide an option to disable cleanup for output dataset in rerun (ryota)
OOZIE-2146 Add option to filter sla information by bundle id or name (ryota)
OOZIE-2188 Fix typos in twiki documentation ( jacobtolar via puru)
OOZIE-2174 Add missing admin commands to OozieClient and OozieCLI (rkanter)