001 /** 002 * Copyright (c) 2010 Yahoo! Inc. All rights reserved. 003 * Licensed under the Apache License, Version 2.0 (the "License"); 004 * you may not use this file except in compliance with the License. 005 * You may obtain a copy of the License at 006 * 007 * http://www.apache.org/licenses/LICENSE-2.0 008 * 009 * Unless required by applicable law or agreed to in writing, software 010 * distributed under the License is distributed on an "AS IS" BASIS, 011 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 012 * See the License for the specific language governing permissions and 013 * limitations under the License. See accompanying LICENSE file. 014 */ 015 package org.apache.oozie.command.wf; 016 017 import org.apache.hadoop.conf.Configuration; 018 import org.apache.hadoop.fs.Path; 019 import org.apache.hadoop.fs.FileSystem; 020 import org.apache.oozie.WorkflowJobBean; 021 import org.apache.oozie.ErrorCode; 022 import org.apache.oozie.service.HadoopAccessorException; 023 import org.apache.oozie.service.JPAService; 024 import org.apache.oozie.service.WorkflowStoreService; 025 import org.apache.oozie.service.WorkflowAppService; 026 import org.apache.oozie.service.HadoopAccessorService; 027 import org.apache.oozie.service.Services; 028 import org.apache.oozie.service.DagXLogInfoService; 029 import org.apache.oozie.util.XLog; 030 import org.apache.oozie.util.ParamChecker; 031 import org.apache.oozie.util.XConfiguration; 032 import org.apache.oozie.util.XmlUtils; 033 import org.apache.oozie.command.CommandException; 034 import org.apache.oozie.executor.jpa.WorkflowJobInsertJPAExecutor; 035 import org.apache.oozie.service.ELService; 036 import org.apache.oozie.service.SchemaService; 037 import org.apache.oozie.store.StoreException; 038 import org.apache.oozie.workflow.WorkflowApp; 039 import org.apache.oozie.workflow.WorkflowException; 040 import org.apache.oozie.workflow.WorkflowInstance; 041 import org.apache.oozie.workflow.WorkflowLib; 042 import org.apache.oozie.util.ELEvaluator; 043 import org.apache.oozie.util.InstrumentUtils; 044 import org.apache.oozie.util.PropertiesUtils; 045 import org.apache.oozie.util.db.SLADbOperations; 046 import org.apache.oozie.service.SchemaService.SchemaName; 047 import org.apache.oozie.client.OozieClient; 048 import org.apache.oozie.client.WorkflowJob; 049 import org.apache.oozie.client.SLAEvent.SlaAppType; 050 import org.jdom.Element; 051 import org.jdom.Namespace; 052 053 import java.util.Date; 054 import java.util.List; 055 import java.util.Map; 056 import java.util.Set; 057 import java.util.HashSet; 058 import java.io.IOException; 059 import java.net.URI; 060 061 public class SubmitXCommand extends WorkflowXCommand<String> { 062 public static final String CONFIG_DEFAULT = "config-default.xml"; 063 064 private Configuration conf; 065 private String authToken; 066 067 public SubmitXCommand(Configuration conf, String authToken) { 068 super("submit", "submit", 1); 069 this.conf = ParamChecker.notNull(conf, "conf"); 070 this.authToken = ParamChecker.notEmpty(authToken, "authToken"); 071 } 072 073 private static final Set<String> DISALLOWED_DEFAULT_PROPERTIES = new HashSet<String>(); 074 private static final Set<String> DISALLOWED_USER_PROPERTIES = new HashSet<String>(); 075 076 static { 077 String[] badUserProps = {PropertiesUtils.DAYS, PropertiesUtils.HOURS, PropertiesUtils.MINUTES, 078 PropertiesUtils.KB, PropertiesUtils.MB, PropertiesUtils.GB, PropertiesUtils.TB, PropertiesUtils.PB, 079 PropertiesUtils.RECORDS, PropertiesUtils.MAP_IN, PropertiesUtils.MAP_OUT, PropertiesUtils.REDUCE_IN, 080 PropertiesUtils.REDUCE_OUT, PropertiesUtils.GROUPS}; 081 PropertiesUtils.createPropertySet(badUserProps, DISALLOWED_USER_PROPERTIES); 082 083 String[] badDefaultProps = {PropertiesUtils.HADOOP_USER, PropertiesUtils.HADOOP_UGI, 084 WorkflowAppService.HADOOP_JT_KERBEROS_NAME, WorkflowAppService.HADOOP_NN_KERBEROS_NAME}; 085 PropertiesUtils.createPropertySet(badUserProps, DISALLOWED_DEFAULT_PROPERTIES); 086 PropertiesUtils.createPropertySet(badDefaultProps, DISALLOWED_DEFAULT_PROPERTIES); 087 } 088 089 @Override 090 protected String execute() throws CommandException { 091 InstrumentUtils.incrJobCounter(getName(), 1, getInstrumentation()); 092 WorkflowAppService wps = Services.get().get(WorkflowAppService.class); 093 try { 094 XLog.Info.get().setParameter(DagXLogInfoService.TOKEN, conf.get(OozieClient.LOG_TOKEN)); 095 WorkflowApp app = wps.parseDef(conf, authToken); 096 XConfiguration protoActionConf = wps.createProtoActionConf(conf, authToken, true); 097 WorkflowLib workflowLib = Services.get().get(WorkflowStoreService.class).getWorkflowLibWithNoDB(); 098 099 String user = conf.get(OozieClient.USER_NAME); 100 String group = conf.get(OozieClient.GROUP_NAME); 101 URI uri = new URI(conf.get(OozieClient.APP_PATH)); 102 FileSystem fs = Services.get().get(HadoopAccessorService.class).createFileSystem(user, 103 group, uri, new Configuration()); 104 105 Path configDefault = null; 106 // app path could be a directory 107 Path path = new Path(uri.getPath()); 108 if (!fs.isFile(path)) { 109 configDefault = new Path(path, SubmitCommand.CONFIG_DEFAULT); 110 } else { 111 configDefault = new Path(path.getParent(), SubmitCommand.CONFIG_DEFAULT); 112 } 113 114 if (fs.exists(configDefault)) { 115 try { 116 Configuration defaultConf = new XConfiguration(fs.open(configDefault)); 117 PropertiesUtils.checkDisallowedProperties(defaultConf, DISALLOWED_DEFAULT_PROPERTIES); 118 XConfiguration.injectDefaults(defaultConf, conf); 119 } 120 catch (IOException ex) { 121 throw new IOException("default configuration file, " + ex.getMessage(), ex); 122 } 123 } 124 125 PropertiesUtils.checkDisallowedProperties(conf, DISALLOWED_USER_PROPERTIES); 126 127 // Resolving all variables in the job properties. 128 // This ensures the Hadoop Configuration semantics is preserved. 129 XConfiguration resolvedVarsConf = new XConfiguration(); 130 for (Map.Entry<String, String> entry : conf) { 131 resolvedVarsConf.set(entry.getKey(), conf.get(entry.getKey())); 132 } 133 conf = resolvedVarsConf; 134 135 WorkflowInstance wfInstance; 136 try { 137 wfInstance = workflowLib.createInstance(app, conf); 138 } 139 catch (WorkflowException e) { 140 throw new StoreException(e); 141 } 142 143 Configuration conf = wfInstance.getConf(); 144 // System.out.println("WF INSTANCE CONF:"); 145 // System.out.println(XmlUtils.prettyPrint(conf).toString()); 146 147 WorkflowJobBean workflow = new WorkflowJobBean(); 148 workflow.setId(wfInstance.getId()); 149 workflow.setAppName(app.getName()); 150 workflow.setAppPath(conf.get(OozieClient.APP_PATH)); 151 workflow.setConf(XmlUtils.prettyPrint(conf).toString()); 152 workflow.setProtoActionConf(protoActionConf.toXmlString()); 153 workflow.setCreatedTime(new Date()); 154 workflow.setLastModifiedTime(new Date()); 155 workflow.setLogToken(conf.get(OozieClient.LOG_TOKEN, "")); 156 workflow.setStatus(WorkflowJob.Status.PREP); 157 workflow.setRun(0); 158 workflow.setUser(conf.get(OozieClient.USER_NAME)); 159 workflow.setGroup(conf.get(OozieClient.GROUP_NAME)); 160 workflow.setAuthToken(authToken); 161 workflow.setWorkflowInstance(wfInstance); 162 workflow.setExternalId(conf.get(OozieClient.EXTERNAL_ID)); 163 164 //setLogInfo(workflow); 165 Element wfElem = XmlUtils.parseXml(app.getDefinition()); 166 ELEvaluator evalSla = createELEvaluatorForGroup(conf, "wf-sla-submit"); 167 String jobSlaXml = verifySlaElements(wfElem, evalSla); 168 writeSLARegistration(jobSlaXml, workflow.getId(), workflow.getUser(), workflow.getGroup(), LOG); 169 workflow.setSlaXml(jobSlaXml); 170 // System.out.println("SlaXml :"+ slaXml); 171 172 //store.insertWorkflow(workflow); 173 JPAService jpaService = Services.get().get(JPAService.class); 174 if (jpaService != null) { 175 jpaService.execute(new WorkflowJobInsertJPAExecutor(workflow)); 176 } 177 else { 178 LOG.error(ErrorCode.E0610); 179 return null; 180 } 181 182 return workflow.getId(); 183 } 184 catch (WorkflowException ex) { 185 throw new CommandException(ex); 186 } 187 catch (HadoopAccessorException ex) { 188 throw new CommandException(ex); 189 } 190 catch (Exception ex) { 191 throw new CommandException(ErrorCode.E0803, ex); 192 } 193 } 194 195 private String verifySlaElements(Element eWfJob, ELEvaluator evalSla) throws CommandException { 196 String jobSlaXml = ""; 197 // Validate WF job 198 Element eSla = eWfJob.getChild("info", Namespace.getNamespace(SchemaService.SLA_NAME_SPACE_URI)); 199 if (eSla != null) { 200 jobSlaXml = resolveSla(eSla, evalSla); 201 } 202 203 // Validate all actions 204 for (Element action : (List<Element>) eWfJob.getChildren("action", eWfJob.getNamespace())) { 205 eSla = action.getChild("info", Namespace.getNamespace(SchemaService.SLA_NAME_SPACE_URI)); 206 if (eSla != null) { 207 resolveSla(eSla, evalSla); 208 } 209 } 210 return jobSlaXml; 211 } 212 213 private void writeSLARegistration(String slaXml, String id, String user, String group, XLog log) 214 throws CommandException { 215 try { 216 if (slaXml != null && slaXml.length() > 0) { 217 Element eSla = XmlUtils.parseXml(slaXml); 218 SLADbOperations.writeSlaRegistrationEvent(eSla, id, SlaAppType.WORKFLOW_JOB, user, group, log); 219 } 220 } 221 catch (Exception e) { 222 e.printStackTrace(); 223 throw new CommandException(ErrorCode.E1007, "workflow " + id, e); 224 } 225 } 226 227 /** 228 * Resolve variables in sla xml element. 229 * 230 * @param eSla sla xml element 231 * @param evalSla sla evaluator 232 * @return sla xml string after evaluation 233 * @throws CommandException 234 */ 235 public static String resolveSla(Element eSla, ELEvaluator evalSla) throws CommandException { 236 // EL evaluation 237 String slaXml = XmlUtils.prettyPrint(eSla).toString(); 238 try { 239 slaXml = XmlUtils.removeComments(slaXml); 240 slaXml = evalSla.evaluate(slaXml, String.class); 241 XmlUtils.validateData(slaXml, SchemaName.SLA_ORIGINAL); 242 return slaXml; 243 } 244 catch (Exception e) { 245 throw new CommandException(ErrorCode.E1004, "Validation erro :" + e.getMessage(), e); 246 } 247 } 248 249 /** 250 * Create an EL evaluator for a given group. 251 * 252 * @param conf configuration variable 253 * @param group group variable 254 * @return the evaluator created for the group 255 */ 256 public static ELEvaluator createELEvaluatorForGroup(Configuration conf, String group) { 257 ELEvaluator eval = Services.get().get(ELService.class).createEvaluator(group); 258 for (Map.Entry<String, String> entry : conf) { 259 eval.setVariable(entry.getKey(), entry.getValue()); 260 } 261 return eval; 262 } 263 264 @Override 265 protected String getEntityKey() { 266 return null; 267 } 268 269 @Override 270 protected boolean isLockRequired() { 271 return false; 272 } 273 274 @Override 275 protected void loadState() { 276 277 } 278 279 @Override 280 protected void verifyPrecondition() throws CommandException { 281 282 } 283 284 }