001/**
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *      http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018package org.apache.oozie.sla;
019
020import java.text.ParseException;
021import java.util.Date;
022import org.apache.oozie.AppType;
023import org.apache.oozie.ErrorCode;
024import org.apache.oozie.client.event.SLAEvent.EventStatus;
025import org.apache.oozie.command.CommandException;
026import org.apache.oozie.executor.jpa.JPAExecutorException;
027import org.apache.oozie.executor.jpa.sla.SLARegistrationGetJPAExecutor;
028import org.apache.oozie.service.JPAService;
029import org.apache.oozie.service.ServiceException;
030import org.apache.oozie.service.Services;
031import org.apache.oozie.sla.SLARegistrationBean;
032import org.apache.oozie.sla.service.SLAService;
033import org.apache.oozie.util.DateUtils;
034import org.apache.oozie.util.XLog;
035import org.apache.oozie.util.XmlUtils;
036import org.jdom.Element;
037
038public class SLAOperations {
039
040    private static final String NOMINAL_TIME = "nominal-time";
041    private static final String SHOULD_START = "should-start";
042    private static final String SHOULD_END = "should-end";
043    private static final String MAX_DURATION = "max-duration";
044    private static final String ALERT_EVENTS = "alert-events";
045
046    public static SLARegistrationBean createSlaRegistrationEvent(Element eSla, String jobId, String parentId,
047            AppType appType, String user, String appName, XLog log, boolean rerun) throws CommandException {
048        if (eSla == null || !SLAService.isEnabled()) {
049            log.debug("Not registering SLA for job [{0}]. Sla-Xml null OR SLAService not enabled", jobId);
050            return null;
051        }
052        SLARegistrationBean sla = new SLARegistrationBean();
053
054        // Setting nominal time
055        String strNominalTime = getTagElement(eSla, NOMINAL_TIME);
056        if (strNominalTime == null || strNominalTime.length() == 0) {
057            throw new CommandException(ErrorCode.E1101, NOMINAL_TIME);
058        }
059        Date nominalTime;
060        try {
061            nominalTime = DateUtils.parseDateOozieTZ(strNominalTime);
062            sla.setNominalTime(nominalTime);
063        }
064        catch (ParseException pex) {
065            throw new CommandException(ErrorCode.E0302, strNominalTime, pex);
066        }
067
068        // Setting expected start time
069        String strExpectedStart = getTagElement(eSla, SHOULD_START);
070        if (strExpectedStart != null) {
071            float expectedStart = Float.parseFloat(strExpectedStart);
072            if (expectedStart < 0) {
073                throw new CommandException(ErrorCode.E0302, strExpectedStart, "for SLA Expected start time");
074            }
075            else {
076                Date expectedStartTime = new Date(nominalTime.getTime() + (long) (expectedStart * 60 * 1000));
077                sla.setExpectedStart(expectedStartTime);
078            }
079        }
080
081        // Setting expected end time
082        String strExpectedEnd = getTagElement(eSla, SHOULD_END);
083        if (strExpectedEnd == null || strExpectedEnd.length() == 0) {
084            throw new CommandException(ErrorCode.E1101, SHOULD_END);
085        }
086        float expectedEnd = Float.parseFloat(strExpectedEnd);
087        if (expectedEnd < 0) {
088            throw new CommandException(ErrorCode.E0302, strExpectedEnd, "for SLA Expected end time");
089        }
090        else {
091            Date expectedEndTime = new Date(nominalTime.getTime() + (long) (expectedEnd * 60 * 1000));
092            sla.setExpectedEnd(expectedEndTime);
093        }
094
095        // Setting expected duration in milliseconds
096        String expectedDurationStr = getTagElement(eSla, MAX_DURATION);
097        if (expectedDurationStr != null && expectedDurationStr.length() > 0) {
098            float expectedDuration = Float.parseFloat(expectedDurationStr);
099            if (expectedDuration > 0) {
100                sla.setExpectedDuration((long) (expectedDuration * 60 * 1000));
101            }
102        }
103        else if (sla.getExpectedStart() != null) {
104            sla.setExpectedDuration(sla.getExpectedEnd().getTime() - sla.getExpectedStart().getTime());
105        }
106
107        // Parse desired alert-types i.e. start-miss, end-miss, start-met etc..
108        String alertEvents = getTagElement(eSla, ALERT_EVENTS);
109        if (alertEvents != null) {
110            String events[] = alertEvents.split(",");
111            StringBuilder alertsStr = new StringBuilder();
112            for (int i = 0; i < events.length; i++) {
113                String event = events[i].trim().toUpperCase();
114                try {
115                    EventStatus.valueOf(event);
116                }
117                catch (IllegalArgumentException iae) {
118                    XLog.getLog(SLAService.class).warn(
119                            "Invalid value: [" + event + "]" + " for SLA Alert-event. Should be one of "
120                                    + EventStatus.values() + ". Setting it to default [" + EventStatus.END_MISS.name()
121                                    + "]");
122                    event = EventStatus.END_MISS.name();
123                }
124                alertsStr.append(event).append(",");
125            }
126            sla.setAlertEvents(alertsStr.toString().substring(0, alertsStr.lastIndexOf(",")));
127        }
128
129        // Other sla config
130        sla.setNotificationMsg(getTagElement(eSla, "notification-msg"));
131        sla.setAlertContact(getTagElement(eSla, "alert-contact"));
132        sla.setUpstreamApps(getTagElement(eSla, "upstream-apps"));
133
134        // Oozie defined
135        sla.setId(jobId);
136        sla.setAppType(appType);
137        sla.setAppName(appName);
138        sla.setUser(user);
139        sla.setParentId(parentId);
140
141        SLAService slaService = Services.get().get(SLAService.class);
142        try {
143            if (!rerun) {
144                slaService.addRegistrationEvent(sla);
145            }
146            else {
147                slaService.updateRegistrationEvent(sla);
148            }
149        }
150        catch (ServiceException e) {
151            throw new CommandException(ErrorCode.E1007, " id " + jobId, e.getMessage(), e);
152        }
153
154        log.debug("Job [{0}] reg for SLA. Size of Sla Xml = [{1}]", jobId, XmlUtils.prettyPrint(eSla).toString().length());
155        return sla;
156    }
157
158    /**
159     * Retrieve registration event
160     * @param jobId the jobId
161     * @throws CommandException
162     * @throws JPAExecutorException
163     */
164    public static void updateRegistrationEvent(String jobId) throws CommandException, JPAExecutorException {
165        JPAService jpaService = Services.get().get(JPAService.class);
166        SLAService slaService = Services.get().get(SLAService.class);
167        try {
168            SLARegistrationBean reg = jpaService.execute(new SLARegistrationGetJPAExecutor(jobId));
169            if (reg != null) { //handle coord rerun with different config without sla
170                slaService.updateRegistrationEvent(reg);
171            }
172        }
173        catch (ServiceException e) {
174            throw new CommandException(ErrorCode.E1007, " id " + jobId, e.getMessage(), e);
175        }
176
177    }
178
179    /*
180     * parentId null
181     */
182    public static SLARegistrationBean createSlaRegistrationEvent(Element eSla, String jobId, AppType appType,
183            String user, String appName, XLog log) throws CommandException {
184        return createSlaRegistrationEvent(eSla, jobId, null, appType, user, appName, log, false);
185    }
186
187    /*
188     * appName null
189     */
190    public static SLARegistrationBean createSlaRegistrationEvent(Element eSla, String jobId, String parentId,
191            AppType appType, String user, XLog log) throws CommandException {
192        return createSlaRegistrationEvent(eSla, jobId, parentId, appType, user, null, log, false);
193    }
194
195    /*
196     * parentId + appName null
197     */
198    public static SLARegistrationBean createSlaRegistrationEvent(Element eSla, String jobId, AppType appType,
199            String user, XLog log) throws CommandException {
200        return createSlaRegistrationEvent(eSla, jobId, null, appType, user, null, log, false);
201    }
202
203    private static String getTagElement(Element elem, String tagName) {
204        if (elem != null && elem.getChild(tagName, elem.getNamespace("sla")) != null) {
205            return elem.getChild(tagName, elem.getNamespace("sla")).getText().trim();
206        }
207        else {
208            return null;
209        }
210    }
211
212}