changeset 1215:7012500bb995

Add support for deleting of data for all agents Reviewed-by: omajid Review-thread: http://icedtea.classpath.org/pipermail/thermostat/2013-August/007775.html
author Andriy Petrus <apetrus@redhat.com>
date Fri, 09 Aug 2013 15:45:12 -0400
parents 69692e087b13
children 91e56d2ba874
files client/cli/src/main/java/com/redhat/thermostat/client/cli/internal/Activator.java client/cli/src/main/java/com/redhat/thermostat/client/cli/internal/CleanDataCommand.java client/cli/src/main/java/com/redhat/thermostat/client/cli/internal/CleanHistoryCommand.java client/cli/src/main/resources/com/redhat/thermostat/client/cli/strings.properties client/cli/src/test/java/com/redhat/thermostat/client/cli/internal/CleanDataCommandTest.java client/cli/src/test/java/com/redhat/thermostat/client/cli/internal/CleanHistoryCommandTest.java distribution/config/commands/clean-data.properties distribution/config/commands/clean-history.properties storage/core/src/main/java/com/redhat/thermostat/storage/core/Categories.java
diffstat 9 files changed, 505 insertions(+), 330 deletions(-) [+]
line wrap: on
line diff
--- a/client/cli/src/main/java/com/redhat/thermostat/client/cli/internal/Activator.java	Wed Aug 14 13:11:11 2013 -0400
+++ b/client/cli/src/main/java/com/redhat/thermostat/client/cli/internal/Activator.java	Fri Aug 09 15:45:12 2013 -0400
@@ -56,7 +56,7 @@
         reg.registerCommand("vm-stat", new VMStatCommand());
         reg.registerCommand("disconnect", new DisconnectCommand());
         reg.registerCommand("connect", new ConnectCommand());
