changeset 1908:cb6390cf824b

Add vm-numa plugin base to Thermostat. This is a backport of the vm-numa plugin from the hg/thermostat repository revision fd3632b6449c. This changeset does 'not' compile and relies on the commit ahead: "Complete vm-numa plugin" which includes the changes to work with this repository. This commit separation is to make it easier to see relevant changes for the vm-numa plugin. PR3007 Reviewed-by: jerboaa Review-thread: http://icedtea.classpath.org/pipermail/thermostat/2016-June/019313.html
author Jie Kang <jkang@redhat.com>
date Thu, 09 Jun 2016 08:58:57 -0400
parents fe6cbf89e84e
children 323e9c6e2192
files distribution/assembly/all-plugin-assembly.xml distribution/packaging/fedora/thermostat.spec distribution/pom.xml pom.xml vm-numa/agent/pom.xml vm-numa/agent/src/main/java/com/redhat/thermostat/vm/numa/agent/internal/Activator.java vm-numa/agent/src/main/java/com/redhat/thermostat/vm/numa/agent/internal/VmNumaBackend.java vm-numa/agent/src/main/java/com/redhat/thermostat/vm/numa/agent/internal/VmNumaCollector.java vm-numa/agent/src/main/java/com/redhat/thermostat/vm/numa/agent/internal/VmNumaStatParser.java vm-numa/agent/src/test/java/com/redhat/thermostat/vm/numa/agent/internal/ActivatorTest.java vm-numa/agent/src/test/java/com/redhat/thermostat/vm/numa/agent/internal/VmNumaBackendTest.java vm-numa/agent/src/test/java/com/redhat/thermostat/vm/numa/agent/internal/VmNumaCollectorTest.java vm-numa/agent/src/test/java/com/redhat/thermostat/vm/numa/agent/internal/VmNumaStatParserTest.java vm-numa/client-core/pom.xml vm-numa/client-core/src/main/java/com/redhat/thermostat/vm/numa/client/core/VmNumaService.java vm-numa/client-core/src/main/java/com/redhat/thermostat/vm/numa/client/core/VmNumaServiceImpl.java vm-numa/client-core/src/main/java/com/redhat/thermostat/vm/numa/client/core/VmNumaView.java vm-numa/client-core/src/main/java/com/redhat/thermostat/vm/numa/client/core/VmNumaViewProvider.java vm-numa/client-core/src/main/java/com/redhat/thermostat/vm/numa/client/core/internal/Activator.java vm-numa/client-core/src/main/java/com/redhat/thermostat/vm/numa/client/core/internal/VmNumaController.java vm-numa/client-core/src/main/java/com/redhat/thermostat/vm/numa/client/core/locale/LocaleResources.java vm-numa/client-core/src/main/resources/com/redhat/thermostat/vm/numa/client/core/locale/strings.properties vm-numa/client-core/src/test/java/com/redhat/thermostat/vm/numa/client/core/VmNumaServiceImplTest.java vm-numa/client-core/src/test/java/com/redhat/thermostat/vm/numa/client/core/VmNumaServiceTest.java vm-numa/client-core/src/test/java/com/redhat/thermostat/vm/numa/client/core/internal/ActivatorTest.java vm-numa/client-core/src/test/java/com/redhat/thermostat/vm/numa/client/core/internal/VmNumaControllerTest.java vm-numa/client-core/src/test/java/com/redhat/thermostat/vm/numa/client/core/locale/LocaleResourcesTest.java vm-numa/client-swing/pom.xml vm-numa/client-swing/src/main/java/com/redhat/thermostat/vm/numa/client/swing/internal/Activator.java vm-numa/client-swing/src/main/java/com/redhat/thermostat/vm/numa/client/swing/internal/SwingVmNumaViewProvider.java vm-numa/client-swing/src/main/java/com/redhat/thermostat/vm/numa/client/swing/internal/VmNumaPanel.java vm-numa/client-swing/src/test/java/com/redhat/thermostat/vm/numa/client/swing/internal/ActivatorTest.java vm-numa/client-swing/src/test/java/com/redhat/thermostat/vm/numa/client/swing/internal/VmNumaPanelTest.java vm-numa/common/pom.xml vm-numa/common/src/main/java/com/redhat/thermostat/vm/numa/common/NumaMemoryLocations.java vm-numa/common/src/main/java/com/redhat/thermostat/vm/numa/common/VmNumaDAO.java vm-numa/common/src/main/java/com/redhat/thermostat/vm/numa/common/VmNumaNodeStat.java vm-numa/common/src/main/java/com/redhat/thermostat/vm/numa/common/VmNumaStat.java vm-numa/common/src/main/java/com/redhat/thermostat/vm/numa/common/internal/Activator.java vm-numa/common/src/main/java/com/redhat/thermostat/vm/numa/common/internal/VmNumaCategoryRegistration.java vm-numa/common/src/main/java/com/redhat/thermostat/vm/numa/common/internal/VmNumaDAOImpl.java vm-numa/common/src/main/java/com/redhat/thermostat/vm/numa/common/internal/VmNumaDAOImplStatementDescriptorRegistration.java vm-numa/common/src/main/resources/META-INF/services/com.redhat.thermostat.storage.core.auth.CategoryRegistration vm-numa/common/src/main/resources/META-INF/services/com.redhat.thermostat.storage.core.auth.StatementDescriptorRegistration vm-numa/common/src/test/java/com/redhat/thermostat/vm/numa/common/VmNumaDAOTest.java vm-numa/common/src/test/java/com/redhat/thermostat/vm/numa/common/VmNumaNodeStatTest.java vm-numa/common/src/test/java/com/redhat/thermostat/vm/numa/common/VmNumaStatTest.java vm-numa/common/src/test/java/com/redhat/thermostat/vm/numa/common/internal/ActivatorTest.java vm-numa/common/src/test/java/com/redhat/thermostat/vm/numa/common/internal/VmNumaCategoryRegistrationTest.java vm-numa/common/src/test/java/com/redhat/thermostat/vm/numa/common/internal/VmNumaDAOImplStatementDescriptorRegistrationTest.java vm-numa/common/src/test/java/com/redhat/thermostat/vm/numa/common/internal/VmNumaDAOImplTest.java vm-numa/distribution/pom.xml vm-numa/distribution/thermostat-plugin.xml vm-numa/pom.xml web/war/pom.xml
diffstat 55 files changed, 4288 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- a/distribution/assembly/all-plugin-assembly.xml	Tue Jun 07 11:55:47 2016 -0400
+++ b/distribution/assembly/all-plugin-assembly.xml	Thu Jun 09 08:58:57 2016 -0400
@@ -70,6 +70,7 @@
         <include>com.redhat.thermostat:thermostat-schema-info-distribution</include>
         <include>com.redhat.thermostat:thermostat-web-endpoint-distribution</include>
         <include>com.redhat.thermostat:thermostat-killvm-distribution</include>
+        <include>com.redhat.thermostat:thermostat-vm-numa-distribution</include>
       </includes>
     </dependencySet>  
   </dependencySets>
--- a/distribution/packaging/fedora/thermostat.spec	Tue Jun 07 11:55:47 2016 -0400
+++ b/distribution/packaging/fedora/thermostat.spec	Thu Jun 09 08:58:57 2016 -0400
@@ -1045,6 +1045,7 @@
 %{_datadir}/%{pkg_name}/plugins/vm-heap-analysis
 %{_datadir}/%{pkg_name}/plugins/vm-jmx
 %{_datadir}/%{pkg_name}/plugins/vm-memory
+%{_datadir}/%{pkg_name}/plugins/vm-numa
 %{_datadir}/%{pkg_name}/plugins/vm-overview
 %{_datadir}/%{pkg_name}/plugins/vm-profiler
 %{_datadir}/%{pkg_name}/cache
--- a/distribution/pom.xml	Tue Jun 07 11:55:47 2016 -0400
+++ b/distribution/pom.xml	Thu Jun 09 08:58:57 2016 -0400
@@ -560,6 +560,12 @@
       <version>${project.version}</version>
       <type>zip</type>
     </dependency>
+    <dependency>
+      <groupId>com.redhat.thermostat</groupId>
+      <artifactId>thermostat-vm-numa-distribution</artifactId>
+      <version>${project.version}</version>
+      <type>zip</type>
+    </dependency>
 
     <!-- list-categories command -->
     <dependency>
--- a/pom.xml	Tue Jun 07 11:55:47 2016 -0400
+++ b/pom.xml	Thu Jun 09 08:58:57 2016 -0400
@@ -243,6 +243,7 @@
     <module>vm-profiler</module>
     <module>notes</module>
     <module>numa</module>
+    <module>vm-numa</module>
     <module>laf-utils</module>
     <module>thermostat-plugin-validator</module>
     <module>validate-command</module>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-numa/agent/pom.xml	Thu Jun 09 08:58:57 2016 -0400
