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    }