From 949053f0c0b14576bd291769f4f2ddd06772cdc6 Mon Sep 17 00:00:00 2001 From: Dhandapani Date: Wed, 2 Nov 2011 12:40:12 +0530 Subject: Story #70: "Force" option in volume stop --- .../console/GlusterDataModelManager.java | 48 +++++++-- .../console/actions/DeleteVolumeAction.java | 111 +++++++++++++-------- .../console/actions/StartVolumeAction.java | 14 +-- .../console/actions/StopVolumeAction.java | 68 +++++++------ .../console/views/GlusterServersSummaryView.java | 7 +- .../console/views/VolumesSummaryView.java | 18 +++- .../management/core/model/ClusterListener.java | 4 +- .../core/model/DefaultClusterListener.java | 7 +- .../storage/management/core/model/Event.java | 2 + 9 files changed, 182 insertions(+), 97 deletions(-) diff --git a/src/com.gluster.storage.management.console/src/com/gluster/storage/management/console/GlusterDataModelManager.java b/src/com.gluster.storage.management.console/src/com/gluster/storage/management/console/GlusterDataModelManager.java index 5fff61b9..a6daee79 100644 --- a/src/com.gluster.storage.management.console/src/com/gluster/storage/management/console/GlusterDataModelManager.java +++ b/src/com.gluster.storage.management.console/src/com/gluster/storage/management/console/GlusterDataModelManager.java @@ -408,6 +408,11 @@ public class GlusterDataModelManager { public void volumeChanged(Volume oldVolume, Volume newVolume) { oldVolume.copyFrom(newVolume); + + if (oldVolume.getStatus() != newVolume.getStatus()) { + updateVolumeStatusAlert(newVolume, newVolume.getStatus()); + } + for (ClusterListener listener : listeners) { listener.volumeChanged(oldVolume, new Event(EVENT_TYPE.VOLUME_CHANGED, newVolume)); } @@ -746,6 +751,7 @@ public class GlusterDataModelManager { public void updateVolumeStatus(Volume volume, VOLUME_STATUS newStatus) { volume.setStatus(newStatus); + updateVolumeStatusAlert(volume, newStatus); if(newStatus == VOLUME_STATUS.OFFLINE) { // mark as bricks also as offline @@ -762,6 +768,42 @@ public class GlusterDataModelManager { listener.volumeChanged(volume, new Event(EVENT_TYPE.BRICKS_CHANGED, volume.getBricks())); } } + + private void updateVolumeStatusAlert(Volume volume, VOLUME_STATUS newStatus) { + Alert alert = null; + if (newStatus == VOLUME_STATUS.OFFLINE) { + alert = createOfflineVolumeAlert(volume); + for (ClusterListener listener : listeners) { + listener.alertCreated(alert); + } + } else { + alert = removeOfflineVolumeAlert(volume); + for (ClusterListener listener : listeners) { + listener.alertRemoved(alert); + } + } + } + + private Alert createOfflineVolumeAlert(Volume volume) { + Alert alert = new Alert(ALERT_TYPES.OFFLINE_VOLUME_ALERT, volume.getName(), + Alert.ALERT_TYPE_STR[ALERT_TYPES.OFFLINE_VOLUME_ALERT.ordinal()] + " [" + volume.getName() + "]"); + getModel().getCluster().addAlert(alert); + return alert; + } + + private Alert removeOfflineVolumeAlert(Volume volume) { + List clusterAlerts = getModel().getCluster().getAlerts(); + Alert removedAlert = null; + for (Alert alert : clusterAlerts) { + if (alert.getType().equals(ALERT_TYPES.OFFLINE_VOLUME_ALERT) + && alert.getReference().equals(volume.getName())) { + removedAlert = alert; + clusterAlerts.remove(alert); + break; + } + } + return removedAlert; + } public void resetVolumeOptions(Volume volume) { volume.getOptions().clear(); @@ -882,12 +924,6 @@ public class GlusterDataModelManager { } } - public void alertsRemoved() { - for (ClusterListener listener : listeners) { - listener.alertsRemoved(); - } - } - public List getVolumeOptionsInfo() { if(volumeOptionsInfo == null || volumeOptionsInfo.isEmpty()) { initializeVolumeOptionsInfo(getModel().getCluster()); diff --git a/src/com.gluster.storage.management.console/src/com/gluster/storage/management/console/actions/DeleteVolumeAction.java b/src/com.gluster.storage.management.console/src/com/gluster/storage/management/console/actions/DeleteVolumeAction.java index d764dcc0..6d784676 100644 --- a/src/com.gluster.storage.management.console/src/com/gluster/storage/management/console/actions/DeleteVolumeAction.java +++ b/src/com.gluster.storage.management.console/src/com/gluster/storage/management/console/actions/DeleteVolumeAction.java @@ -42,7 +42,6 @@ public class DeleteVolumeAction extends AbstractMonitoredActionDelegate { private List onlineVolumeNames = new ArrayList(); private List deletedVolumeNames = new ArrayList(); private List failedVolumes = new ArrayList(); - private List failedVolumeNames = new ArrayList(); @Override protected void performAction(final IAction action, IProgressMonitor monitor) { @@ -72,85 +71,113 @@ public class DeleteVolumeAction extends AbstractMonitoredActionDelegate { warningMessage = "Are you sure to delete the volumes " + selectedVolumeNames + " ?"; } - final Integer directoryDeleteOption = new MessageDialog(getShell(), "Delete Volume", GUIHelper.getInstance() + final Integer deleteOption = new MessageDialog(getShell(), "Delete Volume", GUIHelper.getInstance() .getImage(IImageKeys.VOLUME_16x16), warningMessage, MessageDialog.QUESTION, new String[] { "Cancel", "Delete volume and data", "Delete volume, keep data" }, -1).open(); - if (directoryDeleteOption <= 0) { // By Cancel button(0) or Escape key(-1) + if (deleteOption <= 0) { // By Cancel button(0) or Escape key(-1) return; } - String errorMessage = performDeleteVolume(monitor,selectedVolumes, directoryDeleteOption, false); + String errorMessage = deleteVolumes(selectedVolumes, deleteOption, monitor); // Display the success or failure info if (deletedVolumeNames.size() == 0) { // No volume(s) deleted successfully - String message = "volumes " + failedVolumeNames + " could not be delete! " + CoreConstants.NEWLINE - + "Error: [" + errorMessage + "]"; - errorMessage = performForceStopAndDeleteVolume(monitor, actionDesc, failedVolumes, directoryDeleteOption, - message); + if (!errorMessage.isEmpty()) { + showErrorDialog(actionDesc, "Volume(s) could not be deleted! " + CoreConstants.NEWLINE + errorMessage); + } } else { - String info = "Volumes " + deletedVolumeNames + " deleted successfully!"; - if (!errorMessage.equals("")) { - String message = info + CoreConstants.NEWLINE + CoreConstants.NEWLINE + "Volumes " + failedVolumeNames - + " could not be deleted! [" + errorMessage + "]"; - errorMessage = performForceStopAndDeleteVolume(monitor, actionDesc, failedVolumes, - directoryDeleteOption, message); + String info = "Volume(s) " + deletedVolumeNames + " deleted successfully!"; + if (!errorMessage.isEmpty()) { + info += CoreConstants.NEWLINE + CoreConstants.NEWLINE + "Volumes " + failedVolumes + + " could not be deleted!" + errorMessage; } - if (errorMessage.equals("")) { - errorMessage = info; + if (selectedVolumes.size() == deletedVolumeNames.size()) { + showInfoDialog(actionDesc, info); + } else { + showWarningDialog(actionDesc, info); } } - showInfoDialog(actionDesc, errorMessage); } - private String performForceStopAndDeleteVolume(IProgressMonitor monitor, String actionDesc, List volumesToDelete, - Integer directoryDeleteOption, String message) { - boolean forceStop = showConfirmDialog(actionDesc, message + CoreConstants.NEWLINE + CoreConstants.NEWLINE - + "Do you want to stop forcefully and delete?"); - if (!forceStop) { - return ""; - } - return performDeleteVolume(monitor, volumesToDelete, directoryDeleteOption, true); - } - - private String performDeleteVolume(IProgressMonitor monitor, List volumeNeedsToDelete, - final Integer directoryDeleteOption, Boolean force) { + private String deleteVolumes(List volumes, final Integer deleteOption, IProgressMonitor monitor) { deletedVolumeNames.clear(); - failedVolumeNames.clear(); failedVolumes.clear(); VolumesClient vc = new VolumesClient(); - boolean confirmDeleteDir = (directoryDeleteOption == 1) ? true : false; + boolean confirmDeleteDir = (deleteOption == 1) ? true : false; String errorMessage = ""; - monitor.beginTask("Deleting Selected Volumes...", volumeNeedsToDelete.size()); - for (Volume volume : volumeNeedsToDelete.toArray(new Volume[0])) { - try { - monitor.setTaskName("Deleting volume [" + volume.getName() + "]"); - if (volume.getStatus() == VOLUME_STATUS.ONLINE) { // stop if online volume - vc.stopVolume(volume.getName(), force); + // To calculate the total work we need to sum volumes size + online volumes (because we treat stop and delete as + // separate steps) + List onlineVolumes = getOnlineVolumes(volumes); + monitor.beginTask("Deleting Selected Volumes...", volumes.size() + onlineVolumes.size()); + + // Deletion of a volume results in changes to the model, and ultimately updates the "selectedVolumes" list, + // over which we are iterating, thus resulting in ConcurrentModificationException. To avoid this, we iterate + // over an array obtained from the list. + for (Volume volume : volumes.toArray(new Volume[0])) { + if (volume.getStatus() == VOLUME_STATUS.ONLINE) { // stop if online volume + monitor.setTaskName("Stopping volume [" + volume.getName() + "]"); + try { + vc.stopVolume(volume.getName(), false); + } catch (Exception e1) { + // try again with force = true + try { + vc.stopVolume(volume.getName(), true); + } catch(Exception e2) { + // force stop also failed. + // Mark as deletion failed, append error message. + errorMessage += CoreConstants.NEWLINE + "Stop[" + volume.getName() + "] : [" + e2.getMessage() + + "]"; + failedVolumes.add(volume); + + // since we are not going to perform delete on this volume, + // mark the deletion task as worked + monitor.worked(1); + + // continue to next volume without trying to delete this one + continue; + } + } finally { + // worked for "stop" operation + monitor.worked(1); } + } + + monitor.setTaskName("Deleting volume [" + volume.getName() + "]"); + try { vc.deleteVolume(volume.getName(), confirmDeleteDir); modelManager.deleteVolume(volume); deletedVolumeNames.add(volume.getName()); } catch (Exception e) { // Volume delete succeeded and post delete operation (directory cleanup, CIFS etc) may fail if (vc.volumeExists(volume.getName())) { - errorMessage += CoreConstants.NEWLINE + "[" + volume.getName() + "] : [" + e.getMessage() + "]"; - failedVolumeNames.add(volume.getName()); + errorMessage += CoreConstants.NEWLINE + "Delete [" + volume.getName() + "] : [" + e.getMessage() + "]"; failedVolumes.add(volume); } else { - errorMessage += CoreConstants.NEWLINE + "Volume deleted, but following error occured: [" - + e.getMessage() + "]"; + errorMessage += CoreConstants.NEWLINE + "Volume [" + volume.getName() + + "] deleted, but following error occured: [" + e.getMessage() + "]"; modelManager.deleteVolume(volume); deletedVolumeNames.add(volume.getName()); } + } finally { + monitor.worked(1); } - monitor.worked(1); } monitor.done(); return errorMessage; } + private List getOnlineVolumes(List volumes) { + List onlineVolumes = new ArrayList(); + for (Volume volume : volumes) { + if (volume.getStatus() == VOLUME_STATUS.ONLINE) { + onlineVolumes.add(volume); + } + } + return onlineVolumes; + } + private void collectVolumeNames() { selectedVolumeNames.clear(); onlineVolumeNames.clear(); diff --git a/src/com.gluster.storage.management.console/src/com/gluster/storage/management/console/actions/StartVolumeAction.java b/src/com.gluster.storage.management.console/src/com/gluster/storage/management/console/actions/StartVolumeAction.java index 09b01805..d7c6d2e3 100644 --- a/src/com.gluster.storage.management.console/src/com/gluster/storage/management/console/actions/StartVolumeAction.java +++ b/src/com.gluster.storage.management.console/src/com/gluster/storage/management/console/actions/StartVolumeAction.java @@ -77,6 +77,9 @@ public class StartVolumeAction extends AbstractMonitoredActionDelegate { } monitor.beginTask("Starting Selected Volumes...", selectedVolumes.size()); + // Starting of a volume results in changes to the model, and ultimately updates the "selectedVolumes" list, + // over which we are iterating, thus resulting in ConcurrentModificationException. To avoid this, we iterate + // over an array obtained from the list. for (Volume volume : selectedVolumes.toArray(new Volume[0])) { if(monitor.isCanceled()) { break; @@ -101,11 +104,6 @@ public class StartVolumeAction extends AbstractMonitoredActionDelegate { try { newVolume = vc.getVolume(volume.getName()); modelManager.volumeChanged(volume, newVolume); - // Remove the offline volume alert from the AlertsManager - AlertsManager alertManager = new AlertsManager(modelManager.getModel().getCluster()); - alertManager.removeAlert(Base64.encode( - (ALERT_TYPES.OFFLINE_VOLUME_ALERT + "-" + volume.getName()).getBytes()).toString()); - modelManager.alertsRemoved(); } catch (Exception e) { errorMessage += "Updating volume info failed on UI. [" + e.getMessage() + "]"; } @@ -123,7 +121,11 @@ public class StartVolumeAction extends AbstractMonitoredActionDelegate { info += CoreConstants.NEWLINE + CoreConstants.NEWLINE + "Volumes " + failedVolumes + " failed to start! [" + errorMessage + "]"; } - showInfoDialog(actionDesc, info); + if (selectedVolumes.size() == startedVolumes.size()) { + showInfoDialog(actionDesc, info); + } else { + showWarningDialog(actionDesc, info); + } } } diff --git a/src/com.gluster.storage.management.console/src/com/gluster/storage/management/console/actions/StopVolumeAction.java b/src/com.gluster.storage.management.console/src/com/gluster/storage/management/console/actions/StopVolumeAction.java index 21bf3432..3bd055f2 100644 --- a/src/com.gluster.storage.management.console/src/com/gluster/storage/management/console/actions/StopVolumeAction.java +++ b/src/com.gluster.storage.management.console/src/com/gluster/storage/management/console/actions/StopVolumeAction.java @@ -44,7 +44,6 @@ public class StopVolumeAction extends AbstractMonitoredActionDelegate { private List selectedVolumeNames = new ArrayList(); private List onlineVolumeNames = new ArrayList(); private List stoppedVolumes = new ArrayList(); - private List failedVolumeNames = new ArrayList(); private List failedVolumes = new ArrayList(); @Override @@ -82,49 +81,57 @@ public class StopVolumeAction extends AbstractMonitoredActionDelegate { } } - String errorMessage = performStopVolume(monitor, selectedVolumes, false); + String errorMessage = stopVolume(selectedVolumes, false, monitor); // Display the success or failure info if (stoppedVolumes.size() == 0) { // No volume(s) stopped successfully - String message = "Volumes " + failedVolumeNames + " could not be stopped! " + CoreConstants.NEWLINE + String message = "Volume(s) " + failedVolumes + " could not be stopped! " + CoreConstants.NEWLINE + "Error: [" + errorMessage + "]"; - errorMessage = performForceStopVolume(monitor, actionDesc, message); - + errorMessage = forceStopVolume(actionDesc, message, monitor); + if (errorMessage.isEmpty()) { + return; + } + showErrorDialog(actionDesc, errorMessage); } else { - String info = "Volumes " + stoppedVolumes + " stopped successfully!"; + String info = "Volume(s) " + stoppedVolumes + " stopped successfully!"; if (!errorMessage.equals("")) { - String message = info + CoreConstants.NEWLINE + CoreConstants.NEWLINE + "Volumes " + failedVolumeNames - + " failed to stop! [" + errorMessage + "]"; - errorMessage = performForceStopVolume(monitor, actionDesc, message); + if (failedVolumes.size() > 0) { + String message = info + CoreConstants.NEWLINE + CoreConstants.NEWLINE + "Volume(s) " + failedVolumes + + " failed to stop! [" + errorMessage + "]"; + info += forceStopVolume(actionDesc, message, monitor); + } else { // Stop volume success, but post stop volume fails, append the error message + info += CoreConstants.NEWLINE + CoreConstants.NEWLINE + errorMessage; + } } - if (errorMessage.equals("")) { - errorMessage = info; + if (stoppedVolumes.size() == selectedVolumes.size()) { + showInfoDialog(actionDesc, info); + } else { + showWarningDialog(actionDesc, info); } } - showInfoDialog(actionDesc, errorMessage); } - - private String performForceStopVolume(IProgressMonitor monitor, String actionDesc, String message) { + private String forceStopVolume(String actionDesc, String message, IProgressMonitor monitor) { boolean forceStop = showConfirmDialog(actionDesc, message + CoreConstants.NEWLINE + "Do you want to stop forcefully?"); if (!forceStop) { return ""; } - return performStopVolume(monitor, failedVolumes, true); + return stopVolume(failedVolumes, true, monitor); } - private String performStopVolume(IProgressMonitor monitor, List volumesNeedsToStop, Boolean force) { + private String stopVolume(List volumes, Boolean force, IProgressMonitor monitor) { VolumesClient vc = new VolumesClient(); Volume newVolume = new Volume(); stoppedVolumes.clear(); - failedVolumeNames.clear(); failedVolumes.clear(); String errorMessage = ""; - List volumeStopAlert = new ArrayList(); - monitor.beginTask("Stopping Selected Volumes...", volumesNeedsToStop.size()); - for (Volume volume : volumesNeedsToStop.toArray(new Volume[0])) { + monitor.beginTask("Stopping Selected Volumes...", volumes.size()); + // Stopping of a volume results in changes to the model, and ultimately updates the "selectedVolumes" list, + // over which we are iterating, thus resulting in ConcurrentModificationException. To avoid this, we iterate + // over an array obtained from the list. + for (Volume volume : volumes.toArray(new Volume[0])) { if(monitor.isCanceled()) { break; } @@ -138,17 +145,19 @@ public class StopVolumeAction extends AbstractMonitoredActionDelegate { vc.stopVolume(volume.getName(), force); // modelManager.updateVolumeStatus(volume, VOLUME_STATUS.OFFLINE); stoppedVolumes.add(volume.getName()); - volumeStopAlert.add(new Alert(ALERT_TYPES.OFFLINE_VOLUME_ALERT, volume.getName(), - Alert.ALERT_TYPE_STR[ALERT_TYPES.OFFLINE_VOLUME_ALERT.ordinal()] + " [" + volume.getName() - + "]")); } catch (Exception e) { - failedVolumeNames.add(volume.getName()); - failedVolumes.add(volume); // If any post volume stop activity failed, update the volume status if (vc.getVolume(volume.getName()).getStatus() == VOLUME_STATUS.OFFLINE) { + // stop volume succeed, so add it to stoppedVolumes + stoppedVolumes.add(volume.getName()); modelManager.updateVolumeStatus(volume, VOLUME_STATUS.OFFLINE); + errorMessage += "Volume [" + volume.getName() + "] stopped, but following error occured: [" + + e.getMessage() + "]"; + } else { + failedVolumes.add(volume); + errorMessage += "[" + volume.getName() + "] : " + e.getMessage() + CoreConstants.NEWLINE; } - errorMessage += e.getMessage() + CoreConstants.NEWLINE; + } // Update the model by fetching latest volume info (NOT JUST STATUS) @@ -161,13 +170,6 @@ public class StopVolumeAction extends AbstractMonitoredActionDelegate { monitor.worked(1); } monitor.done(); - - // Add the offline volume alert in AlertsManager - if (volumeStopAlert.size() > 0) { - AlertsManager alertManager = new AlertsManager(modelManager.getModel().getCluster()); - alertManager.addAlerts(volumeStopAlert); - modelManager.alertsGenerated(); - } return errorMessage; } diff --git a/src/com.gluster.storage.management.console/src/com/gluster/storage/management/console/views/GlusterServersSummaryView.java b/src/com.gluster.storage.management.console/src/com/gluster/storage/management/console/views/GlusterServersSummaryView.java index ad2f6331..afca5ad5 100644 --- a/src/com.gluster.storage.management.console/src/com/gluster/storage/management/console/views/GlusterServersSummaryView.java +++ b/src/com.gluster.storage.management.console/src/com/gluster/storage/management/console/views/GlusterServersSummaryView.java @@ -197,7 +197,9 @@ public class GlusterServersSummaryView extends ViewPart { List alerts = GlusterDataModelManager.getInstance().getModel().getCluster().getAlerts(); for (Alert alert : alerts) { - if (alert.getType() != ALERT_TYPES.OFFLINE_VOLUME_BRICKS_ALERT) { + if (alert.getType() == ALERT_TYPES.DISK_USAGE_ALERT || alert.getType() != ALERT_TYPES.OFFLINE_SERVERS_ALERT + || alert.getType() == ALERT_TYPES.MEMORY_USAGE_ALERT + || alert.getType() == ALERT_TYPES.CPU_USAGE_ALERT) { addAlertLabel(alertsSection, alert); } } @@ -209,9 +211,6 @@ public class GlusterServersSummaryView extends ViewPart { CLabel lblAlert = new CLabel(section, SWT.FLAT); Image alertImage = null; switch (alert.getType()) { - case OFFLINE_VOLUME_BRICKS_ALERT: - alertImage = guiHelper.getImage(IImageKeys.BRICK_OFFLINE_22x22); - break; case DISK_USAGE_ALERT: alertImage = guiHelper.getImage(IImageKeys.LOW_DISK_SPACE_22x22); break; diff --git a/src/com.gluster.storage.management.console/src/com/gluster/storage/management/console/views/VolumesSummaryView.java b/src/com.gluster.storage.management.console/src/com/gluster/storage/management/console/views/VolumesSummaryView.java index 944956f7..cd937215 100644 --- a/src/com.gluster.storage.management.console/src/com/gluster/storage/management/console/views/VolumesSummaryView.java +++ b/src/com.gluster.storage.management.console/src/com/gluster/storage/management/console/views/VolumesSummaryView.java @@ -105,6 +105,11 @@ public class VolumesSummaryView extends ViewPart { updateSummarySection(); } + private void updateAlertSection() { + guiHelper.clearSection(alertsSection); + populateAlertSection(); + } + private void updateSummarySection() { guiHelper.clearSection(summarySection); populateSummarySection(); @@ -120,10 +125,15 @@ public class VolumesSummaryView extends ViewPart { } @Override - public void alertsRemoved() { - super.alertsRemoved(); - guiHelper.clearSection(alertsSection); - populateAlertSection(); + public void alertRemoved(Alert alert) { + super.alertRemoved(alert); + updateAlertSection(); + } + + @Override + public void alertCreated(Alert alert) { + super.alertCreated(alert); + updateAlertSection(); } @Override diff --git a/src/com.gluster.storage.management.core/src/com/gluster/storage/management/core/model/ClusterListener.java b/src/com.gluster.storage.management.core/src/com/gluster/storage/management/core/model/ClusterListener.java index e0bb9c63..eb8c191e 100644 --- a/src/com.gluster.storage.management.core/src/com/gluster/storage/management/core/model/ClusterListener.java +++ b/src/com.gluster.storage.management.core/src/com/gluster/storage/management/core/model/ClusterListener.java @@ -51,5 +51,7 @@ public interface ClusterListener { public void aggregatedStatsChanged(); - public void alertsRemoved(); + public void alertRemoved(Alert alert); + + public void alertCreated(Alert alert); } diff --git a/src/com.gluster.storage.management.core/src/com/gluster/storage/management/core/model/DefaultClusterListener.java b/src/com.gluster.storage.management.core/src/com/gluster/storage/management/core/model/DefaultClusterListener.java index 50a5d2ac..eb98090f 100644 --- a/src/com.gluster.storage.management.core/src/com/gluster/storage/management/core/model/DefaultClusterListener.java +++ b/src/com.gluster.storage.management.core/src/com/gluster/storage/management/core/model/DefaultClusterListener.java @@ -107,7 +107,12 @@ public class DefaultClusterListener implements ClusterListener { } @Override - public void alertsRemoved() { + public void alertRemoved(Alert alert) { + modelChanged(); + } + + @Override + public void alertCreated(Alert alert) { modelChanged(); } } diff --git a/src/com.gluster.storage.management.core/src/com/gluster/storage/management/core/model/Event.java b/src/com.gluster.storage.management.core/src/com/gluster/storage/management/core/model/Event.java index 4ff52189..fe7b8495 100644 --- a/src/com.gluster.storage.management.core/src/com/gluster/storage/management/core/model/Event.java +++ b/src/com.gluster.storage.management.core/src/com/gluster/storage/management/core/model/Event.java @@ -24,6 +24,8 @@ public class Event { BRICKS_REMOVED, BRICKS_CHANGED, VOLUME_STATUS_CHANGED, + ALERT_CREATED, + ALERT_REMOVED, VOLUME_OPTIONS_RESET, VOLUME_OPTION_SET, VOLUME_CHANGED, -- cgit