You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@oozie.apache.org by an...@apache.org on 2018/09/24 08:46:28 UTC
[2/3] oozie git commit: OOZIE-3229 [client] [ui] Improved SLA
filtering options (asalamon74, andras.piros)
http://git-wip-us.apache.org/repos/asf/oozie/blob/90a97694/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
deleted file mode 100644
index aa63322..0000000
--- a/core/src/test/java/org/apache/oozie/servlet/TestV2SLAServlet.java
+++ /dev/null
@@ -1,384 +0,0 @@
-/**
- * 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.oozie.servlet;
-
-import java.io.InputStreamReader;
-import java.net.HttpURLConnection;
-import java.net.URL;
-import java.util.ArrayList;
-import java.util.Calendar;
-import java.util.Date;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-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;
-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;
-import org.json.simple.JSONObject;
-import org.json.simple.JSONValue;
-
-public class TestV2SLAServlet extends DagServletTestCase {
-
- private static final boolean IS_SECURITY_ENABLED = false;
-
- @Override
- protected void setUp() throws Exception {
- super.setUp();
- }
-
- public void testSLA() throws Exception {
- runTest("/v2/sla", V2SLAServlet.class, IS_SECURITY_ENABLED, new Callable<Void>() {
- public Void call() throws Exception {
-
- final Date currentTime = new Date(System.currentTimeMillis());
- final Date nominalTime1 = DateUtils.parseDateUTC("2012-06-01T10:00Z");
- 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, EventStatus.END_MISS, SLAStatus.IN_PROCESS);
- insertEntriesIntoSLASummaryTable(3, "2-", "-W", null, nominalTime2, "testapp-2", AppType.WORKFLOW_JOB,
- currentTime, EventStatus.END_MISS, SLAStatus.IN_PROCESS);
- insertEntriesIntoSLASummaryTable(6, "3-", "-W", "2-C", nominalTime3, "testapp-3", AppType.WORKFLOW_JOB,
- currentTime, EventStatus.END_MISS, SLAStatus.IN_PROCESS);
-
- 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());
-
- queryParams.put(RestConstants.JOBS_FILTER_PARAM, "app_name=testapp-1");
- array = getSLAJSONResponse(queryParams);
- // Matches first two - 1-1-W and 1-2-W
- assertSLAJSONResponse(array, 1, 2, "1-", "-W", "1-C", nominalTime1, "testapp-1", AppType.WORKFLOW_JOB,
- currentTime);
-
- queryParams.put(RestConstants.JOBS_FILTER_PARAM, "app_name=testapp-2;id=2-2-W");
- array = getSLAJSONResponse(queryParams);
- // Matches second element - 2-2-W
- assertSLAJSONResponse(array, 2, 2, "2-", "-W", null, nominalTime2, "testapp-2", AppType.WORKFLOW_JOB,
- currentTime);
-
- queryParams.put(RestConstants.JOBS_FILTER_PARAM, "app_name=testapp-3;nominal_start=2012-06-03T16:00Z");
- array = getSLAJSONResponse(queryParams);
- // Matches 3-6 elements - 3-3-W 3-4-W 3-5-W 3-6-W
- assertSLAJSONResponse(array, 3, 6, "3-", "-W", "2-C", nominalTime3, "testapp-3", AppType.WORKFLOW_JOB,
- currentTime);
-
- queryParams.put(RestConstants.JOBS_FILTER_PARAM,
- "parent_id=2-C;nominal_start=2012-06-03T016:00Z;nominal_end=2012-06-03T17:00Z");
- array = getSLAJSONResponse(queryParams);
- // 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 nonexistent bundle ID
- queryParams.put(RestConstants.TIME_ZONE_PARAM, "GMT");
- queryParams.put(RestConstants.JOBS_FILTER_PARAM, String.format("bundle=%s","xxxx"));
- array = getSLAJSONResponse(queryParams);
- assertEquals(0, array.size());
-
- //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(0L, json.get(JsonTags.SLA_SUMMARY_DURATION_DELAY));
- assertEquals(-1L, json.get(JsonTags.SLA_SUMMARY_END_DELAY));
- }
- }
-
- //test filter id + bundle ID
- queryParams.put(RestConstants.TIME_ZONE_PARAM, "GMT");
- queryParams.put(RestConstants.JOBS_FILTER_PARAM,
- String.format("id=%s;bundle=%s", cjBean2.getId() + "@1", bundleId));
- array = getSLAJSONResponse(queryParams);
- assertEquals("sla filter result size for id + bundleId", 1, 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("id + bundleId filter summary start delay", -2L, json.get(JsonTags.SLA_SUMMARY_START_DELAY));
- assertEquals("id + bundleId filter summary duration delay", 0L,
- json.get(JsonTags.SLA_SUMMARY_DURATION_DELAY));
- assertEquals("id + bundleId filter summary end delay", -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(0L, 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(0L, json.get(JsonTags.SLA_SUMMARY_DURATION_DELAY));
- return null;
- }
- });
- }
-
- private JSONArray getSLAJSONResponse(Map<String, String> queryParams) throws Exception {
- URL url = createURL("", queryParams);
- HttpURLConnection conn = (HttpURLConnection) url.openConnection();
- conn.setRequestMethod("GET");
- assertEquals(HttpServletResponse.SC_OK, conn.getResponseCode());
- assertTrue(conn.getHeaderField("content-type").startsWith(RestConstants.JSON_CONTENT_TYPE));
- JSONObject json = (JSONObject) JSONValue.parse(new InputStreamReader(conn.getInputStream()));
- JSONArray array = (JSONArray) json.get(JsonTags.SLA_SUMMARY_LIST);
- return array;
- }
-
- private void assertSLAJSONResponse(JSONArray array, int startRange, int endRange, String jobIDPrefix,
- String jobIDSuffix, String parentId, Date startNominalTime, String appName, AppType appType,
- Date currentTime) throws Exception {
- Calendar nominalTime = Calendar.getInstance();
- nominalTime.setTime(startNominalTime);
- nominalTime.add(Calendar.HOUR, (startRange - 1));
- int index = 0;
- assertEquals(endRange - (startRange - 1), array.size());
- for (int i = startRange; i <= endRange; i++) {
- Calendar actualStart = (Calendar) nominalTime.clone();
- actualStart.add(Calendar.MINUTE, i);
- Calendar expectedEnd = (Calendar) nominalTime.clone();
- expectedEnd.add(Calendar.MINUTE, 60);
- Calendar actualEnd = (Calendar) expectedEnd.clone();
- actualEnd.add(Calendar.MINUTE, i);
- JSONObject json = (JSONObject) array.get(index++);
- assertEquals(jobIDPrefix + i + jobIDSuffix, json.get(JsonTags.SLA_SUMMARY_ID));
- assertEquals(parentId, json.get(JsonTags.SLA_SUMMARY_PARENT_ID));
- assertEquals(appName, json.get(JsonTags.SLA_SUMMARY_APP_NAME));
- assertEquals(appType.name(), json.get(JsonTags.SLA_SUMMARY_APP_TYPE));
- assertEquals("RUNNING", json.get(JsonTags.SLA_SUMMARY_JOB_STATUS));
- assertEquals(SLAStatus.IN_PROCESS.name(), json.get(JsonTags.SLA_SUMMARY_SLA_STATUS));
- assertEquals(nominalTime.getTimeInMillis(), json.get(JsonTags.SLA_SUMMARY_NOMINAL_TIME));
- assertEquals(nominalTime.getTimeInMillis(), json.get(JsonTags.SLA_SUMMARY_EXPECTED_START));
- assertEquals(actualStart.getTimeInMillis(), json.get(JsonTags.SLA_SUMMARY_ACTUAL_START));
- assertEquals(expectedEnd.getTimeInMillis(), json.get(JsonTags.SLA_SUMMARY_EXPECTED_END));
- 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));
- nominalTime.add(Calendar.HOUR, 1);
- }
- }
-
- private void insertEntriesIntoSLASummaryTable(int numEntries, String jobIDPrefix, String jobIDSuffix,
- 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++) {
- Calendar actualStart = (Calendar) nominalTime.clone();
- actualStart.add(Calendar.MINUTE, i);
- Calendar expectedEnd = (Calendar) nominalTime.clone();
- expectedEnd.add(Calendar.MINUTE, 60);
- Calendar actualEnd = (Calendar) expectedEnd.clone();
- actualEnd.add(Calendar.MINUTE, i);
- 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);
- }
- }
-
- 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/90a97694/core/src/test/java/org/apache/oozie/servlet/TestV2SLAServletBundle.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/oozie/servlet/TestV2SLAServletBundle.java b/core/src/test/java/org/apache/oozie/servlet/TestV2SLAServletBundle.java
new file mode 100644
index 0000000..72bbac4
--- /dev/null
+++ b/core/src/test/java/org/apache/oozie/servlet/TestV2SLAServletBundle.java
@@ -0,0 +1,272 @@
+/**
+ * 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.oozie.servlet;
+
+import com.google.common.collect.LinkedListMultimap;
+import com.google.common.collect.ListMultimap;
+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;
+import org.apache.oozie.client.rest.JsonBean;
+import org.apache.oozie.client.rest.JsonTags;
+import org.apache.oozie.executor.jpa.BatchQueryExecutor;
+import org.apache.oozie.service.Services;
+import org.json.simple.JSONArray;
+import org.json.simple.JSONObject;
+
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.List;
+
+public class TestV2SLAServletBundle extends V2SLAServletTestCase {
+
+ private String sampleBundleId;
+ private String sampleBundleName;
+ private CoordinatorJobBean cjBean1;
+ private CoordinatorJobBean cjBean2;
+ private Date actualStartForMet;
+ private Date expectedStart;
+ private Date actualStartForMiss;
+ private Date actualEndForMet;
+ private Date expectedEnd;
+ private Date actualEndForMiss;
+ private Date futureExpectedEnd;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ Calendar cal = Calendar.getInstance();
+ cal.add(Calendar.MINUTE, -12); //current -12
+ actualStartForMet = cal.getTime();
+ cal.add(Calendar.MINUTE, 2); //current -10
+ expectedStart = cal.getTime();
+ cal.add(Calendar.MINUTE, 1); //current -9
+ actualStartForMiss = cal.getTime();
+ cal.add(Calendar.MINUTE, 3); //current -6
+ actualEndForMet = cal.getTime();
+ cal.add(Calendar.MINUTE, 1); //current -5
+ expectedEnd = cal.getTime();
+ cal.add(Calendar.MINUTE, 2); //current -3
+ actualEndForMiss = cal.getTime();
+ cal.add(Calendar.MINUTE, 8); //current + 5
+ futureExpectedEnd = cal.getTime();
+ setUpBundle();
+ }
+
+
+ private void setUpBundle() throws Exception {
+ //insert Bundle Job/Action, Coord Job/Action
+ List<JsonBean> beans = new ArrayList<>();
+ sampleBundleId = "0000000-000000000000000-" + Services.get().getSystemId() + "-B";
+ BundleJobBean bjBean = createBundleJob(sampleBundleId, Job.Status.RUNNING, false);
+ sampleBundleName = bjBean.getAppName();
+ beans.add(bjBean);
+ cjBean1 = createCoordJob(CoordinatorJob.Status.SUCCEEDED, false, true);
+ beans.add(cjBean1);
+ cjBean2 = createCoordJob(CoordinatorJob.Status.SUCCEEDED, false, true);
+ beans.add(cjBean2);
+
+ BundleActionBean baBean1 = createBundleAction(sampleBundleId, cjBean1.getId(), "bundle-action-1", 0,
+ Job.Status.RUNNING);
+ beans.add(baBean1);
+ BundleActionBean baBean2 = createBundleAction(sampleBundleId, cjBean2.getId(), "bundle-action-2", 0,
+ Job.Status.RUNNING);
+ beans.add(baBean2);
+
+ BatchQueryExecutor.getInstance().executeBatchInsertUpdateDelete(beans, null, null);
+
+ // START_MET, DURATION_MET, END_MET
+ insertEntriesIntoSLASummaryTable(cjBean1.getId() + "@1", cjBean1.getId(), "testapp-1",
+ AppType.COORDINATOR_ACTION, SLAEvent.EventStatus.END_MET, SLAEvent.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, SLAEvent.EventStatus.END_MISS, SLAEvent.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, SLAEvent.EventStatus.DURATION_MISS, SLAEvent.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, SLAEvent.SLAStatus.NOT_STARTED, expectedStart, null, 10, -1,
+ futureExpectedEnd, null, expectedStart);
+ }
+
+ public void testNonExistentBundleId() throws Exception {
+ ListMultimap<String, String> filterParams = LinkedListMultimap.create();
+ filterParams.put("bundle", "xxxx");
+ JSONArray array = getSLAJSONResponse("GMT", filterParams);
+ assertEquals( "JSON array size", 0, array.size());
+ }
+
+ public void testBundleId() throws Exception {
+ ListMultimap<String, String> filterParams = LinkedListMultimap.create();
+ filterParams.put("bundle", sampleBundleId);
+ JSONArray array = getSLAJSONResponse("GMT", filterParams);
+ 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("startDelay JSON tag", -2L, json.get(JsonTags.SLA_SUMMARY_START_DELAY));
+ assertEquals("durationDelay JSON tag", 0L, json.get(JsonTags.SLA_SUMMARY_DURATION_DELAY));
+ assertEquals("endDelay JSON tag",-1L, json.get(JsonTags.SLA_SUMMARY_END_DELAY));
+ }
+ }
+ }
+
+ public void testIdBundleId() throws Exception {
+ ListMultimap<String, String> filterParams = LinkedListMultimap.create();
+ filterParams.put("id", cjBean2.getId() + "@1");
+ filterParams.put("bundle", sampleBundleId);
+ JSONArray array = getSLAJSONResponse("GMT", filterParams);
+ assertEquals("sla filter result size for id + sampleBundleId", 1, 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("id + sampleBundleId filter summary start delay",
+ -2L, json.get(JsonTags.SLA_SUMMARY_START_DELAY));
+ assertEquals("id + sampleBundleId filter summary duration delay", 0L,
+ json.get(JsonTags.SLA_SUMMARY_DURATION_DELAY));
+ assertEquals("id + sampleBundleId filter summary end delay",
+ -1L, json.get(JsonTags.SLA_SUMMARY_END_DELAY));
+ }
+ }
+ }
+
+ public void testNonMatchingParentIdBundleId() throws Exception {
+ ListMultimap<String, String> filterParams = LinkedListMultimap.create();
+ filterParams.put("parent_id", "xxx");
+ filterParams.put("bundle", sampleBundleId);
+ JSONArray array = getSLAJSONResponse("GMT", filterParams);
+ assertEquals("sla filter result size for parent_id + sampleBundleId", 0, array.size());
+ }
+
+ public void testBundleName() throws Exception {
+ ListMultimap<String, String> filterParams = LinkedListMultimap.create();
+ filterParams.put("bundle", sampleBundleName);
+ JSONArray array = getSLAJSONResponse("GMT", filterParams);
+ assertEquals("JSON array size", 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("startDelay JSON tag", -2L, json.get(JsonTags.SLA_SUMMARY_START_DELAY));
+ assertEquals("durationDelay JSON tag", 0L, json.get(JsonTags.SLA_SUMMARY_DURATION_DELAY));
+ assertEquals("endDelay JSON tag", -1L, json.get(JsonTags.SLA_SUMMARY_END_DELAY));
+ }
+ }
+ }
+
+ public void testBundleSLAEventEventStatus() throws Exception {
+ ListMultimap<String, String> filterParams = LinkedListMultimap.create();
+ filterParams.put("bundle", sampleBundleId);
+ filterParams.put("event_status", "END_MISS");
+ JSONArray array = getSLAJSONResponse("GMT", filterParams);
+ assertEquals("JSON array size", 1, array.size());
+ JSONObject json = (JSONObject) array.get(0);
+ String parentId = (String) json.get(JsonTags.SLA_SUMMARY_PARENT_ID);
+ assertTrue("Invalid parentId JSON tag", parentId.equals(cjBean1.getId()) || parentId.equals(cjBean2.getId()));
+ String id = (String) json.get(JsonTags.SLA_SUMMARY_ID);
+ assertEquals("id JSON tag", cjBean1.getId() + "@2", id);
+ String es = (String) json.get(JsonTags.SLA_SUMMARY_EVENT_STATUS);
+ assertTrue("eventStatus JSON tag should contain END_MISS", es.contains("END_MISS"));
+ }
+
+ public void testBundleSLAEventEventStatusStartMet() throws Exception {
+ ListMultimap<String, String> filterParams = LinkedListMultimap.create();
+ filterParams.put("bundle", sampleBundleId);
+ filterParams.put("event_status", "START_MET");
+ JSONArray array = getSLAJSONResponse("GMT", filterParams);
+ assertEquals("JSON array size", 1, array.size());
+ JSONObject json = (JSONObject) array.get(0);
+ String parentId = (String) json.get(JsonTags.SLA_SUMMARY_PARENT_ID);
+ assertTrue("Invalid parentId JSON tag", parentId.equals(cjBean1.getId()) || parentId.equals(cjBean2.getId()));
+ String id = (String) json.get(JsonTags.SLA_SUMMARY_ID);
+ assertEquals("id JSON tag", cjBean1.getId() + "@1", id);
+ String es = (String) json.get(JsonTags.SLA_SUMMARY_EVENT_STATUS);
+ assertTrue("eventStatus JSON tag should contain START_MET", es.contains("START_MET"));
+ }
+
+ public void testBundleSLAEventSlaStatus() throws Exception {
+ ListMultimap<String, String> filterParams = LinkedListMultimap.create();
+ filterParams.put("bundle", sampleBundleId);
+ filterParams.put("sla_status", "MISS");
+ JSONArray array = getSLAJSONResponse("GMT", filterParams);
+ assertEquals("JSON array size", 1, array.size());
+ JSONObject json = (JSONObject) array.get(0);
+ String id = (String) json.get(JsonTags.SLA_SUMMARY_ID);
+ assertEquals("id JSON tag", cjBean1.getId() + "@2", id);
+ String parentId = (String) json.get(JsonTags.SLA_SUMMARY_PARENT_ID);
+ assertEquals("parentId JSON tag", cjBean1.getId(), parentId);
+ assertEquals("startDelay JSON tag", 1L, json.get(JsonTags.SLA_SUMMARY_START_DELAY));
+ assertEquals("durationDelay JSON tag", 0L, json.get(JsonTags.SLA_SUMMARY_DURATION_DELAY));
+ assertEquals("endDelay JSON tag", 2L, json.get(JsonTags.SLA_SUMMARY_END_DELAY));
+ }
+
+ public void testBundleSLAEventMultipleEventStatus() throws Exception {
+ ListMultimap<String, String> filterParams = LinkedListMultimap.create();
+ filterParams.put("bundle", sampleBundleName);
+ filterParams.put("event_status", "START_MISS");
+ filterParams.put("event_status", "END_MISS");
+ JSONArray array = getSLAJSONResponse(filterParams);
+ assertEquals("JSON array size", 3, 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);
+ assertTrue("invalid id JSON tag", id.equals(cjBean1.getId() + "@2") || id.equals(cjBean2.getId() + "@1")
+ || id.equals(cjBean2.getId() + "@2"));
+ String parentId = (String) json.get(JsonTags.SLA_SUMMARY_PARENT_ID);
+ assertTrue("invalid parentId JSON tag", parentId.equals(cjBean1.getId()) || parentId.equals(cjBean2.getId()));
+ }
+ }
+
+ public void testBundleSLAEventEventStatusSlaStatus() throws Exception {
+ ListMultimap<String, String> filterParams = LinkedListMultimap.create();
+ filterParams.put("bundle", sampleBundleName);
+ filterParams.put("event_status", "DURATION_MISS");
+ filterParams.put("sla_status", "IN_PROCESS");
+ JSONArray array = getSLAJSONResponse(filterParams);
+ assertEquals("JSON array size", 1, array.size());
+ JSONObject json = (JSONObject) array.get(0);
+ assertEquals("id JSON tag", cjBean2.getId() + "@1", (String) json.get(JsonTags.SLA_SUMMARY_ID));
+ assertEquals("parentId JSON tag", cjBean2.getId(), (String) json.get(JsonTags.SLA_SUMMARY_PARENT_ID));
+ String eventStatus = (String) json.get(JsonTags.SLA_SUMMARY_EVENT_STATUS);
+ assertTrue("eventStatus JSON tag should contain DURATION_MISS", eventStatus.contains("DURATION_MISS"));
+ assertTrue("eventStatus JSON tag should contain START_MISS", eventStatus.contains("START_MISS"));
+ assertFalse("eventStatus JSON tag should contain END_MISS or END_MET",
+ eventStatus.contains("END_MISS") || eventStatus.contains("END_MET"));
+ // actualDuration is null on DB while job is running, populates it in API call
+ assertEquals("actualDuration JSON tag", 9L, json.get(JsonTags.SLA_SUMMARY_ACTUAL_DURATION));
+ assertEquals("durationDelay JSON tag", 0L, json.get(JsonTags.SLA_SUMMARY_DURATION_DELAY));
+ }
+
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/oozie/blob/90a97694/core/src/test/java/org/apache/oozie/servlet/TestV2SLAServletIntegration.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/oozie/servlet/TestV2SLAServletIntegration.java b/core/src/test/java/org/apache/oozie/servlet/TestV2SLAServletIntegration.java
new file mode 100644
index 0000000..03d96a0
--- /dev/null
+++ b/core/src/test/java/org/apache/oozie/servlet/TestV2SLAServletIntegration.java
@@ -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.
+ */
+
+package org.apache.oozie.servlet;
+
+import org.apache.oozie.client.rest.RestConstants;
+
+import javax.servlet.http.HttpServletResponse;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.Callable;
+
+public class TestV2SLAServletIntegration extends DagServletTestCase {
+
+ private static final boolean IS_SECURITY_ENABLED = false;
+
+ public void testEmptyQueryParams() throws Exception {
+ Map<String, String> queryParams = new HashMap<>();
+ callHttpEndpointAndAssertResponse(queryParams, HttpServletResponse.SC_BAD_REQUEST);
+ }
+
+ public void testFilterNameTypo() throws Exception {
+ Map<String, String> queryParams = new HashMap<>();
+ queryParams.put(RestConstants.JOBS_FILTER_PARAM + "typo", "app_name=testapp-1");
+ callHttpEndpointAndAssertResponse(queryParams, HttpServletResponse.SC_BAD_REQUEST);
+ }
+
+ public void testValidRequest() throws Exception {
+ Map<String, String> queryParams = new HashMap<>();
+ queryParams.put(RestConstants.JOBS_FILTER_PARAM, "app_name=testapp-1");
+ callHttpEndpointAndAssertResponse(queryParams, HttpServletResponse.SC_OK);
+ }
+
+ private void callHttpEndpointAndAssertResponse(Map<String, String> queryParams, int expectedResponseCode) throws Exception {
+ runTest("/v2/sla", V2SLAServlet.class, IS_SECURITY_ENABLED, new Callable<Void>() {
+ public Void call() throws Exception {
+ URL url = createURL("", queryParams);
+ HttpURLConnection conn = (HttpURLConnection) url.openConnection();
+ conn.setRequestMethod("GET");
+ assertEquals("HTTP response code", expectedResponseCode, conn.getResponseCode());
+ return null;
+ }
+ });
+ }
+
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/oozie/blob/90a97694/core/src/test/java/org/apache/oozie/servlet/TestV2SLAServletSLAJSONResponse.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/oozie/servlet/TestV2SLAServletSLAJSONResponse.java b/core/src/test/java/org/apache/oozie/servlet/TestV2SLAServletSLAJSONResponse.java
new file mode 100644
index 0000000..3ca52ff
--- /dev/null
+++ b/core/src/test/java/org/apache/oozie/servlet/TestV2SLAServletSLAJSONResponse.java
@@ -0,0 +1,307 @@
+/**
+ * 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.oozie.servlet;
+
+import com.google.common.collect.LinkedListMultimap;
+import com.google.common.collect.ListMultimap;
+import org.apache.oozie.AppType;
+import org.apache.oozie.client.event.SLAEvent.EventStatus;
+import org.apache.oozie.client.event.SLAEvent.SLAStatus;
+import org.apache.oozie.executor.jpa.JPAExecutorException;
+import org.apache.oozie.util.DateUtils;
+import org.json.simple.JSONArray;
+
+import java.util.Collections;
+import java.util.Date;
+
+public class TestV2SLAServletSLAJSONResponse extends V2SLAServletTestCase {
+
+ private Date currentTime;
+ private Date nominalTime1;
+ private Date nominalTime2;
+ private Date nominalTime3;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ currentTime = new Date(System.currentTimeMillis());
+ nominalTime1 = DateUtils.parseDateUTC("2012-06-01T10:00Z");
+ nominalTime2 = DateUtils.parseDateUTC("2012-06-02T10:20Z");
+ nominalTime3 = DateUtils.parseDateUTC("2012-06-03T14:00Z");
+ setUpSLASummaryTableForTestSLA();
+ }
+
+ private void setUpSLASummaryTableForTestSLA() throws JPAExecutorException {
+ insertEntriesIntoSLASummaryTable(2, "1-", "-W", "1-C", nominalTime1, "test_app-1", AppType.WORKFLOW_JOB,
+ currentTime, EventStatus.END_MISS, SLAStatus.IN_PROCESS);
+ insertEntriesIntoSLASummaryTable(3, "2-", "-W", null, nominalTime2, "test_app-2", AppType.WORKFLOW_JOB,
+ currentTime, EventStatus.END_MISS, SLAStatus.IN_PROCESS);
+ insertEntriesIntoSLASummaryTable(6, "3-", "-W", "2-C", nominalTime3, "test_app-3", AppType.WORKFLOW_JOB,
+ currentTime, EventStatus.END_MISS, SLAStatus.IN_PROCESS);
+ }
+
+ public void testSLAAppName() throws Exception {
+ ListMultimap<String, String> filterParams = LinkedListMultimap.create();
+ filterParams.put("app_name", "test_app-1");
+ JSONArray array = getSLAJSONResponse(filterParams);
+ // Matches first two - 1-1-W and 1-2-W
+ assertSLAJSONResponse(array, 1, 2, "1-", "-W", "1-C", nominalTime1, "test_app-1", AppType.WORKFLOW_JOB,
+ currentTime);
+ }
+
+ public void testSLAAppNameId() throws Exception {
+ ListMultimap<String, String> filterParams = LinkedListMultimap.create();
+ filterParams.put("app_name", "test_app-2");
+ filterParams.put("id", "2-2-W");
+ JSONArray array = getSLAJSONResponse(filterParams);
+ // Matches second element - 2-2-W
+ assertSLAJSONResponse(array, 2, 2, "2-", "-W", null, nominalTime2, "test_app-2", AppType.WORKFLOW_JOB,
+ currentTime);
+ }
+
+ public void testSLAAppNameNominalStart() throws Exception {
+ ListMultimap<String, String> filterParams = LinkedListMultimap.create();
+ filterParams.put("app_name", "test_app-3");
+ filterParams.put("nominal_after", "2012-06-03T16:00Z");
+ JSONArray array = getSLAJSONResponse(filterParams);
+ // Matches 3-6 elements - 3-3-W 3-4-W 3-5-W 3-6-W
+ assertSLAJSONResponse(array, 3, 6, "3-", "-W", "2-C", nominalTime3, "test_app-3", AppType.WORKFLOW_JOB,
+ currentTime);
+ }
+
+ public void testSLAAppNameNominalEnd() throws Exception {
+ ListMultimap<String, String> filterParams = LinkedListMultimap.create();
+ filterParams.put("parent_id", "2-C");
+ filterParams.put("nominal_after", "2012-06-03T16:00Z");
+ filterParams.put("nominal_before", "2012-06-03T17:00Z");
+ JSONArray array = getSLAJSONResponse(filterParams);
+ // Matches 3rd and 4th element - 3-3-W 3-4-W
+ assertSLAJSONResponse(array, 3, 4, "3-", "-W", "2-C", nominalTime3, "test_app-3", AppType.WORKFLOW_JOB,
+ currentTime);
+ }
+
+ public void testSLAAppNameCreatedStart() throws Exception {
+ ListMultimap<String, String> filterParams = LinkedListMultimap.create();
+ filterParams.put("app_name", "test_app-3");
+ filterParams.put("created_after", "2012-06-03T16:00Z");
+ JSONArray array = getSLAJSONResponse(filterParams);
+ // No matches
+ assertEquals("sla filter result size for app_name + created_after", 0, array.size());
+ }
+
+ public void testSLAAppNameCreatedEnd() throws Exception {
+ ListMultimap<String, String> filterParams = LinkedListMultimap.create();
+ filterParams.put("parent_id", "2-C");
+ filterParams.put("created_after", "2012-06-03T16:00Z");
+ filterParams.put("created_before", "2012-06-03T17:00Z");
+ JSONArray array = getSLAJSONResponse(filterParams);
+ // No matches
+ assertEquals("sla filter result size for parent_id + created_after + created_before", 0, array.size());
+ }
+
+ public void testSLAAppNameExpectedStart() throws Exception {
+ ListMultimap<String, String> filterParams = LinkedListMultimap.create();
+ filterParams.put("app_name", "test_app-3");
+ filterParams.put("expectedstart_after", "2012-06-03T16:00Z");
+ JSONArray array = getSLAJSONResponse(filterParams);
+ // Matches 3-6 elements - 3-3-W 3-4-W 3-5-W 3-6-W
+ assertSLAJSONResponse(array, 3, 6, "3-", "-W", "2-C", nominalTime3, "test_app-3", AppType.WORKFLOW_JOB,
+ currentTime);
+ }
+
+ public void testSLAParentIdExpectedStartInterval() throws Exception {
+ ListMultimap<String, String> filterParams = LinkedListMultimap.create();
+ filterParams.put("parent_id", "2-C");
+ filterParams.put("expectedstart_after", "2012-06-03T16:00Z");
+ filterParams.put("expectedstart_before", "2012-06-03T17:00Z");
+ JSONArray array = getSLAJSONResponse(filterParams);
+ // Matches 3rd and 4th element - 3-3-W 3-4-W
+ assertSLAJSONResponse(array, 3, 4, "3-", "-W", "2-C", nominalTime3, "test_app-3", AppType.WORKFLOW_JOB,
+ currentTime);
+ }
+
+ public void testSLAAppNameExpectedEndStart() throws Exception {
+ ListMultimap<String, String> filterParams = LinkedListMultimap.create();
+ filterParams.put("app_name", "test_app-3");
+ filterParams.put("expectedend_after", "2012-06-03T17:00Z");
+ JSONArray array = getSLAJSONResponse(filterParams);
+ // Matches 3-6 elements - 3-3-W 3-4-W 3-5-W 3-6-W
+ assertSLAJSONResponse(array, 3, 6, "3-", "-W", "2-C", nominalTime3, "test_app-3", AppType.WORKFLOW_JOB,
+ currentTime);
+ }
+
+ public void testSLASLAParentIdExpectedEndInterval() throws Exception {
+ ListMultimap<String, String> filterParams = LinkedListMultimap.create();
+ filterParams.put("parent_id", "2-C");
+ filterParams.put("expectedend_after", "2012-06-03T15:00Z");
+ filterParams.put("expectedend_before", "2012-06-03T16:00Z");
+ JSONArray array = getSLAJSONResponse(filterParams);
+ // Matches 1st and 2nd element - 3-3-W 3-4-W
+ assertSLAJSONResponse(array, 1, 2, "3-", "-W", "2-C", nominalTime3, "test_app-3", AppType.WORKFLOW_JOB,
+ currentTime);
+ }
+
+ public void testSLAAppNameActualStartStart() throws Exception {
+ ListMultimap<String, String> filterParams = LinkedListMultimap.create();
+ filterParams.put("app_name", "test_app-3");
+ filterParams.put("actualstart_after", "2012-06-03T16:00Z");
+ JSONArray array = getSLAJSONResponse(filterParams);
+ // Matches 3-6 elements - 3-3-W 3-4-W 3-5-W 3-6-W
+ assertSLAJSONResponse(array, 3, 6, "3-", "-W", "2-C", nominalTime3, "test_app-3", AppType.WORKFLOW_JOB,
+ currentTime);
+ }
+
+ public void testSLASLAParentIdActualStartInterval() throws Exception {
+ ListMultimap<String, String> filterParams = LinkedListMultimap.create();
+ filterParams.put("parent_id", "2-C");
+ filterParams.put("actualstart_after", "2012-06-03T16:00Z");
+ filterParams.put("actualstart_before", "2012-06-03T18:00Z");
+ JSONArray array = getSLAJSONResponse(filterParams);
+ // Matches 3rd and 4th element - 3-3-W 3-4-W
+ assertSLAJSONResponse(array, 3, 4, "3-", "-W", "2-C", nominalTime3, "test_app-3", AppType.WORKFLOW_JOB,
+ currentTime);
+ }
+
+ public void testSLAAppNameActualEndStart() throws Exception {
+ ListMultimap<String, String> filterParams = LinkedListMultimap.create();
+ filterParams.put("app_name", "test_app-3");
+ filterParams.put("actualend_after", "2012-06-03T16:30Z");
+
+ JSONArray array = getSLAJSONResponse(filterParams);
+ // Matches 3-6 elements - 3-3-W 3-4-W 3-5-W 3-6-W
+ assertSLAJSONResponse(array, 3, 6, "3-", "-W", "2-C", nominalTime3, "test_app-3", AppType.WORKFLOW_JOB,
+ currentTime);
+ }
+
+ public void testSLASLAParentIdActualEndInterval() throws Exception {
+ ListMultimap<String, String> filterParams = LinkedListMultimap.create();
+ filterParams.put("parent_id", "2-C");
+ filterParams.put("actualend_after", "2012-06-03T16:30Z");
+ filterParams.put("actualend_before", "2012-06-03T18:30Z");
+ JSONArray array = getSLAJSONResponse(filterParams);
+ // Matches 3rd and 4th element - 3-3-W 3-4-W
+ assertSLAJSONResponse(array, 3, 4, "3-", "-W", "2-C", nominalTime3, "test_app-3", AppType.WORKFLOW_JOB,
+ currentTime);
+ }
+
+ public void testSLAAppNameAppType() throws Exception {
+ ListMultimap<String, String> filterParams = LinkedListMultimap.create();
+ filterParams.put("app_name", "test_app-1");
+ filterParams.put("app_type", AppType.WORKFLOW_JOB.toString());
+ JSONArray array = getSLAJSONResponse(filterParams);
+ // Matches first two - 1-1-W and 1-2-W
+ assertSLAJSONResponse(array, 1, 2, "1-", "-W", "1-C", nominalTime1, "test_app-1", AppType.WORKFLOW_JOB,
+ currentTime);
+ }
+
+ public void testSLAAppNameUserName() throws Exception {
+ ListMultimap<String, String> filterParams = LinkedListMultimap.create();
+ filterParams.put("app_name", "test_app-1");
+ filterParams.put("user_name", "testuser");
+ JSONArray array = getSLAJSONResponse(filterParams);
+ // Matches first two - 1-1-W and 1-2-W
+ assertSLAJSONResponse(array, 1, 2, "1-", "-W", "1-C", nominalTime1, "test_app-1", AppType.WORKFLOW_JOB,
+ currentTime);
+ }
+
+ public void testSLAAppNameJobStatus() throws Exception {
+ ListMultimap<String, String> filterParams = LinkedListMultimap.create();
+ filterParams.put("app_name", "test_app-3");
+ filterParams.put("job_status", "RUNNING");
+ JSONArray array = getSLAJSONResponse(filterParams);
+ // Matches 1-6 elements - 3-1-W 3-2-W 3-3-W 3-4-W 3-5-W 3-6-W
+ assertSLAJSONResponse(array, 1, 6, "3-", "-W", "2-C", nominalTime3, "test_app-3", AppType.WORKFLOW_JOB,
+ currentTime);
+ }
+
+ public void testSLAAppNameJobStatusDescendingDefaultField() throws Exception {
+ ListMultimap<String, String> filterParams = LinkedListMultimap.create();
+ filterParams.put("app_name", "test_app-3");
+ filterParams.put("job_status", "RUNNING");
+ JSONArray array = getSLAJSONResponse(null, filterParams, null, true);
+ // Matches 1-6 elements i reverse order- 3-6-W 3-5-W 3-4-W 3-3-W 3-2-W 3-1-W
+ Collections.reverse(array);
+ assertSLAJSONResponse(array, 1, 6, "3-", "-W", "2-C", nominalTime3, "test_app-3", AppType.WORKFLOW_JOB,
+ currentTime);
+ }
+
+ public void testSLAAppNameJobStatusAscendingJobId() throws Exception {
+ ListMultimap<String, String> filterParams = LinkedListMultimap.create();
+ filterParams.put("app_name", "test_app-3");
+ filterParams.put("job_status", "RUNNING");
+ JSONArray array = getSLAJSONResponse(null, filterParams, "jobId", false);
+ // Matches 1-6 elements - 3-1-W 3-2-W 3-3-W 3-4-W 3-5-W 3-6-W
+ assertSLAJSONResponse(array, 1, 6, "3-", "-W", "2-C", nominalTime3, "test_app-3", AppType.WORKFLOW_JOB,
+ currentTime);
+ }
+
+ public void testSLAAppNameJobStatusDescendingJobId() throws Exception {
+ ListMultimap<String, String> filterParams = LinkedListMultimap.create();
+ filterParams.put("app_name", "test_app-3");
+ filterParams.put("job_status", "RUNNING");
+ JSONArray array = getSLAJSONResponse(null, filterParams, "jobId", true);
+ // Matches 1-6 elements i reverse order- 3-6-W 3-5-W 3-4-W 3-3-W 3-2-W 3-1-W
+ Collections.reverse(array);
+ assertSLAJSONResponse(array, 1, 6, "3-", "-W", "2-C", nominalTime3, "test_app-3", AppType.WORKFLOW_JOB,
+ currentTime);
+ }
+
+ public void testSLAAscendingAppName() throws Exception {
+ ListMultimap<String, String> filterParams = LinkedListMultimap.create();
+ filterParams.put("job_status", "RUNNING");
+ JSONArray array = getSLAJSONResponse(null, filterParams, "appName", false);
+ // Matches all elements
+ // 3-1-W 3-2-W 3-3-W 3-4-W 3-5-W 3-6-W
+ // 2-1-W 2-2-W 2-3-W
+ // 1-1-W and 1-2-W
+ assertSLAJSONResponse(array, 0, 2, 1, 2, "1-", "-W", "1-C", nominalTime1, "test_app-1", AppType.WORKFLOW_JOB,
+ currentTime);
+ assertSLAJSONResponse(array, 2, 5, 1, 3, "2-", "-W", null, nominalTime2, "test_app-2", AppType.WORKFLOW_JOB,
+ currentTime);
+ assertSLAJSONResponse(array, 5, 11, 1, 6, "3-", "-W", "2-C", nominalTime3, "test_app-3", AppType.WORKFLOW_JOB,
+ currentTime);
+ }
+
+ public void testSLALikeAppNamePercentSign() throws Exception {
+ ListMultimap<String, String> filterParams = LinkedListMultimap.create();
+ filterParams.put("app_name", "test_app%");
+ filterParams.put("job_status", "RUNNING");
+ JSONArray array = getSLAJSONResponse(null, filterParams, "appName", false);
+ // Matches all elements
+ // 3-1-W 3-2-W 3-3-W 3-4-W 3-5-W 3-6-W
+ // 2-1-W 2-2-W 2-3-W
+ // 1-1-W and 1-2-W
+ assertSLAJSONResponse(array, 0, 2, 1, 2, "1-", "-W", "1-C", nominalTime1, "test_app-1", AppType.WORKFLOW_JOB,
+ currentTime);
+ assertSLAJSONResponse(array, 2, 5, 1, 3, "2-", "-W", null, nominalTime2, "test_app-2", AppType.WORKFLOW_JOB,
+ currentTime);
+ assertSLAJSONResponse(array, 5, 11, 1, 6, "3-", "-W", "2-C", nominalTime3, "test_app-3", AppType.WORKFLOW_JOB,
+ currentTime);
+ }
+
+ public void testSLALikeAppNameLikeUnderscore1() throws Exception {
+ ListMultimap<String, String> filterParams = LinkedListMultimap.create();
+ filterParams.put("app_name", "_est_app-1");
+ JSONArray array = getSLAJSONResponse(filterParams);
+ assertEquals( "JSON array size", 2, array.size());
+ assertSLAJSONResponse(array, 0, 2, 1, 2, "1-", "-W", "1-C", nominalTime1, "test_app-1", AppType.WORKFLOW_JOB,
+ currentTime);
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/oozie/blob/90a97694/core/src/test/java/org/apache/oozie/servlet/V2SLAServletTestCase.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/oozie/servlet/V2SLAServletTestCase.java b/core/src/test/java/org/apache/oozie/servlet/V2SLAServletTestCase.java
new file mode 100644
index 0000000..35cbc03
--- /dev/null
+++ b/core/src/test/java/org/apache/oozie/servlet/V2SLAServletTestCase.java
@@ -0,0 +1,152 @@
+/**
+ * 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.oozie.servlet;
+
+import com.google.common.collect.ListMultimap;
+import org.apache.oozie.AppType;
+import org.apache.oozie.client.event.SLAEvent.EventStatus;
+import org.apache.oozie.client.event.SLAEvent.SLAStatus;
+import org.apache.oozie.client.rest.JsonTags;
+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.test.XDataTestCase;
+import org.json.simple.JSONArray;
+import org.json.simple.JSONObject;
+
+import java.util.Calendar;
+import java.util.Date;
+
+public abstract class V2SLAServletTestCase extends XDataTestCase {
+
+ private V2SLAServlet v2SLAServlet;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ Services services = new Services();
+ services.init();
+ v2SLAServlet = new V2SLAServlet();
+ }
+
+ protected void insertEntriesIntoSLASummaryTable(int numEntries, String jobIDPrefix, String jobIDSuffix,
+ 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++) {
+ Calendar actualStart = (Calendar) nominalTime.clone();
+ actualStart.add(Calendar.MINUTE, i);
+ Calendar expectedEnd = (Calendar) nominalTime.clone();
+ expectedEnd.add(Calendar.MINUTE, 60);
+ Calendar actualEnd = (Calendar) expectedEnd.clone();
+ actualEnd.add(Calendar.MINUTE, i);
+ 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);
+ }
+ }
+
+ protected 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);
+ }
+
+ protected JSONArray getSLAJSONResponse(ListMultimap<String, String> filterParams) throws Exception {
+ return getSLAJSONResponse(null, filterParams);
+ }
+
+ protected JSONArray getSLAJSONResponse(String timeZoneId, ListMultimap<String, String> filterParams) throws Exception {
+ return getSLAJSONResponse(timeZoneId, filterParams, null, false);
+ }
+
+
+ protected JSONArray getSLAJSONResponse(String timeZoneId, ListMultimap<String, String> filterParams, String sortbyColumn,
+ boolean isDescendingOrder) throws Exception {
+ JSONObject json = v2SLAServlet.getSLASummaryListByFilterParams(timeZoneId, 1000, filterParams, sortbyColumn,
+ isDescendingOrder);
+ JSONArray array = (JSONArray)json.get(JsonTags.SLA_SUMMARY_LIST);
+ return array;
+ }
+
+ protected void assertSLAJSONResponse(JSONArray array, int startRange, int endRange, String jobIDPrefix,
+ String jobIDSuffix, String parentId, Date startNominalTime, String appName,
+ AppType appType, Date currentTime) {
+ assertSLAJSONResponse(array, 0, array.size(), startRange, endRange, jobIDPrefix, jobIDSuffix, parentId, startNominalTime,
+ appName, appType, currentTime);
+ }
+
+ protected void assertSLAJSONResponse(JSONArray array, int arrayStartIndex, int arrayEndIndex, int startRange, int endRange,
+ String jobIDPrefix,
+ String jobIDSuffix, String parentId, Date startNominalTime, String appName, AppType appType,
+ Date currentTime) {
+ Calendar nominalTime = Calendar.getInstance();
+ nominalTime.setTime(startNominalTime);
+ nominalTime.add(Calendar.HOUR, (startRange - 1));
+ int index = arrayStartIndex;
+ assertEquals("JSON array size", endRange - (startRange - 1), arrayEndIndex - arrayStartIndex);
+ for (int i = startRange; i <= endRange; i++) {
+ Calendar actualStart = (Calendar) nominalTime.clone();
+ actualStart.add(Calendar.MINUTE, i);
+ Calendar expectedEnd = (Calendar) nominalTime.clone();
+ expectedEnd.add(Calendar.MINUTE, 60);
+ Calendar actualEnd = (Calendar) expectedEnd.clone();
+ actualEnd.add(Calendar.MINUTE, i);
+ JSONObject json = (JSONObject) array.get(index++);
+ assertEquals("id JSON tag", jobIDPrefix + i + jobIDSuffix, json.get(JsonTags.SLA_SUMMARY_ID));
+ assertEquals("parentId JSON tag", parentId, json.get(JsonTags.SLA_SUMMARY_PARENT_ID));
+ assertEquals("appName JSON tag", appName, json.get(JsonTags.SLA_SUMMARY_APP_NAME));
+ assertEquals("appType JSON tag", appType.name(), json.get(JsonTags.SLA_SUMMARY_APP_TYPE));
+ assertEquals("jobStatus JSON tag", "RUNNING", json.get(JsonTags.SLA_SUMMARY_JOB_STATUS));
+ assertEquals("slaStatus JSON tag", SLAStatus.IN_PROCESS.name(), json.get(JsonTags.SLA_SUMMARY_SLA_STATUS));
+ assertEquals("nominalTime JSON tag", nominalTime.getTimeInMillis(), json.get(JsonTags.SLA_SUMMARY_NOMINAL_TIME));
+ assertEquals("expectedStart JSON tag", nominalTime.getTimeInMillis(), json.get(JsonTags.SLA_SUMMARY_EXPECTED_START));
+ assertEquals("actualStart JSON tag", actualStart.getTimeInMillis(), json.get(JsonTags.SLA_SUMMARY_ACTUAL_START));
+ assertEquals("expectedEnd JSON tag", expectedEnd.getTimeInMillis(), json.get(JsonTags.SLA_SUMMARY_EXPECTED_END));
+ assertEquals("actualEnd JSON tag", actualEnd.getTimeInMillis(), json.get(JsonTags.SLA_SUMMARY_ACTUAL_END));
+ assertEquals("expectedDuration JSON tag", 10L, json.get(JsonTags.SLA_SUMMARY_EXPECTED_DURATION));
+ assertEquals("actualDuration JSON tag", 15L, json.get(JsonTags.SLA_SUMMARY_ACTUAL_DURATION));
+ nominalTime.add(Calendar.HOUR, 1);
+ }
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/oozie/blob/90a97694/docs/src/site/markdown/DG_SLAMonitoring.md
----------------------------------------------------------------------
diff --git a/docs/src/site/markdown/DG_SLAMonitoring.md b/docs/src/site/markdown/DG_SLAMonitoring.md
index 0831b93..deacef9 100644
--- a/docs/src/site/markdown/DG_SLAMonitoring.md
+++ b/docs/src/site/markdown/DG_SLAMonitoring.md
@@ -179,27 +179,55 @@ hook on a subscriber to receive those messages. For more info on setting up and
In the REST API, the following filters can be applied while fetching SLA information:
- * app_name - Application name
- * 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 status 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,
+ * `app_name` - Application name
+ * `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_after` and `nominal_before` - 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 status 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
+ * `job_status` - job status such as CREATED/STARTED/SUCCEEDED/KILLED/FAILED
+ * `app_type` - application type such as COORDINATOR_ACTION/COORDINATOR_JOB/WORKFLOW_JOB/WORKFLOW_ACTION
+ * `user_name` - the username of the user who submitted the job
+ * `created_after` and `created_before` - Start and End range for created time of the workflow or coordinator.
+ * `expectedstart_after` and `expectedstart_before` - Start and End range for expected start time of the workflow or coordinator.
+ * `expectedend_after` and `expectedend_before` - Start and End range for expected end time of the workflow or coordinator.
+ * `actualstart_after` and `actualstart_before` - Start and End range for actual start time of the workflow or coordinator.
+ * `actualend_after` and `actualend_before` - Start and End range for actual end time of the workflow or coordinator.
+ * `actual_duration_min` and `actual_duration_max` - Min and Max range for actual duration (in milliseconds)
+ * `expected_duration_min` and `expected_duration_max` - Min and Max range for expected duration (in milliseconds)
+
+It is possible to specify multiple filter conditions with semicolon separation, only information meeting all the conditions will be
+fetched.
+
+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`.
+
+For the `app_name`, `app_type`, `user_name`, and `job_status` filter fields two wildchars can also be used:
+the percent sign ( `%` ) represents zero, one, or multiple characters, the underscore ( `_` ) character represents a single
+character.
+
+For compatibility reasons `nominal_start` and `nominal_end` filter names can also be used instead of `nominal_after`
+and `nominal_before`.
+
+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.
+It is possible to specify the ordering of the list by using the `sortby` and the `order` parameters. The possible values for the
+`sortby` parameter are: actualDuration, actualEndTS, actualStartTS, appName, appType, createdTimeTS, eventProcessed, eventStatus,
+expectedDuration, expectedEndTS, expectedStartTS, jobId, jobStatus, lastModifiedTS, nominalTimeTS, parentId, slaStatus, and user.
+The possible value for the `order` parameter are: desc and asc. The default value for the `sortby` parameter is nominalTimeTS, the
+default value for the `order` parameter is asc. If several items has the same sortby column values the these items will be sorted
+by the ascending order of the nominalTimeTS field.
+
The examples below demonstrate the use of REST API and explains the JSON response.
### Scenario 1: Workflow Job Start_Miss
**Request:**
```
-GET <oozie-host>:<port>/oozie/v2/sla?timezone=GMT&filter=nominal_start=2013-06-18T00:01Z;nominal_end=2013-06-23T00:01Z;app_name=my-sla-app
+GET <oozie-host>:<port>/oozie/v2/sla?timezone=GMT&filter=nominal_after=2013-06-18T00:01Z;nominal_before=2013-06-23T00:01Z;app_name=my-sla-app
```
**JSON Response**
@@ -352,6 +380,361 @@ GET <oozie-host>:<port>/oozie/v2/sla?timezone=GMT&filter=bundle=1234567-15013022
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).
+### Scenario 5: Workflow jobs actually started in a 24 hour period
+*Request:*
+```
+GET <oozie-host>:<port>/oozie/v2/sla?timezone=GMT&filter=app_type=WORKFLOW_JOB;actualstart_after=2018-08-13T00:01Z;actualstart_before=2018-08-14T00:01Z
+```
+
+*JSON Response*
+```
+{
+ "nominalTime": "Fri, 01 Jan 2010 01:00:00 GMT",
+ "jobStatus": "SUCCEEDED",
+ "expectedEnd": "Fri, 01 Jan 2010 01:10:00 GMT",
+ "appName": "one-op-wf",
+ "actualEnd": "Mon, 13 Aug 2018 14:49:21 GMT",
+ "actualDuration": 503,
+ "expectedStart": "Fri, 01 Jan 2010 01:01:00 GMT",
+ "expectedDuration": 300000,
+ "durationDelay": -4,
+ "slaStatus": "MISS",
+ "appType": "WORKFLOW_JOB",
+ "slaAlertStatus": "Enabled",
+ "eventStatus": "DURATION_MET,END_MISS,START_MISS",
+ "startDelay": 4531068,
+ "id": "0000001-180813160322492-oozie-test-W",
+ "lastModified": "Mon, 13 Aug 2018 14:49:31 GMT",
+ "user": "testuser",
+ "actualStart": "Mon, 13 Aug 2018 14:49:20 GMT",
+ "endDelay": 4531059
+ },
+ {
+ "nominalTime": "Fri, 01 Jan 2010 02:00:00 GMT",
+ "jobStatus": "SUCCEEDED",
+ "expectedEnd": "Fri, 01 Jan 2010 02:10:00 GMT",
+ "appName": "one-op-wf",
+ "actualEnd": "Mon, 13 Aug 2018 14:49:21 GMT",
+ "actualDuration": 222,
+ "expectedStart": "Fri, 01 Jan 2010 02:01:00 GMT",
+ "expectedDuration": 300000,
+ "durationDelay": -4,
+ "slaStatus": "MISS",
+ "appType": "WORKFLOW_JOB",
+ "slaAlertStatus": "Enabled",
+ "eventStatus": "DURATION_MET,END_MISS,START_MISS",
+ "startDelay": 4531008,
+ "id": "0000002-180813160322492-oozie-test-W",
+ "lastModified": "Mon, 13 Aug 2018 14:49:41 GMT",
+ "user": "testuser",
+ "actualStart": "Mon, 13 Aug 2018 14:49:21 GMT",
+ "endDelay": 4530999
+ }
+```
+
+Scenario #5 is to get SLA information of all workflow jobs by filtering for the actual start date
+instead of the nominal start date.
+
+### Scenario 6: Workflow jobs executed much faster than required
+*Request:*
+```
+GET <oozie-host>:<port>/oozie/v2/sla?timezone=GMT&filter=app_type=WORKFLOW_JOB;expected_duration_min=10000;actual_duration_max=1000
+```
+
+*JSON Response*
+```
+{
+ "nominalTime": "Fri, 01 Jan 2010 01:00:00 GMT",
+ "jobStatus": "SUCCEEDED",
+ "expectedEnd": "Fri, 01 Jan 2010 01:10:00 GMT",
+ "appName": "one-op-wf",
+ "actualEnd": "Mon, 13 Aug 2018 14:49:21 GMT",
+ "actualDuration": 503,
+ "expectedStart": "Fri, 01 Jan 2010 01:01:00 GMT",
+ "expectedDuration": 300000,
+ "durationDelay": -4,
+ "slaStatus": "MISS",
+ "appType": "WORKFLOW_JOB",
+ "slaAlertStatus": "Enabled",
+ "eventStatus": "DURATION_MET,END_MISS,START_MISS",
+ "startDelay": 4531068,
+ "id": "0000001-180813160322492-oozie-test-W",
+ "lastModified": "Mon, 13 Aug 2018 14:49:31 GMT",
+ "user": "testuser",
+ "actualStart": "Mon, 13 Aug 2018 14:49:20 GMT",
+ "endDelay": 4531059
+ },
+ {
+ "nominalTime": "Fri, 01 Jan 2010 02:00:00 GMT",
+ "jobStatus": "SUCCEEDED",
+ "expectedEnd": "Fri, 01 Jan 2010 02:10:00 GMT",
+ "appName": "one-op-wf",
+ "actualEnd": "Mon, 13 Aug 2018 14:49:21 GMT",
+ "actualDuration": 222,
+ "expectedStart": "Fri, 01 Jan 2010 02:01:00 GMT",
+ "expectedDuration": 300000,
+ "durationDelay": -4,
+ "slaStatus": "MISS",
+ "appType": "WORKFLOW_JOB",
+ "slaAlertStatus": "Enabled",
+ "eventStatus": "DURATION_MET,END_MISS,START_MISS",
+ "startDelay": 4531008,
+ "id": "0000002-180813160322492-oozie-test-W",
+ "lastModified": "Mon, 13 Aug 2018 14:49:41 GMT",
+ "user": "testuser",
+ "actualStart": "Mon, 13 Aug 2018 14:49:21 GMT",
+ "endDelay": 4530999
+ }
+```
+
+Scenario #6 is to get SLA information of all workflow jobs where the expected duration was more than 10 seconds (10000ms),
+but the actual duration was less than a second (1000ms).
+
+### Scenario 7: Coordinator actions with START_MET or END_MET event status
+
+*Request:*
+```
+GET <oozie-host>:<port>/oozie/v2/sla?timezone=GMT&filter=app_type=COORDINATOR_ACTION;event_status=START_MET,END_MET
+```
+
+*JSON Response*
+```
+ {
+ "nominalTime": "Fri, 01 Jan 2010 01:00:00 GMT",
+ "jobStatus": "SUCCEEDED",
+ "expectedEnd": "Fri, 01 Jan 2010 01:10:00 GMT",
+ "appName": "aggregator-coord",
+ "actualEnd": "Wed, 29 Aug 2018 10:29:59 GMT",
+ "actualDuration": 167,
+ "expectedStart": "Tue, 18 Feb 2200 11:41:46 GMT",
+ "expectedDuration": 60000,
+ "parentId": "0000006-180829120813646-oozie-test-C",
+ "durationDelay": 0,
+ "slaStatus": "MISS",
+ "appType": "COORDINATOR_ACTION",
+ "slaAlertStatus": "Disabled",
+ "eventStatus": "START_MET,DURATION_MET,END_MISS",
+ "startDelay": -95446151,
+ "id": "0000006-180829120813646-oozie-test-C@1",
+ "lastModified": "Wed, 29 Aug 2018 10:30:07 GMT",
+ "user": "testuser",
+ "actualStart": "Wed, 29 Aug 2018 10:29:59 GMT",
+ "endDelay": 4553839
+ },
+ {
+ "nominalTime": "Fri, 01 Jan 2010 01:00:00 GMT",
+ "jobStatus": "SUCCEEDED",
+ "expectedEnd": "Fri, 05 Jan 2029 11:39:31 GMT",
+ "appName": "aggregator-coord",
+ "actualEnd": "Wed, 29 Aug 2018 10:15:48 GMT",
+ "actualDuration": 394,
+ "expectedStart": "Fri, 01 Jan 2010 01:01:00 GMT",
+ "expectedDuration": 60000,
+ "parentId": "0000000-180829120813646-oozie-test-C",
+ "durationDelay": 0,
+ "slaStatus": "MET",
+ "appType": "COORDINATOR_ACTION",
+ "slaAlertStatus": "Disabled",
+ "eventStatus": "START_MISS,DURATION_MET,END_MET",
+ "startDelay": 4553834,
+ "id": "0000000-180829120813646-oozie-test-C@1",
+ "lastModified": "Wed, 29 Aug 2018 10:15:57 GMT",
+ "user": "testuser",
+ "actualStart": "Wed, 29 Aug 2018 10:15:48 GMT",
+ "endDelay": -5446163
+ },
+ {
+ "nominalTime": "Fri, 01 Jan 2010 02:00:00 GMT",
+ "jobStatus": "SUCCEEDED",
+ "expectedEnd": "Fri, 01 Jan 2010 02:10:00 GMT",
+ "appName": "aggregator-coord",
+ "actualEnd": "Wed, 29 Aug 2018 10:29:59 GMT",
+ "actualDuration": 172,
+ "expectedStart": "Tue, 18 Feb 2200 12:41:46 GMT",
+ "expectedDuration": 60000,
+ "parentId": "0000006-180829120813646-oozie-test-C",
+ "durationDelay": 0,
+ "slaStatus": "MISS",
+ "appType": "COORDINATOR_ACTION",
+ "slaAlertStatus": "Disabled",
+ "eventStatus": "START_MET,DURATION_MET,END_MISS",
+ "startDelay": -95446211,
+ "id": "0000006-180829120813646-oozie-test-C@2",
+ "lastModified": "Wed, 29 Aug 2018 10:30:17 GMT",
+ "user": "testuser",
+ "actualStart": "Wed, 29 Aug 2018 10:29:59 GMT",
+ "endDelay": 4553779
+ },
+ {
+ "nominalTime": "Fri, 01 Jan 2010 02:00:00 GMT",
+ "jobStatus": "SUCCEEDED",
+ "expectedEnd": "Fri, 05 Jan 2029 12:39:31 GMT",
+ "appName": "aggregator-coord",
+ "actualEnd": "Wed, 29 Aug 2018 10:15:48 GMT",
+ "actualDuration": 208,
+ "expectedStart": "Fri, 01 Jan 2010 02:01:00 GMT",
+ "expectedDuration": 60000,
+ "parentId": "0000000-180829120813646-oozie-test-C",
+ "durationDelay": 0,
+ "slaStatus": "MET",
+ "appType": "COORDINATOR_ACTION",
+ "slaAlertStatus": "Disabled",
+ "eventStatus": "START_MISS,DURATION_MET,END_MET",
+ "startDelay": 4553774,
+ "id": "0000000-180829120813646-oozie-test-C@2",
+ "lastModified": "Wed, 29 Aug 2018 10:16:07 GMT",
+ "user": "testuser",
+ "actualStart": "Wed, 29 Aug 2018 10:15:48 GMT",
+ "endDelay": -5446223
+ }
+```
+
+Scenario #7 shows the possibility of filtering multiple event statuses. We list two comma separated statuses
+(START_MET,END_MET) and list coordinator actions with either START_MET or END_MET event status.
+
+### Scenario 8: Not yet started workflow jobs expected to start before a specified date.
+
+*Request:*
+```
+GET <oozie-host>:<port>/oozie/v2/sla?timezone=GMT&filter=app_type=WORKFLOW_JOB;sla_status=NOT_STARTED;expectedstart_before=2018-08-14T00:01Z
+```
+
+*JSON Response*
+```
+ {
+ "nominalTime": "Fri, 01 Jan 2010 01:00:00 GMT",
+ "jobStatus": "PREP",
+ "expectedEnd": "Fri, 01 Jan 2010 01:10:00 GMT",
+ "appName": "one-op-wf",
+ "actualEnd": null,
+ "actualDuration": -1,
+ "expectedStart": "Fri, 01 Jan 2010 01:01:00 GMT",
+ "expectedDuration": 300000,
+ "slaStatus": "NOT_STARTED",
+ "appType": "WORKFLOW_JOB",
+ "slaAlertStatus": "Enabled",
+ "eventStatus": "START_MISS,END_MISS",
+ "startDelay": 4561259,
+ "id": "0000031-180903152228376-oozie-test-W",
+ "lastModified": "Mon, 03 Sep 2018 14:00:50 GMT",
+ "user": "testuser",
+ "actualStart": null,
+ "endDelay": 4561250
+ }
+```
+
+Scenario #8 shows the possibility to list problematic jobs even before they start. It also shows the possibility to combine
+several filter fields.
+
+### Scenario 9: Filtering for app_name using % wildchar
+
+*Request:*
+```
+GET <oozie-host>:<port>/oozie/v2/sla?timezone=GMT&filter=app_name=appname-%25
+```
+
+Note that the filter is URL encoded, its decoded value is `app_name=appname-%`
+
+*JSON Response*
+```
+ "nominalTime": "Fri, 01 Jan 2010 01:00:00 GMT",
+ "jobStatus": "SUCCEEDED",
+ "expectedEnd": "Fri, 01 Jan 2010 01:10:00 GMT",
+ "appName": "appname-2",
+ "actualEnd": "Wed, 19 Sep 2018 15:02:48 GMT",
+ "actualDuration": 245,
+ "expectedStart": "Fri, 01 Jan 2010 01:01:00 GMT",
+ "expectedDuration": 300000,
+ "durationDelay": -4,
+ "slaStatus": "MISS",
+ "appType": "WORKFLOW_JOB",
+ "slaAlertStatus": "Enabled",
+ "eventStatus": "START_MISS,END_MISS,DURATION_MET",
+ "startDelay": 4584361,
+ "id": "0000003-180919170132414-oozie-test-W",
+ "lastModified": "Wed, 19 Sep 2018 15:02:56 GMT",
+ "user": "testuser",
+ "actualStart": "Wed, 19 Sep 2018 15:02:48 GMT",
+ "endDelay": 4584352
+ },
+ {
+ "nominalTime": "Fri, 01 Jan 2010 01:00:00 GMT",
+ "jobStatus": "SUCCEEDED",
+ "expectedEnd": "Fri, 01 Jan 2010 01:10:00 GMT",
+ "appName": "appname-1",
+ "actualEnd": "Wed, 19 Sep 2018 15:02:23 GMT",
+ "actualDuration": 378,
+ "expectedStart": "Fri, 01 Jan 2010 01:01:00 GMT",
+ "expectedDuration": 300000,
+ "durationDelay": -4,
+ "slaStatus": "MISS",
+ "appType": "WORKFLOW_JOB",
+ "slaAlertStatus": "Enabled",
+ "eventStatus": "START_MISS,END_MISS,DURATION_MET",
+ "startDelay": 4584361,
+ "id": "0000001-180919170132414-oozie-test-W",
+ "lastModified": "Wed, 19 Sep 2018 15:02:26 GMT",
+ "user": "testuser",
+ "actualStart": "Wed, 19 Sep 2018 15:02:23 GMT",
+ "endDelay": 4584352
+ }
+```
+
+### Scenario 9: Filtering for app_name using % wildchar and sorting the order by the application name in descending order
+
+*Request:*
+```
+GET <oozie-host>:<port>/oozie/v2/sla?timezone=GMT&filter=app_name=appname-%25&sortby=appName&order=desc
+```
+
+Note that the filter is URL encoded, its decoded value is `app_name=appname-%`
+
+*JSON Response*
+```
+{
+ "nominalTime": "Fri, 01 Jan 2010 01:00:00 GMT",
+ "jobStatus": "SUCCEEDED",
+ "expectedEnd": "Fri, 01 Jan 2010 01:10:00 GMT",
+ "appName": "appname-2",
+ "actualEnd": "Wed, 19 Sep 2018 15:02:48 GMT",
+ "actualDuration": 245,
+ "expectedStart": "Fri, 01 Jan 2010 01:01:00 GMT",
+ "expectedDuration": 300000,
+ "durationDelay": -4,
+ "slaStatus": "MISS",
+ "appType": "WORKFLOW_JOB",
+ "slaAlertStatus": "Enabled",
+ "eventStatus": "START_MISS,END_MISS,DURATION_MET",
+ "startDelay": 4584361,
+ "id": "0000003-180919170132414-oozie-test-W",
+ "lastModified": "Wed, 19 Sep 2018 15:02:56 GMT",
+ "user": "testuser",
+ "actualStart": "Wed, 19 Sep 2018 15:02:48 GMT",
+ "endDelay": 4584352
+ },
+ {
+ "nominalTime": "Fri, 01 Jan 2010 01:00:00 GMT",
+ "jobStatus": "SUCCEEDED",
+ "expectedEnd": "Fri, 01 Jan 2010 01:10:00 GMT",
+ "appName": "appname-1",
+ "actualEnd": "Wed, 19 Sep 2018 15:02:23 GMT",
+ "actualDuration": 378,
+ "expectedStart": "Fri, 01 Jan 2010 01:01:00 GMT",
+ "expectedDuration": 300000,
+ "durationDelay": -4,
+ "slaStatus": "MISS",
+ "appType": "WORKFLOW_JOB",
+ "slaAlertStatus": "Enabled",
+ "eventStatus": "START_MISS,END_MISS,DURATION_MET",
+ "startDelay": 4584361,
+ "id": "0000001-180919170132414-oozie-test-W",
+ "lastModified": "Wed, 19 Sep 2018 15:02:26 GMT",
+ "user": "testuser",
+ "actualStart": "Wed, 19 Sep 2018 15:02:23 GMT",
+ "endDelay": 4584352
+ }
+```
+
### Sample Email Alert
```
http://git-wip-us.apache.org/repos/asf/oozie/blob/90a97694/release-log.txt
----------------------------------------------------------------------
diff --git a/release-log.txt b/release-log.txt
index 330cee6..27ce4d2 100644
--- a/release-log.txt
+++ b/release-log.txt
@@ -1,5 +1,6 @@
-- Oozie 5.1.0 release (trunk - unreleased)
+OOZIE-3229 [client] [ui] Improved SLA filtering options (asalamon74, andras.piros)
OOZIE-3346 [examples] [action] Fix Git example. PrepareActionsHandler should support XML namespace prefixes (asalamon74, andras.piros)
OOZIE-3347 [examples] Fix Fluent Job global example (asalamon74 via andras.piros)
OOZIE-3160 amend PriorityDelayQueue put()/take() can cause significant CPU load due to busy waiting (pbacsko)
http://git-wip-us.apache.org/repos/asf/oozie/blob/90a97694/webapp/src/main/webapp/console/sla/css/oozie-sla.css
----------------------------------------------------------------------
diff --git a/webapp/src/main/webapp/console/sla/css/oozie-sla.css b/webapp/src/main/webapp/console/sla/css/oozie-sla.css
index d2f2dee..ce6bb4d 100644
--- a/webapp/src/main/webapp/console/sla/css/oozie-sla.css
+++ b/webapp/src/main/webapp/console/sla/css/oozie-sla.css
@@ -42,7 +42,7 @@ body {
margin-right: 20px;
}
-.NominalStart {
+#parent, #nominalCreated, #expected, #actual {
margin: 0px 10px 0px 10px;
}
http://git-wip-us.apache.org/repos/asf/oozie/blob/90a97694/webapp/src/main/webapp/console/sla/js/oozie-sla.js
----------------------------------------------------------------------
diff --git a/webapp/src/main/webapp/console/sla/js/oozie-sla.js b/webapp/src/main/webapp/console/sla/js/oozie-sla.js
index 2ecad22..3a70a15 100644
--- a/webapp/src/main/webapp/console/sla/js/oozie-sla.js
+++ b/webapp/src/main/webapp/console/sla/js/oozie-sla.js
@@ -17,45 +17,43 @@
*/
function initializeDatePicker() {
- $("#startDate").datetimepicker({
- dateFormat: 'yy-mm-dd'
- });
-
- $("#endDate").datetimepicker({
- dateFormat: 'yy-mm-dd'
+ $(".datepicker").datetimepicker({
+ dateFormat: 'yy-mm-dd'
});
}
function onSearchClick(){
- var queryParams = null;
- var filter = "filter=";
+ var queryParams = "";
+ var elements = document.querySelectorAll("#inputArea input")
+
+ for (var i = 0; i < elements.length; i++) {
+ if(elements[i].value != "" && elements[i].id != "job_id") {
+ if (i!=0) {
+ queryParams += ";";
+ }
+ if (elements[i].classList.contains("datepicker")) {
+ var splitDate = elements[i].value.split(" ");
+ queryParams += elements[i].id + "=" + splitDate[0] + "T" + splitDate[1] + "Z";
+ } else {
+ queryParams += elements[i].id + "=" + encodeURIComponent(elements[i].value);
+ }
+ }
+ }
+
var appName = $("#app_name").val();
var jobId = $("#job_id").val();
- var nominalStart = $("#startDate").val();
- var nominalEnd = $("#endDate").val();
if (appName == "" && jobId == "") {
alert("AppName or JobId is required");
}
- else if (appName != "" && jobId != "") {
- alert("Enter only one of AppName or JobId");
- }
else {
- if (appName != "") {
- queryParams = filter+"app_name="+appName;
- }
- else if (jobId != "") {
- queryParams = filter+"id="+jobId+";parent_id="+jobId;
- }
- if (nominalStart != "") {
- var splitNominalStart = nominalStart.split(" ");
- queryParams += ";nominal_start="+splitNominalStart[0]+"T"+splitNominalStart[1]+"Z";
- }
- if (nominalEnd != "") {
- var splitNominalEnd = nominalEnd.split(" ");
- queryParams += ";nominal_end="+splitNominalEnd[0]+"T"+splitNominalEnd[1]+"Z";
+ if (jobId != "") {
+ if (queryParams.length>0) {
+ queryParams += ";";
+ }
+ queryParams += "id=" + jobId + ";parent_id=" + jobId;
}
- fetchData(queryParams);
+ fetchData("filter="+queryParams);
}
}