Remove Tabs, per Jococo
[aaf/authz.git] / auth / auth-batch / src / main / java / org / onap / aaf / auth / batch / Batch.java
1 /**
2  * ============LICENSE_START====================================================
3  * org.onap.aaf
4  * ===========================================================================
5  * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved.
6  * ===========================================================================
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  * 
11  *      http://www.apache.org/licenses/LICENSE-2.0
12  * 
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  * ============LICENSE_END====================================================
19  *
20  */
21
22 package org.onap.aaf.auth.batch;
23
24 import java.io.File;
25 import java.io.FileInputStream;
26 import java.io.FileOutputStream;
27 import java.io.IOException;
28 import java.io.InputStream;
29 import java.io.PrintStream;
30 import java.lang.reflect.Constructor;
31 import java.net.InetAddress;
32 import java.net.URL;
33 import java.net.UnknownHostException;
34 import java.nio.ByteBuffer;
35 import java.text.SimpleDateFormat;
36 import java.util.ArrayList;
37 import java.util.Date;
38 import java.util.GregorianCalendar;
39 import java.util.HashSet;
40 import java.util.List;
41 import java.util.Set;
42 import java.util.TimeZone;
43
44 import org.apache.log4j.Logger;
45 import org.onap.aaf.auth.common.Define;
46 import org.onap.aaf.auth.dao.CassAccess;
47 import org.onap.aaf.auth.env.AuthzEnv;
48 import org.onap.aaf.auth.env.AuthzTrans;
49 import org.onap.aaf.auth.log4j.Log4JAccessAppender;
50 import org.onap.aaf.auth.org.Organization;
51 import org.onap.aaf.auth.org.OrganizationException;
52 import org.onap.aaf.auth.org.OrganizationFactory;
53 import org.onap.aaf.cadi.Access.Level;
54 import org.onap.aaf.cadi.PropAccess;
55 import org.onap.aaf.cadi.config.Config;
56 import org.onap.aaf.misc.env.APIException;
57 import org.onap.aaf.misc.env.Env;
58 import org.onap.aaf.misc.env.StaticSlot;
59 import org.onap.aaf.misc.env.TimeTaken;
60 import org.onap.aaf.misc.env.util.Chrono;
61 import org.onap.aaf.misc.env.util.Split;
62 import org.onap.aaf.misc.env.util.StringBuilderOutputStream;
63
64 import com.datastax.driver.core.Cluster;
65 import com.datastax.driver.core.ResultSet;
66 import com.datastax.driver.core.Row;
67 import com.datastax.driver.core.Session;
68 import com.datastax.driver.core.Statement;
69
70 public abstract class Batch {
71
72     private static StaticSlot ssargs;
73
74     protected static final String STARS = "*****";
75
76     protected static Cluster cluster; 
77     protected static AuthzEnv env;
78     protected static Session session;
79     protected static Set<String> specialNames;
80     protected static List<String> specialDomains;
81     protected static boolean dryRun; 
82     protected static String batchEnv;
83
84     private static File logdir;
85
86     public static final String CASS_ENV = "CASS_ENV";
87     public static final String LOG_DIR = "LOG_DIR";
88     protected static final String MAX_EMAILS="MAX_EMAILS";
89     protected static final String VERSION="VERSION";
90     public static final String GUI_URL="GUI_URL";
91     
92     protected final Organization org;
93     protected String version;
94     protected static final Date now = new Date();
95     protected static final Date never = new Date(0);
96     
97     protected Batch(AuthzEnv env) throws APIException, IOException, OrganizationException {
98         if (batchEnv != null) {
99             env.info().log("Redirecting to ",batchEnv,"environment");
100             String str;
101             for (String key : new String[]{
102                     CassAccess.CASSANDRA_CLUSTERS,
103                     CassAccess.CASSANDRA_CLUSTERS_PORT,
104                     CassAccess.CASSANDRA_CLUSTERS_USER_NAME,
105                     CassAccess.CASSANDRA_CLUSTERS_PASSWORD,
106                     VERSION,GUI_URL,MAX_EMAILS,
107                     LOG_DIR,
108                     "SPECIAL_NAMES",
109                     "MAIL_TEST_TO"
110                     }) {
111                 if ((str = env.getProperty(batchEnv+'.'+key))!=null) {
112                     env.setProperty(key, str);
113                 }
114             }
115         }
116
117         // Setup for Dry Run
118         if(cluster==null) {
119             cluster = CassAccess.cluster(env,batchEnv);
120         }
121         env.info().log("cluster name - ",cluster.getClusterName());
122         String dryRunStr = env.getProperty( "DRY_RUN" );
123         if ( dryRunStr == null || "false".equals(dryRunStr.trim()) ) {
124             dryRun = false;
125         } else {
126             dryRun = true;
127             env.info().log("dryRun set to TRUE");
128         }
129
130         org = OrganizationFactory.init(env);
131         if(org==null) {
132             throw new OrganizationException("Organization MUST be defined for Batch");
133         }
134         org.setTestMode(dryRun);
135
136         // Special names to allow behaviors beyond normal rules
137         specialNames = new HashSet<>();
138         specialDomains = new ArrayList<>();
139         String names = env.getProperty( "SPECIAL_NAMES" );
140         if ( names != null )
141         {
142             env.info().log("Loading SPECIAL_NAMES");
143             for (String s :names.split(",") ) {
144                 env.info().log("\tspecial: " + s );
145                 if(s.indexOf('@')>0) {
146                     specialNames.add( s.trim() );
147                 } else {
148                     specialDomains.add(s.trim());
149                 }
150             }
151         }
152         
153         version = env.getProperty(VERSION,Config.AAF_DEFAULT_API_VERSION);
154     }
155
156     protected abstract void run(AuthzTrans trans);
157     protected void _close(AuthzTrans trans) {}
158     
159     public String[] args() {
160         return env.get(ssargs);
161     }
162     
163     public boolean isDryRun()
164     {
165         return dryRun;
166     }
167     
168     public boolean isSpecial(String user) {
169         if(user==null) {
170             return false;
171         }
172         if (specialNames != null && specialNames.contains(user)) {
173             env.info().log("specialName: " + user);
174             return (true);
175         } else {
176             if(specialDomains!=null) {
177                 for(String sd : specialDomains) {
178                     if(user.endsWith(sd)) {
179                         env.info().log("specialDomain: " + user + " matches " + sd);
180                         return (true);
181                     }
182                 }
183             }
184         }
185         return (false);
186     }
187
188
189     protected PrintStream fallout(PrintStream inFallout, String logType)
190             throws IOException {
191         PrintStream fallout = inFallout;
192         if (fallout == null) {
193             File dir = new File("logs");
194             if (!dir.exists()) {
195                 dir.mkdirs();
196             }
197
198             File f = null;
199             long uniq = System.currentTimeMillis();
200
201             f = new File(dir, getClass().getSimpleName() + "_" + logType + "_"
202                     + uniq + ".log");
203
204             fallout = new PrintStream(new FileOutputStream(f, true));
205         }
206         return fallout;
207     }
208
209     public Organization getOrgFromID(AuthzTrans trans, String user) {
210         Organization organization;
211         try {
212             organization = OrganizationFactory.obtain(trans.env(),user.toLowerCase());
213         } catch (OrganizationException e1) {
214             trans.error().log(e1);
215             organization=null;
216         }
217
218         if (organization == null) {
219             PrintStream fallout = null;
220
221             try {
222                 fallout = fallout(fallout, "Fallout");
223                 fallout.print("INVALID_ID,");
224                 fallout.println(user);
225             } catch (Exception e) {
226                 env.error().log("Could not write to Fallout File", e);
227             }
228             return (null);
229         }
230
231         return (organization);
232     }
233     
234     public static Row executeDeleteQuery(Statement stmt) {
235         Row row = null;
236         if (!dryRun) {
237             row = session.execute(stmt).one();
238         }
239
240         return (row);
241
242     }
243         
244     public static int acquireRunLock(String className) {
245         Boolean testEnv = true;
246         String envStr = env.getProperty("AFT_ENVIRONMENT");
247
248         if (envStr != null) {
249             if ("AFTPRD".equals(envStr)) {
250                 testEnv = false;
251             }
252         } else {
253             env.fatal()
254                     .log("AFT_ENVIRONMENT property is required and was not found. Exiting.");
255             System.exit(1);
256         }
257
258         if (testEnv) {
259             env.info().log("TESTMODE: skipping RunLock");
260             return (1);
261         }
262
263         String hostname = null;
264         try {
265             hostname = InetAddress.getLocalHost().getHostName();
266         } catch (UnknownHostException e) {
267             e.printStackTrace();
268             env.warn().log("Unable to get hostname");
269             return (0);
270         }
271
272         ResultSet existing = session.execute(String.format(
273                 "select * from authz.run_lock where class = '%s'", className));
274
275         for (Row row : existing) {
276             long curr = System.currentTimeMillis();
277             ByteBuffer lastRun = row.getBytesUnsafe(2); // Can I get this field
278                                                         // by name?
279
280             long interval = (1 * 60 * 1000); // @@ Create a value in props file
281                                                 // for this
282             long prev = lastRun.getLong();
283
284             if ((curr - prev) <= interval) {
285                 env.warn().log(
286                         String.format("Too soon! Last run was %d minutes ago.",
287                                 ((curr - prev) / 1000) / 60));
288                 env.warn().log(
289                         String.format("Min time between runs is %d minutes ",
290                                 (interval / 1000) / 60));
291                 env.warn().log(
292                         String.format("Last ran on machine: %s at %s",
293                                 row.getString("host"), row.getDate("start")));
294                 return (0);
295             } else {
296                 env.info().log("Delete old lock");
297                 deleteLock(className);
298             }
299         }
300
301         GregorianCalendar current = new GregorianCalendar();
302
303         // We want our time in UTC, hence "+0000"
304         SimpleDateFormat fmt = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss+0000");
305         fmt.setTimeZone(TimeZone.getTimeZone("UTC"));
306
307         String cql = String
308                 .format("INSERT INTO authz.run_lock (class,host,start) VALUES ('%s','%s','%s') IF NOT EXISTS",
309                         className, hostname, fmt.format(current.getTime()));
310
311         env.info().log(cql);
312
313         Row row = session.execute(cql).one();
314         if (!row.getBool("[applied]")) {
315             env.warn().log("Lightweight Transaction failed to write lock.");
316             env.warn().log(
317                     String.format("host with lock: %s, running at %s",
318                             row.getString("host"), row.getDate("start")));
319             return (0);
320         }
321         return (1);
322     }
323     
324     private static void deleteLock( String className) {
325         Row row = session.execute( String.format( "DELETE FROM authz.run_lock WHERE class = '%s' IF EXISTS", className ) ).one();
326         if (! row.getBool("[applied]")) {
327             env.info().log( "delete failed" );
328         }
329     }
330
331     private static void transferVMProps(AuthzEnv env, String ... props) {
332         String value;
333         for (String key : props) {
334             if ((value = System.getProperty(key))!=null) {
335                 env.setProperty(key, value);
336             }
337         }
338     }
339     
340     protected static File logDir() {
341         if(logdir == null) {
342             String ld = env.getProperty(LOG_DIR);
343             if (ld==null) {
344                 if (batchEnv==null) { // Deployed Batch doesn't use different ENVs, and a common logdir
345                     ld = "logs/";
346                 } else {
347                     ld = "logs/"+batchEnv;
348                 }
349             }
350             logdir = new File(ld);
351             if(!logdir.exists()) {
352                 logdir.mkdirs();
353             }
354         } 
355         return logdir;
356     }
357     protected int count(String str, char c) {
358         if (str==null || str.isEmpty()) {
359             return 0;
360         } else {
361             int count=1;
362             for (int i=str.indexOf(c);i>=0;i=str.indexOf(c,i+1)) {
363                 ++count;
364             }
365             return count;
366         }
367     }
368
369     public final void close(AuthzTrans trans) {
370         _close(trans);
371         if(session!=null) {
372             session.close();
373             session = null;
374         }
375         if(cluster!=null && !cluster.isClosed()) {
376             cluster.close();
377         }
378     }
379
380     public static void main(String[] args) {
381         // Use a StringBuilder to save off logs until a File can be setup
382         StringBuilderOutputStream sbos = new StringBuilderOutputStream();
383         PropAccess access = new PropAccess(new PrintStream(sbos),args);
384         access.log(Level.INIT, "------- Starting Batch ------\n  Args: ");
385         for(String s: args) {
386             sbos.getBuffer().append(s);
387             sbos.getBuffer().append(' ');
388         }
389         
390         InputStream is = null;
391         String filename;
392         String propLoc;
393         try {
394             Define.set(access);
395             
396             if(access.getProperty(Config.CADI_PROP_FILES)==null) {
397                 File f = new File("authBatch.props");
398                 try {
399                     if (f.exists()) {
400                         filename = f.getAbsolutePath();
401                         is = new FileInputStream(f);
402                         propLoc = f.getPath();
403                     } else {
404                         URL rsrc = ClassLoader.getSystemResource("authBatch.props");
405                         filename = rsrc.toString();
406                         is = rsrc.openStream();
407                         propLoc = rsrc.getPath();
408                     }
409                     access.load(is);
410                 } finally {
411                     if (is == null) {
412                         System.err.println("authBatch.props must exist in current dir, or in Classpath");
413                         System.exit(1);
414                     }
415                     is.close();
416                 }
417                 if (filename != null) {
418                     access.log(Level.INFO,"Instantiated properties from", filename);
419                 }
420
421                 // Log where Config found
422                 access.log(Level.INFO,"Configuring from", propLoc);
423
424             }
425
426             env = new AuthzEnv(access);
427             
428             transferVMProps(env, CASS_ENV, "DRY_RUN", "NS", "Organization");
429
430             // Be able to change Environments
431             // load extra properties, i.e.
432             // PERF.cassandra.clusters=....
433             batchEnv = env.getProperty(CASS_ENV);
434             if(batchEnv!=null) {
435                 batchEnv = batchEnv.trim();
436             }
437
438             File logFile = new File(logDir() + "/batch" + Chrono.dateOnlyStamp(new Date()) + ".log" );
439             PrintStream batchLog = new PrintStream(new FileOutputStream(logFile,true));
440             try {
441                 access.setStreamLogIt(batchLog);
442                 sbos.flush();
443                 batchLog.print(sbos.getBuffer());
444                 sbos = null;
445                 Logger.getRootLogger().addAppender(new Log4JAccessAppender(access));
446
447                 Batch batch = null;
448                 AuthzTrans trans = env.newTrans();
449     
450                 TimeTaken tt = trans.start("Total Run", Env.SUB);
451                 try {
452                     int len = args.length;
453                     if (len > 0) {
454                         String toolName = args[0];
455                         len -= 1;
456                         if (len < 0)
457                             len = 0;
458                         String nargs[] = new String[len];
459                         if (len > 0) {
460                             System.arraycopy(args, 1, nargs, 0, len);
461                         }
462     
463                         env.put(ssargs = env.staticSlot("ARGS"), nargs);
464     
465                         /*
466                          * Add New Batch Programs (inherit from Batch) here
467                          */
468     
469                         // Might be a Report, Update or Temp Batch
470                         Class<?> cls = null;
471                         String classifier = "";
472     
473                         String[] pkgs = new String[] {
474                                 "org.onap.aaf.auth.batch.update",
475                                 "org.onap.aaf.auth.batch.reports",
476                                 "org.onap.aaf.auth.batch.temp"
477                                 };
478                                 
479                         String ebp = env.getProperty("EXTRA_BATCH_PKGS");
480                         if(ebp!=null) {
481                             String[] ebps = Split.splitTrim(':', ebp);
482                             String[] temp = new String[ebps.length + pkgs.length];
483                             System.arraycopy(pkgs,0, temp, 0, pkgs.length);
484                             System.arraycopy(ebps,0,temp,pkgs.length,ebps.length);
485                             pkgs = temp;
486                         }
487                         
488                         for(String p : pkgs) {
489                             try {
490                                 cls = ClassLoader.getSystemClassLoader().loadClass(p + '.' + toolName);
491                                 int lastDot = p.lastIndexOf('.');
492                                 if(p.length()>0 || p.length()!=lastDot) { 
493                                     StringBuilder sb = new StringBuilder();
494                                     sb.append(Character.toUpperCase(p.charAt(++lastDot)));
495                                     while(++lastDot<p.length()) {
496                                         sb.append(p.charAt(lastDot));
497                                     }
498                                     sb.append(':');
499                                     classifier = sb.toString();
500                                     break;
501                                 }
502                             } catch (ClassNotFoundException e) {
503                                 cls = null;
504                             }
505                         }
506                         if (cls != null) {
507                             Constructor<?> cnst = cls.getConstructor(AuthzTrans.class);
508                             batch = (Batch) cnst.newInstance(trans);
509                             env.info().log("Begin", classifier, toolName);
510                         }
511                     
512     
513                         if (batch == null) {
514                             trans.error().log("No Batch named", toolName, "found");
515                         }
516                         /*
517                          * End New Batch Programs (inherit from Batch) here
518                          */
519     
520                     }
521                     if (batch != null) {
522                         try {
523                             batch.run(trans);
524                         } catch (Exception e) {
525                             if(cluster!=null && !cluster.isClosed()) {
526                                 cluster.close();
527                             }
528                             trans.error().log(e);
529                         }
530                     }
531                 } finally {
532                     tt.done();
533                     if (batch != null) {
534                         batch.close(trans);
535                     }
536                     StringBuilder sb = new StringBuilder("Task Times\n");
537                     trans.auditTrail(4, sb, AuthzTrans.SUB, AuthzTrans.REMOTE);
538                     trans.info().log(sb);
539                 }
540             } finally {
541                 batchLog.close();
542             }
543
544         } catch (Exception e) {
545             if(cluster!=null && !cluster.isClosed()) {
546                 cluster.close();
547             }
548             e.printStackTrace(System.err);
549         }
550     }
551
552 }
553