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 */
018
019package org.apache.oozie.sla;
020
021import java.text.ParseException;
022import java.util.Date;
023
024import org.apache.oozie.AppType;
025import org.apache.oozie.ErrorCode;
026import org.apache.oozie.client.OozieClient;
027import org.apache.oozie.client.event.SLAEvent.EventStatus;
028import org.apache.oozie.command.CommandException;
029import org.apache.oozie.executor.jpa.JPAExecutorException;
030import org.apache.oozie.executor.jpa.SLARegistrationQueryExecutor;
031import org.apache.oozie.executor.jpa.SLARegistrationQueryExecutor.SLARegQuery;
032import org.apache.oozie.service.ServiceException;
033import org.apache.oozie.service.Services;
034import org.apache.oozie.sla.service.SLAService;
035import org.apache.oozie.util.DateUtils;
036import org.apache.oozie.util.XLog;
037import org.apache.oozie.util.XmlUtils;
038import org.jdom.Element;
039
040public class SLAOperations {
041
042    public static final String NOMINAL_TIME = "nominal-time";
043    public static final String SHOULD_START = "should-start";
044    public static final String SHOULD_END = "should-end";
045    public static final String MAX_DURATION = "max-duration";
046    public static final String ALERT_EVENTS = "alert-events";
047    public static final String ALL_VALUE = "ALL";
048
049
050    static public XLog LOG = XLog.getLog(SLAOperations.class);
051
052
053    public static SLARegistrationBean createSlaRegistrationEvent(Element eSla, String jobId, String parentId,
054            AppType appType, String user, String appName, XLog log, boolean rerun, boolean disableAlert)
055            throws CommandException {
056        if (eSla == null || !SLAService.isEnabled()) {
057            log.debug("Not registering SLA for job [{0}]. Sla-Xml null OR SLAService not enabled", jobId);
058            return null;
059        }
060        SLARegistrationBean sla = new SLARegistrationBean();
061
062        // Setting nominal time
063        String strNominalTime = getTagElement(eSla, NOMINAL_TIME);
064        Date nominalTime = setNominalTime(strNominalTime, sla);
065
066        // Setting expected start time
067        String strExpectedStart = getTagElement(eSla, SHOULD_START);
068        setExpectedStart(strExpectedStart, nominalTime, sla);
069
070        // Setting expected end time
071        String strExpectedEnd = getTagElement(eSla, SHOULD_END);
072        setExpectedEnd(strExpectedEnd, nominalTime, sla);
073
074        // Setting expected duration in milliseconds
075        String expectedDurationStr = getTagElement(eSla, MAX_DURATION);
076        setExpectedDuration(expectedDurationStr, sla);
077
078        // Parse desired alert-types i.e. start-miss, end-miss, start-met etc..
079        String alertEvents = getTagElement(eSla, ALERT_EVENTS);
080        if (alertEvents != null) {
081            String events[] = alertEvents.split(",");
082            StringBuilder alertsStr = new StringBuilder();
083            for (int i = 0; i < events.length; i++) {
084                String event = events[i].trim().toUpperCase();
085                try {
086                    EventStatus.valueOf(event);
087                }
088                catch (IllegalArgumentException iae) {
089                    XLog.getLog(SLAService.class).warn(
090                            "Invalid value: [" + event + "]" + " for SLA Alert-event. Should be one of "
091                                    + EventStatus.values() + ". Setting it to default [" + EventStatus.END_MISS.name()
092                                    + "]");
093                    event = EventStatus.END_MISS.name();
094                }
095                alertsStr.append(event).append(",");
096            }
097            sla.setAlertEvents(alertsStr.toString().substring(0, alertsStr.lastIndexOf(",")));
098        }
099
100        // Other sla config
101        sla.setNotificationMsg(getTagElement(eSla, "notification-msg"));
102        sla.setAlertContact(getTagElement(eSla, "alert-contact"));
103        sla.setUpstreamApps(getTagElement(eSla, "upstream-apps"));
104
105        //disable Alert flag in slaConfig
106        if (disableAlert) {
107            sla.addToSLAConfigMap(OozieClient.SLA_DISABLE_ALERT, Boolean.toString(disableAlert));
108        }
109        // Oozie defined
110        sla.setId(jobId);
111        sla.setAppType(appType);
112        sla.setAppName(appName);
113        sla.setUser(user);
114        sla.setParentId(parentId);
115
116        SLAService slaService = Services.get().get(SLAService.class);
117        try {
118            if (!rerun) {
119                slaService.addRegistrationEvent(sla);
120            }
121            else {
122                slaService.updateRegistrationEvent(sla);
123            }
124        }
125        catch (ServiceException e) {
126            throw new CommandException(ErrorCode.E1007, " id " + jobId, e.getMessage(), e);
127        }
128
129        log.debug("Job [{0}] reg for SLA. Size of Sla Xml = [{1}]", jobId, XmlUtils.prettyPrint(eSla).toString().length());
130        return sla;
131    }
132
133    public static Date setNominalTime(String strNominalTime, SLARegistrationBean sla) throws CommandException {
134        if (strNominalTime == null || strNominalTime.length() == 0) {
135            return sla.getNominalTime();
136        }
137        Date nominalTime;
138        try {
139            nominalTime = DateUtils.parseDateOozieTZ(strNominalTime);
140            sla.setNominalTime(nominalTime);
141        }
142        catch (ParseException pex) {
143            throw new CommandException(ErrorCode.E0302, strNominalTime, pex);
144        }
145        return nominalTime;
146    }
147
148    public static void setExpectedStart(String strExpectedStart, Date nominalTime, SLARegistrationBean sla)
149            throws CommandException {
150        if (strExpectedStart != null) {
151            float expectedStart = Float.parseFloat(strExpectedStart);
152            if (expectedStart < 0) {
153                throw new CommandException(ErrorCode.E0302, strExpectedStart, "for SLA Expected start time");
154            }
155            else {
156                Date expectedStartTime = new Date(nominalTime.getTime() + (long) (expectedStart * 60 * 1000));
157                sla.setExpectedStart(expectedStartTime);
158                LOG.debug("Setting expected start to " + expectedStartTime + " for job " + sla.getId());
159            }
160        }
161    }
162
163    public static void setExpectedEnd(String strExpectedEnd, Date nominalTime, SLARegistrationBean sla)
164            throws CommandException {
165        if (strExpectedEnd != null) {
166            float expectedEnd = Float.parseFloat(strExpectedEnd);
167            if (expectedEnd < 0) {
168                throw new CommandException(ErrorCode.E0302, strExpectedEnd, "for SLA Expected end time");
169            }
170            else {
171                Date expectedEndTime = new Date(nominalTime.getTime() + (long) (expectedEnd * 60 * 1000));
172                sla.setExpectedEnd(expectedEndTime);
173                LOG.debug("Setting expected end to " + expectedEndTime + " for job " + sla.getId());
174
175            }
176        }
177    }
178
179    public static void setExpectedDuration(String expectedDurationStr, SLARegistrationBean sla) {
180        if (expectedDurationStr != null && expectedDurationStr.length() > 0) {
181            float expectedDuration = Float.parseFloat(expectedDurationStr);
182            if (expectedDuration > 0) {
183                long duration = (long) (expectedDuration * 60 * 1000);
184                LOG.debug("Setting expected duration to " + duration + " for job " + sla.getId());
185                sla.setExpectedDuration(duration);
186            }
187        }
188        else if (sla.getExpectedStart() != null) {
189            long duration = sla.getExpectedEnd().getTime() - sla.getExpectedStart().getTime();
190            LOG.debug("Setting expected duration to " + duration + " for job " + sla.getId());
191            sla.setExpectedDuration(sla.getExpectedEnd().getTime() - sla.getExpectedStart().getTime());
192        }
193    }
194
195    /**
196     * Retrieve registration event
197     * @param jobId the jobId
198     * @throws CommandException
199     * @throws JPAExecutorException
200     */
201    public static void updateRegistrationEvent(String jobId) throws CommandException, JPAExecutorException {
202        SLAService slaService = Services.get().get(SLAService.class);
203        try {
204            SLARegistrationBean reg = SLARegistrationQueryExecutor.getInstance().get(SLARegQuery.GET_SLA_REG_ALL, jobId);
205            if (reg != null) { //handle coord rerun with different config without sla
206                slaService.updateRegistrationEvent(reg);
207            }
208        }
209        catch (ServiceException e) {
210            throw new CommandException(ErrorCode.E1007, " id " + jobId, e.getMessage(), e);
211        }
212
213    }
214
215    /*
216     * parentId null
217     */
218    public static SLARegistrationBean createSlaRegistrationEvent(Element eSla, String jobId, AppType appType,
219            String user, String appName, XLog log) throws CommandException {
220        return createSlaRegistrationEvent(eSla, jobId, null, appType, user, appName, log, false);
221    }
222
223    /*
224     * appName null
225     */
226    public static SLARegistrationBean createSlaRegistrationEvent(Element eSla, String jobId, String parentId,
227            AppType appType, String user, XLog log) throws CommandException {
228        return createSlaRegistrationEvent(eSla, jobId, parentId, appType, user, null, log, false);
229    }
230
231    /*
232     * parentId + appName null
233     */
234    public static SLARegistrationBean createSlaRegistrationEvent(Element eSla, String jobId, AppType appType,
235            String user, XLog log) throws CommandException {
236        return createSlaRegistrationEvent(eSla, jobId, null, appType, user, null, log, false);
237    }
238
239    /*
240     * default disableAlert flag
241     */
242    public static SLARegistrationBean createSlaRegistrationEvent(Element eSla, String jobId, String parentId,
243            AppType appType, String user, String appName, XLog log, boolean rerun) throws CommandException {
244        return createSlaRegistrationEvent(eSla, jobId, null, appType, user, appName, log, rerun, false);
245    }
246
247    public static String getTagElement(Element elem, String tagName) {
248        if (elem != null && elem.getChild(tagName, elem.getNamespace("sla")) != null) {
249            return elem.getChild(tagName, elem.getNamespace("sla")).getText().trim();
250        }
251        else {
252            return null;
253        }
254    }
255
256}