ff605203ea8e7e9ebfa33803cf455187ec34d78e
[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 final 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     public static final String CASS_ENV = "CASS_ENV";
85     public static final String LOG_DIR = "LOG_DIR";
86     protected static final String MAX_EMAILS="MAX_EMAILS";
87     protected static final String VERSION="VERSION";
88     public static final String GUI_URL="GUI_URL";
89     
90     protected final Organization org;
91     
92     protected Batch(AuthzEnv env) throws APIException, IOException, OrganizationException {
93         if (batchEnv != null) {
94             env.info().log("Redirecting to ",batchEnv,"environment");
95             String str;
96             for (String key : new String[]{
97                     CassAccess.CASSANDRA_CLUSTERS,
98                     CassAccess.CASSANDRA_CLUSTERS_PORT,
99                     CassAccess.CASSANDRA_CLUSTERS_USER_NAME,
100                     CassAccess.CASSANDRA_CLUSTERS_PASSWORD,
101                     VERSION,GUI_URL,MAX_EMAILS,
102                     LOG_DIR,
103                     "SPECIAL_NAMES"
104                     }) {
105                 if ((str = env.getProperty(batchEnv+'.'+key))!=null) {
106                     env.setProperty(key, str);
107                 }
108             }
109         }
110
111         // Setup for Dry Run
112         cluster = CassAccess.cluster(env,batchEnv);
113         env.info().log("cluster name - ",cluster.getClusterName());
114         String dryRunStr = env.getProperty( "DRY_RUN" );
115         if ( dryRunStr == null || "false".equals(dryRunStr.trim()) ) {
116             dryRun = false;
117         } else {
118             dryRun = true;
119             env.info().log("dryRun set to TRUE");
120         }
121
122         org = OrganizationFactory.init(env);
123         org.setTestMode(dryRun);
124
125         // Special names to allow behaviors beyond normal rules
126         specialNames = new HashSet<>();
127         specialDomains = new ArrayList<>();
128         String names = env.getProperty( "SPECIAL_NAMES" );
129         if ( names != null )
130         {
131             env.info().log("Loading SPECIAL_NAMES");
132             for (String s :names.split(",") ) {
133                 env.info().log("\tspecial: " + s );
134                 if(s.indexOf('@')>0) {
135                         specialNames.add( s.trim() );
136                 } else {
137                         specialDomains.add(s.trim());
138                 }
139             }
140         }
141     }
142
143     protected abstract void run(AuthzTrans trans);
144     protected abstract void _close(AuthzTrans trans);
145     
146     public String[] args() {
147         return env.get(ssargs);
148     }
149     
150     public boolean isDryRun()
151     {
152         return dryRun;
153     }
154     
155     public boolean isSpecial(String user) {
156         if(user==null) {
157                 return false;
158         }
159         if (specialNames != null && specialNames.contains(user)) {
160             env.info().log("specialName: " + user);
161             return (true);
162         } else {
163                 if(specialDomains!=null) {
164                         for(String sd : specialDomains) {
165                                 if(user.endsWith(sd)) {
166                                         env.info().log("specialDomain: " + user + " matches " + sd);
167                                         return (true);
168                                 }
169                         }
170                 }
171         }
172         return (false);
173     }
174
175
176     protected PrintStream fallout(PrintStream inFallout, String logType)
177             throws IOException {
178         PrintStream fallout = inFallout;
179         if (fallout == null) {
180             File dir = new File("logs");
181             if (!dir.exists()) {
182                 dir.mkdirs();
183             }
184
185             File f = null;
186             long uniq = System.currentTimeMillis();
187
188             f = new File(dir, getClass().getSimpleName() + "_" + logType + "_"
189                     + uniq + ".log");
190
191             fallout = new PrintStream(new FileOutputStream(f, true));
192         }
193         return fallout;
194     }
195
196     public Organization getOrgFromID(AuthzTrans trans, String user) {
197         Organization organization;
198         try {
199             organization = OrganizationFactory.obtain(trans.env(),user.toLowerCase());
200         } catch (OrganizationException e1) {
201             trans.error().log(e1);
202             organization=null;
203         }
204
205         if (organization == null) {
206             PrintStream fallout = null;
207
208             try {
209                 fallout = fallout(fallout, "Fallout");
210                 fallout.print("INVALID_ID,");
211                 fallout.println(user);
212             } catch (Exception e) {
213                 env.error().log("Could not write to Fallout File", e);
214             }
215             return (null);
216         }
217
218         return (organization);
219     }
220     
221     public static Row executeDeleteQuery(Statement stmt) {
222         Row row = null;
223         if (!dryRun) {
224             row = session.execute(stmt).one();
225         }
226
227         return (row);
228
229     }
230         
231     public static int acquireRunLock(String className) {
232         Boolean testEnv = true;
233         String envStr = env.getProperty("AFT_ENVIRONMENT");
234
235         if (envStr != null) {
236             if ("AFTPRD".equals(envStr)) {
237                 testEnv = false;
238             }
239         } else {
240             env.fatal()
241                     .log("AFT_ENVIRONMENT property is required and was not found. Exiting.");
242             System.exit(1);
243         }
244
245         if (testEnv) {
246             env.info().log("TESTMODE: skipping RunLock");
247             return (1);
248         }
249
250         String hostname = null;
251         try {
252             hostname = InetAddress.getLocalHost().getHostName();
253         } catch (UnknownHostException e) {
254             e.printStackTrace();
255             env.warn().log("Unable to get hostname");
256             return (0);
257         }
258
259         ResultSet existing = session.execute(String.format(
260                 "select * from authz.run_lock where class = '%s'", className));
261
262         for (Row row : existing) {
263             long curr = System.currentTimeMillis();
264             ByteBuffer lastRun = row.getBytesUnsafe(2); // Can I get this field
265                                                         // by name?
266
267             long interval = (1 * 60 * 1000); // @@ Create a value in props file
268                                                 // for this
269             long prev = lastRun.getLong();
270
271             if ((curr - prev) <= interval) {
272                 env.warn().log(
273                         String.format("Too soon! Last run was %d minutes ago.",
274                                 ((curr - prev) / 1000) / 60));
275                 env.warn().log(
276                         String.format("Min time between runs is %d minutes ",
277                                 (interval / 1000) / 60));
278                 env.warn().log(
279                         String.format("Last ran on machine: %s at %s",
280                                 row.getString("host"), row.getDate("start")));
281                 return (0);
282             } else {
283                 env.info().log("Delete old lock");
284                 deleteLock(className);
285             }
286         }
287
288         GregorianCalendar current = new GregorianCalendar();
289
290         // We want our time in UTC, hence "+0000"
291         SimpleDateFormat fmt = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss+0000");
292         fmt.setTimeZone(TimeZone.getTimeZone("UTC"));
293
294         String cql = String
295                 .format("INSERT INTO authz.run_lock (class,host,start) VALUES ('%s','%s','%s') IF NOT EXISTS",
296                         className, hostname, fmt.format(current.getTime()));
297
298         env.info().log(cql);
299
300         Row row = session.execute(cql).one();
301         if (!row.getBool("[applied]")) {
302             env.warn().log("Lightweight Transaction failed to write lock.");
303             env.warn().log(
304                     String.format("host with lock: %s, running at %s",
305                             row.getString("host"), row.getDate("start")));
306             return (0);
307         }
308         return (1);
309     }
310     
311     private static void deleteLock( String className) {
312         Row row = session.execute( String.format( "DELETE FROM authz.run_lock WHERE class = '%s' IF EXISTS", className ) ).one();
313         if (! row.getBool("[applied]")) {
314             env.info().log( "delete failed" );
315         }
316     }
317
318     private static void transferVMProps(AuthzEnv env, String ... props) {
319         String value;
320         for (String key : props) {
321             if ((value = System.getProperty(key))!=null) {
322                 env.setProperty(key, value);
323             }
324         }
325     }
326     
327     protected static String logDir() {
328         String ld = env.getProperty(LOG_DIR);
329         if (ld==null) {
330             if (batchEnv==null) { // Deployed Batch doesn't use different ENVs, and a common logdir
331                 ld = "logs/";
332             } else {
333                 ld = "logs/"+batchEnv;
334             }
335         }
336         return ld;
337     }
338     protected int count(String str, char c) {
339         if (str==null || str.isEmpty()) {
340             return 0;
341         } else {
342             int count=1;
343             for (int i=str.indexOf(c);i>=0;i=str.indexOf(c,i+1)) {
344                 ++count;
345             }
346             return count;
347         }
348     }
349
350     public final void close(AuthzTrans trans) {
351         _close(trans);
352         cluster.close();
353     }
354
355     public static void main(String[] args) {
356         // Use a StringBuilder to save off logs until a File can be setup
357         StringBuilderOutputStream sbos = new StringBuilderOutputStream();
358         PropAccess access = new PropAccess(new PrintStream(sbos),args);
359         access.log(Level.INIT, "------- Starting Batch ------\n  Args: ");
360         for(String s: args) {
361                 sbos.getBuffer().append(s);
362                 sbos.getBuffer().append(' ');
363         }
364         
365         InputStream is = null;
366         String filename;
367         String propLoc;
368         try {
369             Define.set(access);
370             
371             if(access.getProperty(Config.CADI_PROP_FILES)==null) {
372                     File f = new File("authBatch.props");
373                     try {
374                         if (f.exists()) {
375                             filename = f.getAbsolutePath();
376                             is = new FileInputStream(f);
377                             propLoc = f.getPath();
378                         } else {
379                             URL rsrc = ClassLoader.getSystemResource("authBatch.props");
380                             filename = rsrc.toString();
381                             is = rsrc.openStream();
382                             propLoc = rsrc.getPath();
383                         }
384                         access.load(is);
385                     } finally {
386                         if (is == null) {
387                             System.err.println("authBatch.props must exist in current dir, or in Classpath");
388                             System.exit(1);
389                         }
390                         is.close();
391                     }
392                     if (filename != null) {
393                         access.log(Level.INFO,"Instantiated properties from", filename);
394                     }
395
396                     // Log where Config found
397                     access.log(Level.INFO,"Configuring from", propLoc);
398
399             }
400
401             env = new AuthzEnv(access);
402             
403             transferVMProps(env, CASS_ENV, "DRY_RUN", "NS", "Organization");
404
405             // Be able to change Environments
406             // load extra properties, i.e.
407             // PERF.cassandra.clusters=....
408             batchEnv = env.getProperty(CASS_ENV);
409             if(batchEnv!=null) {
410                 batchEnv = batchEnv.trim();
411             }
412
413             File logFile = new File(logDir() + "/batch" + Chrono.dateOnlyStamp(new Date()) + ".log" );
414             PrintStream batchLog = new PrintStream(new FileOutputStream(logFile,true));
415             try {
416                     access.setStreamLogIt(batchLog);
417                     sbos.flush();
418                     batchLog.print(sbos.getBuffer());
419                     sbos = null;
420                     Logger.getRootLogger().addAppender(new Log4JAccessAppender(access));
421
422                     Batch batch = null;
423                     AuthzTrans trans = env.newTrans();
424         
425                     TimeTaken tt = trans.start("Total Run", Env.SUB);
426                     try {
427                         int len = args.length;
428                         if (len > 0) {
429                             String toolName = args[0];
430                             len -= 1;
431                             if (len < 0)
432                                 len = 0;
433                             String nargs[] = new String[len];
434                             if (len > 0) {
435                                 System.arraycopy(args, 1, nargs, 0, len);
436                             }
437         
438                             env.put(ssargs = env.staticSlot("ARGS"), nargs);
439         
440                             /*
441                              * Add New Batch Programs (inherit from Batch) here
442                              */
443         
444                             // Might be a Report, Update or Temp Batch
445                             Class<?> cls = null;
446                             String classifier = "";
447         
448                             String[] pkgs = new String[] {
449                                         "org.onap.aaf.auth.batch.update",
450                                         "org.onap.aaf.auth.batch.reports",
451                                         "org.onap.aaf.auth.batch.temp"
452                                         };
453                                         
454                             String ebp = env.getProperty("EXTRA_BATCH_PKGS");
455                             if(ebp!=null) {
456                                 String[] ebps = Split.splitTrim(':', ebp);
457                                 String[] temp = new String[ebps.length + pkgs.length];
458                                 System.arraycopy(pkgs,0, temp, 0, pkgs.length);
459                                 System.arraycopy(ebps,0,temp,pkgs.length,ebps.length);
460                                 pkgs = temp;
461                             }
462                             
463                             for(String p : pkgs) {
464                                     try {
465                                         cls = ClassLoader.getSystemClassLoader().loadClass(p + '.' + toolName);
466                                         int lastDot = p.lastIndexOf('.');
467                                         if(p.length()>0 || p.length()!=lastDot) { 
468                                                 StringBuilder sb = new StringBuilder();
469                                                 sb.append(Character.toUpperCase(p.charAt(++lastDot)));
470                                                 while(++lastDot<p.length()) {
471                                                         sb.append(p.charAt(lastDot));
472                                                 }
473                                                 sb.append(':');
474                                                 classifier = sb.toString();
475                                                 break;
476                                         }
477                                     } catch (ClassNotFoundException e) {
478                                         cls = null;
479                                     }
480                             }
481                             if (cls != null) {
482                                 Constructor<?> cnst = cls.getConstructor(AuthzTrans.class);
483                                 batch = (Batch) cnst.newInstance(trans);
484                                 env.info().log("Begin", classifier, toolName);
485                             }
486                         
487         
488                             if (batch == null) {
489                                 trans.error().log("No Batch named", toolName, "found");
490                             }
491                             /*
492                              * End New Batch Programs (inherit from Batch) here
493                              */
494         
495                         }
496                         if (batch != null) {
497                             batch.run(trans);
498                         }
499                     } finally {
500                         tt.done();
501                         if (batch != null) {
502                             batch.close(trans);
503                         }
504                         StringBuilder sb = new StringBuilder("Task Times\n");
505                         trans.auditTrail(4, sb, AuthzTrans.SUB, AuthzTrans.REMOTE);
506                         trans.info().log(sb);
507                     }
508             } finally {
509                 batchLog.close();
510             }
511
512         } catch (Exception e) {
513             e.printStackTrace(System.err);
514             // Exceptions thrown by DB aren't stopping the whole process.
515             System.exit(1);
516         }
517     }
518
519 }
520