-        reg.registerCommand("clean-history", new CleanHistoryCommand(context));
+        reg.registerCommand("clean-data", new CleanDataCommand(context));
     }
 
     @Override
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/client/cli/src/main/java/com/redhat/thermostat/client/cli/internal/CleanDataCommand.java	Fri Aug 09 15:45:12 2013 -0400
@@ -0,0 +1,200 @@
+/*
+ * Copyright 2013 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.client.cli.internal;
+
+import java.io.PrintStream;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
+
+import com.redhat.thermostat.common.cli.AbstractCommand;
+import com.redhat.thermostat.common.cli.Arguments;
+import com.redhat.thermostat.common.cli.CommandContext;
+import com.redhat.thermostat.common.cli.CommandException;
+import com.redhat.thermostat.common.utils.LoggingUtils;
+import com.redhat.thermostat.shared.locale.Translate;
+import com.redhat.thermostat.storage.core.Categories;
+import com.redhat.thermostat.storage.core.Category;
+import com.redhat.thermostat.storage.core.Cursor;
+import com.redhat.thermostat.storage.core.DescriptorParsingException;
+import com.redhat.thermostat.storage.core.HostRef;
+import com.redhat.thermostat.storage.core.PreparedStatement;
+import com.redhat.thermostat.storage.core.StatementDescriptor;
+import com.redhat.thermostat.storage.core.StatementExecutionException;
+import com.redhat.thermostat.storage.core.Storage;
+import com.redhat.thermostat.storage.dao.AgentInfoDAO;
+import com.redhat.thermostat.storage.model.AgentInformation;
+import com.redhat.thermostat.storage.model.BasePojo;
+
+public class CleanDataCommand extends AbstractCommand {
+
+    private static final Translate<LocaleResources> translator = LocaleResources.createLocalizer();
+    private static final Logger logger = LoggingUtils.getLogger(CleanDataCommand.class);
+    private BundleContext bundleContext;
+    private boolean removeLiveAgent = false;
+
+    CleanDataCommand (BundleContext bundleContext) {
+        this.bundleContext = bundleContext;
+    }
+
+    @Override
+    public void run(CommandContext ctx) throws CommandException {
+        ServiceReference storageServiceRef = bundleContext.getServiceReference(Storage.class);
+        if (storageServiceRef == null) {
+            throw new CommandException(translator.localize(LocaleResources.STORAGE_UNAVAILABLE));
+        }
+        Storage storage = (Storage) bundleContext.getService(storageServiceRef);
+        
+        try {
+            Arguments args = ctx.getArguments();
+            List<String> agentIdList = args.getNonOptionArguments();
+            removeLiveAgent = args.hasArgument(CleanOptions.ALIVE.option);
+            PrintStream output = ctx.getConsole().getOutput();
+            
+            if (args.hasArgument(CleanOptions.ALL.option)) {
+                removeDataForAllAgents(storage, output);
+            } else {
+                removeDataForSpecifiedAgents(storage, agentIdList, output);
+            }
+        } finally {
+            bundleContext.ungetService(storageServiceRef);
+        }
+    }
+
+    public void removeDataForSpecifiedAgents(Storage storage, List <String> agentIdList, PrintStream output) throws CommandException {
+        ServiceReference agentServiceRef = bundleContext.getServiceReference(AgentInfoDAO.class);
+        if (agentServiceRef == null) {
+            throw new CommandException(translator.localize(LocaleResources.AGENT_UNAVAILABLE));
+        }
+        AgentInfoDAO agentInfoDAO = (AgentInfoDAO) bundleContext.getService(agentServiceRef);
+        
+        try {
+            Set<String> storedAgentIdList = getAllRegisteredAgents(storage);
+            
+            for (String agentId : agentIdList) {
+                AgentInformation agentInfo = agentInfoDAO.getAgentInformation(new HostRef(agentId, agentId));
+                if (agentInfo != null) {
+                    removeAgentDataIfSane(storage, agentId, !agentInfo.isAlive(), output);
+                } else if (storedAgentIdList.contains(agentId)) {
+                    removeAgentDataIfSane(storage, agentId, true, output);
+                } else {
+                    output.println(translator.localize(LocaleResources.AGENT_NOT_FOUND, agentId).getContents());
+                }
+            }
+        } finally {
+            bundleContext.ungetService(agentServiceRef);
+        }
+    }
+
+    public void removeDataForAllAgents(Storage storage, PrintStream output) throws CommandException {
+        ServiceReference agentServiceRef = bundleContext.getServiceReference(AgentInfoDAO.class);
+        if (agentServiceRef == null) {
+            throw new CommandException(translator.localize(LocaleResources.AGENT_UNAVAILABLE));
+        }
+        AgentInfoDAO agentInfoDAO = (AgentInfoDAO) bundleContext.getService(agentServiceRef);
+        
+        try {
+            Set<String> storedAgentIdList = getAllRegisteredAgents(storage);
+            List<String> aliveAgentsId = new ArrayList<String>();
+            List<AgentInformation> allAliveAgentsInfo = agentInfoDAO.getAliveAgents(); 
+            for (AgentInformation aliveAgent : allAliveAgentsInfo) {
+                aliveAgentsId.add(aliveAgent.getAgentId());
+            }
+            
+            for (String agentId : storedAgentIdList) {
+                boolean isDead = !aliveAgentsId.contains(agentId);
+                removeAgentDataIfSane(storage, agentId, isDead, output);
+            }
+        } finally {
+            bundleContext.ungetService(agentServiceRef);
+        }
+    }
+
+    private void removeAgentDataIfSane(Storage storage, String agentId, boolean isDead, PrintStream output) {
+        if (isDead || removeLiveAgent) {
+            output.println(translator.localize(LocaleResources.PURGING_AGENT_DATA).getContents() + agentId);
+            storage.purge(agentId);
+        } else {
+            output.println(translator.localize(LocaleResources.CANNOT_PURGE_AGENT_RUNNING, agentId).getContents());
+        }
+    }
+
+    private enum CleanOptions {
+        ALL("all"),
+        
+        ALIVE("alive");
+        
+        private String option;
+        
+        CleanOptions(String option) {
+            this.option = option;
+        }
+    }
+
+    private Set<String> getAllRegisteredAgents(Storage storage) {
+        List<Category> categories = Categories.getAllCategories();
+        Set<String> agents = new HashSet<>();
+        PreparedStatement<BasePojo> prepared = null;
+        Cursor<BasePojo> agentCursor = null;
+        for (Category category : categories) {
+            String query = "QUERY " + category.getName();
+            StatementDescriptor<BasePojo> desc = new StatementDescriptor<>(category, query);
+            try {
+                prepared = storage.prepareStatement(desc);
+                agentCursor = prepared.executeQuery();
+            } catch (DescriptorParsingException e) {
+                logger.log(Level.SEVERE, "Preparing query '" + desc + "' failed!", e);
+                return Collections.emptySet();
+            } catch (StatementExecutionException e) {
+                logger.log(Level.SEVERE, "Executing query '" + desc + "' failed!", e);
+                return Collections.emptySet();
+            }
+            while (agentCursor.hasNext()) {
+                agents.add(agentCursor.next().getAgentId());
+            }
+        }
+        return agents;
+    }
+
+}
--- a/client/cli/src/main/java/com/redhat/thermostat/client/cli/internal/CleanHistoryCommand.java	Wed Aug 14 13:11:11 2013 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,110 +0,0 @@
-/*
- * Copyright 2013 Red Hat, Inc.
- *
- * This file is part of Thermostat.
- *
- * Thermostat is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published
- * by the Free Software Foundation; either version 2, or (at your
- * option) any later version.
- *
- * Thermostat is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Thermostat; see the file COPYING.  If not see
- * <http://www.gnu.org/licenses/>.
- *
- * Linking this code with other modules is making a combined work
- * based on this code.  Thus, the terms and conditions of the GNU
- * General Public License cover the whole combination.
- *
- * As a special exception, the copyright holders of this code give
- * you permission to link this code with independent modules to
- * produce an executable, regardless of the license terms of these
- * independent modules, and to copy and distribute the resulting
- * executable under terms of your choice, provided that you also
- * meet, for each linked independent module, the terms and conditions
- * of the license of that module.  An independent module is a module
- * which is not derived from or based on this code.  If you modify
- * this code, you may extend this exception to your version of the
- * library, but you are not obligated to do so.  If you do not wish
- * to do so, delete this exception statement from your version.
- */
-
-package com.redhat.thermostat.client.cli.internal;
-
-import java.io.PrintStream;
-import java.util.List;
-
-import org.osgi.framework.BundleContext;
-import org.osgi.framework.ServiceReference;
-
-import com.redhat.thermostat.common.cli.AbstractCommand;
-import com.redhat.thermostat.common.cli.CommandContext;
-import com.redhat.thermostat.common.cli.CommandException;
-import com.redhat.thermostat.shared.locale.Translate;
-import com.redhat.thermostat.storage.core.HostRef;
-import com.redhat.thermostat.storage.core.Storage;
-import com.redhat.thermostat.storage.dao.AgentInfoDAO;
-import com.redhat.thermostat.storage.model.AgentInformation;
-
-public class CleanHistoryCommand extends AbstractCommand {
-
-    private static final Translate<LocaleResources> translator = LocaleResources.createLocalizer();
-    private BundleContext bundleContext;
-
-    CleanHistoryCommand (BundleContext bundleContext) {
-        this.bundleContext = bundleContext;
-    }
-
-    @Override
-    public void run(CommandContext ctx) throws CommandException {
-        ServiceReference storageServiceRef = bundleContext.getServiceReference(Storage.class);
-        if (storageServiceRef == null) {
-            throw new CommandException(translator.localize(LocaleResources.STORAGE_UNAVAILABLE));
-        }
-        Storage storage = (Storage) bundleContext.getService(storageServiceRef);
-        
-        try {
-            List <String> agentIdList = ctx.getArguments().getNonOptionArguments();
-            PrintStream output = ctx.getConsole().getOutput();
-            removeDataForSpecifiedAgents(storage, agentIdList, output);
-        } finally {
-            bundleContext.ungetService(storageServiceRef);
-        }
-    }
-
-    public void removeDataForSpecifiedAgents(Storage storage, List <String> agentIdList, PrintStream output) throws CommandException {
-        ServiceReference agentServiceRef = bundleContext.getServiceReference(AgentInfoDAO.class);
-        if (agentServiceRef == null) {
-            throw new CommandException(translator.localize(LocaleResources.AGENT_UNAVAILABLE));
-        }
-        AgentInfoDAO agentInfoDAO = (AgentInfoDAO) bundleContext.getService(agentServiceRef);
-        
-        try {
-            for (String agentId : agentIdList) {
-                AgentInformation agentInfo = agentInfoDAO.getAgentInformation(new HostRef(agentId, agentId));
-                if (agentInfo != null) {
-                    removeDataIfAgentDead(storage, agentId, agentInfo, output);
-                } else {
-                    output.println(translator.localize(LocaleResources.AGENT_NOT_FOUND, agentId).getContents());
-                }
-            }
-        } finally {
-            bundleContext.ungetService(agentServiceRef);
-        }
-    }
-
-    private void removeDataIfAgentDead(Storage storage, String agentId, AgentInformation agentInfo, PrintStream output) {
-        if (!agentInfo.isAlive()) {
-            output.println(translator.localize(LocaleResources.PURGING_AGENT_DATA).getContents() + agentId);
-            storage.purge(agentId);
-        } else {
-            output.println(translator.localize(LocaleResources.CANNOT_PURGE_AGENT_RUNNING, agentId).getContents());
-        }
-    }
-
-}
--- a/client/cli/src/main/resources/com/redhat/thermostat/client/cli/strings.properties	Wed Aug 14 13:11:11 2013 -0400
+++ b/client/cli/src/main/resources/com/redhat/thermostat/client/cli/strings.properties	Fri Aug 09 15:45:12 2013 -0400
@@ -49,4 +49,4 @@
 STORAGE_UNAVAILABLE = Storage is unavailable
 AGENT_UNAVAILABLE = Agent is unavailable
 CANNOT_PURGE_AGENT_RUNNING = Cannot purge data for agent {0}. This agent is currently running
