/********************************************************************************************* * Copyright (c) 2014-2015 Software Behaviour Analysis Lab, Concordia University, Montreal, Canada * * All rights reserved. This program and the accompanying materials * are made available under the terms of Eclipse Public License v1.0 License which * accompanies this distribution, and is available at http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Syed Shariyar Murtaza -- Initial design and implementation **********************************************************************************************/ package org.eclipse.tracecompass.totalads.ui.live; import java.io.File; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedList; import java.util.Set; import java.util.concurrent.TimeUnit; import org.apache.log4j.Logger; import org.eclipse.jface.dialogs.MessageDialog; import org.eclipse.osgi.util.NLS; import org.eclipse.swt.widgets.Button; import org.eclipse.swt.widgets.Display; import org.eclipse.tracecompass.internal.totalads.ssh.SSHConnector; import org.eclipse.tracecompass.totalads.algorithms.AlgorithmOutStream; import org.eclipse.tracecompass.totalads.algorithms.AlgorithmUtility; import org.eclipse.tracecompass.totalads.algorithms.IDetectionAlgorithm; import org.eclipse.tracecompass.totalads.algorithms.Results; import org.eclipse.tracecompass.totalads.dbms.DBMSFactory; import org.eclipse.tracecompass.totalads.exceptions.TotalADSDBMSException; import org.eclipse.tracecompass.totalads.exceptions.TotalADSGeneralException; import org.eclipse.tracecompass.totalads.exceptions.TotalADSNetException; import org.eclipse.tracecompass.totalads.exceptions.TotalADSReaderException; import org.eclipse.tracecompass.totalads.readers.ITraceIterator; import org.eclipse.tracecompass.totalads.readers.ITraceTypeReader; import org.eclipse.tracecompass.totalads.readers.TraceTypeFactory; import org.eclipse.tracecompass.totalads.ui.io.ProgressConsole; import org.eclipse.tracecompass.totalads.ui.results.ResultsAndFeedback; import org.eclipse.ui.PlatformUI; /** * This class connects to a remote system using SSH, evaluate algorithms on * collected traces, trains them, and it also updates the live chart. It does * all this by executing as a thread * * @author <p> * Syed Shariyar Murtaza justsshary@hotmail.com * </p> * */ public class BackgroundLiveMonitor implements Runnable { private String fUserAtHost; private String fPassword; private String fSudoPassword; private String fPathToPrivateKey; private Integer fPort; private AlgorithmOutStream fOutStreamAlg; private ProgressConsole fProgConsole; private SSHConnector fSsh; private Integer fSnapshotDuration;// In Seconds private Integer fIntervalsBetweenSnapshots; // In Minutes private Button fBtnStart; private Button fBtnStop; private HashSet<String> fModelsList; private ResultsAndFeedback fResults; private HashMap<String, LinkedList<Double>> fModelsAndAnomaliesOnChart; private Integer fAnomalyIdx = 0; private Integer fMaxPoints; private LiveXYChart fLiveXYChart; private String[] fSeriesNames; private ITraceTypeReader fLttngSyscallReader; private LinkedList<Double> fXSeries; private boolean fIsTrainAndEval; private Integer fTotalTraces; private String fDirectoryToStoreTraces; private volatile boolean fIsExecuting = true; private HashMap<String, Double> fModelsAndAnomalyCount; /** * Constructor * * @param userAtHost * User and host name (user@host) * @param password * Password * @param sudoPassowrd * Sudo Password * @param pathToPrivateKey * Private key * @param port * Port * @param snapshotDuration * Snapshot duration * @param intervalBetweenSnapshots * Snapshot Interval * @param btnStart * Start button * @param btnStop * Stop button * @param modelsList * Models' list * @param results * Results's object * @param xyChart * Chart object * @param traceStorageDirectory * Directory to store traces * @param isTrainEval * True for training and testing simultaneously, and false for * only testing */ public BackgroundLiveMonitor(String userAtHost, String password, String sudoPassowrd, String pathToPrivateKey, Integer port, Integer snapshotDuration, Integer intervalBetweenSnapshots, Button btnStart, Button btnStop, HashSet<String> modelsList, ResultsAndFeedback results, LiveXYChart xyChart, String traceStorageDirectory, Boolean isTrainEval) { this.fUserAtHost = userAtHost; this.fPassword = password; this.fSudoPassword = sudoPassowrd; this.fPathToPrivateKey = pathToPrivateKey; this.fPort = port; this.fSnapshotDuration = snapshotDuration; this.fIntervalsBetweenSnapshots = intervalBetweenSnapshots; this.fBtnStart = btnStart; this.fBtnStop = btnStop; this.fModelsList = modelsList; this.fResults = results; this.fLiveXYChart = xyChart; this.fDirectoryToStoreTraces = traceStorageDirectory; this.fIsTrainAndEval = isTrainEval; // Setting up the maximum threshold beyond which the number of points // would start getting reduced on the chart. In other words the chart // would moveright this.fMaxPoints = 30; fProgConsole = new ProgressConsole(Messages.BackgroundLiveMonitor_LiveMonitor); fOutStreamAlg = new AlgorithmOutStream(); fOutStreamAlg.addObserver(fProgConsole); fModelsAndAnomaliesOnChart = new HashMap<>(); fModelsAndAnomalyCount = new HashMap<>(); fSeriesNames = new String[modelsList.size()]; int idx = 0; Iterator<String> it = modelsList.iterator(); while (it.hasNext()) { String model = it.next(); LinkedList<Double> anomalyCounts = new LinkedList<>(); this.fModelsAndAnomaliesOnChart.put(model, anomalyCounts); fSeriesNames[idx] = model; fModelsAndAnomalyCount.put(fSeriesNames[idx], 0.0); idx++; } this.fResults.registerAllModelNames(fSeriesNames); this.fResults.setMaxAllowableTrace(30); fSsh = new SSHConnector(); // algFac=AlgorithmFactory.getInstance(); fLttngSyscallReader = TraceTypeFactory.getInstance().getCTFKernelReaderOrSimpleTextReader(true); fXSeries = new LinkedList<>(); fTotalTraces = 0; } /* * (non-Javadoc) * * @see java.lang.Runnable#run() */ @Override public void run() { String exception = ""; //$NON-NLS-1$ try { initialise(); while (fIsExecuting) {// keep running untill the stop function is // called // Getting a trace from remote system String tracePath = ""; //$NON-NLS-1$ if (fSudoPassword != null) { tracePath = fSsh.collectATrace(fSudoPassword, fDirectoryToStoreTraces); } else { tracePath = fSsh.collectATrace(fDirectoryToStoreTraces); } if (fXSeries.isEmpty()) { fXSeries.add(0.0); } else { if (fAnomalyIdx > fMaxPoints) { fXSeries.remove();// remove first point on the series } Double interval = 1.0; if (fIntervalsBetweenSnapshots.doubleValue() != 0) { interval = fIntervalsBetweenSnapshots.doubleValue(); } fXSeries.add(interval + fXSeries.getLast()); fLiveXYChart.setXRange(fXSeries.getFirst().intValue(), fXSeries.getLast().intValue(), 100); } // Convert it into a series for plotting a chart Double[] xVals = new Double[fXSeries.size()]; xVals = fXSeries.toArray(xVals); processTraceOnModels(tracePath, xVals); fTotalTraces++; fResults.setTotalTraceCount(fTotalTraces.toString()); // calculate percentages HashMap<String, Double> modelsAnoms = new HashMap<>(); for (int i = 0; i < this.fSeriesNames.length; i++) { Double anoms = (fModelsAndAnomalyCount.get(fSeriesNames[i]) / fTotalTraces) * 100; modelsAnoms.put(fSeriesNames[i], anoms); } fResults.setTotalAnomalyCount(modelsAnoms); // Check if stop has been requested if (fIsExecuting == false) { break;// break out of the loop } // if there is more than 0 interval duration if (fIntervalsBetweenSnapshots > 10) { fOutStreamAlg.addOutputEvent( NLS.bind(Messages.BackgroundLiveMonitor_PauseFor, fIntervalsBetweenSnapshots, fUserAtHost.replaceAll(".*@", ""))); //$NON-NLS-1$ //$NON-NLS-2$ try { TimeUnit.MINUTES.sleep(fIntervalsBetweenSnapshots); } catch (InterruptedException ex) { } } }// End of while } catch (TotalADSNetException ex) { exception = ex.getMessage(); Logger.getLogger(BackgroundLiveMonitor.class.getName()).error(exception, ex); } catch (TotalADSReaderException ex) { exception = ex.getMessage(); Logger.getLogger(BackgroundLiveMonitor.class.getName()).error(exception, ex); } catch (TotalADSDBMSException ex) { exception = ex.getMessage(); Logger.getLogger(BackgroundLiveMonitor.class.getName()).error(exception, ex); } catch (TotalADSGeneralException ex) { exception = ex.getMessage(); Logger.getLogger(BackgroundLiveMonitor.class.getName()).error(exception, ex); } catch (Exception ex) { if (ex.getMessage() != null) { exception = ex.getMessage(); } else { exception = Messages.BackgroundLiveMonitor_UnknwonErr; } Logger.getLogger(BackgroundLiveMonitor.class.getName()).error(exception, ex); ex.printStackTrace(); // An exception could be thrown due to unavailability of the db, // make sure that the connection is not lost DBMSFactory.INSTANCE.verifyConnection(); // We don't have to worry about exceptions here as the above // function handles all the exceptions // and just returns a message. This function also initializes // connection info to a correct value // We cannot write above function under ConnectinException block // because such exception is never thrown // and Eclipse starts throwing errors } finally { fSsh.close(); fProgConsole.println(Messages.BackgroundLiveMonitor_SSHTerminated); fProgConsole.println(Messages.BackgroundLiveMonitor_StopMonitor); final String err = exception; // // Run on main GUI thread // Display.getDefault().syncExec(new Runnable() { @Override public void run() { String msgTitle="TotalADS"; //$NON-NLS-1$ if (err != null && !err.isEmpty()) { MessageDialog.openError(PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell(),msgTitle, err); } fBtnStop.setEnabled(false); fBtnStart.setEnabled(true); } }); fProgConsole.closeConsole(); } } /** * Stops the thread */ public void stopMonitoring() { fIsExecuting = false; fProgConsole.println(Messages.BackgroundLiveMonitor_StoppingMonitor); fProgConsole.println(Messages.BackgroundLiveMonitor_FewMins); fProgConsole.println(Messages.BackgroundLiveMonitor_Wait); } /** * This is a name function that evaluates traces on models and trains the * model on those traces if needed * * @param tracePath * @param xVals * @throws TotalADSReaderException * @throws TotalADSDBMSException * @throws TotalADSGeneralException */ private void processTraceOnModels(String tracePath, Double[] xVals) throws TotalADSReaderException, TotalADSGeneralException, TotalADSDBMSException { HashMap<String, Results> modelsAndResults = new HashMap<>(); boolean isAnomCountThres = false; Iterator<String> it = fModelsList.iterator(); while (it.hasNext()) { String model = it.next(); IDetectionAlgorithm algorithm = AlgorithmUtility.getAlgorithmFromModelName(model); fProgConsole.println(NLS.bind(Messages.BackgroundLiveMonitor_EvalOnModel, model, algorithm.getName())); fProgConsole.println(Messages.BackgroundLiveMonitor_PleaseWait); Results results; // Getting a trace iterator try (ITraceIterator traceIterator = fLttngSyscallReader.getTraceIterator(new File(tracePath))) { // Testing it results = algorithm.test(traceIterator, model, DBMSFactory.INSTANCE.getDataAccessObject(), fOutStreamAlg); } if (results == null) { results = new Results(); } Double anomCount = fModelsAndAnomalyCount.get(model); if (results.getAnomaly() == null || results.getAnomaly() == true) { if (anomCount == null) { fModelsAndAnomalyCount.put(model, 1.0); } else { fModelsAndAnomalyCount.put(model, ++anomCount); } } modelsAndResults.put(model, results); if (fIsTrainAndEval) {// if it is both training and evaluation fProgConsole.println(NLS.bind(Messages.BackgroundLiveMonitor_UpdatingModel, model, algorithm.getName())); fProgConsole.println(Messages.BackgroundLiveMonitor_UpdateModel); try (ITraceIterator traceIterator = fLttngSyscallReader.getTraceIterator(new File(tracePath))) { algorithm.train(traceIterator, true, model, DBMSFactory.INSTANCE.getDataAccessObject(), fOutStreamAlg); } } fProgConsole.println(NLS.bind(Messages.BackgroundLiveMonitor_ExecFinish, model)); LinkedList<Double> anomalies = fModelsAndAnomaliesOnChart.get(model); if (results.getAnomaly() == null || results.getAnomaly()) { anomalies.add(1.0); fProgConsole.println(Messages.BackgroundLiveMonitor_AnAnomaly); } else { anomalies.add(0.0); fProgConsole.println(Messages.BackgroundLiveMonitor_NotAnomaly); } fProgConsole.println(Messages.BackgroundLiveMonitor_Plotting); if (fAnomalyIdx > fMaxPoints) { anomalies.remove();// remove head isAnomCountThres = true; } else { isAnomCountThres = false; } // Convert it into a series for plotting a chart Double[] ySeries = new Double[anomalies.size()]; ySeries = anomalies.toArray(ySeries); fLiveXYChart.addYSeriesValues(ySeries, model); fLiveXYChart.addXSeriesValues(xVals, model); fLiveXYChart.drawChart(); } if (isAnomCountThres) { fAnomalyIdx--; } else { fAnomalyIdx++; } String traceName = tracePath.substring(tracePath.lastIndexOf(File.separator) + 1, tracePath.length()); String traceToDelete = fResults.addTraceResult(traceName, modelsAndResults); if (!traceToDelete.isEmpty()) { // decrease total traces when a trace is removed from the fResults fTotalTraces--; // Also decrease the count of total anomalies for each model Set<String> keys = fModelsAndAnomalyCount.keySet(); Iterator<String> iter = keys.iterator(); while (iter.hasNext()) { String key = iter.next(); Double anom = fModelsAndAnomalyCount.get(key); fModelsAndAnomalyCount.put(key, --anom); } // This code will be enabled if it is necessary to delete traces on // the folder after // piling up a certain number of them // String // folderName=tracePath.substring(0,tracePath.lastIndexOf(File.separator)); // deleteLTTngTrace(new File(folderName+traceName)); } } /** * Initializes the connection and chart * * @throws TotalADSNetException */ private void initialise() throws TotalADSNetException { fProgConsole.clearConsole(); fProgConsole.println(Messages.BackgroundLiveMonitor_WaitingForHost); // Connecting to SSH if (!fPathToPrivateKey.isEmpty()) { fSsh.openSSHConnectionUsingPrivateKey(fUserAtHost, fPathToPrivateKey, fPort, fOutStreamAlg, fSnapshotDuration); } else { fSsh.openSSHConnectionUsingPassword(fUserAtHost, fPassword, fPort, fOutStreamAlg, fSnapshotDuration); } fLiveXYChart.clearChart(); fLiveXYChart.inititaliseSeries(fSeriesNames); fLiveXYChart.setXRange(1, 10, 100); fLiveXYChart.setYRange(-1, 2); fLiveXYChart.drawChart(); } /** * Deletes all the contents of a folder. This function is used to delete an * LTTng trace, which is a collection of files in a folder. Currently * unused. * * @param folder * Folder name * * private void deleteLTTngTrace(File folder) { File[] files = * folder.listFiles(); if(files!=null) { for(File f: files) { * if(f.isDirectory()) { deleteLTTngTrace(folder); } else { * f.delete(); } } } folder.delete(); } */ }