Merge pull request #3777 from esaunders/3734_ingest_progress

Add ingest progress snapshot support to AID
This commit is contained in:
Richard Cordovano 2018-05-17 18:11:57 -04:00 committed by GitHub
commit 03764f0891
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 260 additions and 79 deletions

View File

@ -18,6 +18,7 @@
*/ */
package org.sleuthkit.autopsy.ingest; package org.sleuthkit.autopsy.ingest;
import java.io.Serializable;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.Date; import java.util.Date;
@ -37,6 +38,9 @@ import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.NetworkUtils; import org.sleuthkit.autopsy.coreutils.NetworkUtils;
import org.sleuthkit.autopsy.ingest.DataSourceIngestPipeline.PipelineModule;
import org.sleuthkit.autopsy.ingest.IngestJob.CancellationReason;
import org.sleuthkit.autopsy.ingest.IngestTasksScheduler.IngestJobTasksSnapshot;
import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.Content;
import org.sleuthkit.datamodel.IngestJobInfo; import org.sleuthkit.datamodel.IngestJobInfo;
@ -51,7 +55,7 @@ import org.sleuthkit.autopsy.modules.interestingitems.FilesSet;
* Encapsulates a data source and the ingest module pipelines used to process * Encapsulates a data source and the ingest module pipelines used to process
* it. * it.
*/ */
final class DataSourceIngestJob { public final class DataSourceIngestJob {
private static final Logger logger = Logger.getLogger(DataSourceIngestJob.class.getName()); private static final Logger logger = Logger.getLogger(DataSourceIngestJob.class.getName());
@ -1079,71 +1083,90 @@ final class DataSourceIngestJob {
* @return An ingest job statistics object. * @return An ingest job statistics object.
*/ */
Snapshot getSnapshot(boolean getIngestTasksSnapshot) { Snapshot getSnapshot(boolean getIngestTasksSnapshot) {
return new Snapshot(getIngestTasksSnapshot); /**
* Determine whether file ingest is running at the time of this snapshot
* and determine the earliest file ingest level pipeline start time, if
* file ingest was started at all.
*/
boolean fileIngestRunning = false;
Date fileIngestStartTime = null;
for (FileIngestPipeline pipeline : this.fileIngestPipelines) {
if (pipeline.isRunning()) {
fileIngestRunning = true;
}
Date pipelineStartTime = pipeline.getStartTime();
if (null != pipelineStartTime && (null == fileIngestStartTime || pipelineStartTime.before(fileIngestStartTime))) {
fileIngestStartTime = pipelineStartTime;
}
}
long processedFilesCount = 0;
long estimatedFilesToProcessCount = 0;
long snapShotTime = new Date().getTime();
IngestJobTasksSnapshot tasksSnapshot = null;
if (getIngestTasksSnapshot) {
synchronized (fileIngestProgressLock) {
processedFilesCount = this.processedFiles;
estimatedFilesToProcessCount = this.estimatedFilesToProcess;
snapShotTime = new Date().getTime();
}
tasksSnapshot = DataSourceIngestJob.taskScheduler.getTasksSnapshotForJob(id);
}
return new Snapshot(this.dataSource.getName(), id, createTime,
getCurrentDataSourceIngestModule(), fileIngestRunning, fileIngestStartTime,
cancelled, cancellationReason, cancelledDataSourceIngestModules,
processedFilesCount, estimatedFilesToProcessCount, snapShotTime, tasksSnapshot);
} }
/** /**
* Stores basic diagnostic statistics for a data source ingest job. * Stores basic diagnostic statistics for a data source ingest job.
*/ */
final class Snapshot { public static final class Snapshot implements Serializable {
private static final long serialVersionUID = 1L;
private final String dataSource; private final String dataSource;
private final long jobId; private final long jobId;
private final long jobStartTime; private final long jobStartTime;
private final long snapShotTime; private final long snapShotTime;
private final DataSourceIngestPipeline.PipelineModule dataSourceLevelIngestModule; transient private final PipelineModule dataSourceLevelIngestModule;
private boolean fileIngestRunning; private final boolean fileIngestRunning;
private Date fileIngestStartTime; private final Date fileIngestStartTime;
private final long processedFiles; private final long processedFiles;
private final long estimatedFilesToProcess; private final long estimatedFilesToProcess;
private final IngestTasksScheduler.IngestJobTasksSnapshot tasksSnapshot; private final IngestJobTasksSnapshot tasksSnapshot;
private final boolean jobCancelled; transient private final boolean jobCancelled;
private final IngestJob.CancellationReason jobCancellationReason; transient private final CancellationReason jobCancellationReason;
private final List<String> cancelledDataSourceModules; transient private final List<String> cancelledDataSourceModules;
/** /**
* Constructs an object to store basic diagnostic statistics for a data * Constructs an object to store basic diagnostic statistics for a data
* source ingest job. * source ingest job.
*/ */
Snapshot(boolean getIngestTasksSnapshot) { Snapshot(String dataSourceName, long jobId, long jobStartTime, PipelineModule dataSourceIngestModule,
this.dataSource = DataSourceIngestJob.this.dataSource.getName(); boolean fileIngestRunning, Date fileIngestStartTime,
this.jobId = DataSourceIngestJob.this.id; boolean jobCancelled, CancellationReason cancellationReason, List<String> cancelledModules,
this.jobStartTime = DataSourceIngestJob.this.createTime; long processedFiles, long estimatedFilesToProcess,
this.dataSourceLevelIngestModule = DataSourceIngestJob.this.getCurrentDataSourceIngestModule(); long snapshotTime, IngestJobTasksSnapshot tasksSnapshot) {
this.dataSource = dataSourceName;
this.jobId = jobId;
this.jobStartTime = jobStartTime;
this.dataSourceLevelIngestModule = dataSourceIngestModule;
/** this.fileIngestRunning = fileIngestRunning;
* Determine whether file ingest is running at the time of this this.fileIngestStartTime = fileIngestStartTime;
* snapshot and determine the earliest file ingest level pipeline this.jobCancelled = jobCancelled;
* start time, if file ingest was started at all.
*/
for (FileIngestPipeline pipeline : DataSourceIngestJob.this.fileIngestPipelines) {
if (pipeline.isRunning()) {
this.fileIngestRunning = true;
}
Date pipelineStartTime = pipeline.getStartTime();
if (null != pipelineStartTime && (null == this.fileIngestStartTime || pipelineStartTime.before(this.fileIngestStartTime))) {
this.fileIngestStartTime = pipelineStartTime;
}
}
this.jobCancelled = cancelled;
this.jobCancellationReason = cancellationReason; this.jobCancellationReason = cancellationReason;
this.cancelledDataSourceModules = new ArrayList<>(DataSourceIngestJob.this.cancelledDataSourceIngestModules); this.cancelledDataSourceModules = cancelledModules;
if (getIngestTasksSnapshot) { this.processedFiles = processedFiles;
synchronized (DataSourceIngestJob.this.fileIngestProgressLock) { this.estimatedFilesToProcess = estimatedFilesToProcess;
this.processedFiles = DataSourceIngestJob.this.processedFiles; this.snapShotTime = snapshotTime;
this.estimatedFilesToProcess = DataSourceIngestJob.this.estimatedFilesToProcess; this.tasksSnapshot = tasksSnapshot;
this.snapShotTime = new Date().getTime();
}
this.tasksSnapshot = DataSourceIngestJob.taskScheduler.getTasksSnapshotForJob(this.jobId);
} else {
this.processedFiles = 0;
this.estimatedFilesToProcess = 0;
this.snapShotTime = new Date().getTime();
this.tasksSnapshot = null;
}
} }
/** /**
@ -1190,11 +1213,11 @@ final class DataSourceIngestJob {
return this.dataSourceLevelIngestModule; return this.dataSourceLevelIngestModule;
} }
boolean fileIngestIsRunning() { boolean getFileIngestIsRunning() {
return this.fileIngestRunning; return this.fileIngestRunning;
} }
Date fileIngestStartTime() { Date getFileIngestStartTime() {
return this.fileIngestStartTime; return this.fileIngestStartTime;
} }

View File

@ -355,10 +355,10 @@ public final class IngestJob {
dataSourceModule = new DataSourceIngestModuleHandle(dataSourceJobs.get(snapshot.getJobId()), module); dataSourceModule = new DataSourceIngestModuleHandle(dataSourceJobs.get(snapshot.getJobId()), module);
} }
} }
if (snapshot.fileIngestIsRunning()) { if (snapshot.getFileIngestIsRunning()) {
fileIngestRunning = true; fileIngestRunning = true;
} }
Date childFileIngestStartTime = snapshot.fileIngestStartTime(); Date childFileIngestStartTime = snapshot.getFileIngestStartTime();
if (null != childFileIngestStartTime && (null == fileIngestStartTime || childFileIngestStartTime.before(fileIngestStartTime))) { if (null != childFileIngestStartTime && (null == fileIngestStartTime || childFileIngestStartTime.before(fileIngestStartTime))) {
fileIngestStartTime = childFileIngestStartTime; fileIngestStartTime = childFileIngestStartTime;
} }

View File

@ -22,6 +22,7 @@ import com.google.common.util.concurrent.ThreadFactoryBuilder;
import java.awt.EventQueue; import java.awt.EventQueue;
import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener; import java.beans.PropertyChangeListener;
import java.io.Serializable;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
@ -107,7 +108,7 @@ import org.sleuthkit.datamodel.Content;
* job progress, and ingest module run times. * job progress, and ingest module run times.
*/ */
@ThreadSafe @ThreadSafe
public class IngestManager { public class IngestManager implements IngestProgressSnapshotProvider {
private final static Logger logger = Logger.getLogger(IngestManager.class.getName()); private final static Logger logger = Logger.getLogger(IngestManager.class.getName());
private final static String INGEST_JOB_EVENT_CHANNEL_NAME = "%s-Ingest-Job-Events"; //NON-NLS private final static String INGEST_JOB_EVENT_CHANNEL_NAME = "%s-Ingest-Job-Events"; //NON-NLS
@ -756,7 +757,8 @@ public class IngestManager {
* *
* @return Map of module name to run time (in milliseconds) * @return Map of module name to run time (in milliseconds)
*/ */
Map<String, Long> getModuleRunTimes() { @Override
public Map<String, Long> getModuleRunTimes() {
synchronized (ingestModuleRunTimes) { synchronized (ingestModuleRunTimes) {
Map<String, Long> times = new HashMap<>(ingestModuleRunTimes); Map<String, Long> times = new HashMap<>(ingestModuleRunTimes);
return times; return times;
@ -769,7 +771,8 @@ public class IngestManager {
* *
* @return A collection of ingest manager ingest task snapshots. * @return A collection of ingest manager ingest task snapshots.
*/ */
List<IngestThreadActivitySnapshot> getIngestThreadActivitySnapshots() { @Override
public List<IngestThreadActivitySnapshot> getIngestThreadActivitySnapshots() {
return new ArrayList<>(ingestThreadActivitySnapshots.values()); return new ArrayList<>(ingestThreadActivitySnapshots.values());
} }
@ -778,7 +781,8 @@ public class IngestManager {
* *
* @return A list of ingest job state snapshots. * @return A list of ingest job state snapshots.
*/ */
List<DataSourceIngestJob.Snapshot> getIngestJobSnapshots() { @Override
public List<DataSourceIngestJob.Snapshot> getIngestJobSnapshots() {
List<DataSourceIngestJob.Snapshot> snapShots = new ArrayList<>(); List<DataSourceIngestJob.Snapshot> snapShots = new ArrayList<>();
synchronized (ingestJobsById) { synchronized (ingestJobsById) {
ingestJobsById.values().forEach((job) -> { ingestJobsById.values().forEach((job) -> {
@ -916,7 +920,9 @@ public class IngestManager {
* running in an ingest thread. * running in an ingest thread.
*/ */
@Immutable @Immutable
static final class IngestThreadActivitySnapshot { public static final class IngestThreadActivitySnapshot implements Serializable {
private static final long serialVersionUID = 1L;
private final long threadId; private final long threadId;
private final Date startTime; private final Date startTime;

View File

@ -50,12 +50,14 @@ public final class IngestProgressSnapshotDialog extends JDialog {
/** /**
* Constructs an instance of the dialog with its own frame. Could be modal. * Constructs an instance of the dialog with its own frame. Could be modal.
* Uses the given provider as the source of data for the dialog.
* *
* @param owner - the owner of this dialog. If this dialog should be * @param owner - the owner of this dialog. If this dialog should be
* modal, the owner gets set to non modal. * modal, the owner gets set to non modal.
* @param shouldBeModal - true if this should be modal, false otherwise. * @param shouldBeModal - true if this should be modal, false otherwise.
* @param provider - the provider to use as the source of data.
*/ */
public IngestProgressSnapshotDialog(Container owner, Boolean shouldBeModal) { public IngestProgressSnapshotDialog(Container owner, Boolean shouldBeModal, IngestProgressSnapshotProvider provider) {
super((Window) owner, TITLE, ModalityType.MODELESS); super((Window) owner, TITLE, ModalityType.MODELESS);
if (shouldBeModal && owner instanceof JDialog) { // if called from a modal dialog, manipulate the parent be just under this in z order, and not modal. if (shouldBeModal && owner instanceof JDialog) { // if called from a modal dialog, manipulate the parent be just under this in z order, and not modal.
final JDialog pseudoOwner = (JDialog) owner; final JDialog pseudoOwner = (JDialog) owner;
@ -82,7 +84,7 @@ public final class IngestProgressSnapshotDialog extends JDialog {
this.getRootPane().registerKeyboardAction(e -> { this.getRootPane().registerKeyboardAction(e -> {
this.dispose(); this.dispose();
}, KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), JComponent.WHEN_IN_FOCUSED_WINDOW); }, KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), JComponent.WHEN_IN_FOCUSED_WINDOW);
add(new IngestProgressSnapshotPanel(this)); add(new IngestProgressSnapshotPanel(this, provider));
pack(); pack();
setResizable(false); setResizable(false);
if (shouldBeModal) { // if called from a modal dialog, become modal, otherwise don't. if (shouldBeModal) { // if called from a modal dialog, become modal, otherwise don't.
@ -90,4 +92,17 @@ public final class IngestProgressSnapshotDialog extends JDialog {
} }
setVisible(true); setVisible(true);
} }
/**
* Constructs an instance of the dialog with its own frame. Could be modal.
* Uses the internal IngestManager instance as the source of data for the
* dialog
*
* @param owner - the owner of this dialog. If this dialog should be
* modal, the owner gets set to non modal.
* @param shouldBeModal - true if this should be modal, false otherwise.
*/
public IngestProgressSnapshotDialog(Container owner, Boolean shouldBeModal) {
this(owner, shouldBeModal, IngestManager.getInstance());
}
} }

View File

@ -33,15 +33,17 @@ import org.openide.util.NbBundle;
/** /**
* A panel that displays ingest task progress snapshots. * A panel that displays ingest task progress snapshots.
*/ */
public class IngestProgressSnapshotPanel extends javax.swing.JPanel { class IngestProgressSnapshotPanel extends javax.swing.JPanel {
private final JDialog parent; private final JDialog parent;
private final IngestProgressSnapshotProvider snapshotProvider;
private final IngestThreadActivitySnapshotsTableModel threadActivityTableModel; private final IngestThreadActivitySnapshotsTableModel threadActivityTableModel;
private final IngestJobTableModel jobTableModel; private final IngestJobTableModel jobTableModel;
private final ModuleTableModel moduleTableModel; private final ModuleTableModel moduleTableModel;
IngestProgressSnapshotPanel(JDialog parent) { IngestProgressSnapshotPanel(JDialog parent, IngestProgressSnapshotProvider snapshotProvider) {
this.parent = parent; this.parent = parent;
this.snapshotProvider = snapshotProvider;
threadActivityTableModel = new IngestThreadActivitySnapshotsTableModel(); threadActivityTableModel = new IngestThreadActivitySnapshotsTableModel();
jobTableModel = new IngestJobTableModel(); jobTableModel = new IngestJobTableModel();
moduleTableModel = new ModuleTableModel(); moduleTableModel = new ModuleTableModel();
@ -105,7 +107,7 @@ public class IngestProgressSnapshotPanel extends javax.swing.JPanel {
} }
private void refresh() { private void refresh() {
snapshots = IngestManager.getInstance().getIngestThreadActivitySnapshots(); snapshots = snapshotProvider.getIngestThreadActivitySnapshots();
fireTableDataChanged(); fireTableDataChanged();
} }
@ -187,7 +189,7 @@ public class IngestProgressSnapshotPanel extends javax.swing.JPanel {
} }
private void refresh() { private void refresh() {
jobSnapshots = IngestManager.getInstance().getIngestJobSnapshots(); jobSnapshots = snapshotProvider.getIngestJobSnapshots();
fireTableDataChanged(); fireTableDataChanged();
} }
@ -299,7 +301,7 @@ public class IngestProgressSnapshotPanel extends javax.swing.JPanel {
} }
private void refresh() { private void refresh() {
Map<String, Long> moduleStatMap = IngestManager.getInstance().getModuleRunTimes(); Map<String, Long> moduleStatMap = snapshotProvider.getModuleRunTimes();
moduleStats.clear(); moduleStats.clear();
totalTime = 0; totalTime = 0;
for (String k : moduleStatMap.keySet()) { for (String k : moduleStatMap.keySet()) {

View File

@ -0,0 +1,49 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2018 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.ingest;
import java.util.List;
import java.util.Map;
/**
* Interface that provides a snapshot of ingest progress.
*/
public interface IngestProgressSnapshotProvider {
/**
* Get a snapshot of the state of ingest threads.
*
* @return A list of IngestThreadActivitySnapshot
*/
List<IngestManager.IngestThreadActivitySnapshot> getIngestThreadActivitySnapshots();
/**
* Get a snapshot of the state of ingest jobs.
*
* @return A list of ingest job snapshots.
*/
List<DataSourceIngestJob.Snapshot> getIngestJobSnapshots();
/**
* Gets the cumulative run times for the ingest module.
*
* @return Map of module name to run time (in milliseconds)
*/
Map<String, Long> getModuleRunTimes();
}

View File

@ -18,6 +18,7 @@
*/ */
package org.sleuthkit.autopsy.ingest; package org.sleuthkit.autopsy.ingest;
import java.io.Serializable;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
@ -555,7 +556,11 @@ final class IngestTasksScheduler {
* @return * @return
*/ */
synchronized IngestJobTasksSnapshot getTasksSnapshotForJob(long jobId) { synchronized IngestJobTasksSnapshot getTasksSnapshotForJob(long jobId) {
return new IngestJobTasksSnapshot(jobId); return new IngestJobTasksSnapshot(jobId, this.dataSourceIngestThreadQueue.countQueuedTasksForJob(jobId),
countTasksForJob(this.rootFileTaskQueue, jobId),
countTasksForJob(this.pendingFileTaskQueue, jobId),
this.fileIngestThreadsQueue.countQueuedTasksForJob(jobId),
this.dataSourceIngestThreadQueue.countRunningTasksForJob(jobId) + this.fileIngestThreadsQueue.countRunningTasksForJob(jobId));
} }
/** /**
@ -825,8 +830,9 @@ final class IngestTasksScheduler {
/** /**
* A snapshot of ingest tasks data for an ingest job. * A snapshot of ingest tasks data for an ingest job.
*/ */
class IngestJobTasksSnapshot { public static final class IngestJobTasksSnapshot implements Serializable {
private static final long serialVersionUID = 1L;
private final long jobId; private final long jobId;
private final long dsQueueSize; private final long dsQueueSize;
private final long rootQueueSize; private final long rootQueueSize;
@ -839,13 +845,13 @@ final class IngestTasksScheduler {
* *
* @param jobId The identifier associated with the job. * @param jobId The identifier associated with the job.
*/ */
IngestJobTasksSnapshot(long jobId) { IngestJobTasksSnapshot(long jobId, long dsQueueSize, long rootQueueSize, long dirQueueSize, long fileQueueSize, long runningListSize) {
this.jobId = jobId; this.jobId = jobId;
this.dsQueueSize = IngestTasksScheduler.this.dataSourceIngestThreadQueue.countQueuedTasksForJob(jobId); this.dsQueueSize = dsQueueSize;
this.rootQueueSize = countTasksForJob(IngestTasksScheduler.this.rootFileTaskQueue, jobId); this.rootQueueSize = rootQueueSize;
this.dirQueueSize = countTasksForJob(IngestTasksScheduler.this.pendingFileTaskQueue, jobId); this.dirQueueSize = dirQueueSize;
this.fileQueueSize = IngestTasksScheduler.this.fileIngestThreadsQueue.countQueuedTasksForJob(jobId);; this.fileQueueSize = fileQueueSize;
this.runningListSize = IngestTasksScheduler.this.dataSourceIngestThreadQueue.countRunningTasksForJob(jobId) + IngestTasksScheduler.this.fileIngestThreadsQueue.countRunningTasksForJob(jobId); this.runningListSize = runningListSize;
} }
/** /**

View File

@ -143,19 +143,20 @@ final class AutoIngestAdminActions {
static final class ProgressDialogAction extends AbstractAction { static final class ProgressDialogAction extends AbstractAction {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
private final AutoIngestJob job;
ProgressDialogAction() { ProgressDialogAction(AutoIngestJob job) {
super(Bundle.AutoIngestAdminActions_progressDialogAction_title()); super(Bundle.AutoIngestAdminActions_progressDialogAction_title());
this.job = job;
} }
@Override @Override
public void actionPerformed(ActionEvent e) { public void actionPerformed(ActionEvent e) {
//TODO JIRA-3734
final AutoIngestDashboardTopComponent tc = (AutoIngestDashboardTopComponent) WindowManager.getDefault().findTopComponent(AutoIngestDashboardTopComponent.PREFERRED_ID); final AutoIngestDashboardTopComponent tc = (AutoIngestDashboardTopComponent) WindowManager.getDefault().findTopComponent(AutoIngestDashboardTopComponent.PREFERRED_ID);
if (tc != null) { if (tc != null) {
AutoIngestDashboard dashboard = tc.getAutoIngestDashboard(); AutoIngestDashboard dashboard = tc.getAutoIngestDashboard();
if (dashboard != null) { if (dashboard != null) {
new IngestProgressSnapshotDialog(dashboard.getTopLevelAncestor(), true); new IngestProgressSnapshotDialog(dashboard.getTopLevelAncestor(), true, job);
} }
} }
} }

View File

@ -1,7 +1,7 @@
/* /*
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2011-2017 Basis Technology Corp. * Copyright 2011-2018 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -25,23 +25,28 @@ import java.time.Instant;
import java.util.Collections; import java.util.Collections;
import java.util.Comparator; import java.util.Comparator;
import java.util.Date; import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Objects; import java.util.Objects;
import javax.annotation.concurrent.GuardedBy; import javax.annotation.concurrent.GuardedBy;
import javax.annotation.concurrent.Immutable; import javax.annotation.concurrent.Immutable;
import javax.annotation.concurrent.ThreadSafe; import javax.annotation.concurrent.ThreadSafe;
import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessor; import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessor;
import org.sleuthkit.autopsy.coreutils.NetworkUtils; import org.sleuthkit.autopsy.coreutils.NetworkUtils;
import org.sleuthkit.autopsy.ingest.DataSourceIngestJob.Snapshot;
import org.sleuthkit.autopsy.ingest.IngestJob; import org.sleuthkit.autopsy.ingest.IngestJob;
import org.sleuthkit.autopsy.ingest.IngestManager.IngestThreadActivitySnapshot;
import org.sleuthkit.autopsy.ingest.IngestProgressSnapshotProvider;
/** /**
* An automated ingest job, which is an ingest job performed by the automated * An automated ingest job, which is an ingest job performed by the automated
* ingest service. * ingest service.
*/ */
@ThreadSafe @ThreadSafe
final class AutoIngestJob implements Comparable<AutoIngestJob>, Serializable { final class AutoIngestJob implements Comparable<AutoIngestJob>, IngestProgressSnapshotProvider, Serializable {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
private static final int CURRENT_VERSION = 2; private static final int CURRENT_VERSION = 3;
private static final int DEFAULT_PRIORITY = 0; private static final int DEFAULT_PRIORITY = 0;
private static final String LOCAL_HOST_NAME = NetworkUtils.getLocalHostName(); private static final String LOCAL_HOST_NAME = NetworkUtils.getLocalHostName();
@ -89,6 +94,13 @@ final class AutoIngestJob implements Comparable<AutoIngestJob>, Serializable {
@GuardedBy("this") @GuardedBy("this")
private long dataSourceSize; private long dataSourceSize;
/*
* Version 3 fields.
*/
private List<IngestThreadActivitySnapshot> ingestThreadsSnapshot;
private List<Snapshot> ingestJobsSnapshot;
private Map<String, Long> moduleRunTimesSnapshot;
/** /**
* Constructs a new automated ingest job. All job state not specified in the * Constructs a new automated ingest job. All job state not specified in the
* job manifest is set to the default state for a new job. * job manifest is set to the default state for a new job.
@ -125,6 +137,13 @@ final class AutoIngestJob implements Comparable<AutoIngestJob>, Serializable {
* Version 2 fields. * Version 2 fields.
*/ */
this.dataSourceSize = 0; this.dataSourceSize = 0;
/*
* Version 3 fields.
*/
this.ingestThreadsSnapshot = Collections.emptyList();
this.ingestJobsSnapshot = Collections.emptyList();
this.moduleRunTimesSnapshot = Collections.emptyMap();
} catch (Exception ex) { } catch (Exception ex) {
throw new AutoIngestJobException(String.format("Error creating automated ingest job"), ex); throw new AutoIngestJobException(String.format("Error creating automated ingest job"), ex);
} }
@ -167,6 +186,13 @@ final class AutoIngestJob implements Comparable<AutoIngestJob>, Serializable {
* Version 2 fields. * Version 2 fields.
*/ */
this.dataSourceSize = nodeData.getDataSourceSize(); this.dataSourceSize = nodeData.getDataSourceSize();
/*
* Version 3 fields
*/
this.ingestThreadsSnapshot = Collections.emptyList();
this.ingestJobsSnapshot = Collections.emptyList();
this.moduleRunTimesSnapshot = Collections.emptyMap();
} catch (Exception ex) { } catch (Exception ex) {
throw new AutoIngestJobException(String.format("Error creating automated ingest job"), ex); throw new AutoIngestJobException(String.format("Error creating automated ingest job"), ex);
} }
@ -340,6 +366,31 @@ final class AutoIngestJob implements Comparable<AutoIngestJob>, Serializable {
return this.ingestJob; return this.ingestJob;
} }
/**
* Sets the ingest thread snapshot for the auto ingest job.
*
* @param snapshot
*/
synchronized void setIngestThreadSnapshot(List<IngestThreadActivitySnapshot> snapshot) {
this.ingestThreadsSnapshot = snapshot;
}
/**
* Sets the ingest job snapshot for the auto ingest job.
* @param snapshot
*/
synchronized void setIngestJobsSnapshot(List<Snapshot> snapshot) {
this.ingestJobsSnapshot = snapshot;
}
/**
* Sets the module run times snapshot for the auto ingest job.
* @param snapshot
*/
synchronized void setModuleRuntimesSnapshot(Map<String, Long> snapshot) {
this.moduleRunTimesSnapshot = snapshot;
}
/** /**
* Cancels the job. * Cancels the job.
*/ */
@ -541,6 +592,21 @@ final class AutoIngestJob implements Comparable<AutoIngestJob>, Serializable {
return -this.getManifest().getDateFileCreated().compareTo(otherJob.getManifest().getDateFileCreated()); return -this.getManifest().getDateFileCreated().compareTo(otherJob.getManifest().getDateFileCreated());
} }
@Override
public List<IngestThreadActivitySnapshot> getIngestThreadActivitySnapshots() {
return this.ingestThreadsSnapshot;
}
@Override
public List<Snapshot> getIngestJobSnapshots() {
return this.ingestJobsSnapshot;
}
@Override
public Map<String, Long> getModuleRunTimes() {
return this.moduleRunTimesSnapshot;
}
/** /**
* Comparator that supports doing a descending sort of jobs based on job * Comparator that supports doing a descending sort of jobs based on job
* completion date. * completion date.

View File

@ -260,9 +260,9 @@ final class AutoIngestJobsNode extends AbstractNode {
actions.add(deprioritizeCaseAction); actions.add(deprioritizeCaseAction);
break; break;
case RUNNING_JOB: case RUNNING_JOB:
actions.add(new AutoIngestAdminActions.ProgressDialogAction()); actions.add(new AutoIngestAdminActions.ProgressDialogAction(autoIngestJob));
actions.add(new AutoIngestAdminActions.CancelJobAction(autoIngestJob)); actions.add(new AutoIngestAdminActions.CancelJobAction(autoIngestJob));
actions.add(new AutoIngestAdminActions.CancelModuleAction()); // actions.add(new AutoIngestAdminActions.CancelModuleAction());
break; break;
case COMPLETED_JOB: case COMPLETED_JOB:
actions.add(new AutoIngestAdminActions.ReprocessJobAction(autoIngestJob)); actions.add(new AutoIngestAdminActions.ReprocessJobAction(autoIngestJob));

View File

@ -3052,6 +3052,9 @@ final class AutoIngestManager extends Observable implements PropertyChangeListen
synchronized (jobsLock) { synchronized (jobsLock) {
if (currentJob != null) { if (currentJob != null) {
currentJob.getProcessingStageDetails(); currentJob.getProcessingStageDetails();
currentJob.setIngestThreadSnapshot(IngestManager.getInstance().getIngestThreadActivitySnapshots());
currentJob.setIngestJobsSnapshot(IngestManager.getInstance().getIngestJobSnapshots());
currentJob.setModuleRuntimesSnapshot(IngestManager.getInstance().getModuleRunTimes());
setChanged(); setChanged();
notifyObservers(Event.JOB_STATUS_UPDATED); notifyObservers(Event.JOB_STATUS_UPDATED);
updateCoordinationServiceManifestNode(currentJob); updateCoordinationServiceManifestNode(currentJob);

View File

@ -188,7 +188,17 @@ final class AutoIngestMonitor extends Observable implements PropertyChangeListen
*/ */
AutoIngestJob job = event.getJob(); AutoIngestJob job = event.getJob();
jobsSnapshot.removePendingJob(job); jobsSnapshot.removePendingJob(job);
jobsSnapshot.addOrReplaceRunningJob(job);
// Update the state of the existing job in the running jobs table
for (AutoIngestJob runningJob : jobsSnapshot.getRunningJobs()) {
if (runningJob.equals(job)) {
runningJob.setIngestJobsSnapshot(job.getIngestJobSnapshots());
runningJob.setIngestThreadSnapshot(job.getIngestThreadActivitySnapshots());
runningJob.setModuleRuntimesSnapshot(job.getModuleRunTimes());
runningJob.setProcessingStage(job.getProcessingStage(), job.getProcessingStageStartDate());
runningJob.setProcessingStatus(job.getProcessingStatus());
}
}
setChanged(); setChanged();
notifyObservers(jobsSnapshot); notifyObservers(jobsSnapshot);
} }