Merge pull request #3715 from esaunders/3737_ain_pause_resume

3737 Added pause/resume/shutdown functionality to AID
This commit is contained in:
Richard Cordovano 2018-05-08 10:50:38 -04:00 committed by GitHub
commit 7a6c8d8a8f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 305 additions and 91 deletions

View File

@ -52,6 +52,14 @@ final class AinStatusDashboard extends javax.swing.JPanel implements Observer {
autoIngestMonitor.addObserver(this); autoIngestMonitor.addObserver(this);
} }
AutoIngestMonitor getMonitor() {
return autoIngestMonitor;
}
AinStatusPanel getNodesStatusPanel() {
return nodesPanel;
}
/** /**
* This method is called from within the constructor to initialize the form. * This method is called from within the constructor to initialize the form.
* WARNING: Do NOT modify this code. The content of this method is always * WARNING: Do NOT modify this code. The content of this method is always
@ -143,9 +151,10 @@ final class AinStatusDashboard extends javax.swing.JPanel implements Observer {
@Override @Override
public void update(Observable o, Object arg) { public void update(Observable o, Object arg) {
if (arg instanceof AutoIngestNodeState) if (arg instanceof AutoIngestNodeState) {
EventQueue.invokeLater(() -> { EventQueue.invokeLater(() -> {
refreshTables(); refreshTables();
}); });
}
} }
} }

View File

@ -18,6 +18,7 @@
*/ */
package org.sleuthkit.autopsy.experimental.autoingest; package org.sleuthkit.autopsy.experimental.autoingest;
import java.awt.Component;
import java.util.List; import java.util.List;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@ -74,8 +75,6 @@ final class AinStatusDashboardTopComponent extends TopComponent {
tc.add(nodeTab); tc.add(nodeTab);
tc.open(); tc.open();
} }
tc.toFront();
tc.requestActive();
} }
} }
@ -115,6 +114,19 @@ final class AinStatusDashboardTopComponent extends TopComponent {
WindowManager.getDefault().setTopComponentFloating(this, true); WindowManager.getDefault().setTopComponentFloating(this, true);
} }
/**
* Get the current AutoIngestDashboard if there is one.
*
* @return the current AutoIngestDashboard or null if there is not one
*/
AinStatusDashboard getAinStatusDashboard() {
for (Component comp : getComponents()) {
if (comp instanceof AinStatusDashboard) {
return (AinStatusDashboard) comp;
}
}
return null;
}
/** /**
* This method is called from within the constructor to initialize the form. * This method is called from within the constructor to initialize the form.
* WARNING: Do NOT modify this code. The content of this method is always * WARNING: Do NOT modify this code. The content of this method is always

View File

@ -97,6 +97,7 @@ final class AinStatusNode extends AbstractNode {
@Messages({"AinStatusNode.hostName.title=Host Name", @Messages({"AinStatusNode.hostName.title=Host Name",
"AinStatusNode.status.title=Status", "AinStatusNode.status.title=Status",
"AinStatusNode.status.running=Running", "AinStatusNode.status.running=Running",
"AinStatusNode.status.pauseRequested=Pause Requested",
"AinStatusNode.status.pausedByUser=Paused By User", "AinStatusNode.status.pausedByUser=Paused By User",
"AinStatusNode.status.pausedForError=Paused Due to System Error", "AinStatusNode.status.pausedForError=Paused Due to System Error",
"AinStatusNode.status.startingup=Starting Up", "AinStatusNode.status.startingup=Starting Up",
@ -129,6 +130,9 @@ final class AinStatusNode extends AbstractNode {
case PAUSED_DUE_TO_SYSTEM_ERROR: case PAUSED_DUE_TO_SYSTEM_ERROR:
status = Bundle.AinStatusNode_status_pausedForError(); status = Bundle.AinStatusNode_status_pausedForError();
break; break;
case PAUSE_REQUESTED:
status = Bundle.AinStatusNode_status_pauseRequested();
break;
default: default:
break; break;
} }
@ -142,12 +146,11 @@ final class AinStatusNode extends AbstractNode {
List<Action> actions = new ArrayList<>(); List<Action> actions = new ArrayList<>();
if (AutoIngestDashboard.isAdminAutoIngestDashboard()) { if (AutoIngestDashboard.isAdminAutoIngestDashboard()) {
if (nodeState.getState() == AutoIngestNodeState.State.PAUSED_BY_REQUEST if (nodeState.getState() == AutoIngestNodeState.State.PAUSED_BY_REQUEST
|| nodeState.getState() == AutoIngestNodeState.State.PAUSED_DUE_TO_SYSTEM_ERROR) { || nodeState.getState() == AutoIngestNodeState.State.PAUSED_DUE_TO_SYSTEM_ERROR
actions.add(new AutoIngestAdminActions.ResumeAction()); || nodeState.getState() == AutoIngestNodeState.State.RUNNING) {
} else if (nodeState.getState() == AutoIngestNodeState.State.RUNNING){ actions.add(new AutoIngestAdminActions.AutoIngestNodeControlAction.PauseResumeAction(nodeState));
actions.add(new AutoIngestAdminActions.PauseAction());
} }
actions.add(new AutoIngestAdminActions.ShutdownAction()); actions.add(new AutoIngestAdminActions.AutoIngestNodeControlAction.ShutdownAction(nodeState));
} }
return actions.toArray(new Action[actions.size()]); return actions.toArray(new Action[actions.size()]);
} }

View File

@ -99,12 +99,12 @@ final class AinStatusPanel extends javax.swing.JPanel implements ExplorerManager
void refresh(AutoIngestMonitor monitor) { void refresh(AutoIngestMonitor monitor) {
outline.setRowSelectionAllowed(false); outline.setRowSelectionAllowed(false);
Node[] selectedNodes = explorerManager.getSelectedNodes(); Node[] selectedNodes = explorerManager.getSelectedNodes();
AinStatusNode autoIngestNode = new AinStatusNode(monitor); AinStatusNode ainStatusNode = new AinStatusNode(monitor);
explorerManager.setRootContext(autoIngestNode); explorerManager.setRootContext(ainStatusNode);
outline.setRowSelectionAllowed(true); outline.setRowSelectionAllowed(true);
if (selectedNodes.length > 0 && autoIngestNode.getChildren().findChild(selectedNodes[0].getName()) != null && outline.isFocusable()) { //don't allow saved selections of empty nodes to be restored if (selectedNodes.length > 0 && ainStatusNode.getChildren().findChild(selectedNodes[0].getName()) != null && outline.isFocusable()) { //don't allow saved selections of empty nodes to be restored
try { try {
explorerManager.setSelectedNodes(new Node[]{autoIngestNode.getChildren().findChild(selectedNodes[0].getName())}); explorerManager.setSelectedNodes(new Node[]{ainStatusNode.getChildren().findChild(selectedNodes[0].getName())});
} catch (PropertyVetoException ignore) { } catch (PropertyVetoException ignore) {
//Unable to select previously selected node //Unable to select previously selected node
} }

View File

@ -18,14 +18,122 @@
*/ */
package org.sleuthkit.autopsy.experimental.autoingest; package org.sleuthkit.autopsy.experimental.autoingest;
import java.awt.Cursor;
import java.awt.EventQueue;
import java.awt.event.ActionEvent; import java.awt.event.ActionEvent;
import java.util.logging.Level;
import javax.swing.AbstractAction; import javax.swing.AbstractAction;
import javax.swing.JOptionPane;
import org.openide.util.NbBundle; import org.openide.util.NbBundle;
import org.openide.windows.WindowManager; import org.openide.windows.WindowManager;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.experimental.autoingest.AutoIngestMonitor.AutoIngestNodeState;
import org.sleuthkit.autopsy.ingest.IngestProgressSnapshotDialog; import org.sleuthkit.autopsy.ingest.IngestProgressSnapshotDialog;
final class AutoIngestAdminActions { final class AutoIngestAdminActions {
static abstract class AutoIngestNodeControlAction extends AbstractAction {
private final AutoIngestNodeState nodeState;
private final Logger logger = Logger.getLogger(AutoIngestNodeControlAction.class.getName());
AutoIngestNodeControlAction(AutoIngestNodeState nodeState, String title) {
super(title);
this.nodeState = nodeState;
}
@Override
public void actionPerformed(ActionEvent e) {
if (nodeState != null) {
final AinStatusDashboardTopComponent tc = (AinStatusDashboardTopComponent) WindowManager.getDefault().findTopComponent(AinStatusDashboardTopComponent.PREFERRED_ID);
if (tc != null) {
AinStatusDashboard dashboard = tc.getAinStatusDashboard();
if (dashboard != null) {
dashboard.getNodesStatusPanel().setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
EventQueue.invokeLater(() -> {
try {
controlAutoIngestNode(dashboard);
} catch (AutoIngestMonitor.AutoIngestMonitorException ex) {
logger.log(Level.WARNING, "Error sending control event to node", ex);
} finally {
dashboard.getNodesStatusPanel().setCursor(Cursor.getDefaultCursor());
}
});
}
}
}
}
protected abstract void controlAutoIngestNode(AinStatusDashboard dashboard) throws AutoIngestMonitor.AutoIngestMonitorException;
AutoIngestNodeState getNodeState() {
return nodeState;
}
@NbBundle.Messages({"AutoIngestAdminActions.pause.title=Pause Node",
"AutoIngestAdminActions.resume.title=Resume Node"})
static final class PauseResumeAction extends AutoIngestNodeControlAction {
private static final long serialVersionUID = 1L;
PauseResumeAction(AutoIngestNodeState nodeState) {
super(nodeState, nodeState.getState() == AutoIngestNodeState.State.RUNNING
? Bundle.AutoIngestAdminActions_pause_title() : Bundle.AutoIngestAdminActions_resume_title());
}
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone(); //To change body of generated methods, choose Tools | Templates.
}
@Override
protected void controlAutoIngestNode(AinStatusDashboard dashboard) throws AutoIngestMonitor.AutoIngestMonitorException {
if (getNodeState().getState() == AutoIngestNodeState.State.RUNNING) {
dashboard.getMonitor().pauseAutoIngestNode(getNodeState().getName());
} else {
dashboard.getMonitor().resumeAutoIngestNode(getNodeState().getName());
}
}
}
@NbBundle.Messages({"AutoIngestAdminActions.shutdown.title=Shutdown Node",
"AutoIngestAdminActions.shutdown.OK=OK", "AutoIngestAdminActions.shutdown.Cancel=Cancel",
"AutoIngestAdminActions.shutdown.consequences=This will cancel any currently running job on this host. Exiting while a job is running potentially leaves the case in an inconsistent or corrupted state."})
static final class ShutdownAction extends AutoIngestNodeControlAction {
private static final long serialVersionUID = 1L;
ShutdownAction(AutoIngestNodeState nodeState) {
super(nodeState, Bundle.AutoIngestAdminActions_shutdown_title());
}
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone(); //To change body of generated methods, choose Tools | Templates.
}
@Override
protected void controlAutoIngestNode(AinStatusDashboard dashboard) throws AutoIngestMonitor.AutoIngestMonitorException {
Object[] options = {Bundle.AutoIngestAdminActions_shutdown_OK(),
Bundle.AutoIngestAdminActions_shutdown_Cancel()
};
int reply = JOptionPane.showOptionDialog(dashboard.getNodesStatusPanel(),
Bundle.AutoIngestAdminActions_shutdown_consequences(),
NbBundle.getMessage(AutoIngestControlPanel.class, "ConfirmationDialog.ConfirmExitHeader"),
JOptionPane.DEFAULT_OPTION,
JOptionPane.WARNING_MESSAGE,
null,
options,
options[JOptionPane.NO_OPTION]);
if (reply == JOptionPane.OK_OPTION) {
dashboard.getMonitor().shutdownAutoIngestNode(getNodeState().getName());
}
}
}
}
@NbBundle.Messages({"AutoIngestAdminActions.progressDialogAction.title=Ingest Progress"}) @NbBundle.Messages({"AutoIngestAdminActions.progressDialogAction.title=Ingest Progress"})
static final class ProgressDialogAction extends AbstractAction { static final class ProgressDialogAction extends AbstractAction {
@ -152,65 +260,4 @@ final class AutoIngestAdminActions {
return super.clone(); //To change body of generated methods, choose Tools | Templates. return super.clone(); //To change body of generated methods, choose Tools | Templates.
} }
} }
@NbBundle.Messages({"AutoIngestAdminActions.pause.title=Pause Node"})
static final class PauseAction extends AbstractAction {
private static final long serialVersionUID = 1L;
PauseAction() {
super(Bundle.AutoIngestAdminActions_pause_title());
}
@Override
public void actionPerformed(ActionEvent e) {
//TODO JIRA-
}
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone(); //To change body of generated methods, choose Tools | Templates.
}
}
@NbBundle.Messages({"AutoIngestAdminActions.resume.title=Resume Node"})
static final class ResumeAction extends AbstractAction {
private static final long serialVersionUID = 1L;
ResumeAction() {
super(Bundle.AutoIngestAdminActions_resume_title());
}
@Override
public void actionPerformed(ActionEvent e) {
//TODO JIRA-
}
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone(); //To change body of generated methods, choose Tools | Templates.
}
}
@NbBundle.Messages({"AutoIngestAdminActions.shutdown.title=Shutdown Node"})
static final class ShutdownAction extends AbstractAction {
private static final long serialVersionUID = 1L;
ShutdownAction() {
super(Bundle.AutoIngestAdminActions_shutdown_title());
}
@Override
public void actionPerformed(ActionEvent e) {
//TODO JIRA-
}
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone(); //To change body of generated methods, choose Tools | Templates.
}
}
} }