@@ -0,0 +1,104 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+ Copyright 2012-2016 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.
+
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <artifactId>thermostat-vm-numa</artifactId>
+    <groupId>com.redhat.thermostat</groupId>
+    <version>1.5.8-SNAPSHOT</version>
+  </parent>
+  <artifactId>thermostat-vm-numa-agent</artifactId>
+  <packaging>bundle</packaging>
+  <name>Thermostat VM NUMA Agent plugin</name>
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.felix</groupId>
+        <artifactId>maven-bundle-plugin</artifactId>
+        <extensions>true</extensions>
+        <configuration>
+          <instructions>
+            <Bundle-Vendor>Red Hat, Inc.</Bundle-Vendor>
+            <Bundle-SymbolicName>com.redhat.thermostat.vm.numa.agent</Bundle-SymbolicName>
+            <Bundle-Activator>com.redhat.thermostat.vm.numa.agent.internal.Activator</Bundle-Activator>
+            <Export-Package />
+            <Private-Package>
+              com.redhat.thermostat.vm.numa.agent.internal
+            </Private-Package>
+            <!-- Do not autogenerate uses clauses in Manifests -->
+            <_nouses>true</_nouses>
+          </instructions>
+        </configuration>
+      </plugin>
+    </plugins>
+  </build>
+  <dependencies>
+    <dependency>
+      <groupId>junit</groupId>
+      <artifactId>junit</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.mockito</groupId>
+      <artifactId>mockito-core</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>com.redhat.thermostat</groupId>
+      <artifactId>thermostat-common-test</artifactId>
+      <version>${project.version}</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.osgi</groupId>
+      <artifactId>org.osgi.core</artifactId>
+      <scope>provided</scope>
+    </dependency>
+    <dependency>
+      <groupId>com.redhat.thermostat</groupId>
+      <artifactId>thermostat-agent-core</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>com.redhat.thermostat</groupId>
+      <artifactId>thermostat-vm-numa-common</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+  </dependencies>
+</project>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-numa/agent/src/main/java/com/redhat/thermostat/vm/numa/agent/internal/Activator.java	Thu Jun 09 08:58:57 2016 -0400
@@ -0,0 +1,113 @@
+/*
+ * Copyright 2012-2016 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.vm.numa.agent.internal;
+
+import java.util.Map;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceRegistration;
+
+import com.redhat.thermostat.agent.VmStatusListenerRegistrar;
+import com.redhat.thermostat.backend.Backend;
+import com.redhat.thermostat.backend.BackendService;
+import com.redhat.thermostat.common.MultipleServiceTracker;
+import com.redhat.thermostat.common.Version;
+import com.redhat.thermostat.storage.core.WriterID;
+import com.redhat.thermostat.vm.numa.common.VmNumaDAO;
+
+public class Activator implements BundleActivator {
+
+    private ScheduledExecutorService executor;
+    private MultipleServiceTracker tracker;
+    private VmNumaBackend backend;
+    private ServiceRegistration<Backend> reg;
+
+    @Override
+    public void start(final BundleContext context) throws Exception {
+        final VmStatusListenerRegistrar registrar = new VmStatusListenerRegistrar(context);
+        executor = Executors.newSingleThreadScheduledExecutor();
+        Class<?>[] deps = new Class<?>[] {
+                BackendService.class,
+                VmNumaDAO.class,
+                WriterID.class,
+        };
+
+        tracker = new MultipleServiceTracker(context, deps, new MultipleServiceTracker.Action() {
+            @Override
+            public void dependenciesAvailable(Map<String, Object> services) {
+                VmNumaDAO vmNumaDAO = (VmNumaDAO) services.get(VmNumaDAO.class.getName());
+                Version version = new Version(context.getBundle());
+                WriterID writerID = (WriterID) services.get(WriterID.class.getName());
+                backend = constructBackend(executor, vmNumaDAO, version, registrar, writerID);
+                if (backend.canRegister()) {
+                    reg = context.registerService(Backend.class, backend, null);
+                }
+            }
+
+            @Override
+            public void dependenciesUnavailable() {
+                if (backend.isActive()) {
+                    backend.deactivate();
+                }
+                if (reg != null) {
+                    reg.unregister();
+                }
+            }
+        });
+
+        tracker.open();
+
+    }
+
+    @Override
+    public void stop(BundleContext context) throws Exception {
+        tracker.close();
+    }
+
+    //Package private for testing
+    VmNumaBackend getBackend() {
+        return backend;
+    }
+
+    //Package private for testing
+    VmNumaBackend constructBackend(ScheduledExecutorService executor, VmNumaDAO vmNumaDAO, Version version, VmStatusListenerRegistrar registrar, WriterID writerID) {
+        return new VmNumaBackend(executor, vmNumaDAO, version, registrar, writerID);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-numa/agent/src/main/java/com/redhat/thermostat/vm/numa/agent/internal/VmNumaBackend.java	Thu Jun 09 08:58:57 2016 -0400
@@ -0,0 +1,122 @@
+/*
+ * Copyright 2012-2016 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.vm.numa.agent.internal;
+
+import java.io.IOException;
+import java.text.ParseException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import com.redhat.thermostat.agent.VmStatusListenerRegistrar;
+import com.redhat.thermostat.backend.VmPollingAction;
+import com.redhat.thermostat.backend.VmPollingBackend;
+import com.redhat.thermostat.common.Version;
+import com.redhat.thermostat.common.utils.LoggingUtils;
+import com.redhat.thermostat.storage.core.WriterID;
+import com.redhat.thermostat.vm.numa.common.VmNumaDAO;
+import com.redhat.thermostat.vm.numa.common.VmNumaStat;
+
+public class VmNumaBackend extends VmPollingBackend {
+
+    private final VmNumaBackendAction action;
+    private static final Logger logger = LoggingUtils.getLogger(VmNumaBackend.class);
+
+    public VmNumaBackend(ScheduledExecutorService executor, VmNumaDAO vmNumaDAO, Version version,
+                         VmStatusListenerRegistrar registrar, WriterID id) {
+        super("VM NUMA Backend",
+                "Gathers NUMA statistics about a vm",
+                "Red Hat, Inc.",
+                version, executor, registrar);
+        this.action = new VmNumaBackendAction(id, vmNumaDAO);
+        registerAction(action);
+    }
+
+    @Override
+    public int getOrderValue() {
+        return ORDER_MEMORY_GROUP;
+    }
+
+    private static class VmNumaBackendAction implements VmPollingAction {
+        private VmNumaDAO dao;
+        private WriterID writerID;
+        private Map<Integer, VmNumaCollector> collectors;
+
+        private VmNumaBackendAction(final WriterID writerID, VmNumaDAO dao) {
+            this.writerID = writerID;
+            this.dao = dao;
+            this.collectors = new HashMap<>();
+        }
+
+        @Override
+        public void run(String vmId, int pid) {
+            if (!collectors.containsKey(pid)) {
+                collectors.put(pid, new VmNumaCollector(pid));
+            }
+
+            try {
+                VmNumaStat data = collectors.get(pid).collect();
+                data.setAgentId(writerID.getWriterID());
+                data.setVmId(vmId);
+                dao.putVmNumaStat(data);
+            } catch (ParseException e) {
+                logger.log(Level.WARNING, "Unable to add numastat data for vm: " + vmId);
+            }
+        }
+    }
+
+    /**
+     * VmNumaBackend requires numastat process to function
+     * @return true if numastat process exists, false otherwise
+     */
+    public boolean canRegister() {
+        try {
+            Runtime.getRuntime().exec("numastat");
+            return true;
+        } catch (IOException e) {
+            //numastat does not exist, do nothing
+        }
+        return false;
+    }
+
+    // For testing purposes only
+    void setVmNumaBackendCollector(int pid, VmNumaCollector collector) {
+        action.collectors.put(pid, collector);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-numa/agent/src/main/java/com/redhat/thermostat/vm/numa/agent/internal/VmNumaCollector.java	Thu Jun 09 08:58:57 2016 -0400
@@ -0,0 +1,85 @@
+/*
+ * Copyright 2012-2016 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.vm.numa.agent.internal;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.text.ParseException;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import com.redhat.thermostat.common.utils.LoggingUtils;
+import com.redhat.thermostat.common.utils.StreamUtils;
+import com.redhat.thermostat.vm.numa.common.VmNumaStat;
+
+public class VmNumaCollector {
+
+    private static final String PROCESS = "numastat";
+    private static final Logger logger = LoggingUtils.getLogger(VmNumaCollector.class);
+
+    private final ProcessBuilder processBuilder;
+    private final VmNumaStatParser parser;
+
+    public VmNumaCollector(int pid) {
+        this.processBuilder = new ProcessBuilder(PROCESS, String.valueOf(pid));
+        this.parser = new VmNumaStatParser();
+    }
+
+    public VmNumaStat collect() throws ParseException {
+        String output = getOutput();
+        return parser.parse(output);
+    }
+
+    private String getOutput() {
+        try {
+            Process p = startProcess();
+
+            InputStream is = p.getInputStream();
+            return new String(StreamUtils.readAll(is));
+        } catch (InterruptedException | IOException e) {
+            logger.log(Level.WARNING, "Unable to run process numastat");
+        }
+        return "";
+    }
+
+    //For testing
+    Process startProcess() throws InterruptedException, IOException {
+        Process p = processBuilder.start();
+        p.waitFor();
+        return p;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-numa/agent/src/main/java/com/redhat/thermostat/vm/numa/agent/internal/VmNumaStatParser.java	Thu Jun 09 08:58:57 2016 -0400
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2012-2016 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.vm.numa.agent.internal;
+
+import java.text.ParseException;
+
+import com.redhat.thermostat.vm.numa.common.VmNumaNodeStat;
+import com.redhat.thermostat.vm.numa.common.VmNumaStat;
+
+public class VmNumaStatParser {
+    public VmNumaStat parse(String input) throws ParseException {
+        VmNumaStat stat = new VmNumaStat();
+        stat.setTimeStamp(System.currentTimeMillis());
+        String[] lines = input.split(System.lineSeparator());
+        try {
+            VmNumaNodeStat[] stats = parseStats(lines[4], lines[5], lines[6], lines[7]);
+            stat.setVmNodeStats(stats);
+            return stat;
+        } catch (NumberFormatException | ArrayIndexOutOfBoundsException | NegativeArraySizeException e) {
+            throw new ParseException("Unexpected input to VmNumaStatParser", 0);
+        }
+    }
+
+    private VmNumaNodeStat[] parseStats(String HUGE, String HEAP, String STACK, String PRIVATE) {
+        VmNumaNodeStat[] stats;
+
+        String[] hugeStats = splitWhitespaces(HUGE);
+        String[] heapStats = splitWhitespaces(HEAP);
+        String[] stackStats = splitWhitespaces(STACK);
+        String[] privateStats = splitWhitespaces(PRIVATE);
+
+        //Take the maximum in-case of erroneous input
+        int numberOfNodes = -2 + Math.max(hugeStats.length,
+                                    Math.max(heapStats.length,
+                                    Math.max(stackStats.length, privateStats.length)));
+
+        stats = new VmNumaNodeStat[numberOfNodes];
+        for (int i = 0; i < numberOfNodes; i++) {
+            stats[i] = new VmNumaNodeStat();
+            stats[i].setNode(i);
+            stats[i].setHugeMemory(Double.parseDouble(hugeStats[i + 1]));
+            stats[i].setHeapMemory(Double.parseDouble(heapStats[i + 1]));
+            stats[i].setStackMemory(Double.parseDouble(stackStats[i + 1]));
+            stats[i].setPrivateMemory(Double.parseDouble(privateStats[i + 1]));
+        }
+        return stats;
+    }
+
+    private String[] splitWhitespaces(String s) {
+        return s.split("\\s+");
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-numa/agent/src/test/java/com/redhat/thermostat/vm/numa/agent/internal/ActivatorTest.java	Thu Jun 09 08:58:57 2016 -0400
@@ -0,0 +1,125 @@
+/*
+ * Copyright 2012-2016 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.vm.numa.agent.internal;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.util.concurrent.ScheduledExecutorService;
+
+import org.junit.Ignore;
+import org.junit.Test;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.Version;
+
+import com.redhat.thermostat.agent.VmStatusListenerRegistrar;
+import com.redhat.thermostat.backend.Backend;
+import com.redhat.thermostat.backend.BackendService;
+import com.redhat.thermostat.storage.core.WriterID;
+import com.redhat.thermostat.testutils.StubBundleContext;
+import com.redhat.thermostat.vm.numa.common.VmNumaDAO;
+
+public class ActivatorTest {
+    @Test
+    public void verifyActivatorDoesNotRegisterServiceOnMissingDeps() throws Exception {
+        StubBundleContext context = new StubBundleContext();
+
+        Activator activator = new Activator();
+
+        activator.start(context);
+
+        assertEquals(0, context.getAllServices().size());
+        assertEquals(3, context.getServiceListeners().size());
+
+        activator.stop(context);
+    }
+
+    @Test
+    public void verifyActivatorRegistersServices() throws Exception {
+        StubBundleContext context = new StubBundleContext() {
+            @Override
+            public Bundle getBundle() {
+                Bundle result = mock(Bundle.class);
+                when(result.getVersion()).thenReturn(Version.emptyVersion);
+                return result;
+            }
+        };
+        BackendService service = mock(BackendService.class);
+        VmNumaDAO vmNumaDAO = mock(VmNumaDAO.class);
+        WriterID idService = mock(WriterID.class);
+
+        context.registerService(BackendService.class.getName(), service, null);
+        context.registerService(VmNumaDAO.class, vmNumaDAO, null);
+        context.registerService(WriterID.class, idService, null);
+
+        final VmNumaBackend[] mock = new VmNumaBackend[1];
+
+        Activator activator = new Activator() {
+            @Override
+            VmNumaBackend constructBackend(ScheduledExecutorService executor, VmNumaDAO vmNumaDAO, com.redhat.thermostat.common.Version version, VmStatusListenerRegistrar registrar, WriterID writerID) {
+                mock[0] = new VmNumaBackend(executor, vmNumaDAO, version, registrar, writerID)
+                {
+                    @Override
+                    public boolean canRegister() {
+                        return true;
+                    }
+                };
+                return mock[0];
+            }
+        };
+
+        activator.start(context);
+
+        assertTrue(context.isServiceRegistered(Backend.class.getName(), mock[0].getClass()));
+        VmNumaBackend backend = activator.getBackend();
+        assertNotNull(backend);
+
+        // core thermostat will activate the backend once it's registered
+        backend.activate();
+
+        activator.stop(context);
+
+        assertFalse(backend.isActive());
+
+        assertEquals(0, context.getServiceListeners().size());
+        assertEquals(3, context.getAllServices().size());
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-numa/agent/src/test/java/com/redhat/thermostat/vm/numa/agent/internal/VmNumaBackendTest.java	Thu Jun 09 08:58:57 2016 -0400
@@ -0,0 +1,167 @@
+/*
+ * Copyright 2012-2016 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.vm.numa.agent.internal;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Matchers.isA;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
+
+import java.text.ParseException;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+
+import com.redhat.thermostat.agent.VmStatusListener;
+import com.redhat.thermostat.agent.VmStatusListenerRegistrar;
+import com.redhat.thermostat.common.Ordered;
+import com.redhat.thermostat.common.Version;
+import com.redhat.thermostat.storage.core.WriterID;
+import com.redhat.thermostat.vm.numa.common.VmNumaDAO;
+import com.redhat.thermostat.vm.numa.common.VmNumaStat;
+
+public class VmNumaBackendTest {
+    private VmNumaBackend backend;
+    private VmNumaDAO vmNumaDAO;
+    private ScheduledExecutorService executor;
+    private VmStatusListenerRegistrar registrar;
+
+    @Before
+    public void setup() {
+        executor = mock(ScheduledExecutorService.class);
+        vmNumaDAO = mock(VmNumaDAO.class);
+
+        Version version = mock(Version.class);
+        when(version.getVersionNumber()).thenReturn("0.0.0");
+
+        registrar = mock(VmStatusListenerRegistrar.class);
+
+        WriterID id = mock(WriterID.class);
+        when(id.getWriterID()).thenReturn("id");
+        backend = new VmNumaBackend(executor, vmNumaDAO, version, registrar, id);
+    }
+
+    @Test
+    public void testActivate() {
+        backend.activate();
+
+        verify(executor).scheduleAtFixedRate(isA(Runnable.class), eq(0l), eq(1000l), eq(TimeUnit.MILLISECONDS));
+        verify(registrar).register(backend);
+        assertTrue(backend.isActive());
+    }
+
+    @Test
+    public void testActivateTwice() {
+        assertTrue(backend.activate());
+        assertTrue(backend.isActive());
+        assertTrue(backend.activate());
+        assertTrue(backend.isActive());
+
+        assertTrue(backend.deactivate());
+    }
+
+    @Test
+    public void testDeactivate() {
+        backend.activate();
+        backend.deactivate();
+
+        verify(executor).shutdown();
+        verify(registrar).unregister(backend);
+        assertFalse(backend.isActive());
+    }
+
+
+    @Test
+    public void testDeactivateTwice() {
+        assertTrue(backend.activate());
+        assertTrue(backend.isActive());
+
+        assertTrue(backend.deactivate());
+        assertFalse(backend.isActive());
+        assertTrue(backend.deactivate());
+        assertFalse(backend.isActive());
+    }
+
+    @Test
+    public void testStart() throws ParseException {
+        mockCollector(backend, 0);
+        mockCollector(backend, 1);
+
+        backend.activate();
+
+        verify(registrar).register(backend);
+        ArgumentCaptor<Runnable> captor = ArgumentCaptor.forClass(Runnable.class);
+        verify(executor).scheduleAtFixedRate(captor.capture(), any(Long.class), any(Long.class), any(TimeUnit.class));
+        assertTrue(backend.isActive());
+
+        backend.vmStatusChanged(VmStatusListener.Status.VM_ACTIVE, "vm1", 0);
+        backend.vmStatusChanged(VmStatusListener.Status.VM_STARTED, "vm2", 1);
+
+        Runnable runnable = captor.getValue();
+        runnable.run();
+
+        verify(vmNumaDAO, times(2)).putVmNumaStat(any(VmNumaStat.class));
+
+        backend.vmStatusChanged(VmStatusListener.Status.VM_STOPPED, "vm1", 0);
+        backend.vmStatusChanged(VmStatusListener.Status.VM_STOPPED, "vm2", 1);
+
+        runnable.run();
+        verifyNoMoreInteractions(vmNumaDAO);
+    }
+
+    private void mockCollector(VmNumaBackend backend, int pid) throws ParseException {
+        VmNumaCollector collector = mock(VmNumaCollector.class);
+        when(collector.collect()).thenReturn(mock(VmNumaStat.class));
+        backend.setVmNumaBackendCollector(pid, collector);
+    }
+
+    @Test
+    public void testOrderValue() {
+        int orderValue = backend.getOrderValue();
+
+        assertTrue(orderValue == Ordered.ORDER_MEMORY_GROUP);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-numa/agent/src/test/java/com/redhat/thermostat/vm/numa/agent/internal/VmNumaCollectorTest.java	Thu Jun 09 08:58:57 2016 -0400
@@ -0,0 +1,156 @@
+/*
+ * Copyright 2012-2016 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.vm.numa.agent.internal;
+
+import static org.junit.Assert.assertTrue;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.text.ParseException;
+
+import org.junit.Ignore;
+import org.junit.Test;
+
+import com.redhat.thermostat.vm.numa.common.VmNumaNodeStat;
+import com.redhat.thermostat.vm.numa.common.VmNumaStat;
+
+public class VmNumaCollectorTest {
+
+    private VmNumaCollector collector;
+
+    @Test
+    public void testCollectSingleNodeStat() throws ParseException {
+        final String input = "\n" +
+                "Per-node process memory usage (in MBs) for PID 16816 (java)\n" +
+                "                           Node 0           Total\n" +
+                "                  --------------- ---------------\n" +
+                "Huge                         0.00            0.00\n" +
+                "Heap                         0.05            0.05\n" +
+                "Stack                        6.27            6.27\n" +
+                "Private                    385.07          385.07\n" +
+                "----------------  --------------- ---------------\n" +
+                "Total                      391.39          391.39";
+        setupCollector(input);
+
+        VmNumaStat stat = collector.collect();
+        VmNumaNodeStat[] stats = stat.getVmNodeStats();
+        assertTrue(stats.length == 1);
+        VmNumaNodeStat nodeStat = stats[0];
+        assertTrue(nodeStat.getNode() == 0);
+        assertTrue(nodeStat.getHugeMemory() == 0);
+        assertTrue(nodeStat.getHeapMemory() == 0.05d);
+        assertTrue(nodeStat.getStackMemory() == 6.27d);
+        assertTrue(nodeStat.getPrivateMemory() == 385.07d);
+    }
+
+    @Test
+    public void testCollectMultipleNodeStat() throws ParseException {
+        final String input = "\n" +
+                "Per-node process memory usage (in MBs) for PID 3 (ksoftirqd/0)\n" +
+                "                           Node 0          Node 1           Total\n" +
+                "                  --------------- --------------- ---------------\n" +
+                "Huge                         0.00            0.00            0.00\n" +
+                "Heap                         0.00            0.00            0.00\n" +
+                "Stack                        4.00            1.00            5.00\n" +
+                "Private                      5.00            2.00            7.00\n" +
+                "----------------  --------------- --------------- ---------------\n" +
+                "Total                        9.00            3.00           12.00";
+
+        setupCollector(input);
+
+        VmNumaStat stat = collector.collect();
+        VmNumaNodeStat[] stats = stat.getVmNodeStats();
+
+        assertTrue(stats.length == 2);
+
+        VmNumaNodeStat nodeStat1 = stats[0];
+        assertTrue(nodeStat1.getNode() == 0);
+        assertTrue(nodeStat1.getHugeMemory() == 0d);
+        assertTrue(nodeStat1.getHeapMemory() == 0d);
+        assertTrue(nodeStat1.getStackMemory() == 4d);
+        assertTrue(nodeStat1.getPrivateMemory() == 5d);
+
+        VmNumaNodeStat nodeStat2 = stats[1];
+        assertTrue(nodeStat2.getNode() == 1);
+        assertTrue(nodeStat2.getHugeMemory() == 0d);
+        assertTrue(nodeStat2.getHeapMemory() == 0d);
+        assertTrue(nodeStat2.getStackMemory() == 1d);
+        assertTrue(nodeStat2.getPrivateMemory() == 2d);
+    }
+
+    private void setupCollector(final String input) {
+        collector = new VmNumaCollector(0) {
+            @Override
+            protected Process startProcess() {
+                return new Process() {
+                    @Override
+                    public OutputStream getOutputStream() {
+                        return null;
+                    }
+
+                    @Override
+                    public InputStream getInputStream() {
+                        return new ByteArrayInputStream(input.getBytes());
+                    }
+
+                    @Override
+                    public InputStream getErrorStream() {
+                        return null;
+                    }
+
+                    @Override
+                    public int waitFor() throws InterruptedException {
+                        return 0;
+                    }
+
+                    @Override
+                    public int exitValue() {
+                        return 0;
+                    }
+
+                    @Override
+                    public void destroy() {
+                        //Do nothing
+                    }
+                };
+            }
+        };
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-numa/agent/src/test/java/com/redhat/thermostat/vm/numa/agent/internal/VmNumaStatParserTest.java	Thu Jun 09 08:58:57 2016 -0400
@@ -0,0 +1,147 @@
+/*
+ * Copyright 2012-2016 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.vm.numa.agent.internal;
+
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.text.ParseException;
+
+import org.junit.Test;
+
+import com.redhat.thermostat.vm.numa.common.VmNumaNodeStat;
+import com.redhat.thermostat.vm.numa.common.VmNumaStat;
+
+public class VmNumaStatParserTest {
+
+    private final VmNumaStatParser parser = new VmNumaStatParser();
+
+    @Test
+    public void testParseSingleNodeStat() throws ParseException {
+        final String input = "\n" +
+                "Per-node process memory usage (in MBs) for PID 16816 (java)\n" +
+                "                           Node 0           Total\n" +
+                "                  --------------- ---------------\n" +
+                "Huge                         0.00            0.00\n" +
+                "Heap                         0.05            0.05\n" +
+                "Stack                        6.27            6.27\n" +
+                "Private                    385.07          385.07\n" +
+                "----------------  --------------- ---------------\n" +
+                "Total                      391.39          391.39";
+
+        VmNumaStat stat = parser.parse(input);
+        VmNumaNodeStat[] stats = stat.getVmNodeStats();
+        assertTrue(stats.length == 1);
+        VmNumaNodeStat nodeStat = stats[0];
+        assertTrue(nodeStat.getNode() == 0);
+        assertTrue(nodeStat.getHugeMemory() == 0);
+        assertTrue(nodeStat.getHeapMemory() == 0.05d);
+        assertTrue(nodeStat.getStackMemory() == 6.27d);
+        assertTrue(nodeStat.getPrivateMemory() == 385.07d);
+    }
+
+    @Test
+    public void testParseMultipleNodeStat() throws ParseException {
+        final String input = "\n" +
+                "Per-node process memory usage (in MBs) for PID 3 (ksoftirqd/0)\n" +
+                "                           Node 0          Node 1           Total\n" +
+                "                  --------------- --------------- ---------------\n" +
+                "Huge                         0.00            0.00            0.00\n" +
+                "Heap                         0.00            0.00            0.00\n" +
+                "Stack                        4.00            1.00            5.00\n" +
+                "Private                      5.00            2.00            7.00\n" +
+                "----------------  --------------- --------------- ---------------\n" +
+                "Total                        9.00            3.00           12.00";
+
+        VmNumaStat stat = parser.parse(input);
+        VmNumaNodeStat[] stats = stat.getVmNodeStats();
+
+        assertTrue(stats.length == 2);
+
+        VmNumaNodeStat nodeStat1 = stats[0];
+        assertTrue(nodeStat1.getNode() == 0);
+        assertTrue(nodeStat1.getHugeMemory() == 0d);
+        assertTrue(nodeStat1.getHeapMemory() == 0d);
+        assertTrue(nodeStat1.getStackMemory() == 4d);
+        assertTrue(nodeStat1.getPrivateMemory() == 5d);
+
+        VmNumaNodeStat nodeStat2 = stats[1];
+        assertTrue(nodeStat2.getNode() == 1);
+        assertTrue(nodeStat2.getHugeMemory() == 0d);
+        assertTrue(nodeStat2.getHeapMemory() == 0d);
+        assertTrue(nodeStat2.getStackMemory() == 1d);
+        assertTrue(nodeStat2.getPrivateMemory() == 2d);
+    }
+
+    @Test (expected = ParseException.class)
+    public void testParseEmptyString() throws ParseException {
+        String input = "";
+        VmNumaStat stat = parser.parse(input);
+    }
+
+    @Test (expected = ParseException.class)
+    public void testParseIncorrectMemoryData() throws ParseException {
+        final String input = "\n" +
+                "Per-node process memory usage (in MBs) for PID 3 (ksoftirqd/0)\n" +
+                "                           Node 0          Node 1           Total\n" +
+                "                  --------------- --------------- ---------------\n" +
+                "Huge                         ABCD            0.00            0.00\n" +
+                "Heap                         0.00            0.00            0.00\n" +
+                "Stack                        4.00            1.00            5.00\n" +
+                "Private                      5.00            2.00            7.00\n" +
+                "----------------  --------------- --------------- ---------------\n" +
+                "Total                        9.00            3.00           12.00";
+
+        parser.parse(input);
+    }
+
+    @Test (expected = ParseException.class)
+    public void testParseIncorrectMemory() throws ParseException {
+        final String input = "\n" +
+                "Per-node process memory usage (in MBs) for PID 3 (ksoftirqd/0)\n" +
+                "                           Node 0          Node 1           Total\n" +
+                "                  --------------- --------------- ---------------\n" +
+                "Huge\n" +
+                "Heap                         0.00            0.00            0.00\n" +
+                "Stack                        4.00            1.00            5.00\n" +
+                "Private                      5.00            2.00            7.00\n" +
+                "----------------  --------------- --------------- ---------------\n" +
+                "Total                        9.00            3.00           12.00";
+
+        parser.parse(input);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-numa/client-core/pom.xml	Thu Jun 09 08:58:57 2016 -0400
@@ -0,0 +1,122 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+ Copyright 2012-2016 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.
+
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <artifactId>thermostat-vm-numa</artifactId>
+    <groupId>com.redhat.thermostat</groupId>
+    <version>1.5.8-SNAPSHOT</version>
+  </parent>
+  <artifactId>thermostat-vm-numa-client-core</artifactId>
+  <packaging>bundle</packaging>
+  <name>Thermostat VM NUMA Core Client plugin</name>
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.felix</groupId>
+        <artifactId>maven-bundle-plugin</artifactId>
+        <extensions>true</extensions>
+        <configuration>
+          <instructions>
+            <Bundle-Vendor>Red Hat, Inc.</Bundle-Vendor>
+            <Bundle-SymbolicName>com.redhat.thermostat.vm.numa.client.core</Bundle-SymbolicName>
+            <Bundle-Activator>com.redhat.thermostat.vm.numa.client.core.internal.Activator</Bundle-Activator>
+            <Export-Package>
+              com.redhat.thermostat.vm.numa.client.core,
+              com.redhat.thermostat.vm.numa.client.core.locale
+            </Export-Package>
+            <Private-Package>
+              com.redhat.thermostat.vm.numa.client.core.internal
+            </Private-Package>
+            <!-- Do not autogenerate uses clauses in Manifests -->
+            <_nouses>true</_nouses>
+          </instructions>
+        </configuration>
+      </plugin>
+    </plugins>
+  </build>
+  <dependencies>
+    <dependency>
+      <groupId>junit</groupId>
+      <artifactId>junit</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>com.redhat.thermostat</groupId>
+      <artifactId>thermostat-common-test</artifactId>
+      <version>${project.version}</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.mockito</groupId>
+      <artifactId>mockito-core</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.osgi</groupId>
+      <artifactId>org.osgi.core</artifactId>
+      <scope>provided</scope>
+    </dependency>
+    <dependency>
+      <groupId>com.redhat.thermostat</groupId>
+      <artifactId>thermostat-common-core</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>com.redhat.thermostat</groupId>
+      <artifactId>thermostat-client-core</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>com.redhat.thermostat</groupId>
+      <artifactId>thermostat-numa-common</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>com.redhat.thermostat</groupId>
+      <artifactId>thermostat-vm-numa-common</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>com.redhat.thermostat</groupId>
+      <artifactId>thermostat-storage-core</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+  </dependencies>
+</project>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-numa/client-core/src/main/java/com/redhat/thermostat/vm/numa/client/core/VmNumaService.java	Thu Jun 09 08:58:57 2016 -0400
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2012-2016 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.vm.numa.client.core;
+
+import com.redhat.thermostat.client.core.InformationService;
+import com.redhat.thermostat.storage.core.VmRef;
+
+public interface VmNumaService extends InformationService<VmRef> {
+    public static final String SERVICE_ID = "com.redhat.thermostat.vm.numa";
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-numa/client-core/src/main/java/com/redhat/thermostat/vm/numa/client/core/VmNumaServiceImpl.java	Thu Jun 09 08:58:57 2016 -0400
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2012-2016 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.vm.numa.client.core;
+
+import com.redhat.thermostat.client.core.NameMatchingRefFilter;
+import com.redhat.thermostat.client.core.controllers.InformationServiceController;
+import com.redhat.thermostat.common.ApplicationService;
+import com.redhat.thermostat.common.Filter;
+import com.redhat.thermostat.numa.common.NumaDAO;
+import com.redhat.thermostat.storage.core.AgentId;
+import com.redhat.thermostat.storage.core.VmId;
+import com.redhat.thermostat.storage.core.VmRef;
+import com.redhat.thermostat.vm.numa.client.core.internal.VmNumaController;
+import com.redhat.thermostat.vm.numa.common.VmNumaDAO;
+
+public class VmNumaServiceImpl implements VmNumaService {
+
+    private static final int ORDER = ORDER_MEMORY_GROUP;
+    private static final Filter<VmRef> FILTER = new NameMatchingRefFilter<>();
+
+    private ApplicationService appSvc;
+    private VmNumaDAO vmNumaDAO;
+    private VmNumaViewProvider vmNumaViewProvider;
+    private NumaDAO numaDAO;
+
+    public VmNumaServiceImpl(ApplicationService applicationService, NumaDAO numaDAO, VmNumaDAO vmNumaDAO, VmNumaViewProvider vmNumaViewProvider) {
+        this.appSvc = applicationService;
+        this.vmNumaDAO = vmNumaDAO;
+        this.vmNumaViewProvider = vmNumaViewProvider;
+        this.numaDAO = numaDAO;
+    }
+
+    @Override
+    public Filter<VmRef> getFilter() {
+        return FILTER;
+    }
+
+    @Override
+    public InformationServiceController<VmRef> getInformationServiceController(VmRef ref) {
+        VmId vmId = new VmId(ref.getVmId());
+        AgentId agentId = new AgentId(ref.getHostRef().getAgentId());
+        return new VmNumaController(appSvc, numaDAO, vmNumaDAO, vmId, agentId, vmNumaViewProvider);
+    }
+
+    @Override
+    public int getOrderValue() {
+        return ORDER;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-numa/client-core/src/main/java/com/redhat/thermostat/vm/numa/client/core/VmNumaView.java	Thu Jun 09 08:58:57 2016 -0400
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2012-2016 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.vm.numa.client.core;
+
+import java.util.concurrent.TimeUnit;
+
+import com.redhat.thermostat.client.core.views.BasicView;
+import com.redhat.thermostat.client.core.views.UIComponent;
+import com.redhat.thermostat.common.ActionListener;
+import com.redhat.thermostat.common.Duration;
+import com.redhat.thermostat.storage.model.DiscreteTimeData;
+
+public abstract class VmNumaView extends BasicView implements UIComponent {
+
+    public enum UserAction {
+        USER_CHANGED_TIME_RANGE;
+    }
+
+    public abstract void addUserActionListener(ActionListener<UserAction> listener);
+
+    public abstract void removeUserActionListener(ActionListener<UserAction> listener);
+
+    public abstract Duration getUserDesiredDuration();
+
+    public abstract void setVisibleDataRange(Duration duration);
+
+    public abstract void addData(String seriesName, int nodeNumber, DiscreteTimeData<Double> data);
+
+    public abstract void addChart(int numNumaNodes, String name);
+
+    public abstract void showNumaUnavailable();
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-numa/client-core/src/main/java/com/redhat/thermostat/vm/numa/client/core/VmNumaViewProvider.java	Thu Jun 09 08:58:57 2016 -0400
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2012-2016 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.vm.numa.client.core;
+
+import com.redhat.thermostat.client.core.views.ViewProvider;
+
+public interface VmNumaViewProvider extends ViewProvider {
+    @Override
+    public VmNumaView createView();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-numa/client-core/src/main/java/com/redhat/thermostat/vm/numa/client/core/internal/Activator.java	Thu Jun 09 08:58:57 2016 -0400
@@ -0,0 +1,100 @@
+/*
+ * Copyright 2012-2016 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.vm.numa.client.core.internal;
+
+import java.util.Dictionary;
+import java.util.Hashtable;
+import java.util.Map;
+
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceRegistration;
+
+import com.redhat.thermostat.client.core.InformationService;
+import com.redhat.thermostat.common.ApplicationService;
+import com.redhat.thermostat.common.Constants;
+import com.redhat.thermostat.common.MultipleServiceTracker;
+import com.redhat.thermostat.numa.common.NumaDAO;
+import com.redhat.thermostat.storage.core.VmRef;
+import com.redhat.thermostat.vm.numa.client.core.VmNumaService;
+import com.redhat.thermostat.vm.numa.client.core.VmNumaServiceImpl;
+import com.redhat.thermostat.vm.numa.client.core.VmNumaViewProvider;
+import com.redhat.thermostat.vm.numa.common.VmNumaDAO;
+
+public class Activator implements BundleActivator {
+
+    private MultipleServiceTracker tracker;
+    private ServiceRegistration reg;
+
+    @Override
+    public void start(final BundleContext context) throws Exception {
+        Class<?>[] deps = new Class<?>[] {
+                VmNumaDAO.class,
+                ApplicationService.class,
+                VmNumaViewProvider.class,
+                NumaDAO.class,
+        };
+
+        tracker = new MultipleServiceTracker(context, deps, new MultipleServiceTracker.Action() {
+            @Override
+            public void dependenciesAvailable(Map<String, Object> services) {
+                VmNumaDAO vmNumaDAO = (VmNumaDAO) services.get(VmNumaDAO.class.getName());
+                ApplicationService applicationService = (ApplicationService) services.get(ApplicationService.class.getName());
+                VmNumaViewProvider vmNumaViewProvider = (VmNumaViewProvider) services.get(VmNumaViewProvider.class.getName());
+                NumaDAO numaDAO = (NumaDAO) services.get(NumaDAO.class.getName());
+
+                VmNumaServiceImpl vmNumaService = new VmNumaServiceImpl(applicationService, numaDAO, vmNumaDAO, vmNumaViewProvider);
+                Dictionary<String, String> properties = new Hashtable<>();
+                properties.put(Constants.GENERIC_SERVICE_CLASSNAME, VmRef.class.getName());
+                properties.put(InformationService.KEY_SERVICE_ID, VmNumaService.SERVICE_ID);
+                reg = context.registerService(InformationService.class.getName(), vmNumaService, properties);
+            }
+
+            @Override
+            public void dependenciesUnavailable() {
+                reg.unregister();
+            }
+        });
+
+        tracker.open();
+    }
+
+    @Override
+    public void stop(BundleContext context) throws Exception {
+        tracker.close();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-numa/client-core/src/main/java/com/redhat/thermostat/vm/numa/client/core/internal/VmNumaController.java	Thu Jun 09 08:58:57 2016 -0400
@@ -0,0 +1,179 @@
+/*
+ * Copyright 2012-2016 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.vm.numa.client.core.internal;
+
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+import com.redhat.thermostat.client.core.controllers.InformationServiceController;
+import com.redhat.thermostat.client.core.views.BasicView;
+import com.redhat.thermostat.client.core.views.UIComponent;
+import com.redhat.thermostat.common.ActionEvent;
+import com.redhat.thermostat.common.ActionListener;
+import com.redhat.thermostat.common.ApplicationService;
+import com.redhat.thermostat.common.Duration;
+import com.redhat.thermostat.common.Timer;
+import com.redhat.thermostat.numa.common.NumaDAO;
+import com.redhat.thermostat.shared.locale.LocalizedString;
+import com.redhat.thermostat.shared.locale.Translate;
+import com.redhat.thermostat.storage.core.AgentId;
+import com.redhat.thermostat.storage.core.HostRef;
+import com.redhat.thermostat.storage.core.VmId;
+import com.redhat.thermostat.storage.core.VmRef;
+import com.redhat.thermostat.storage.model.DiscreteTimeData;
+import com.redhat.thermostat.vm.numa.client.core.VmNumaView;
+import com.redhat.thermostat.vm.numa.client.core.VmNumaViewProvider;
+import com.redhat.thermostat.vm.numa.client.core.locale.LocaleResources;
+import com.redhat.thermostat.vm.numa.common.NumaMemoryLocations;
+import com.redhat.thermostat.vm.numa.common.VmNumaDAO;
+import com.redhat.thermostat.vm.numa.common.VmNumaNodeStat;
+import com.redhat.thermostat.vm.numa.common.VmNumaStat;
+
+public class VmNumaController implements InformationServiceController<VmRef> {
+
+    private static final Translate<LocaleResources> translator = LocaleResources.createLocalizer();
+
+    private VmNumaView view;
+
+    private VmId vmId;
+    private AgentId agentId;
+    private VmNumaDAO vmNumaDAO;
+
+    private long lastSeenTimestamp = Long.MIN_VALUE;
+
+    public VmNumaController(ApplicationService appSvc, NumaDAO numaDAO, VmNumaDAO vmNumaDAO, VmId vmId, AgentId agentId, VmNumaViewProvider vmNumaViewProvider) {
+        this.vmId = vmId;
+        this.agentId = agentId;
+
+        this.vmNumaDAO = vmNumaDAO;
+
+        int numNumaNodes = numaDAO.getNumberOfNumaNodes(new HostRef(agentId.get(), ""));
+
+        this.view = vmNumaViewProvider.createView();
+
+        if (numNumaNodes > 1) {
+            for (NumaMemoryLocations location : NumaMemoryLocations.values()) {
+                view.addChart(numNumaNodes, location.getName());
+            }
+
+            setupTimer(appSvc.getTimerFactory().createTimer());
+
+            view.addUserActionListener(new ActionListener<VmNumaView.UserAction>() {
+                @Override
+                public void actionPerformed(ActionEvent<VmNumaView.UserAction> actionEvent) {
+                    switch (actionEvent.getActionId()) {
+                        case USER_CHANGED_TIME_RANGE:
+                            Duration duration = view.getUserDesiredDuration();
+                            lastSeenTimestamp = System.currentTimeMillis() - duration.asMilliseconds();
+                            view.setVisibleDataRange(duration);
+                            break;
+                        default:
+                            throw new AssertionError("Unhandled action type: " + actionEvent.getActionId());
+                    }
+                }
+            });
+        } else {
+            view.showNumaUnavailable();
+        }
+    }
+
+    private void setupTimer(final Timer timer) {
+        timer.setSchedulingType(Timer.SchedulingType.FIXED_RATE);
+        timer.setTimeUnit(TimeUnit.SECONDS);
+        timer.setInitialDelay(0);
+        timer.setDelay(5);
+
+        timer.setAction(new Runnable() {
+            @Override
+            public void run() {
+                updateData();
+            }
+        });
+
+        view.addActionListener(new com.redhat.thermostat.common.ActionListener<VmNumaView.Action>() {
+            @Override
+            public void actionPerformed(com.redhat.thermostat.common.ActionEvent<BasicView.Action> actionEvent) {
+                switch (actionEvent.getActionId()) {
+                    case HIDDEN:
+                        timer.stop();
+                        break;
+                    case VISIBLE:
+                        timer.start();
+                        break;
+                    default:
+                        throw new AssertionError("Unhandled action type");
+                }
+            }
+        });
+    }
+
+    private void updateData() {
+        List<VmNumaStat> stats = vmNumaDAO.getNumaStats(agentId, vmId, lastSeenTimestamp, System.currentTimeMillis());
+
+        if (stats.size() > 0) {
+            lastSeenTimestamp = stats.get(stats.size() - 1).getTimeStamp();
+        }
+
+        for(VmNumaStat stat : stats) {
+            VmNumaNodeStat[] nodeStats = stat.getVmNodeStats();
+            for (VmNumaNodeStat nodeStat : nodeStats) {
+                int node = nodeStat.getNode();
+                long timestamp = stat.getTimeStamp();
+                view.addData(NumaMemoryLocations.HEAP.getName(), node,
+                        new DiscreteTimeData<>(timestamp, nodeStat.getHeapMemory()));
+                view.addData(NumaMemoryLocations.HUGE.getName(), node,
+                        new DiscreteTimeData<>(timestamp, nodeStat.getHugeMemory()));
+                view.addData(NumaMemoryLocations.PRIVATE.getName(), node,
+                        new DiscreteTimeData<>(timestamp, nodeStat.getPrivateMemory()));
+                view.addData(NumaMemoryLocations.STACK.getName(), node,
+                        new DiscreteTimeData<>(timestamp, nodeStat.getStackMemory()));
+            }
+        }
+    }
+
+    @Override
+    public UIComponent getView() {
+        return view;
+    }
+
+    @Override
+    public LocalizedString getLocalizedName() {
+        return translator.localize(LocaleResources.VM_NUMA_TITLE);
+    }
+
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-numa/client-core/src/main/java/com/redhat/thermostat/vm/numa/client/core/locale/LocaleResources.java	Thu Jun 09 08:58:57 2016 -0400
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2012-2016 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.vm.numa.client.core.locale;
+
+
+import com.redhat.thermostat.shared.locale.Translate;
+
+public enum LocaleResources {
+    VM_NUMA_TITLE,
+    VM_NUMA_TIME_LABEL,
+    VM_NUMA_MEMORY_LABEL,
+    VM_NUMA_UNAVAILABLE
+    ;
+
+    static final String RESOURCE_BUNDLE =
+            "com.redhat.thermostat.vm.numa.client.core.locale.strings";
+
+    public static Translate<LocaleResources> createLocalizer() {
+        return new Translate<>(RESOURCE_BUNDLE, LocaleResources.class);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-numa/client-core/src/main/resources/com/redhat/thermostat/vm/numa/client/core/locale/strings.properties	Thu Jun 09 08:58:57 2016 -0400
@@ -0,0 +1,4 @@
+VM_NUMA_TITLE = NUMA
+VM_NUMA_TIME_LABEL = Time
+VM_NUMA_MEMORY_LABEL = Memory (MB)
+VM_NUMA_UNAVAILABLE = NUMA is unavailable on this machine.
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-numa/client-core/src/test/java/com/redhat/thermostat/vm/numa/client/core/VmNumaServiceImplTest.java	Thu Jun 09 08:58:57 2016 -0400
@@ -0,0 +1,99 @@
+/*
+ * Copyright 2012-2016 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.vm.numa.client.core;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import com.redhat.thermostat.common.ApplicationService;
+import com.redhat.thermostat.common.Timer;
+import com.redhat.thermostat.common.TimerFactory;
+import com.redhat.thermostat.numa.common.NumaDAO;
+import com.redhat.thermostat.storage.core.HostRef;
+import com.redhat.thermostat.storage.core.VmRef;
+import com.redhat.thermostat.vm.numa.client.core.internal.VmNumaController;
+import com.redhat.thermostat.vm.numa.common.VmNumaDAO;
+
+public class VmNumaServiceImplTest {
+
+    private VmNumaServiceImpl service;
+
+    @Before
+    public void setup() {
+        ApplicationService appSvc = mock(ApplicationService.class);
+        TimerFactory timerFactory = mock(TimerFactory.class);
+        Timer timer = mock(Timer.class);
+        when(timerFactory.createTimer()).thenReturn(timer);
+        when(appSvc.getTimerFactory()).thenReturn(timerFactory);
+
+        NumaDAO numaDAO = mock(NumaDAO.class);
+        VmNumaDAO vmNumaDAO = mock(VmNumaDAO.class);
+
+        VmNumaViewProvider numaViewProvider = mock(VmNumaViewProvider.class);
+        VmNumaView view = mock(VmNumaView.class);
+        when(numaViewProvider.createView()).thenReturn(view);
+
+        service = new VmNumaServiceImpl(appSvc, numaDAO, vmNumaDAO, numaViewProvider);
+    }
+
+    @Test
+    public void verifyMemoryGroup() {
+        assertEquals(200, service.getOrderValue());
+    }
+
+    @Test
+    public void verifyFilter() {
+        assertNotNull(service.getFilter());
+    }
+
+    @Test
+    public void verifyInformationServiceController() {
+        HostRef hostRef = mock(HostRef.class);
+        when(hostRef.getAgentId()).thenReturn("agentId");
+        VmRef vmRef = mock(VmRef.class);
+        when(vmRef.getVmId()).thenReturn("vmId");
+        when(vmRef.getHostRef()).thenReturn(hostRef);
+
+        assertTrue(service.getInformationServiceController(vmRef) instanceof VmNumaController);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-numa/client-core/src/test/java/com/redhat/thermostat/vm/numa/client/core/VmNumaServiceTest.java	Thu Jun 09 08:58:57 2016 -0400
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2012-2016 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.vm.numa.client.core;
+
+import static org.junit.Assert.assertEquals;
+
+import org.junit.Test;
+
+public class VmNumaServiceTest {
+
+    @Test
+    public void verifyServiceId() {
+        assertEquals("com.redhat.thermostat.vm.numa", VmNumaService.SERVICE_ID);
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-numa/client-core/src/test/java/com/redhat/thermostat/vm/numa/client/core/internal/ActivatorTest.java	Thu Jun 09 08:58:57 2016 -0400
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2012-2016 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.vm.numa.client.core.internal;
+
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
+
+import org.junit.Test;
+
+import com.redhat.thermostat.client.core.InformationService;
+import com.redhat.thermostat.common.ApplicationService;
+import com.redhat.thermostat.numa.common.NumaDAO;
+import com.redhat.thermostat.testutils.StubBundleContext;
+import com.redhat.thermostat.vm.numa.client.core.VmNumaServiceImpl;
+import com.redhat.thermostat.vm.numa.client.core.VmNumaViewProvider;
+import com.redhat.thermostat.vm.numa.common.VmNumaDAO;
+
+public class ActivatorTest {
+
+    @Test
+    public void verifyServiceRegistered() throws Exception {
+        StubBundleContext stub = new StubBundleContext();
+        stub.registerService(VmNumaDAO.class, mock(VmNumaDAO.class), null);
+        stub.registerService(ApplicationService.class, mock(ApplicationService.class), null);
+        stub.registerService(VmNumaViewProvider.class, mock(VmNumaViewProvider.class), null);
+        stub.registerService(NumaDAO.class, mock(NumaDAO.class), null);
+
+        Activator activator = new Activator();
+
+        activator.start(stub);
+
+        assertTrue(stub.isServiceRegistered(InformationService.class.getName(), VmNumaServiceImpl.class));
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-numa/client-core/src/test/java/com/redhat/thermostat/vm/numa/client/core/internal/VmNumaControllerTest.java	Thu Jun 09 08:58:57 2016 -0400
@@ -0,0 +1,212 @@
+/*
+ * Copyright 2012-2016 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.vm.numa.client.core.internal;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyLong;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Locale;
+import java.util.concurrent.TimeUnit;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+
+import com.redhat.thermostat.common.ActionEvent;
+import com.redhat.thermostat.common.ActionListener;
+import com.redhat.thermostat.common.ApplicationService;
+import com.redhat.thermostat.common.Timer;
+import com.redhat.thermostat.common.TimerFactory;
+import com.redhat.thermostat.numa.common.NumaDAO;
+import com.redhat.thermostat.storage.core.AgentId;
+import com.redhat.thermostat.storage.core.HostRef;
+import com.redhat.thermostat.storage.core.VmId;
+import com.redhat.thermostat.storage.model.DiscreteTimeData;
+import com.redhat.thermostat.vm.numa.client.core.VmNumaView;
+import com.redhat.thermostat.vm.numa.client.core.VmNumaViewProvider;
+import com.redhat.thermostat.vm.numa.common.NumaMemoryLocations;
+import com.redhat.thermostat.vm.numa.common.VmNumaDAO;
+import com.redhat.thermostat.vm.numa.common.VmNumaNodeStat;
+import com.redhat.thermostat.vm.numa.common.VmNumaStat;
+
+public class VmNumaControllerTest {
+
+    private VmNumaController vmNumaController;
+    private Timer timer;
+    private VmNumaView view;
+    @SuppressWarnings("rawtypes")
+    private ArgumentCaptor<ActionListener> actionListener;
+    private ArgumentCaptor<Runnable> timerAction;
+
+    private static final long TIMESTAMP = 1000;
+    private static final double DATA = 50;
+    private static final int NUM_NODES = 2;
+
+    @Before
+    public void setup() {
+        ApplicationService appSvc = mock(ApplicationService.class);
+        TimerFactory timerFactory = mock(TimerFactory.class);
+        timer = mock(Timer.class);
+        timerAction = ArgumentCaptor.forClass(Runnable.class);
+        doNothing().when(timer).setAction(timerAction.capture());
+        when(timerFactory.createTimer()).thenReturn(timer);
+        when(appSvc.getTimerFactory()).thenReturn(timerFactory);
+
+        AgentId agentId = new AgentId("agent");
+        VmId vmId = new VmId("vm");
+
+        NumaDAO numaDAO = mock(NumaDAO.class);
+        when(numaDAO.getNumberOfNumaNodes(any(HostRef.class))).thenReturn(NUM_NODES);
+        VmNumaDAO vmNumaDAO = mock(VmNumaDAO.class);
+        when(vmNumaDAO.getNumaStats(eq(agentId), eq(vmId), anyLong(), anyLong())).thenReturn(createStats());
+
+        VmNumaViewProvider viewProvider = mock(VmNumaViewProvider.class);
+        view = mock(VmNumaView.class);
+        when(viewProvider.createView()).thenReturn(view);
+
+        actionListener = ArgumentCaptor.forClass(ActionListener.class);
+        doNothing().when(view).addActionListener(actionListener.capture());
+
+        vmNumaController = new VmNumaController(appSvc, numaDAO, vmNumaDAO, vmId, agentId, viewProvider);
+    }
+
+    private List<VmNumaStat> createStats() {
+        List<VmNumaStat> stats = new ArrayList<>();
+        for (int i = 0; i < 5; i++) {
+            VmNumaStat stat = new VmNumaStat("agent");
+            stat.setTimeStamp(i * TIMESTAMP);
+            stat.setVmId("vm");
+
+            List<VmNumaNodeStat> nodeStats = new ArrayList<>();
+            for (int j = 0; j < NUM_NODES; j++) {
+                VmNumaNodeStat nodeStat = new VmNumaNodeStat();
+                nodeStat.setNode(j);
+                nodeStat.setHeapMemory(i * DATA);
+                nodeStat.setPrivateMemory(i * DATA);
+                nodeStat.setStackMemory(i * DATA);
+                nodeStat.setHugeMemory(i * DATA);
+                nodeStats.add(nodeStat);
+            }
+
+            stat.setVmNodeStats(nodeStats.toArray(new VmNumaNodeStat[0]));
+            stats.add(stat);
+        }
+
+        return stats;
+    }
+
+    @Test
+    public void verifyChartsAdded() {
+        verify(view).addChart(eq(NUM_NODES), eq("Huge"));
+        verify(view).addChart(eq(NUM_NODES), eq("Heap"));
+        verify(view).addChart(eq(NUM_NODES), eq("Stack"));
+        verify(view).addChart(eq(NUM_NODES), eq("Private"));
+    }
+
+    @Test
+    public void verifyTimerSettings() {
+        verify(timer).setAction(any(Runnable.class));
+        verify(timer).setSchedulingType(Timer.SchedulingType.FIXED_RATE);
+        verify(timer).setTimeUnit(TimeUnit.SECONDS);
+        verify(timer).setInitialDelay(0);
+        verify(timer).setDelay(5);
+        verifyNoMoreInteractions(timer);
+    }
+
+    @Test
+    @SuppressWarnings({ "rawtypes", "unchecked" })
+    public void verifyTimerAction() {
+        timerAction.getValue().run();
+        for (int i = 0; i < NUM_NODES; i++) {
+            for (NumaMemoryLocations v : NumaMemoryLocations.values()) {
+                ArgumentCaptor<DiscreteTimeData> dataCaptor = ArgumentCaptor.forClass(DiscreteTimeData.class);
+                verify(view, times(5)).addData(eq(v.getName()), eq(i), dataCaptor.capture());
+
+                verifyValues(dataCaptor.getAllValues());
+            }
+        }
+    }
+
+    private void verifyValues(List<DiscreteTimeData> allValues) {
+        assertEquals(5, allValues.size());
+        for (int i = 0; i < allValues.size(); i++) {
+            DiscreteTimeData data = allValues.get(i);
+            assertEquals(i * TIMESTAMP, data.getTimeInMillis());
+            assertEquals(i * DATA, data.getData());
+        }
+
+    }
+
+    @SuppressWarnings({ "unchecked", "rawtypes" })
+    @Test
+    public void verifyViewActions() {
+        actionListener.getValue().actionPerformed(new ActionEvent(view, VmNumaView.Action.VISIBLE));
+        verify(timer).start();
+
+        actionListener.getValue().actionPerformed(new ActionEvent(view, VmNumaView.Action.HIDDEN));
+        verify(timer).stop();
+    }
+
+    @Test
+    public void testView() {
+        assertSame(view, vmNumaController.getView());
+    }
+
+    @Test
+    public void testLocalizedName() {
+        Locale defaultLocale = Locale.getDefault();
+        try {
+            Locale.setDefault(Locale.US);
+            assertEquals("NUMA", vmNumaController.getLocalizedName().getContents());
+        } finally {
+            Locale.setDefault(defaultLocale);
+        }
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-numa/client-core/src/test/java/com/redhat/thermostat/vm/numa/client/core/locale/LocaleResourcesTest.java	Thu Jun 09 08:58:57 2016 -0400
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2012-2016 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.vm.numa.client.core.locale;
+
+import com.redhat.thermostat.testutils.AbstractLocaleResourcesTest;
+
+public class LocaleResourcesTest extends AbstractLocaleResourcesTest<LocaleResources> {
+
+    @Override
+    protected Class<LocaleResources> getEnumClass() {
+        return LocaleResources.class;
+    }
+
+    @Override
+    protected String getResourceBundle() {
+        return LocaleResources.RESOURCE_BUNDLE;
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-numa/client-swing/pom.xml	Thu Jun 09 08:58:57 2016 -0400
@@ -0,0 +1,126 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+ Copyright 2012-2016 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.
+
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <artifactId>thermostat-vm-numa</artifactId>
+    <groupId>com.redhat.thermostat</groupId>
+    <version>1.5.8-SNAPSHOT</version>
+  </parent>
+  <artifactId>thermostat-vm-numa-client-swing</artifactId>
+  <packaging>bundle</packaging>
+  <name>Thermostat VM NUMA Swing Client plugin</name>
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.felix</groupId>
+        <artifactId>maven-bundle-plugin</artifactId>
+        <extensions>true</extensions>
+        <configuration>
+          <instructions>
+            <Private-Package>com.redhat.thermostat.vm.numa.client.swing.internal</Private-Package>
+            <Bundle-Activator>com.redhat.thermostat.vm.numa.client.swing.internal.Activator</Bundle-Activator>
+            <Bundle-Vendor>Red Hat, Inc.</Bundle-Vendor>
+            <Bundle-SymbolicName>com.redhat.thermostat.vm.numa.client.swing</Bundle-SymbolicName>
+            <!-- Do not autogenerate uses clauses in Manifests -->
+            <_nouses>true</_nouses>
+          </instructions>
+        </configuration>
+      </plugin>
+    </plugins>
+  </build>
+  <dependencies>
+    <dependency>
+      <groupId>junit</groupId>
+      <artifactId>junit</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.mockito</groupId>
+      <artifactId>mockito-core</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>com.redhat.thermostat</groupId>
+      <artifactId>thermostat-common-test</artifactId>
+      <version>${project.version}</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.easytesting</groupId>
+      <artifactId>fest-swing</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>net.java.openjdk.cacio</groupId>
+      <artifactId>cacio-tta</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.osgi</groupId>
+      <artifactId>org.osgi.core</artifactId>
+      <scope>provided</scope>
+    </dependency>
+    <dependency>
+      <groupId>com.redhat.thermostat</groupId>
+      <artifactId>thermostat-common-core</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>com.redhat.thermostat</groupId>
+      <artifactId>thermostat-vm-numa-client-core</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>com.redhat.thermostat</groupId>
+      <artifactId>thermostat-client-core</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>com.redhat.thermostat</groupId>
+      <artifactId>thermostat-client-swing</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>com.redhat.thermostat</groupId>
+      <artifactId>thermostat-storage-core</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+  </dependencies>
+</project>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-numa/client-swing/src/main/java/com/redhat/thermostat/vm/numa/client/swing/internal/Activator.java	Thu Jun 09 08:58:57 2016 -0400
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2012-2016 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.vm.numa.client.swing.internal;
+
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+
+import com.redhat.thermostat.vm.numa.client.core.VmNumaViewProvider;
+
+public class Activator implements BundleActivator {
+
+    @Override
+    public void start(BundleContext context) throws Exception {
+        VmNumaViewProvider viewProvider = new SwingVmNumaViewProvider();
+        // Unregistered on Activator.stop
+        context.registerService(VmNumaViewProvider.class.getName(), viewProvider, null);
+    }
+
+    @Override
+    public void stop(BundleContext context) throws Exception {
+
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-numa/client-swing/src/main/java/com/redhat/thermostat/vm/numa/client/swing/internal/SwingVmNumaViewProvider.java	Thu Jun 09 08:58:57 2016 -0400
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2012-2016 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.vm.numa.client.swing.internal;
+
+import com.redhat.thermostat.vm.numa.client.core.VmNumaView;
+import com.redhat.thermostat.vm.numa.client.core.VmNumaViewProvider;
+
+public class SwingVmNumaViewProvider implements VmNumaViewProvider {
+    @Override
+    public VmNumaView createView() {
+        return new VmNumaPanel();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-numa/client-swing/src/main/java/com/redhat/thermostat/vm/numa/client/swing/internal/VmNumaPanel.java	Thu Jun 09 08:58:57 2016 -0400
@@ -0,0 +1,207 @@
+/*
+ * Copyright 2012-2016 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.vm.numa.client.swing.internal;
+
+import java.awt.BorderLayout;
+import java.awt.Color;
+import java.awt.Component;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.SwingUtilities;
+
+import org.jfree.chart.ChartFactory;
+import org.jfree.chart.JFreeChart;
+import org.jfree.chart.axis.NumberAxis;
+import org.jfree.chart.plot.XYPlot;
+import org.jfree.data.RangeType;
+import org.jfree.data.time.FixedMillisecond;
+import org.jfree.data.time.RegularTimePeriod;
+import org.jfree.data.time.TimeSeries;
+import org.jfree.data.time.TimeSeriesCollection;
+
+import com.redhat.thermostat.client.swing.SwingComponent;
+import com.redhat.thermostat.client.swing.components.HeaderPanel;
+import com.redhat.thermostat.client.swing.components.experimental.ThermostatChartPanel;
+import com.redhat.thermostat.client.swing.experimental.ComponentVisibilityNotifier;
+import com.redhat.thermostat.common.ActionListener;
+import com.redhat.thermostat.common.ActionNotifier;
+import com.redhat.thermostat.common.Duration;
+import com.redhat.thermostat.shared.locale.Translate;
+import com.redhat.thermostat.storage.model.DiscreteTimeData;
+import com.redhat.thermostat.vm.numa.client.core.VmNumaView;
+import com.redhat.thermostat.vm.numa.client.core.locale.LocaleResources;
+
+public class VmNumaPanel extends VmNumaView implements SwingComponent {
+
+    private static final Translate<LocaleResources> translator = LocaleResources.createLocalizer();
+
+    private HeaderPanel visiblePanel;
+
+    private static final int DEFAULT_VALUE = 10;
+    private static final TimeUnit DEFAULT_UNIT = TimeUnit.MINUTES;
+
+    private Duration duration;
+
+    private ActionNotifier<UserAction> userActionNotifier = new ActionNotifier<>(this);
+
+    private final Map<String, TimeSeriesCollection> datasets = new HashMap<>();
+
+    private ThermostatChartPanel chartPanel;
+
+    public VmNumaPanel() {
+        super();
+        initializePanel();
+
+        new ComponentVisibilityNotifier().initialize(visiblePanel, notifier);
+    }
+
+    private void initializePanel() {
+        visiblePanel = new HeaderPanel();
+        visiblePanel.setHeader(translator.localize(LocaleResources.VM_NUMA_TITLE));
+
+        JPanel p = initializeChartsPanel();
+        visiblePanel.setContent(p);
+    }
+
+    private JPanel initializeChartsPanel() {
+        duration = new Duration(DEFAULT_VALUE, DEFAULT_UNIT);
+        chartPanel = new ThermostatChartPanel(duration);
+
+        chartPanel.addPropertyChangeListener(ThermostatChartPanel.PROPERTY_VISIBLE_TIME_RANGE, new PropertyChangeListener() {
+            @Override
+            public void propertyChange(PropertyChangeEvent evt) {
+                duration = (Duration) evt.getNewValue();
+                userActionNotifier.fireAction(UserAction.USER_CHANGED_TIME_RANGE);
+            }
+        });
+
+        return chartPanel;
+    }
+
+    private JFreeChart initializeChart(String title, TimeSeriesCollection collection) {
+        JFreeChart chart = ChartFactory.createTimeSeriesChart(
+                title,
+                translator.localize(LocaleResources.VM_NUMA_TIME_LABEL).getContents(),
+                translator.localize(LocaleResources.VM_NUMA_MEMORY_LABEL).getContents(),
+                collection,
+                true, false, false);
+
+        NumberAxis rangeAxis = (NumberAxis) chart.getXYPlot().getRangeAxis();
+        rangeAxis.setLowerBound(0.0);
+        rangeAxis.setRangeType(RangeType.POSITIVE);
+
+        chart.getXYPlot().getRangeAxis().setAutoRange(true);
+
+        chart.getXYPlot().getDomainAxis().setAutoRange(true);
+
+        XYPlot plot = (XYPlot) chart.getPlot();
+        plot.setBackgroundPaint(Color.WHITE);
+        chart.getPlot().setBackgroundImageAlpha(0.0f);
+        return chart;
+    }
+
+    @Override
+    public void addChart(int numNumaNodes, String name) {
+        TimeSeriesCollection collection = new TimeSeriesCollection();
+        datasets.put(name, collection);
+
+        for (int i = 0; i < numNumaNodes; i++) {
+            TimeSeries series = new TimeSeries("Node: " + i);
+            collection.addSeries(series);
+        }
+        JFreeChart chart = initializeChart(name, collection);
+        chartPanel.addChart(chart);
+    }
+
+    @Override
+    public void showNumaUnavailable() {
+        JPanel statusPanel = new JPanel(new BorderLayout());
+
+        String wrappedText = "<html>" + translator.localize(LocaleResources.VM_NUMA_UNAVAILABLE).getContents() + "</html>";
+        JLabel descriptionLabel = new JLabel(wrappedText);
+        statusPanel.add(descriptionLabel, BorderLayout.PAGE_START);
+
+        visiblePanel.setContent(statusPanel);
+    }
+
+    @Override
+    public Component getUiComponent() {
+        return visiblePanel;
+    }
+
+    @Override
+    public void addUserActionListener(ActionListener<UserAction> listener) {
+        userActionNotifier.addActionListener(listener);
+    }
+
+    @Override
+    public void removeUserActionListener(ActionListener<UserAction> listener) {
+        userActionNotifier.removeActionListener(listener);
+    }
+
+    @Override
+    public Duration getUserDesiredDuration() {
+        return duration;
+    }
+
+    @Override
+    public void setVisibleDataRange(Duration duration) {
+        chartPanel.setTimeRangeToShow(duration);
+    }
+
+    @Override
+    public void addData(final String seriesName, final int nodeNumber, final DiscreteTimeData<Double> data) {
+        SwingUtilities.invokeLater(new Runnable() {
+            @Override
+            public void run() {
+                TimeSeriesCollection collection = datasets.get(seriesName);
+                TimeSeries dataset = collection.getSeries("Node: " + nodeNumber);
+                RegularTimePeriod period = new FixedMillisecond(data.getTimeInMillis());
+                if (dataset.getDataItem(period) == null) {
+                    dataset.add(period, data.getData(), false);
+                }
+                dataset.fireSeriesChanged();
+            }
+        });
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-numa/client-swing/src/test/java/com/redhat/thermostat/vm/numa/client/swing/internal/ActivatorTest.java	Thu Jun 09 08:58:57 2016 -0400
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2012-2016 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.vm.numa.client.swing.internal;
+
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import com.redhat.thermostat.testutils.StubBundleContext;
+import com.redhat.thermostat.vm.numa.client.core.VmNumaViewProvider;
+
+public class ActivatorTest {
+    @Test
+    public void verifyServiceRegistered() throws Exception {
+        StubBundleContext context = new StubBundleContext();
+
+        Activator activator = new Activator();
+
+        activator.start(context);
+
+        assertTrue(context.isServiceRegistered(VmNumaViewProvider.class.getName(), SwingVmNumaViewProvider.class));
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-numa/client-swing/src/test/java/com/redhat/thermostat/vm/numa/client/swing/internal/VmNumaPanelTest.java	Thu Jun 09 08:58:57 2016 -0400
@@ -0,0 +1,71 @@
+/*
+ * Copyright 2012-2016 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.vm.numa.client.swing.internal;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+import java.awt.Container;
+
+import org.fest.swing.fixture.Containers;
+import org.fest.swing.fixture.FrameFixture;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runner.RunWith;
+
+import com.redhat.thermostat.annotations.internal.CacioTest;
+import com.redhat.thermostat.client.core.views.BasicView;
+import com.redhat.thermostat.common.ActionEvent;
+import com.redhat.thermostat.common.ActionListener;
+
+import net.java.openjdk.cacio.ctc.junit.CacioFESTRunner;
+
+@Category(CacioTest.class)
+@RunWith(CacioFESTRunner.class)
+public class VmNumaPanelTest {
+    @Test
+    public void testActionListener() {
+        VmNumaPanel numaPanel = new VmNumaPanel();
+        ActionListener listener = mock(ActionListener.class);
+        numaPanel.addActionListener(listener);
+        FrameFixture frameFixture = Containers.frameFixtureFor((Container) numaPanel.getUiComponent());
+        frameFixture.show();
+        verify(listener).actionPerformed(new ActionEvent(numaPanel, BasicView.Action.VISIBLE));
+        frameFixture.close();
+        verify(listener).actionPerformed(new ActionEvent(numaPanel, BasicView.Action.VISIBLE));
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-numa/common/pom.xml	Thu Jun 09 08:58:57 2016 -0400
@@ -0,0 +1,117 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+ Copyright 2012-2016 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.
+
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <artifactId>thermostat-vm-numa</artifactId>
+    <groupId>com.redhat.thermostat</groupId>
+    <version>1.5.8-SNAPSHOT</version>
+  </parent>
+  <artifactId>thermostat-vm-numa-common</artifactId>
+  <packaging>bundle</packaging>
+  <name>Thermostat VM NUMA Common plugin</name>
+  <build>
+    <resources>
+      <resource>
+        <directory>src/main/resources</directory>
+        <filtering>true</filtering>
+        <excludes>
+          <exclude>**/*.png</exclude>
+        </excludes>
+      </resource>
+      <resource>
+        <directory>src/main/resources</directory>
+        <filtering>false</filtering>
+        <includes>
+          <include>**/*.png</include>
+        </includes>
+      </resource>
+    </resources>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.felix</groupId>
+        <artifactId>maven-bundle-plugin</artifactId>
+        <extensions>true</extensions>
+        <configuration>
+          <instructions>
+            <Bundle-Vendor>Red Hat, Inc.</Bundle-Vendor>
+            <Bundle-SymbolicName>com.redhat.thermostat.vm.numa.common</Bundle-SymbolicName>
+            <Bundle-Activator>com.redhat.thermostat.vm.numa.common.internal.Activator</Bundle-Activator>
+            <Export-Package>
+              com.redhat.thermostat.vm.numa.common
+            </Export-Package>
+            <Private-Package>
+              com.redhat.thermostat.vm.numa.common.internal
+            </Private-Package>
+            <!-- Do not autogenerate uses clauses in Manifests -->
+            <_nouses>true</_nouses>
+          </instructions>
+        </configuration>
+      </plugin>
+    </plugins>
+  </build>
+  <dependencies>
+    <dependency>
+      <groupId>junit</groupId>
+      <artifactId>junit</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.mockito</groupId>
+      <artifactId>mockito-core</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>com.redhat.thermostat</groupId>
+      <artifactId>thermostat-common-test</artifactId>
+      <version>${project.version}</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.osgi</groupId>
+      <artifactId>org.osgi.core</artifactId>
+      <scope>provided</scope>
+    </dependency>
+    <dependency>
+      <groupId>com.redhat.thermostat</groupId>
+      <artifactId>thermostat-storage-core</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+  </dependencies>
+</project>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-numa/common/src/main/java/com/redhat/thermostat/vm/numa/common/NumaMemoryLocations.java	Thu Jun 09 08:58:57 2016 -0400
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2012-2016 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.vm.numa.common;
+
+public enum NumaMemoryLocations {
+    HUGE("Huge"),
+    HEAP("Heap"),
+    STACK("Stack"),
+    PRIVATE("Private"),
+    ;
+
+    private String name;
+
+    private NumaMemoryLocations(String name) {
+        this.name = name;
+    }
+
+    public String getName() {
+        return this.name;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-numa/common/src/main/java/com/redhat/thermostat/vm/numa/common/VmNumaDAO.java	Thu Jun 09 08:58:57 2016 -0400
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2012-2016 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.vm.numa.common;
+
+import java.util.Arrays;
+import java.util.List;
+
+import com.redhat.thermostat.annotations.Service;
+import com.redhat.thermostat.storage.core.AgentId;
+import com.redhat.thermostat.storage.core.Category;
+import com.redhat.thermostat.storage.core.Key;
+import com.redhat.thermostat.storage.core.VmId;
+
+@Service
+public interface VmNumaDAO {
+
+    static final Key<VmNumaNodeStat[]> vmNodeStats = new Key<>("vmNodeStats");
+
+    static final Category<VmNumaStat> vmNumaStatCategory = new Category<>("vm-numa-stats", VmNumaStat.class,
+            Arrays.<Key<?>>asList(Key.AGENT_ID, Key.VM_ID, Key.TIMESTAMP, vmNodeStats),
+            Arrays.<Key<?>>asList(Key.TIMESTAMP));
+
+    void putVmNumaStat(VmNumaStat stat);
+
+    List<VmNumaStat> getNumaStats(AgentId agentId, VmId vmId, long since, long to);
+
+    VmNumaStat getNewest(AgentId agentId, VmId vmId);
+
+    VmNumaStat getOldest(AgentId agentId, VmId id);
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-numa/common/src/main/java/com/redhat/thermostat/vm/numa/common/VmNumaNodeStat.java	Thu Jun 09 08:58:57 2016 -0400
@@ -0,0 +1,101 @@
+/*
+ * Copyright 2012-2016 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.vm.numa.common;
+
+import com.redhat.thermostat.storage.core.Entity;
+import com.redhat.thermostat.storage.core.Persist;
+import com.redhat.thermostat.storage.model.Pojo;
+
+@Entity
+public class VmNumaNodeStat implements Pojo {
+
+    private int node;
+    private double hugeMemory;
+    private double heapMemory;
+    private double stackMemory;
+    private double privateMemory;
+
+    @Persist
+    public int getNode() {
+        return node;
+    }
+
+    @Persist
+    public void setNode(int node) {
+        this.node = node;
+    }
+
+    @Persist
+    public double getHugeMemory() {
+        return hugeMemory;
+    }
+
+    @Persist
+    public void setHugeMemory(double hugeMemory) {
+        this.hugeMemory = hugeMemory;
+    }
+
+    @Persist
+    public double getHeapMemory() {
+        return heapMemory;
+    }
+
+    @Persist
+    public void setHeapMemory(double heapMemory) {
+        this.heapMemory = heapMemory;
+    }
+
+    @Persist
+    public double getStackMemory() {
+        return stackMemory;
+    }
+
+    @Persist
+    public void setStackMemory(double stackMemory) {
+        this.stackMemory = stackMemory;
+    }
+
+    @Persist
+    public double getPrivateMemory() {
+        return privateMemory;
+    }
+
+    @Persist
+    public void setPrivateMemory(double privateMemory) {
+        this.privateMemory = privateMemory;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-numa/common/src/main/java/com/redhat/thermostat/vm/numa/common/VmNumaStat.java	Thu Jun 09 08:58:57 2016 -0400
@@ -0,0 +1,91 @@
+/*
+ * Copyright 2012-2016 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.vm.numa.common;
+
+import com.redhat.thermostat.storage.core.Entity;
+import com.redhat.thermostat.storage.core.Persist;
+import com.redhat.thermostat.storage.model.BasePojo;
+import com.redhat.thermostat.storage.model.TimeStampedPojo;
+
+@Entity
+public class VmNumaStat extends BasePojo implements TimeStampedPojo {
+
+    private long timeStamp = -1;
+
+    private String vmId;
+    private VmNumaNodeStat[] vmNodeStats = new VmNumaNodeStat[0];
+
+    public VmNumaStat() {
+        this(null);
+    }
+
+    public VmNumaStat(String writerId) {
+        super(writerId);
+    }
+
+    @Override
+    @Persist
+    public long getTimeStamp() {
+        return timeStamp;
+    }
+
+    @Persist
+    public void setTimeStamp(long timeStamp) {
+        this.timeStamp = timeStamp;
+    }
+
+    @Persist
+    public String getVmId() {
+        return vmId;
+    }
+
+    @Persist
+    public void setVmId(String vmId) {
+        this.vmId = vmId;
+    }
+
+    @Persist
+    public VmNumaNodeStat[] getVmNodeStats() {
+        return this.vmNodeStats;
+    }
+
+    @Persist
+    public void setVmNodeStats(VmNumaNodeStat[] vmNodeStats) {
+        this.vmNodeStats = vmNodeStats;
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-numa/common/src/main/java/com/redhat/thermostat/vm/numa/common/internal/Activator.java	Thu Jun 09 08:58:57 2016 -0400
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2012-2016 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.vm.numa.common.internal;
+
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
+import org.osgi.framework.ServiceRegistration;
+import org.osgi.util.tracker.ServiceTracker;
+
+import com.redhat.thermostat.storage.core.Storage;
+import com.redhat.thermostat.vm.numa.common.VmNumaDAO;
+
+public class Activator implements BundleActivator {
+
+    private ServiceTracker tracker;
+    private ServiceRegistration reg;
+
+    @Override
+    public void start(BundleContext context) throws Exception {
+        tracker = new ServiceTracker(context, Storage.class.getName(), null) {
+            @Override
+            public Object addingService(ServiceReference reference) {
+                Storage storage = (Storage) context.getService(reference);
+                VmNumaDAO vmNumaDAO = new VmNumaDAOImpl(storage);
+                reg = context.registerService(VmNumaDAO.class.getName(), vmNumaDAO, null);
+                return super.addingService(reference);
+            }
+
+            @Override
+            public void removedService(ServiceReference reference,
+                                       Object service) {
+                reg.unregister();
+                super.removedService(reference, service);
+            }
+        };
+        tracker.open();
+    }
+
+    @Override
+    public void stop(BundleContext context) throws Exception {
+        tracker.close();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-numa/common/src/main/java/com/redhat/thermostat/vm/numa/common/internal/VmNumaCategoryRegistration.java	Thu Jun 09 08:58:57 2016 -0400
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2012-2016 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.vm.numa.common.internal;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import com.redhat.thermostat.storage.core.auth.CategoryRegistration;
+import com.redhat.thermostat.vm.numa.common.VmNumaDAO;
+
+public class VmNumaCategoryRegistration implements CategoryRegistration {
+    @Override
+    public Set<String> getCategoryNames() {
+        Set<String> categories = new HashSet<>(1);
+        categories.add(VmNumaDAO.vmNumaStatCategory.getName());
+        return categories;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-numa/common/src/main/java/com/redhat/thermostat/vm/numa/common/internal/VmNumaDAOImpl.java	Thu Jun 09 08:58:57 2016 -0400
@@ -0,0 +1,117 @@
+/*
+ * Copyright 2012-2016 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.vm.numa.common.internal;
+
+import java.util.List;
+import java.util.logging.Logger;
+
+import com.redhat.thermostat.common.utils.LoggingUtils;
+import com.redhat.thermostat.storage.core.AgentId;
+import com.redhat.thermostat.storage.core.HostRef;
+import com.redhat.thermostat.storage.core.Key;
+import com.redhat.thermostat.storage.core.PreparedStatement;
+import com.redhat.thermostat.storage.core.Storage;
+import com.redhat.thermostat.storage.core.VmBoundaryPojoGetter;
+import com.redhat.thermostat.storage.core.VmId;
+import com.redhat.thermostat.storage.core.VmRef;
+import com.redhat.thermostat.storage.core.VmTimeIntervalPojoListGetter;
+import com.redhat.thermostat.storage.dao.AbstractDao;
+import com.redhat.thermostat.storage.dao.AbstractDaoStatement;
+import com.redhat.thermostat.vm.numa.common.VmNumaDAO;
+import com.redhat.thermostat.vm.numa.common.VmNumaStat;
+
+public class VmNumaDAOImpl extends AbstractDao implements VmNumaDAO {
+
+    private static final Logger logger = LoggingUtils.getLogger(VmNumaDAOImpl.class);
+
+    // ADD vm-numa-stats SET 'agentId' = ?s , \
+    //                      'vmId' = ?s , \
+    //                      'timeStamp' = ?l , \
+    //                      'vm-nodestats' = ?p[
+    static final String DESC_ADD_VM_NUMA_STAT = "ADD " + vmNumaStatCategory.getName() +
+            " SET '" + Key.AGENT_ID.getName() + "' = ?s , " +
+            "'" + Key.VM_ID.getName() + "' = ?s , " +
+            "'" + Key.TIMESTAMP.getName() + "' = ?l , " +
+            "'" + vmNodeStats.getName() + "' = ?p[";
+
+    private final Storage storage;
+
+    private final VmTimeIntervalPojoListGetter<VmNumaStat> intervalGetter;
+    private final VmBoundaryPojoGetter<VmNumaStat> boundaryGetter;
+
+    VmNumaDAOImpl(Storage storage) {
+        this.storage = storage;
+        storage.registerCategory(vmNumaStatCategory);
+
+        this.intervalGetter = new VmTimeIntervalPojoListGetter<>(storage, vmNumaStatCategory);
+        this.boundaryGetter = new VmBoundaryPojoGetter<>(storage, vmNumaStatCategory);
+    }
+
+    @Override
+    public void putVmNumaStat(final VmNumaStat stat) {
+        executeStatement(new AbstractDaoStatement<VmNumaStat>(storage, vmNumaStatCategory, DESC_ADD_VM_NUMA_STAT) {
+            @Override
+            public PreparedStatement<VmNumaStat> customize(PreparedStatement<VmNumaStat> preparedStatement) {
+                preparedStatement.setString(0, stat.getAgentId());
+                preparedStatement.setString(1, stat.getVmId());
+                preparedStatement.setLong(2, stat.getTimeStamp());
+                preparedStatement.setPojoList(3, stat.getVmNodeStats());
+                return preparedStatement;
+            }
+        });
+    }
+
+    @Override
+    public List<VmNumaStat> getNumaStats(AgentId agentId, VmId vmId, long since, long to) {
+        return intervalGetter.getLatest(new VmRef(new HostRef(agentId.get(), ""), vmId.get(), -1, ""), since, to);
+    }
+
+    @Override
+    public VmNumaStat getNewest(AgentId agentId, VmId vmId) {
+        return boundaryGetter.getOldestStat(new VmRef(new HostRef(agentId.get(), ""), "", -1, ""));
+    }
+
+    @Override
+    public VmNumaStat getOldest(AgentId agentId, VmId id) {
+        return boundaryGetter.getOldestStat(new VmRef(new HostRef(agentId.get(), ""), "", -1, ""));
+    }
+
+    @Override
+    protected Logger getLogger() {
+        return logger;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-numa/common/src/main/java/com/redhat/thermostat/vm/numa/common/internal/VmNumaDAOImplStatementDescriptorRegistration.java	Thu Jun 09 08:58:57 2016 -0400
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2012-2016 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.vm.numa.common.internal;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import com.redhat.thermostat.storage.core.VmBoundaryPojoGetter;
+import com.redhat.thermostat.storage.core.VmLatestPojoListGetter;
+import com.redhat.thermostat.storage.core.VmTimeIntervalPojoListGetter;
+import com.redhat.thermostat.storage.core.auth.StatementDescriptorRegistration;
+import com.redhat.thermostat.vm.numa.common.VmNumaDAO;
+
+public class VmNumaDAOImplStatementDescriptorRegistration implements StatementDescriptorRegistration{
+    static final String latestDescriptor = String.format(VmLatestPojoListGetter.VM_LATEST_QUERY_FORMAT,
+            VmNumaDAO.vmNumaStatCategory.getName());
+    static final String rangeDescriptor = String.format(VmTimeIntervalPojoListGetter.VM_INTERVAL_QUERY_FORMAT,
+            VmNumaDAO.vmNumaStatCategory.getName());
+    static final String latestStatDescriptor = String.format(VmBoundaryPojoGetter.DESC_NEWEST_VM_STAT,
+            VmNumaDAO.vmNumaStatCategory.getName());
+    static final String oldestStatDescriptor = String.format(VmBoundaryPojoGetter.DESC_OLDEST_VM_STAT,
+            VmNumaDAO.vmNumaStatCategory.getName());
+
+
+    @Override
+    public Set<String> getStatementDescriptors() {
+        Set<String> descs = new HashSet<>();
+        descs.add(VmNumaDAOImpl.DESC_ADD_VM_NUMA_STAT);
+        descs.add(latestStatDescriptor);
+        descs.add(oldestStatDescriptor);
+        descs.add(latestDescriptor);
+        descs.add(rangeDescriptor);
+        return descs;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-numa/common/src/main/resources/META-INF/services/com.redhat.thermostat.storage.core.auth.CategoryRegistration	Thu Jun 09 08:58:57 2016 -0400
@@ -0,0 +1,1 @@
+com.redhat.thermostat.vm.numa.common.internal.VmNumaCategoryRegistration
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-numa/common/src/main/resources/META-INF/services/com.redhat.thermostat.storage.core.auth.StatementDescriptorRegistration	Thu Jun 09 08:58:57 2016 -0400
@@ -0,0 +1,1 @@
+com.redhat.thermostat.vm.numa.common.internal.VmNumaDAOImplStatementDescriptorRegistration
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-numa/common/src/test/java/com/redhat/thermostat/vm/numa/common/VmNumaDAOTest.java	Thu Jun 09 08:58:57 2016 -0400
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2012-2016 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.vm.numa.common;
+
+import static org.junit.Assert.assertEquals;
+
+import org.junit.Test;
+
+import com.redhat.thermostat.storage.core.Category;
+
+public class VmNumaDAOTest {
+
+    @Test
+    public void testCategory() {
+        Category<?> cat = VmNumaDAO.vmNumaStatCategory;
+        assertEquals("vm-numa-stats", cat.getName());
+        assertEquals(VmNumaStat.class, cat.getDataClass());
+        assertEquals(4, cat.getKeys().size());
+
+        assertEquals("agentId", cat.getKey("agentId").getName());
+        assertEquals("vmId", cat.getKey("vmId").getName());
+        assertEquals("timeStamp", cat.getKey("timeStamp").getName());
+        assertEquals("vmNodeStats", cat.getKey("vmNodeStats").getName());
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-numa/common/src/test/java/com/redhat/thermostat/vm/numa/common/VmNumaNodeStatTest.java	Thu Jun 09 08:58:57 2016 -0400
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2012-2016 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.vm.numa.common;
+
+import static org.junit.Assert.fail;
+
+import org.junit.Test;
+
+import com.redhat.thermostat.testutils.DataObjectTest;
+
+public class VmNumaNodeStatTest extends DataObjectTest {
+
+    @Override
+    public Class<?>[] getDataClasses() {
+        return new Class[] { VmNumaNodeStat.class };
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-numa/common/src/test/java/com/redhat/thermostat/vm/numa/common/VmNumaStatTest.java	Thu Jun 09 08:58:57 2016 -0400
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2012-2016 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.vm.numa.common;
+
+import com.redhat.thermostat.testutils.DataObjectTest;
+
+public class VmNumaStatTest extends DataObjectTest {
+
+    @Override
+    public Class<?>[] getDataClasses() {
+        return new Class[] { VmNumaStat.class };
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-numa/common/src/test/java/com/redhat/thermostat/vm/numa/common/internal/ActivatorTest.java	Thu Jun 09 08:58:57 2016 -0400
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2012-2016 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.vm.numa.common.internal;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
+
+import org.junit.Test;
+
+import com.redhat.thermostat.storage.core.Storage;
+import com.redhat.thermostat.testutils.StubBundleContext;
+import com.redhat.thermostat.vm.numa.common.VmNumaDAO;
+
+public class ActivatorTest {
+    @Test
+    public void verifyActivatorDoesNotRegisterServiceOnMissingDeps() throws Exception {
+        StubBundleContext context = new StubBundleContext();
+
+        Activator activator = new Activator();
+
+        activator.start(context);
+
+        assertEquals(0, context.getAllServices().size());
+        assertEquals(1, context.getServiceListeners().size());
+
+        activator.stop(context);
+    }
+
+    @Test
+    public void verifyActivatorRegistersServices() throws Exception {
+        StubBundleContext context = new StubBundleContext();
+        Storage storage = mock(Storage.class);
+
+        context.registerService(Storage.class, storage, null);
+
+        Activator activator = new Activator();
+
+        activator.start(context);
+
+        assertTrue(context.isServiceRegistered(VmNumaDAO.class.getName(), VmNumaDAOImpl.class));
+
+        activator.stop(context);
+
+        assertEquals(0, context.getServiceListeners().size());
+        assertEquals(1, context.getAllServices().size());
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-numa/common/src/test/java/com/redhat/thermostat/vm/numa/common/internal/VmNumaCategoryRegistrationTest.java	Thu Jun 09 08:58:57 2016 -0400
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2012-2016 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.vm.numa.common.internal;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.util.Set;
+
+import org.junit.Test;
+
+import com.redhat.thermostat.storage.core.auth.CategoryRegistration;
+import com.redhat.thermostat.testutils.ServiceLoaderTest;
+import com.redhat.thermostat.vm.numa.common.VmNumaDAO;
+
+public class VmNumaCategoryRegistrationTest extends ServiceLoaderTest<CategoryRegistration> {
+
+    public VmNumaCategoryRegistrationTest() {
+        super(CategoryRegistration.class, STORAGE_SERVICES, VmNumaCategoryRegistration.class);
+    }
+
+    @Test
+    public void registersAllCategories() {
+        VmNumaCategoryRegistration reg = new VmNumaCategoryRegistration();
+        Set<String> categories = reg.getCategoryNames();
+        assertEquals(1, categories.size());
+        assertFalse("null descriptor not allowed", categories.contains(null));
+        assertTrue(categories.contains(VmNumaDAO.vmNumaStatCategory.getName()));
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-numa/common/src/test/java/com/redhat/thermostat/vm/numa/common/internal/VmNumaDAOImplStatementDescriptorRegistrationTest.java	Thu Jun 09 08:58:57 2016 -0400
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2012-2016 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.vm.numa.common.internal;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+
+import java.util.Set;
+
+import org.junit.Test;
+
+import com.redhat.thermostat.storage.core.auth.StatementDescriptorRegistration;
+import com.redhat.thermostat.testutils.ServiceLoaderTest;
+
+public class VmNumaDAOImplStatementDescriptorRegistrationTest extends ServiceLoaderTest<StatementDescriptorRegistration> {
+
+    public VmNumaDAOImplStatementDescriptorRegistrationTest() {
+        super(StatementDescriptorRegistration.class, STORAGE_SERVICES, VmNumaDAOImplStatementDescriptorRegistration.class);
+    }
+
+    @Test
+    public void registersAllDescriptors() {
+        VmNumaDAOImplStatementDescriptorRegistration reg = new VmNumaDAOImplStatementDescriptorRegistration();
+        Set<String> descriptors = reg.getStatementDescriptors();
+        assertEquals(5, descriptors.size());
+        assertFalse("null descriptor not allowed", descriptors.contains(null));
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-numa/common/src/test/java/com/redhat/thermostat/vm/numa/common/internal/VmNumaDAOImplTest.java	Thu Jun 09 08:58:57 2016 -0400
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2012-2016 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.vm.numa.common.internal;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import com.redhat.thermostat.storage.core.Storage;
+import com.redhat.thermostat.vm.numa.common.VmNumaDAO;
+
+public class VmNumaDAOImplTest {
+    private VmNumaDAO vmNumaDAO;
+    private Storage storage;
+
+    @Before
+    public void setUp() {
+        storage = mock(Storage.class);
+        vmNumaDAO = new VmNumaDAOImpl(storage);
+    }
+
+    @Test
+    public void preparedQueryDescriptorsAreSane() {
+        String addNumaStat = "ADD vm-numa-stats SET 'agentId' = ?s , " +
+                "'vmId' = ?s , " +
+                "'timeStamp' = ?l , " +
+                "'vmNodeStats' = ?p[";
+        assertEquals(addNumaStat, VmNumaDAOImpl.DESC_ADD_VM_NUMA_STAT);
+    }
+
+    @Test
+    public void testRegisterCategory() {
+        verify(storage).registerCategory(VmNumaDAO.vmNumaStatCategory);
+    }
+
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-numa/distribution/pom.xml	Thu Jun 09 08:58:57 2016 -0400
@@ -0,0 +1,113 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+ Copyright 2012-2016 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.
+
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <groupId>com.redhat.thermostat</groupId>
+    <artifactId>thermostat-vm-numa</artifactId>
+    <version>1.5.8-SNAPSHOT</version>
+  </parent>
+
+  <artifactId>thermostat-vm-numa-distribution</artifactId>
+  <packaging>pom</packaging>
+
+  <name>Thermostat VM NUMA plugin distribution</name>
+  
+  <properties>
+    <thermostat.plugin>vm-numa</thermostat.plugin>
+  </properties>
+
+  <build>
+    <plugins>
+      <plugin>
+        <artifactId>maven-assembly-plugin</artifactId>
+        <dependencies>
+          <dependency>
+            <groupId>com.redhat.thermostat</groupId>
+            <artifactId>thermostat-assembly</artifactId>
+            <version>${project.version}</version>
+          </dependency>
+        </dependencies>
+        <configuration>
+          <descriptorRefs>
+            <descriptorRef>plugin-assembly</descriptorRef>
+          </descriptorRefs>
+          <appendAssemblyId>false</appendAssemblyId>
+        </configuration>
+        <executions>
+          <execution>
+            <id>assemble-plugin</id>
+            <phase>package</phase>
+            <goals>
+              <goal>single</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
+    </plugins>
+  </build>
+  
+  <!-- Explicitly list all plug-in artifacts, transitive dependencies
+       are not included in assembly. -->
+  <dependencies>
+    <dependency>
+      <groupId>com.redhat.thermostat</groupId>
+      <artifactId>thermostat-vm-numa-common</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>com.redhat.thermostat</groupId>
+      <artifactId>thermostat-vm-numa-client-core</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>com.redhat.thermostat</groupId>
+      <artifactId>thermostat-vm-numa-client-swing</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>com.redhat.thermostat</groupId>
+      <artifactId>thermostat-vm-numa-agent</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+  </dependencies>
+
+</project>
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-numa/distribution/thermostat-plugin.xml	Thu Jun 09 08:58:57 2016 -0400
@@ -0,0 +1,60 @@
+<?xml version="1.0"?>
+<!--
+
+ Copyright 2012-2016 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.
+
+-->
+<plugin xmlns="http://icedtea.classpath.org/thermostat/plugins/v1.0"
+        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+        xsi:schemaLocation="http://icedtea.classpath.org/thermostat/plugins/v1.0 thermostat-plugin.xsd">
+  <extensions>
+    <extension>
+      <name>gui</name>
+      <bundles>
+        <bundle><symbolic-name>com.redhat.thermostat.vm.numa.common</symbolic-name><version>${project.version}</version></bundle>
+        <bundle><symbolic-name>com.redhat.thermostat.vm.numa.client.core</symbolic-name><version>${project.version}</version></bundle>
+        <bundle><symbolic-name>com.redhat.thermostat.vm.numa.client.swing</symbolic-name><version>${project.version}</version></bundle>
+      </bundles>
+    </extension>
+    <extension>
+      <name>agent</name>
+      <bundles>
+        <bundle><symbolic-name>com.redhat.thermostat.vm.numa.agent</symbolic-name><version>${project.version}</version></bundle>
+        <bundle><symbolic-name>com.redhat.thermostat.vm.numa.common</symbolic-name><version>${project.version}</version></bundle>
+      </bundles>
+    </extension>
+  </extensions>
+</plugin>
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-numa/pom.xml	Thu Jun 09 08:58:57 2016 -0400
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+ Copyright 2012-2016 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.
+
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <groupId>com.redhat.thermostat</groupId>
+    <artifactId>thermostat</artifactId>
+    <version>1.5.8-SNAPSHOT</version>
+  </parent>
+
+  <artifactId>thermostat-vm-numa</artifactId>
+  <packaging>pom</packaging>
+
+  <name>Thermostat VM NUMA plugin</name>
+
+  <modules>
+    <module>agent</module>
+    <module>common</module>
+    <module>client-core</module>
+    <module>client-swing</module>
+    <module>distribution</module>
+  </modules>
+
+</project>
+
--- a/web/war/pom.xml	Tue Jun 07 11:55:47 2016 -0400
+++ b/web/war/pom.xml	Thu Jun 09 08:58:57 2016 -0400
@@ -176,6 +176,11 @@
       <artifactId>thermostat-vm-profiler-common</artifactId>
       <version>${project.version}</version>
     </dependency>
+    <dependency>
+      <groupId>com.redhat.thermostat</groupId>
+      <artifactId>thermostat-vm-numa-common</artifactId>
+      <version>${project.version}</version>
+    </dependency>
   </dependencies>
 
   <build>