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); } }