View File

@ -852,7 +852,7 @@ public final class AutoIngestControlPanel extends JPanel implements Observer {
case CASE_DELETED: case CASE_DELETED:
updateExecutor.submit(new UpdateAllJobsTablesTask()); updateExecutor.submit(new UpdateAllJobsTablesTask());
break; break;
case PAUSED_BY_REQUEST: case PAUSED_BY_USER_REQUEST:
EventQueue.invokeLater(() -> { EventQueue.invokeLater(() -> {
tbStatusMessage.setText(org.openide.util.NbBundle.getMessage(AutoIngestControlPanel.class, "AutoIngestControlPanel.bnPause.paused")); tbStatusMessage.setText(org.openide.util.NbBundle.getMessage(AutoIngestControlPanel.class, "AutoIngestControlPanel.bnPause.paused"));
bnOptions.setEnabled(true); bnOptions.setEnabled(true);
@ -881,6 +881,9 @@ public final class AutoIngestControlPanel extends JPanel implements Observer {
case JOB_STATUS_UPDATED: case JOB_STATUS_UPDATED:
updateExecutor.submit(new UpdateRunningJobsTablesTask()); updateExecutor.submit(new UpdateRunningJobsTablesTask());
break; break;
case SHUTTING_DOWN:
LifecycleManager.getDefault().exit();
break;
default: default:
break; break;
} }

View File

@ -170,7 +170,7 @@ final class AutoIngestJobsPanel extends javax.swing.JPanel implements ExplorerMa
AutoIngestJobsNode autoIngestNode = new AutoIngestJobsNode(jobsSnapshot, status); AutoIngestJobsNode autoIngestNode = new AutoIngestJobsNode(jobsSnapshot, status);
explorerManager.setRootContext(autoIngestNode); explorerManager.setRootContext(autoIngestNode);
outline.setRowSelectionAllowed(true); outline.setRowSelectionAllowed(true);
if (selectedNodes.length > 0 && outline.isFocusable()) { //don't allow saved selections of empty nodes to be restored if (selectedNodes.length > 0 && autoIngestNode.getChildren().findChild(selectedNodes[0].getName()) != null && outline.isFocusable()) { //don't allow saved selections of empty nodes to be restored
try { try {
explorerManager.setSelectedNodes(new Node[]{autoIngestNode.getChildren().findChild(selectedNodes[0].getName())}); explorerManager.setSelectedNodes(new Node[]{autoIngestNode.getChildren().findChild(selectedNodes[0].getName())});
} catch (PropertyVetoException ignore) { } catch (PropertyVetoException ignore) {

View File

@ -93,6 +93,7 @@ import org.sleuthkit.autopsy.experimental.configuration.SharedConfiguration.Shar
import org.sleuthkit.autopsy.datasourceprocessors.AutoIngestDataSourceProcessor; import org.sleuthkit.autopsy.datasourceprocessors.AutoIngestDataSourceProcessor;
import org.sleuthkit.autopsy.datasourceprocessors.AutoIngestDataSourceProcessor.AutoIngestDataSourceProcessorException; import org.sleuthkit.autopsy.datasourceprocessors.AutoIngestDataSourceProcessor.AutoIngestDataSourceProcessorException;
import org.sleuthkit.autopsy.experimental.autoingest.AutoIngestJob.AutoIngestJobException; import org.sleuthkit.autopsy.experimental.autoingest.AutoIngestJob.AutoIngestJobException;
import org.sleuthkit.autopsy.experimental.autoingest.AutoIngestNodeControlEvent.ControlEventType;
import org.sleuthkit.autopsy.ingest.IngestJob; import org.sleuthkit.autopsy.ingest.IngestJob;
import org.sleuthkit.autopsy.ingest.IngestJob.CancellationReason; import org.sleuthkit.autopsy.ingest.IngestJob.CancellationReason;
import org.sleuthkit.autopsy.ingest.IngestJobSettings; import org.sleuthkit.autopsy.ingest.IngestJobSettings;
@ -133,7 +134,10 @@ final class AutoIngestManager extends Observable implements PropertyChangeListen
Event.JOB_COMPLETED.toString(), Event.JOB_COMPLETED.toString(),
Event.CASE_PRIORITIZED.toString(), Event.CASE_PRIORITIZED.toString(),
Event.JOB_STARTED.toString(), Event.JOB_STARTED.toString(),
Event.REPORT_STATE.toString()})); Event.REPORT_STATE.toString(),
ControlEventType.PAUSE.toString(),
ControlEventType.RESUME.toString(),
ControlEventType.SHUTDOWN.toString()}));
private static final long JOB_STATUS_EVENT_INTERVAL_SECONDS = 10; private static final long JOB_STATUS_EVENT_INTERVAL_SECONDS = 10;
private static final String JOB_STATUS_PUBLISHING_THREAD_NAME = "AIM-job-status-event-publisher-%d"; private static final String JOB_STATUS_PUBLISHING_THREAD_NAME = "AIM-job-status-event-publisher-%d";
private static final long MAX_MISSED_JOB_STATUS_UPDATES = 10; private static final long MAX_MISSED_JOB_STATUS_UPDATES = 10;
@ -281,6 +285,8 @@ final class AutoIngestManager extends Observable implements PropertyChangeListen
handleRemoteCaseDeletedEvent((AutoIngestCaseDeletedEvent) event); handleRemoteCaseDeletedEvent((AutoIngestCaseDeletedEvent) event);
} else if (event instanceof AutoIngestRequestNodeStateEvent) { } else if (event instanceof AutoIngestRequestNodeStateEvent) {
handleRemoteRequestNodeStateEvent(); handleRemoteRequestNodeStateEvent();
} else if (event instanceof AutoIngestNodeControlEvent) {
handleRemoteNodeControlEvent((AutoIngestNodeControlEvent) event);
} }
} }
} }
@ -404,6 +410,28 @@ final class AutoIngestManager extends Observable implements PropertyChangeListen
eventPublisher.publishRemotely(lastPublishedStateEvent); eventPublisher.publishRemotely(lastPublishedStateEvent);
} }
private void handleRemoteNodeControlEvent(AutoIngestNodeControlEvent event) {
if (event.getNodeName().compareToIgnoreCase(LOCAL_HOST_NAME) == 0) {
switch (event.getControlEventType()) {
case PAUSE:
pause();
break;
case RESUME:
resume();
break;
case SHUTDOWN:
shutDown();
// Notify the front end (if any) to shutdown.
setChanged();
notifyObservers(Event.SHUTTING_DOWN);
break;
default:
SYS_LOGGER.log(Level.WARNING, "Received unsupported control event: {0}", event.getControlEventType());
break;
}
}
}
/** /**
* Shuts down auto ingest. * Shuts down auto ingest.
*/ */
@ -418,6 +446,7 @@ final class AutoIngestManager extends Observable implements PropertyChangeListen
eventPublisher.removeSubscriber(EVENT_LIST, instance); eventPublisher.removeSubscriber(EVENT_LIST, instance);
stopInputFolderScans(); stopInputFolderScans();
stopJobProcessing(); stopJobProcessing();
eventPublisher.publishRemotely(lastPublishedStateEvent = new AutoIngestNodeStateEvent(Event.SHUTDOWN, AutoIngestManager.LOCAL_HOST_NAME));
eventPublisher.closeRemoteEventChannel(); eventPublisher.closeRemoteEventChannel();
cleanupJobs(); cleanupJobs();
@ -1695,14 +1724,13 @@ final class AutoIngestManager extends Observable implements PropertyChangeListen
* object. * object.
*/ */
setChanged(); setChanged();
notifyObservers(Event.PAUSED_BY_REQUEST); notifyObservers(Event.PAUSED_BY_USER_REQUEST);
/**
* Publish an event to let remote listeners know that the
* node has been paused.
*/
eventPublisher.publishRemotely(lastPublishedStateEvent = new AutoIngestNodeStateEvent(Event.PAUSED_BY_REQUEST, AutoIngestManager.LOCAL_HOST_NAME));
} }
/**
* Publish an event to let remote listeners know that a pause
* has been requested.
*/
eventPublisher.publishRemotely(lastPublishedStateEvent = new AutoIngestNodeStateEvent(Event.PAUSE_REQUESTED, AutoIngestManager.LOCAL_HOST_NAME));
} }
} }
@ -1745,18 +1773,22 @@ final class AutoIngestManager extends Observable implements PropertyChangeListen
* if auto ingest is shutting down. * if auto ingest is shutting down.
*/ */
private void pauseIfRequested() throws InterruptedException { private void pauseIfRequested() throws InterruptedException {
if (State.SHUTTING_DOWN == state) {
return;
}
synchronized (pauseLock) { synchronized (pauseLock) {
if (pauseRequested) { if (pauseRequested) {
SYS_LOGGER.log(Level.INFO, "Job processing paused by request"); SYS_LOGGER.log(Level.INFO, "Job processing paused by request");
pauseRequested = false; pauseRequested = false;
setChanged(); setChanged();
notifyObservers(Event.PAUSED_BY_REQUEST); notifyObservers(Event.PAUSED_BY_USER_REQUEST);
/** /**
* Publish an event to let remote listeners know that the * Publish an event to let remote listeners know that the
* node has been paused. * node has been paused.
*/ */
eventPublisher.publishRemotely(lastPublishedStateEvent = new AutoIngestNodeStateEvent(Event.PAUSED_BY_REQUEST, AutoIngestManager.LOCAL_HOST_NAME)); eventPublisher.publishRemotely(lastPublishedStateEvent = new AutoIngestNodeStateEvent(Event.PAUSED_BY_USER_REQUEST, AutoIngestManager.LOCAL_HOST_NAME));
pauseLock.wait(); pauseLock.wait();
SYS_LOGGER.log(Level.INFO, "Job processing resumed after pause request"); SYS_LOGGER.log(Level.INFO, "Job processing resumed after pause request");
@ -1780,6 +1812,10 @@ final class AutoIngestManager extends Observable implements PropertyChangeListen
* if auto ingest is shutting down. * if auto ingest is shutting down.
*/ */
private void pauseForSystemError() throws InterruptedException { private void pauseForSystemError() throws InterruptedException {
if (State.SHUTTING_DOWN == state) {
return;
}
synchronized (pauseLock) { synchronized (pauseLock) {
SYS_LOGGER.log(Level.SEVERE, "Job processing paused for system error"); SYS_LOGGER.log(Level.SEVERE, "Job processing paused for system error");
setChanged(); setChanged();
@ -3061,12 +3097,14 @@ final class AutoIngestManager extends Observable implements PropertyChangeListen
JOB_COMPLETED, JOB_COMPLETED,
CASE_PRIORITIZED, CASE_PRIORITIZED,
CASE_DELETED, CASE_DELETED,
PAUSED_BY_REQUEST, PAUSE_REQUESTED,
PAUSED_BY_USER_REQUEST,
PAUSED_FOR_SYSTEM_ERROR, PAUSED_FOR_SYSTEM_ERROR,
RESUMED, RESUMED,
STARTING_UP, STARTING_UP,
RUNNING, RUNNING,
SHUTTING_DOWN, SHUTTING_DOWN,
SHUTDOWN,
REPORT_STATE REPORT_STATE
} }

View File

@ -42,6 +42,7 @@ import org.sleuthkit.autopsy.events.AutopsyEventException;
import org.sleuthkit.autopsy.events.AutopsyEventPublisher; import org.sleuthkit.autopsy.events.AutopsyEventPublisher;
import org.sleuthkit.autopsy.experimental.autoingest.AutoIngestJob.ProcessingStatus; import org.sleuthkit.autopsy.experimental.autoingest.AutoIngestJob.ProcessingStatus;
import org.sleuthkit.autopsy.experimental.autoingest.AutoIngestManager.Event; import org.sleuthkit.autopsy.experimental.autoingest.AutoIngestManager.Event;
import org.sleuthkit.autopsy.experimental.autoingest.AutoIngestNodeControlEvent.ControlEventType;
/** /**
* An auto ingest monitor responsible for monitoring and reporting the * An auto ingest monitor responsible for monitoring and reporting the
* processing of auto ingest jobs. * processing of auto ingest jobs.
@ -61,10 +62,12 @@ final class AutoIngestMonitor extends Observable implements PropertyChangeListen
AutoIngestManager.Event.CASE_PRIORITIZED.toString(), AutoIngestManager.Event.CASE_PRIORITIZED.toString(),
AutoIngestManager.Event.JOB_STARTED.toString(), AutoIngestManager.Event.JOB_STARTED.toString(),
AutoIngestManager.Event.RUNNING.toString(), AutoIngestManager.Event.RUNNING.toString(),
AutoIngestManager.Event.PAUSED_BY_REQUEST.toString(), AutoIngestManager.Event.PAUSE_REQUESTED.toString(),
AutoIngestManager.Event.PAUSED_BY_USER_REQUEST.toString(),
AutoIngestManager.Event.PAUSED_FOR_SYSTEM_ERROR.toString(), AutoIngestManager.Event.PAUSED_FOR_SYSTEM_ERROR.toString(),
AutoIngestManager.Event.STARTING_UP.toString(), AutoIngestManager.Event.STARTING_UP.toString(),
AutoIngestManager.Event.SHUTTING_DOWN.toString(), AutoIngestManager.Event.SHUTTING_DOWN.toString(),
AutoIngestManager.Event.SHUTDOWN.toString(),
AutoIngestManager.Event.RESUMED.toString()})); AutoIngestManager.Event.RESUMED.toString()}));
private final AutopsyEventPublisher eventPublisher; private final AutopsyEventPublisher eventPublisher;
private CoordinationService coordinationService; private CoordinationService coordinationService;
@ -221,9 +224,10 @@ final class AutoIngestMonitor extends Observable implements PropertyChangeListen
* @param event A node state change event. * @param event A node state change event.
*/ */
private void handleAutoIngestNodeStateEvent(AutoIngestNodeStateEvent event) { private void handleAutoIngestNodeStateEvent(AutoIngestNodeStateEvent event) {
if (event.getEventType() == AutoIngestManager.Event.SHUTTING_DOWN) { AutoIngestNodeState oldNodeState = null;
if (event.getEventType() == AutoIngestManager.Event.SHUTDOWN) {
// Remove node from collection. // Remove node from collection.
nodeStates.remove(event.getNodeName()); oldNodeState = nodeStates.remove(event.getNodeName());
} else { } else {
// Otherwise either create an entry for the given node name or update // Otherwise either create an entry for the given node name or update
// an existing entry in the map. // an existing entry in the map.
@ -231,7 +235,7 @@ final class AutoIngestMonitor extends Observable implements PropertyChangeListen
} }
setChanged(); setChanged();
// Trigger a dashboard refresh. // Trigger a dashboard refresh.
notifyObservers(nodeStates.get(event.getNodeName())); notifyObservers(oldNodeState == null ? nodeStates.get(event.getNodeName()) : oldNodeState);
} }
/** /**
@ -522,6 +526,45 @@ final class AutoIngestMonitor extends Observable implements PropertyChangeListen
} }
} }
/**
* Send the given control event to the given node.
*
* @param eventType The type of control event to send.
* @param nodeName The name of the node to send it to.
*/
private void sendControlEventToNode(ControlEventType eventType, String nodeName) {
new Thread(() -> {
eventPublisher.publishRemotely(new AutoIngestNodeControlEvent(eventType, nodeName));
}).start();
}
/**
* Tell the specified node to pause.
*
* @param nodeName
*/
void pauseAutoIngestNode(String nodeName) {
sendControlEventToNode(ControlEventType.PAUSE, nodeName);
}
/**
* Tell the specified node to resume.
*
* @param nodeName
*/
void resumeAutoIngestNode(String nodeName) {
sendControlEventToNode(ControlEventType.RESUME, nodeName);
}
/**
* Tell the specified node to shutdown.
*
* @param nodeName
*/
void shutdownAutoIngestNode(String nodeName) {
sendControlEventToNode(ControlEventType.SHUTDOWN, nodeName);
}
/** /**
* A task that queries the coordination service for auto ingest manifest * A task that queries the coordination service for auto ingest manifest
* node data and converts it to auto ingest jobs for publication top its * node data and converts it to auto ingest jobs for publication top its
@ -677,6 +720,7 @@ final class AutoIngestMonitor extends Observable implements PropertyChangeListen
STARTING_UP, STARTING_UP,
SHUTTING_DOWN, SHUTTING_DOWN,
RUNNING, RUNNING,
PAUSE_REQUESTED,
PAUSED_BY_REQUEST, PAUSED_BY_REQUEST,
PAUSED_DUE_TO_SYSTEM_ERROR, PAUSED_DUE_TO_SYSTEM_ERROR,
UNKNOWN UNKNOWN
@ -697,7 +741,7 @@ final class AutoIngestMonitor extends Observable implements PropertyChangeListen
case RUNNING: case RUNNING:
nodeState = State.RUNNING; nodeState = State.RUNNING;
break; break;
case PAUSED_BY_REQUEST: case PAUSED_BY_USER_REQUEST:
nodeState = State.PAUSED_BY_REQUEST; nodeState = State.PAUSED_BY_REQUEST;
break; break;
case PAUSED_FOR_SYSTEM_ERROR: case PAUSED_FOR_SYSTEM_ERROR:
@ -706,6 +750,9 @@ final class AutoIngestMonitor extends Observable implements PropertyChangeListen
case RESUMED: case RESUMED:
nodeState = State.RUNNING; nodeState = State.RUNNING;
break; break;
case PAUSE_REQUESTED:
nodeState = State.PAUSE_REQUESTED;
break;
default: default:
nodeState = State.UNKNOWN; nodeState = State.UNKNOWN;
break; break;

View File

@ -0,0 +1,55 @@
/*
* 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.experimental.autoingest;
import java.io.Serializable;
import org.sleuthkit.autopsy.events.AutopsyEvent;
/**
* Event published to pause, resume or shutdown an AIN.
*/
final class AutoIngestNodeControlEvent extends AutopsyEvent implements Serializable {
/**
* The set of available controls.
*/
enum ControlEventType {
PAUSE,
RESUME,
SHUTDOWN
}
private static final long serialVersionUID = 1L;
private final String nodeName;
private final ControlEventType eventType;
AutoIngestNodeControlEvent(ControlEventType eventType, String nodeName) {
super(eventType.toString(), null, null);
this.eventType = eventType;
this.nodeName = nodeName;
}
String getNodeName() {
return nodeName;
}
ControlEventType getControlEventType() {
return eventType;
}
}