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.WorkflowStoreService;
024    import org.apache.oozie.service.WorkflowAppService;
025    import org.apache.oozie.service.HadoopAccessorService;
026    import org.apache.oozie.service.Services;
027    import org.apache.oozie.service.DagXLogInfoService;
028    import org.apache.oozie.util.XLog;
029    import org.apache.oozie.util.ParamChecker;
030    import org.apache.oozie.util.XConfiguration;
031    import org.apache.oozie.util.XmlUtils;
032    import org.apache.oozie.command.Command;
033    import org.apache.oozie.command.CommandException;
034    import org.apache.oozie.command.coord.CoordSubmitCommand;
035    import org.apache.oozie.coord.CoordELFunctions;
036    import org.apache.oozie.coord.CoordinatorJobException;
037    import org.apache.oozie.service.ELService;
038    import org.apache.oozie.service.SchemaService;
039    import org.apache.oozie.service.WorkflowAppService;
040    import org.apache.oozie.service.DagXLogInfoService;
041    import org.apache.oozie.service.WorkflowStoreService;
042    import org.apache.oozie.store.StoreException;
043    import org.apache.oozie.store.Store;
044    import org.apache.oozie.store.WorkflowStore;
045    import org.apache.oozie.workflow.WorkflowApp;
046    import org.apache.oozie.workflow.WorkflowException;
047    import org.apache.oozie.workflow.WorkflowInstance;
048    import org.apache.oozie.workflow.WorkflowLib;
049    import org.apache.oozie.util.ELEvaluator;
050    import org.apache.oozie.util.ParamChecker;
051    import org.apache.oozie.util.PropertiesUtils;
052    import org.apache.oozie.util.XLog;
053    import org.apache.oozie.util.XmlUtils;
054    import org.apache.oozie.util.XConfiguration;
055    import org.apache.oozie.util.db.SLADbOperations;
056    import org.apache.oozie.service.Services;
057    import org.apache.oozie.service.SchemaService.SchemaName;
058    import org.apache.oozie.client.OozieClient;
059    import org.apache.oozie.client.WorkflowJob;
060    import org.apache.oozie.client.SLAEvent.SlaAppType;
061    import org.jdom.Element;
062    import org.jdom.JDOMException;
063    import org.jdom.Namespace;
064    
065    import java.util.Date;
066    import java.util.List;
067    import java.util.Map;
068    import java.util.Set;
069    import java.util.HashSet;
070    import java.util.Map;
071    import java.io.IOException;
072    
073    public class SubmitCommand extends WorkflowCommand<String> {
074        public static final String CONFIG_DEFAULT = "config-default.xml";
075    
076        private Configuration conf;
077        private String authToken;
078    
079        public SubmitCommand(Configuration conf, String authToken) {
080            super("submit", "submit", 1, XLog.STD);
081            this.conf = ParamChecker.notNull(conf, "conf");
082            this.authToken = ParamChecker.notEmpty(authToken, "authToken");
083        }
084    
085        private static final Set<String> DISALLOWED_DEFAULT_PROPERTIES = new HashSet<String>();
086        private static final Set<String> DISALLOWED_USER_PROPERTIES = new HashSet<String>();
087    
088        static {
089            String[] badUserProps = {PropertiesUtils.DAYS, PropertiesUtils.HOURS, PropertiesUtils.MINUTES,
090                    PropertiesUtils.KB, PropertiesUtils.MB, PropertiesUtils.GB, PropertiesUtils.TB, PropertiesUtils.PB,
091                    PropertiesUtils.RECORDS, PropertiesUtils.MAP_IN, PropertiesUtils.MAP_OUT, PropertiesUtils.REDUCE_IN,
092                    PropertiesUtils.REDUCE_OUT, PropertiesUtils.GROUPS};
093            PropertiesUtils.createPropertySet(badUserProps, DISALLOWED_USER_PROPERTIES);
094    
095            String[] badDefaultProps = {PropertiesUtils.HADOOP_USER, PropertiesUtils.HADOOP_UGI,
096                    WorkflowAppService.HADOOP_JT_KERBEROS_NAME, WorkflowAppService.HADOOP_NN_KERBEROS_NAME};
097            PropertiesUtils.createPropertySet(badUserProps, DISALLOWED_DEFAULT_PROPERTIES);
098            PropertiesUtils.createPropertySet(badDefaultProps, DISALLOWED_DEFAULT_PROPERTIES);
099        }
100    
101        @Override
102        protected String call(WorkflowStore store) throws StoreException, CommandException {
103            incrJobCounter(1);
104            WorkflowAppService wps = Services.get().get(WorkflowAppService.class);
105            try {
106                XLog.Info.get().setParameter(DagXLogInfoService.TOKEN, conf.get(OozieClient.LOG_TOKEN));
107                WorkflowApp app = wps.parseDef(conf, authToken);
108                XConfiguration protoActionConf = wps.createProtoActionConf(conf, authToken);
109                WorkflowLib workflowLib = Services.get().get(WorkflowStoreService.class).getWorkflowLibWithNoDB();
110    
111                Path configDefault = new Path(conf.get(OozieClient.APP_PATH), CONFIG_DEFAULT);
112    
113                String user = conf.get(OozieClient.USER_NAME);
114                String group = conf.get(OozieClient.GROUP_NAME);
115                FileSystem fs = Services.get().get(HadoopAccessorService.class).createFileSystem(user, group,
116                                                                                                 configDefault.toUri(), new Configuration());
117    
118                if (fs.exists(configDefault)) {
119                    try {
120                        Configuration defaultConf = new XConfiguration(fs.open(configDefault));
121                        PropertiesUtils.checkDisallowedProperties(defaultConf, DISALLOWED_DEFAULT_PROPERTIES);
122                        XConfiguration.injectDefaults(defaultConf, conf);
123                    }
124                    catch (IOException ex) {
125                        throw new IOException("default configuration file, " + ex.getMessage(), ex);
126                    }
127                }
128    
129                PropertiesUtils.checkDisallowedProperties(conf, DISALLOWED_USER_PROPERTIES);
130    
131                // Resolving all variables in the job properties.
132                // This ensures the Hadoop Configuration semantics is preserved.
133                XConfiguration resolvedVarsConf = new XConfiguration();
134                for (Map.Entry<String, String> entry : conf) {
135                    resolvedVarsConf.set(entry.getKey(), conf.get(entry.getKey()));
136                }
137                conf = resolvedVarsConf;
138    
139                WorkflowInstance wfInstance;
140                try {
141                    wfInstance = workflowLib.createInstance(app, conf);
142                }
143                catch (WorkflowException e) {
144                    throw new StoreException(e);
145                }
146    
147                Configuration conf = wfInstance.getConf();
148                // System.out.println("WF INSTANCE CONF:");
149                // System.out.println(XmlUtils.prettyPrint(conf).toString());
150    
151                WorkflowJobBean workflow = new WorkflowJobBean();
152                workflow.setId(wfInstance.getId());
153                workflow.setAppName(app.getName());
154                workflow.setAppPath(conf.get(OozieClient.APP_PATH));
155                workflow.setConf(XmlUtils.prettyPrint(conf).toString());
156                workflow.setProtoActionConf(protoActionConf.toXmlString());
157                workflow.setCreatedTime(new Date());
158                workflow.setLastModifiedTime(new Date());
159                workflow.setLogToken(conf.get(OozieClient.LOG_TOKEN, ""));
160                workflow.setStatus(WorkflowJob.Status.PREP);
161                workflow.setRun(0);
162                workflow.setUser(conf.get(OozieClient.USER_NAME));
163                workflow.setGroup(conf.get(OozieClient.GROUP_NAME));
164                workflow.setAuthToken(authToken);
165                workflow.setWorkflowInstance(wfInstance);
166                workflow.setExternalId(conf.get(OozieClient.EXTERNAL_ID));
167    
168                setLogInfo(workflow);
169                Element wfElem = XmlUtils.parseXml(app.getDefinition());
170                ELEvaluator evalSla = createELEvaluatorForGroup(conf, "wf-sla-submit");
171                String jobSlaXml = verifySlaElements(wfElem, evalSla);
172                writeSLARegistration(jobSlaXml, workflow.getId(), workflow.getUser(), workflow.getGroup(), store);
173                workflow.setSlaXml(jobSlaXml);
174                // System.out.println("SlaXml :"+ slaXml);
175    
176                store.insertWorkflow(workflow);
177    
178                // Configuration conf1 = workflow.getWorkflowInstance().getConf();
179                // System.out.println("WF1 INSTANCE CONF:");
180                // System.out.println(XmlUtils.prettyPrint(conf1).toString());
181                // Add WF_JOB SLA Registration event
182    
183                return workflow.getId();
184            }
185            catch (WorkflowException ex) {
186                throw new CommandException(ex);
187            }
188            catch (HadoopAccessorException ex) {
189                throw new CommandException(ex);
190            }
191            catch (Exception ex) {
192                throw new CommandException(ErrorCode.E0803, ex);
193            }
194        }
195    
196        private String verifySlaElements(Element eWfJob, ELEvaluator evalSla) throws CommandException {
197            String jobSlaXml = "";
198            // String prefix = XmlUtils.getNamespacePrefix(eWfJob,
199            // SchemaService.SLA_NAME_SPACE_URI);
200            // Validate WF job
201            Element eSla = eWfJob.getChild("info", Namespace.getNamespace(SchemaService.SLA_NAME_SPACE_URI));
202            if (eSla != null) {
203                jobSlaXml = resolveSla(eSla, evalSla);
204            }
205    
206            // Validate all actions
207            for (Element action : (List<Element>) eWfJob.getChildren("action", eWfJob.getNamespace())) {
208                eSla = action.getChild("info", Namespace.getNamespace(SchemaService.SLA_NAME_SPACE_URI));
209                if (eSla != null) {
210                    resolveSla(eSla, evalSla);
211                }
212            }
213            return jobSlaXml;
214        }
215    
216        private void writeSLARegistration(String slaXml, String id, String user, String group, Store store)
217                throws CommandException {
218            try {
219                if (slaXml != null && slaXml.length() > 0) {
220                    Element eSla = XmlUtils.parseXml(slaXml);
221                    SLADbOperations.writeSlaRegistrationEvent(eSla, store, id, SlaAppType.WORKFLOW_JOB, user, group);
222                }
223            }
224            catch (Exception e) {
225                // TODO Auto-generated catch block
226                e.printStackTrace();
227                throw new CommandException(ErrorCode.E1007, "workflow " + id, e);
228            }
229        }
230    
231        public static String resolveSla(Element eSla, ELEvaluator evalSla) throws CommandException {
232            // EL evaluation
233            String slaXml = XmlUtils.prettyPrint(eSla).toString();
234            try {
235                slaXml = XmlUtils.removeComments(slaXml);
236                slaXml = evalSla.evaluate(slaXml, String.class);
237                XmlUtils.validateData(slaXml, SchemaName.SLA_ORIGINAL);
238                return slaXml;
239            }
240            catch (Exception e) {
241                throw new CommandException(ErrorCode.E1004, "Validation erro :" + e.getMessage(), e);
242            }
243        }
244    
245        public static ELEvaluator createELEvaluatorForGroup(Configuration conf, String group) {
246            ELEvaluator eval = Services.get().get(ELService.class).createEvaluator(group);
247            for (Map.Entry<String, String> entry : conf) {
248                eval.setVariable(entry.getKey(), entry.getValue());
249            }
250            return eval;
251        }
252    
253    }