-AGENT_NOT_FOUND = Agent with an id {0} was not found!
+AGENT_NOT_FOUND = Agent with an id [{0}] was not found!
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/client/cli/src/test/java/com/redhat/thermostat/client/cli/internal/CleanDataCommandTest.java	Fri Aug 09 15:45:12 2013 -0400
@@ -0,0 +1,265 @@
+/*
+ * Copyright 2013 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.client.cli.internal;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.isA;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import static org.mockito.Mockito.times;
+
+import java.io.PrintStream;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import com.redhat.thermostat.common.cli.Arguments;
+import com.redhat.thermostat.common.cli.CommandContext;
+import com.redhat.thermostat.common.cli.CommandException;
+import com.redhat.thermostat.common.cli.Console;
+import com.redhat.thermostat.storage.core.Cursor;
+import com.redhat.thermostat.storage.core.DescriptorParsingException;
+import com.redhat.thermostat.storage.core.HostRef;
+import com.redhat.thermostat.storage.core.PreparedStatement;
+import com.redhat.thermostat.storage.core.StatementDescriptor;
+import com.redhat.thermostat.storage.core.StatementExecutionException;
+import com.redhat.thermostat.storage.core.Storage;
+import com.redhat.thermostat.storage.dao.AgentInfoDAO;
+import com.redhat.thermostat.storage.model.AgentInformation;
+import com.redhat.thermostat.storage.model.BasePojo;
+import com.redhat.thermostat.testutils.StubBundleContext;
+
+import org.powermock.api.mockito.PowerMockito;
+import org.powermock.core.classloader.annotations.PrepareForTest;
+import org.powermock.modules.junit4.PowerMockRunner;
+
+@RunWith(PowerMockRunner.class)
+@PrepareForTest(AgentInformation.class)
+public class CleanDataCommandTest {
+
+    private CleanDataCommand cleanDataCommand;
+    private CommandContext mockCommandContext;
+    private Storage mockStorage;
+    private PrintStream mockOutput;
+    private AgentInfoDAO mockAgentInfoDAO;
+    private Arguments mockArguments;
+
+    @Before
+    public void setUp() throws DescriptorParsingException, StatementExecutionException {
+        StubBundleContext bundleContext = new StubBundleContext();
+        cleanDataCommand = new CleanDataCommand(bundleContext);
+        
+        mockStorage = mock(Storage.class); 
+        bundleContext.registerService(Storage.class, mockStorage, null);
+        mockAgentInfoDAO = mock(AgentInfoDAO.class);
+        bundleContext.registerService(AgentInfoDAO.class, mockAgentInfoDAO, null);
+        
+        mockCommandContext = mock(CommandContext.class);
+        mockArguments = mock(Arguments.class);
+        
+        when(mockCommandContext.getArguments()).thenReturn(mockArguments);
+        when(mockArguments.getNonOptionArguments()).thenReturn(getValidAgentList());
+        
+        Console mockConsole = mock(Console.class);
+        mockOutput = mock(PrintStream.class);
+        when(mockCommandContext.getConsole()).thenReturn(mockConsole);
+        when(mockConsole.getOutput()).thenReturn(mockOutput);
+        
+        List<AgentInformation> liveAgentInfoList = new ArrayList<AgentInformation>();
+        AgentInformation mockAgent1 = PowerMockito.mock(AgentInformation.class);
+        AgentInformation mockAgent2 = PowerMockito.mock(AgentInformation.class);
+        AgentInformation mockAgent3 = PowerMockito.mock(AgentInformation.class);
+        AgentInformation mockAgent4 = PowerMockito.mock(AgentInformation.class);
+        AgentInformation mockAgent5 = PowerMockito.mock(AgentInformation.class);
+        when(mockAgent1.getAgentId()).thenReturn("agentId1");
+        when(mockAgent2.getAgentId()).thenReturn("agentId2");
+        when(mockAgent3.getAgentId()).thenReturn("agentId3");
+        when(mockAgent4.getAgentId()).thenReturn("agentId4");
+        when(mockAgent5.getAgentId()).thenReturn("agentId5");
+        when(mockAgent4.isAlive()).thenReturn(true);
+        when(mockAgent5.isAlive()).thenReturn(true);
+        
+        liveAgentInfoList.add(mockAgent4);
+        liveAgentInfoList.add(mockAgent5);
+        when(mockAgentInfoDAO.getAliveAgents()).thenReturn(liveAgentInfoList);
+        when(mockAgentInfoDAO.getAgentInformation(new HostRef("agentId1", "agentId1"))).thenReturn(mockAgent1);
+        when(mockAgentInfoDAO.getAgentInformation(new HostRef("agentId2", "agentId2"))).thenReturn(mockAgent2);
+        when(mockAgentInfoDAO.getAgentInformation(new HostRef("agentId3", "agentId3"))).thenReturn(mockAgent3);
+        when(mockAgentInfoDAO.getAgentInformation(new HostRef("agentId4", "agentId4"))).thenReturn(mockAgent4);
+        when(mockAgentInfoDAO.getAgentInformation(new HostRef("agentId5", "agentId5"))).thenReturn(mockAgent5);
+        
+        Cursor<BasePojo> agentCursor = (Cursor<BasePojo>) mock(Cursor.class);
+        when(agentCursor.hasNext()).thenReturn(true).thenReturn(true).thenReturn(true).thenReturn(true).thenReturn(true).thenReturn(false);
+        when(agentCursor.next()).thenReturn(mockAgent1).thenReturn(mockAgent2).thenReturn(mockAgent3).thenReturn(mockAgent4).thenReturn(mockAgent5).thenReturn(null);
+        
+        PreparedStatement<BasePojo> prepared = (PreparedStatement<BasePojo>) mock(PreparedStatement.class);
+        when(mockStorage.prepareStatement((StatementDescriptor<BasePojo>) any(StatementDescriptor.class))).thenReturn(prepared);
+        when(prepared.executeQuery()).thenReturn(agentCursor);
+    }
+
+    @Test
+    public void testOneValidArgument() throws CommandException {
+        when(mockArguments.getNonOptionArguments()).thenReturn(getOneValidAgent());
+        
+        cleanDataCommand.run(mockCommandContext);
+        
+        verify(mockStorage, times(1)).purge(isA(String.class));
+        verify(mockStorage).purge("agentId1");
+        
+        verify(mockOutput, times(1)).println(isA(String.class));
+        verify(mockOutput).println("Purging data for agent: agentId1");
+    }
+
+    @Test
+    public void testMultipleValidArguments() throws CommandException {
+        cleanDataCommand.run(mockCommandContext);
+        
+        verify(mockStorage, times(3)).purge(isA(String.class));
+        verify(mockStorage).purge("agentId1");
+        verify(mockStorage).purge("agentId2");
+        verify(mockStorage).purge("agentId3");
+        
+        verify(mockOutput, times(3)).println(isA(String.class));
+        verify(mockOutput).println("Purging data for agent: agentId1");
+        verify(mockOutput).println("Purging data for agent: agentId2");
+        verify(mockOutput).println("Purging data for agent: agentId3");
+    }
+
+    @Test
+    public void testUnauthorizedLiveAgents() throws CommandException {
+        when(mockArguments.getNonOptionArguments()).thenReturn(getAliveAgents());
+        
+        cleanDataCommand.run(mockCommandContext);
+        
+        verify(mockStorage, never()).purge(isA(String.class));
+        
+        verify(mockOutput, times(2)).println(isA(String.class));
+        verify(mockOutput).println("Cannot purge data for agent agentId4. This agent is currently running");
+        verify(mockOutput).println("Cannot purge data for agent agentId5. This agent is currently running");
+    }
+
+    @Test
+    public void testRemoveSpecificLiveAgents() throws CommandException {
+        when(mockArguments.hasArgument("alive")).thenReturn(true);
+        when(mockArguments.getNonOptionArguments()).thenReturn(getAliveAgents());
+        
+        cleanDataCommand.run(mockCommandContext);
+        
+        verify(mockStorage, times(2)).purge(isA(String.class));
+        verify(mockStorage).purge("agentId4");
+        verify(mockStorage).purge("agentId5");
+        
+        verify(mockOutput, times(2)).println(isA(String.class));
+        verify(mockOutput).println("Purging data for agent: agentId4");
+        verify(mockOutput).println("Purging data for agent: agentId5");
+    }
+
+    @Test
+    public void testInvalidArguments() throws CommandException {
+        when(mockArguments.getNonOptionArguments()).thenReturn(getInvalidAgentList());
+        
+        cleanDataCommand.run(mockCommandContext);
+        
+        verify(mockStorage, never()).purge(isA(String.class));
+        verify(mockOutput, times(3)).println(isA(String.class));
+        verify(mockOutput).println("Agent with an id [invalidAgent1] was not found!");
+        verify(mockOutput).println("Agent with an id [invalidAgent2] was not found!");
+        verify(mockOutput).println("Agent with an id [invalidAgent3] was not found!");
+    }
+
+    @Test
+    public void testRemoveAllDeadAgents() throws CommandException {
+        when(mockArguments.hasArgument("all")).thenReturn(true);
+        
+        cleanDataCommand.run(mockCommandContext);
+        
+        verify(mockStorage, times(3)).purge(isA(String.class));
+        verify(mockStorage).purge("agentId1");
+        verify(mockStorage).purge("agentId2");
+        verify(mockStorage).purge("agentId3");
+    }
+
+    @Test
+    public void testRemoveAllLiveAgents() throws CommandException {
+        when(mockArguments.hasArgument("all")).thenReturn(true);
+        when(mockArguments.hasArgument("alive")).thenReturn(true);
+      
+        cleanDataCommand.run(mockCommandContext);
+      
+        verify(mockStorage, times(5)).purge(isA(String.class));
+        verify(mockStorage).purge("agentId1");
+        verify(mockStorage).purge("agentId2");
+        verify(mockStorage).purge("agentId3");
+        verify(mockStorage).purge("agentId4");
+        verify(mockStorage).purge("agentId5");
+    }
+
+    private List<String> getValidAgentList() {
+        List<String> agentIdList = new ArrayList<String>();
+        agentIdList.add("agentId1");
+        agentIdList.add("agentId2");
+        agentIdList.add("agentId3");
+        return agentIdList;
+    }
+
+    private List<String> getInvalidAgentList() {
+        List<String> agentIdList = new ArrayList<String>();
+        agentIdList.add("invalidAgent1");
+        agentIdList.add("invalidAgent2");
+        agentIdList.add("invalidAgent3");
+        return agentIdList;
+    }
+
+    private List<String> getOneValidAgent() {
+        List<String> agentIdList = new ArrayList<String>();
+        agentIdList.add("agentId1");
+        return agentIdList;
+    }
+
+    private List<String> getAliveAgents() {
+        List<String> agentIdList = new ArrayList<String>();
+        agentIdList.add("agentId4");
+        agentIdList.add("agentId5");
+        return agentIdList;
+    }
+
+}
--- a/client/cli/src/test/java/com/redhat/thermostat/client/cli/internal/CleanHistoryCommandTest.java	Wed Aug 14 13:11:11 2013 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,197 +0,0 @@
-/*
- * Copyright 2013 Red Hat, Inc.
- *
- * This file is part of Thermostat.
- *
- * Thermostat is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published
- * by the Free Software Foundation; either version 2, or (at your
- * option) any later version.
- *
- * Thermostat is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Thermostat; see the file COPYING.  If not see
- * <http://www.gnu.org/licenses/>.
- *
- * Linking this code with other modules is making a combined work
- * based on this code.  Thus, the terms and conditions of the GNU
- * General Public License cover the whole combination.
- *
- * As a special exception, the copyright holders of this code give
- * you permission to link this code with independent modules to
- * produce an executable, regardless of the license terms of these
- * independent modules, and to copy and distribute the resulting
- * executable under terms of your choice, provided that you also
- * meet, for each linked independent module, the terms and conditions
- * of the license of that module.  An independent module is a module
- * which is not derived from or based on this code.  If you modify
- * this code, you may extend this exception to your version of the
- * library, but you are not obligated to do so.  If you do not wish
- * to do so, delete this exception statement from your version.
- */
-
-package com.redhat.thermostat.client.cli.internal;
-
-import static org.mockito.Matchers.isA;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-import static org.mockito.Mockito.times;
-
-import java.io.PrintStream;
-import java.util.ArrayList;
-import java.util.List;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import com.redhat.thermostat.common.cli.Arguments;
-import com.redhat.thermostat.common.cli.CommandContext;
-import com.redhat.thermostat.common.cli.CommandException;
-import com.redhat.thermostat.common.cli.Console;
-import com.redhat.thermostat.storage.core.HostRef;
-import com.redhat.thermostat.storage.core.Storage;
-import com.redhat.thermostat.storage.dao.AgentInfoDAO;
-import com.redhat.thermostat.storage.model.AgentInformation;
-import com.redhat.thermostat.testutils.StubBundleContext;
-
-import org.powermock.api.mockito.PowerMockito;
-import org.powermock.core.classloader.annotations.PrepareForTest;
-import org.powermock.modules.junit4.PowerMockRunner;
-
-@RunWith(PowerMockRunner.class)
-@PrepareForTest(AgentInformation.class)
-public class CleanHistoryCommandTest {
-
-    private CleanHistoryCommand cleanHistoryCommand;
-    private CommandContext mockCommandContext;
-    private List<String> agentIdList;
-    private Storage mockStorage;
-    private PrintStream mockOutput;
-    private AgentInfoDAO mockAgentInfoDAO;
-
-    @Before
-    public void setUp() {
-        StubBundleContext bundleContext = new StubBundleContext();
-        cleanHistoryCommand = new CleanHistoryCommand(bundleContext);
-        
-        mockStorage = mock(Storage.class); 
-        bundleContext.registerService(Storage.class, mockStorage, null);
-        mockAgentInfoDAO = mock(AgentInfoDAO.class);
-        bundleContext.registerService(AgentInfoDAO.class, mockAgentInfoDAO, null);
-        
-        mockCommandContext = mock(CommandContext.class);
-        Arguments mockArguments = mock(Arguments.class);
-        
-        when(mockCommandContext.getArguments()).thenReturn(mockArguments);
-        when(mockArguments.getNonOptionArguments()).thenReturn(getArgumentList());
-        
-        Console mockConsole = mock(Console.class);
-        mockOutput = mock(PrintStream.class);
-        when(mockCommandContext.getConsole()).thenReturn(mockConsole);
-        when(mockConsole.getOutput()).thenReturn(mockOutput);
-    }
-
-    @Test
-    public void testOneValidArgument() throws CommandException {
-        AgentInformation mockAgent = PowerMockito.mock(AgentInformation.class);
-        when(mockAgent.getAgentId()).thenReturn("agentId1");
-        when(mockAgent.isAlive()).thenReturn(false);
-        
-        when(mockAgentInfoDAO.getAgentInformation(new HostRef("agentId1", "agentId1"))).thenReturn((mockAgent));
-        
-        cleanHistoryCommand.run(mockCommandContext);
-        
-        verify(mockStorage, times(1)).purge(isA(String.class));
-        verify(mockStorage).purge("agentId1");
-        verify(mockOutput).println("Purging data for agent: agentId1");
-    }
-
-    @Test
-    public void testMultipleValidArguments() throws CommandException {
-        AgentInformation mockAgent1 = PowerMockito.mock(AgentInformation.class);
-        AgentInformation mockAgent2 = PowerMockito.mock(AgentInformation.class);
-        AgentInformation mockAgent3 = PowerMockito.mock(AgentInformation.class);
-        when(mockAgent1.getAgentId()).thenReturn("agentId1");
-        when(mockAgent2.getAgentId()).thenReturn("agentId2");
-        when(mockAgent3.getAgentId()).thenReturn("agentId3");
-        
-        when(mockAgentInfoDAO.getAgentInformation(new HostRef("agentId1", "agentId1"))).thenReturn((mockAgent1));
-        when(mockAgentInfoDAO.getAgentInformation(new HostRef("agentId2", "agentId2"))).thenReturn((mockAgent2));
-        when(mockAgentInfoDAO.getAgentInformation(new HostRef("agentId3", "agentId3"))).thenReturn((mockAgent3));
-        
-        cleanHistoryCommand.run(mockCommandContext);
-        
-        verify(mockStorage, times(3)).purge(isA(String.class));
-        verify(mockStorage).purge("agentId1");
-        verify(mockStorage).purge("agentId2");
-        verify(mockStorage).purge("agentId3");
-        
-        verify(mockOutput, times(3)).println(isA(String.class));
-        verify(mockOutput).println("Purging data for agent: agentId1");
-        verify(mockOutput).println("Purging data for agent: agentId2");
-        verify(mockOutput).println("Purging data for agent: agentId3");
-    }
-
-    @Test
-    public void testLiveAgents() throws CommandException {
-        AgentInformation mockAgent1 = PowerMockito.mock(AgentInformation.class);
-        AgentInformation mockAgent2 = PowerMockito.mock(AgentInformation.class);
-        AgentInformation mockAgent3 = PowerMockito.mock(AgentInformation.class);
-        when(mockAgent1.getAgentId()).thenReturn("agentId1");
-        when(mockAgent2.getAgentId()).thenReturn("agentId2");
-        when(mockAgent3.getAgentId()).thenReturn("agentId3");
-        when(mockAgent1.isAlive()).thenReturn(true);
-        when(mockAgent2.isAlive()).thenReturn(false);
-        when(mockAgent3.isAlive()).thenReturn(true);
-        
-        when(mockAgentInfoDAO.getAgentInformation(new HostRef("agentId1", "agentId1"))).thenReturn((mockAgent1));
-        when(mockAgentInfoDAO.getAgentInformation(new HostRef("agentId2", "agentId2"))).thenReturn((mockAgent2));
-        when(mockAgentInfoDAO.getAgentInformation(new HostRef("agentId3", "agentId3"))).thenReturn((mockAgent3));
-        
-        cleanHistoryCommand.run(mockCommandContext);
-        
-        verify(mockStorage, times(1)).purge(isA(String.class));
-        verify(mockStorage, times(1)).purge("agentId2");
-        
-        verify(mockOutput, times(3)).println(isA(String.class));
-        verify(mockOutput).println("Cannot purge data for agent agentId1. This agent is currently running");
-        verify(mockOutput).println("Purging data for agent: agentId2");
-        verify(mockOutput).println("Cannot purge data for agent agentId3. This agent is currently running");
-    }
-
-    @Test
-    public void testInvalidArguments() throws CommandException {
-        AgentInformation mockAgent1 = PowerMockito.mock(AgentInformation.class);
-        AgentInformation mockAgent2 = PowerMockito.mock(AgentInformation.class);
-        when(mockAgent1.getAgentId()).thenReturn("validAgent1-in-DB");
-        when(mockAgent2.getAgentId()).thenReturn("validAgent2-in-DB");
-        
-        when(mockAgentInfoDAO.getAgentInformation(new HostRef("validAgent1-in-DB", "validAgent1-in-DB"))).thenReturn((mockAgent1));
-        when(mockAgentInfoDAO.getAgentInformation(new HostRef("validAgent2-in-DB", "validAgent2-in-DB"))).thenReturn((mockAgent2));
-        
-        cleanHistoryCommand.run(mockCommandContext);
-        
-        verify(mockStorage, never()).purge(isA(String.class));
-        
-        verify(mockOutput, times(3)).println(isA(String.class));
-        verify(mockOutput).println("Agent with an id agentId1 was not found!");
-        verify(mockOutput).println("Agent with an id agentId2 was not found!");
-        verify(mockOutput).println("Agent with an id agentId3 was not found!");
-    }
-
-    private List<String> getArgumentList() {
-        agentIdList = new ArrayList<String>();
-        agentIdList.add("agentId1");
-        agentIdList.add("agentId2");
-        agentIdList.add("agentId3");
-        return agentIdList;
-    }
-
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/distribution/config/commands/clean-data.properties	Fri Aug 09 15:45:12 2013 -0400
@@ -0,0 +1,31 @@
+bundles = thermostat-client-cli-${project.version}.jar, \
+          thermostat-storage-mongodb-${project.version}.jar, \
+          thermostat-web-common-${project.version}.jar, \
+          thermostat-web-client-${project.version}.jar, \
+          httpcomponents-core.jar, \
+          httpcomponents-client.jar, \
+          gson.jar, \
+          mongo.jar, \
+          commons-beanutils.jar, \
+          commons-codec.jar, \
+          commons-collections.jar, \
+          commons-logging.jar, \
+
+description = Drop all data related to all of the specified agents
+
+usage = clean-data [--alive] [--all] <agent_id>
+
+options = alive, all, AUTO_LOG_OPTION
+
+all.short = a
+all.long = all
+all.hasarg = false
+all.required = false
+all.description = clean data for all agents
+
+alive.long = alive
+alive.hasarg = false
+alive.required = false
+alive.description = authorize deletion of live agents
+
+environments = cli, shell
--- a/distribution/config/commands/clean-history.properties	Wed Aug 14 13:11:11 2013 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,21 +0,0 @@
-bundles = thermostat-client-cli-${project.version}.jar, \
-          thermostat-storage-mongodb-${project.version}.jar, \
-          thermostat-web-common-${project.version}.jar, \
-          thermostat-web-client-${project.version}.jar, \
-          httpcomponents-core.jar, \
-          httpcomponents-client.jar, \
-          gson.jar, \
-          mongo.jar, \
-          commons-beanutils.jar, \
-          commons-codec.jar, \
-          commons-collections.jar, \
-          commons-logging.jar, \
-
-description = Drop all data related to all of the specified agents
-
-usage = clean-history <agent_id>
-
-# This command does not have any options
-options = AUTO_LOG_OPTION
-
-environments = cli, shell
--- a/storage/core/src/main/java/com/redhat/thermostat/storage/core/Categories.java	Wed Aug 14 13:11:11 2013 -0400
+++ b/storage/core/src/main/java/com/redhat/thermostat/storage/core/Categories.java	Fri Aug 09 15:45:12 2013 -0400
@@ -36,7 +36,10 @@
 
 package com.redhat.thermostat.storage.core;
 
+import java.util.ArrayList;
+import java.util.Collections;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 
 public class Categories {
@@ -62,5 +65,9 @@
         return namesToCategories.get(categoryName);
     }
 
+    public static synchronized List<Category> getAllCategories() {
+        return Collections.unmodifiableList(new ArrayList<Category>(namesToCategories.values()));
+    }
+
 }