You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cloudstack.apache.org by wi...@apache.org on 2014/09/29 01:11:16 UTC

[1/2] git commit: updated refs/heads/statscollector-graphite to 9ba50f3

Repository: cloudstack
Updated Branches:
  refs/heads/statscollector-graphite 621156ca5 -> 9ba50f30e (forced update)


CLOUDSTACK-7641: Do not always ask libvirt to refresh a storage pool

On larger (especially RBD) storage pools this can take a lot of
time slowing operations like creating volumes down.

The getStorageStats command will still ask a pool to be refreshed so
that the management server has accurate information about the storage pools.

On larger deployments, with thousands of volumes in one pool, this should
significantly improve storage related operations


Project: http://git-wip-us.apache.org/repos/asf/cloudstack/repo
Commit: http://git-wip-us.apache.org/repos/asf/cloudstack/commit/b53a9dcc
Tree: http://git-wip-us.apache.org/repos/asf/cloudstack/tree/b53a9dcc
Diff: http://git-wip-us.apache.org/repos/asf/cloudstack/diff/b53a9dcc

Branch: refs/heads/statscollector-graphite
Commit: b53a9dcc9f3ee95d40761b9c2c860f821595a661
Parents: 59b0103
Author: Wido den Hollander <wi...@widodh.nl>
Authored: Sun Sep 28 11:55:25 2014 +0200
Committer: Wido den Hollander <wi...@widodh.nl>
Committed: Sun Sep 28 20:51:10 2014 +0200

----------------------------------------------------------------------
 .../kvm/resource/LibvirtComputingResource.java  |  2 +-
 .../kvm/storage/IscsiAdmStorageAdaptor.java     |  5 +++++
 .../kvm/storage/KVMStoragePoolManager.java      |  6 +++++-
 .../kvm/storage/LibvirtStorageAdaptor.java      | 21 +++++++++++++++++++-
 .../hypervisor/kvm/storage/StorageAdaptor.java  |  3 +++
 5 files changed, 34 insertions(+), 3 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cloudstack/blob/b53a9dcc/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java
----------------------------------------------------------------------
diff --git a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java
index 1dc1955..7624034 100755
--- a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java
+++ b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java
@@ -2654,7 +2654,7 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
 
     protected GetStorageStatsAnswer execute(final GetStorageStatsCommand cmd) {
         try {
-            KVMStoragePool sp = _storagePoolMgr.getStoragePool(cmd.getPooltype(), cmd.getStorageId());
+            KVMStoragePool sp = _storagePoolMgr.getStoragePool(cmd.getPooltype(), cmd.getStorageId(), true);
             return new GetStorageStatsAnswer(cmd, sp.getCapacity(), sp.getUsed());
         } catch (CloudRuntimeException e) {
             return new GetStorageStatsAnswer(cmd, e.toString());

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/b53a9dcc/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/IscsiAdmStorageAdaptor.java
----------------------------------------------------------------------
diff --git a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/IscsiAdmStorageAdaptor.java b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/IscsiAdmStorageAdaptor.java
index 2ea17e7..f7a8cf7 100644
--- a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/IscsiAdmStorageAdaptor.java
+++ b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/IscsiAdmStorageAdaptor.java
@@ -53,6 +53,11 @@ public class IscsiAdmStorageAdaptor implements StorageAdaptor {
     }
 
     @Override
+    public KVMStoragePool getStoragePool(String uuid, boolean refreshInfo) {
+        return MapStorageUuidToStoragePool.get(uuid);
+    }
+
+    @Override
     public boolean deleteStoragePool(String uuid) {
         return MapStorageUuidToStoragePool.remove(uuid) != null;
     }

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/b53a9dcc/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/KVMStoragePoolManager.java
----------------------------------------------------------------------
diff --git a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/KVMStoragePoolManager.java b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/KVMStoragePoolManager.java
index ab819b2..583db0f 100644
--- a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/KVMStoragePoolManager.java
+++ b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/KVMStoragePoolManager.java
@@ -195,11 +195,15 @@ public class KVMStoragePoolManager {
     }
 
     public KVMStoragePool getStoragePool(StoragePoolType type, String uuid) {
+        return this.getStoragePool(type, uuid, false);
+    }
+
+    public KVMStoragePool getStoragePool(StoragePoolType type, String uuid, boolean refreshInfo) {
 
         StorageAdaptor adaptor = getStorageAdaptor(type);
         KVMStoragePool pool = null;
         try {
-            pool = adaptor.getStoragePool(uuid);
+            pool = adaptor.getStoragePool(uuid, refreshInfo);
         } catch (Exception e) {
             StoragePoolInformation info = _storagePools.get(uuid);
             if (info != null) {

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/b53a9dcc/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/LibvirtStorageAdaptor.java
----------------------------------------------------------------------
diff --git a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/LibvirtStorageAdaptor.java b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/LibvirtStorageAdaptor.java
index ffd4423..26237ba 100644
--- a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/LibvirtStorageAdaptor.java
+++ b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/LibvirtStorageAdaptor.java
@@ -338,6 +338,11 @@ public class LibvirtStorageAdaptor implements StorageAdaptor {
 
     @Override
     public KVMStoragePool getStoragePool(String uuid) {
+        return this.getStoragePool(uuid, false);
+    }
+
+    @Override
+    public KVMStoragePool getStoragePool(String uuid, boolean refreshInfo) {
         s_logger.debug("Trying to fetch storage pool " + uuid + " from libvirt");
         StoragePool storage = null;
         try {
@@ -383,7 +388,21 @@ public class LibvirtStorageAdaptor implements StorageAdaptor {
                 }
             }
 
-            pool.refresh();
+            /**
+             * On large (RBD) storage pools it can take up to a couple of minutes
+             * for libvirt to refresh the pool.
+             *
+             * Refreshing a storage pool means that libvirt will have to iterate the whole pool
+             * and fetch information of each volume in there
+             *
+             * It is not always required to refresh a pool. So we can control if we want to or not
+             *
+             * By default only the getStorageStats call in the LibvirtComputingResource will ask to
+             * refresh the pool
+             */
+            if (refreshInfo) {
+                pool.refresh();
+            }
             pool.setCapacity(storage.getInfo().capacity);
             pool.setUsed(storage.getInfo().allocation);
             pool.setAvailable(storage.getInfo().available);

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/b53a9dcc/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/StorageAdaptor.java
----------------------------------------------------------------------
diff --git a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/StorageAdaptor.java b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/StorageAdaptor.java
index 38ee3e6..dceb291 100644
--- a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/StorageAdaptor.java
+++ b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/StorageAdaptor.java
@@ -28,6 +28,9 @@ public interface StorageAdaptor {
 
     public KVMStoragePool getStoragePool(String uuid);
 
+    // Get the storage pool from libvirt, but control if libvirt should refresh the pool (can take a long time)
+    public KVMStoragePool getStoragePool(String uuid, boolean refreshInfo);
+
     // given disk path (per database) and pool, create new KVMPhysicalDisk, populate
     // it with info from local disk, and return it
     public KVMPhysicalDisk getPhysicalDisk(String volumeUuid, KVMStoragePool pool);


[2/2] git commit: updated refs/heads/statscollector-graphite to 9ba50f3

Posted by wi...@apache.org.
CLOUDSTACK-7583: Send VmStats to Graphite host when configured

This allows external processing of VmStats information without using
the usage server of CloudStack

Statistics are being send to Graphite using UDP and not TCP.

UDP is used to prevent the management server waiting for TCP timeouts
when the Graphite server is unavailable


Project: http://git-wip-us.apache.org/repos/asf/cloudstack/repo
Commit: http://git-wip-us.apache.org/repos/asf/cloudstack/commit/9ba50f30
Tree: http://git-wip-us.apache.org/repos/asf/cloudstack/tree/9ba50f30
Diff: http://git-wip-us.apache.org/repos/asf/cloudstack/diff/9ba50f30

Branch: refs/heads/statscollector-graphite
Commit: 9ba50f30e5c4fc1e1fae874478786ad76471020f
Parents: b53a9dc
Author: Wido den Hollander <wi...@widodh.nl>
Authored: Fri Sep 19 16:58:00 2014 +0200
Committer: Wido den Hollander <wi...@widodh.nl>
Committed: Mon Sep 29 01:11:00 2014 +0200

----------------------------------------------------------------------
 server/src/com/cloud/configuration/Config.java  |   7 +-
 server/src/com/cloud/server/StatsCollector.java |  56 +++++++++
 setup/db/db/schema-441to450.sql                 |   4 +
 .../utils/graphite/GraphiteClient.java          | 125 +++++++++++++++++++
 .../utils/graphite/GraphiteException.java       |  31 +++++
 5 files changed, 222 insertions(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cloudstack/blob/9ba50f30/server/src/com/cloud/configuration/Config.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/configuration/Config.java b/server/src/com/cloud/configuration/Config.java
index 83badbc..fc7eb89 100755
--- a/server/src/com/cloud/configuration/Config.java
+++ b/server/src/com/cloud/configuration/Config.java
@@ -2048,7 +2048,12 @@ public enum Config {
     PublishAlertEvent("Advanced", ManagementServer.class, Boolean.class, "publish.alert.events", "true", "enable or disable publishing of alert events on the event bus", null),
     PublishResourceStateEvent("Advanced", ManagementServer.class, Boolean.class, "publish.resource.state.events", "true", "enable or disable publishing of alert events on the event bus", null),
     PublishUsageEvent("Advanced", ManagementServer.class, Boolean.class, "publish.usage.events", "true", "enable or disable publishing of usage events on the event bus", null),
-    PublishAsynJobEvent("Advanced", ManagementServer.class, Boolean.class, "publish.async.job.events", "true", "enable or disable publishing of usage events on the event bus", null);
+    PublishAsynJobEvent("Advanced", ManagementServer.class, Boolean.class, "publish.async.job.events", "true", "enable or disable publishing of usage events on the event bus", null),
+
+    // StatsCollector
+    StatsOutPutGraphiteHost("Advanced", ManagementServer.class, String.class, "stats.output.graphite.host", "", "Graphite host to send statistics to", null),
+    StatsOutPutGraphitePort("Advanced", ManagementServer.class, Integer.class, "stats.output.graphite.port", "2003", "The port of the graphite host to send statistics to", null),
+    StatsOutPutGraphitePrefix("Advanced", ManagementServer.class, String.class, "stats.output.graphite.prefix", "", "Prefix to append to statistics going to Graphite", null);
 
     private final String _category;
     private final Class<?> _componentClass;

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/9ba50f30/server/src/com/cloud/server/StatsCollector.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/server/StatsCollector.java b/server/src/com/cloud/server/StatsCollector.java
index 9f4c14e..14d642d 100755
--- a/server/src/com/cloud/server/StatsCollector.java
+++ b/server/src/com/cloud/server/StatsCollector.java
@@ -43,6 +43,8 @@ import org.apache.cloudstack.managed.context.ManagedContextRunnable;
 import org.apache.cloudstack.storage.datastore.db.ImageStoreDao;
 import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao;
 import org.apache.cloudstack.storage.datastore.db.StoragePoolVO;
+import org.apache.cloudstack.graphite.GraphiteClient;
+import org.apache.cloudstack.graphite.GraphiteException;
 
 import com.cloud.agent.AgentManager;
 import com.cloud.agent.api.Answer;
@@ -194,6 +196,11 @@ public class StatsCollector extends ManagerBase implements ComponentMethodInterc
     int vmDiskStatsInterval = 0;
     List<Long> hostIds = null;
 
+    String graphiteHost = null;
+    int graphitePort = -1;
+    String graphitePrefix = null;
+    boolean graphiteEnabled = false;
+
     private ScheduledExecutorService _diskStatsUpdateExecutor;
     private int _usageAggregationRange = 1440;
     private String _usageTimeZone = "GMT";
@@ -233,6 +240,17 @@ public class StatsCollector extends ManagerBase implements ComponentMethodInterc
         autoScaleStatsInterval = NumbersUtil.parseLong(configs.get("autoscale.stats.interval"), 60000L);
         vmDiskStatsInterval = NumbersUtil.parseInt(configs.get("vm.disk.stats.interval"), 0);
 
+        graphiteHost = configs.get("stats.output.graphite.host");
+        graphitePort = NumbersUtil.parseInt(configs.get("stats.output.graphite.port"), 2003);
+        graphitePrefix = configs.get("stats.output.graphite.prefix");
+
+        if (graphiteHost != null && !graphiteHost.equals("")) {
+            graphiteEnabled = true;
+            if (graphitePrefix != null && !graphitePrefix.equals("")) {
+                graphitePrefix += ".";
+            }
+        }
+
         if (hostStatsInterval > 0) {
             _executor.scheduleWithFixedDelay(new HostCollector(), 15000L, hostStatsInterval, TimeUnit.MILLISECONDS);
         }
@@ -372,6 +390,9 @@ public class StatsCollector extends ManagerBase implements ComponentMethodInterc
                 sc.addAnd("type", SearchCriteria.Op.NEQ, Host.Type.SecondaryStorageVM.toString());
                 List<HostVO> hosts = _hostDao.search(sc, null);
 
+                /* HashMap for metrics to be send to Graphite */
+                HashMap metrics = new HashMap<String, Integer>();
+
                 for (HostVO host : hosts) {
                     List<UserVmVO> vms = _userVmDao.listRunningByHostId(host.getId());
                     List<Long> vmIds = new ArrayList<Long>();
@@ -407,6 +428,41 @@ public class StatsCollector extends ManagerBase implements ComponentMethodInterc
 
                                     _VmStats.put(vmId, statsInMemory);
                                 }
+
+                                /**
+                                    Add statistics to HashMap when they should be send to Graphite
+                                    All statistics go into one HashMap and will be send at once using the GraphiteClient
+                                */
+                                if (graphiteEnabled) {
+                                    VMInstanceVO vmVO = _vmInstance.findById(vmId);
+                                    String vmName = vmVO.getInstanceName();
+
+                                    metrics.put(graphitePrefix + "cloudstack.stats.instances." + vmName + ".cpu.num", statsForCurrentIteration.getNumCPUs());
+                                    metrics.put(graphitePrefix + "cloudstack.stats.instances." + vmName + ".cpu.utilization", statsForCurrentIteration.getCPUUtilization());
+                                    metrics.put(graphitePrefix + "cloudstack.stats.instances." + vmName + ".network.read_kbs", statsForCurrentIteration.getNetworkReadKBs());
+                                    metrics.put(graphitePrefix + "cloudstack.stats.instances." + vmName + ".network.write_kbs", statsForCurrentIteration.getNetworkWriteKBs());
+                                    metrics.put(graphitePrefix + "cloudstack.stats.instances." + vmName + ".disk.write_kbs", statsForCurrentIteration.getDiskWriteKBs());
+                                    metrics.put(graphitePrefix + "cloudstack.stats.instances." + vmName + ".disk.read_kbs", statsForCurrentIteration.getDiskReadKBs());
+                                    metrics.put(graphitePrefix + "cloudstack.stats.instances." + vmName + ".disk.write_iops", statsForCurrentIteration.getDiskWriteIOs());
+                                    metrics.put(graphitePrefix + "cloudstack.stats.instances." + vmName + ".disk.read_iops", statsForCurrentIteration.getDiskReadIOs());
+                                }
+
+                            }
+
+                            /**
+                             * Send the metrics to Graphite
+                             * We send it on a per-host basis to prevent that we suddenly send a lot of metrics
+                             * to the Graphite server at once
+                             */
+                            if (graphiteEnabled) {
+                                try {
+                                    s_logger.debug("Sending VmStats to Graphite host " + graphiteHost + ":" + graphitePort);
+                                    GraphiteClient g = new GraphiteClient(graphiteHost, graphitePort);
+                                    g.sendMetrics(metrics);
+                                    metrics.clear();
+                                } catch (GraphiteException e) {
+                                    s_logger.debug("Failed sending VmStats to Graphite host " + graphiteHost + ":" + graphitePort + ": " + e.getMessage());
+                                }
                             }
                         }
 

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/9ba50f30/setup/db/db/schema-441to450.sql
----------------------------------------------------------------------
diff --git a/setup/db/db/schema-441to450.sql b/setup/db/db/schema-441to450.sql
index dbb6754..96ab28b 100644
--- a/setup/db/db/schema-441to450.sql
+++ b/setup/db/db/schema-441to450.sql
@@ -746,3 +746,7 @@ CREATE TABLE `cloud`.`baremetal_rct` (
   `rct` text NOT NULL,
    PRIMARY KEY (`id`)
 ) ENGINE = InnoDB DEFAULT CHARSET=utf8;
+
+INSERT IGNORE INTO `cloud`.`configuration` VALUES ("Advanced", 'DEFAULT', 'management-server', "stats.output.graphite.host", "", "Graphite host to send statistics to", "", NULL, NULL, 0);
+INSERT IGNORE INTO `cloud`.`configuration` VALUES ("Advanced", 'DEFAULT', 'management-server', "stats.output.graphite.port", "2003", "The port of the graphite host to send statistics to", "2003", NULL, NULL, 0);
+INSERT IGNORE INTO `cloud`.`configuration` VALUES ("Advanced", 'DEFAULT', 'management-server', "stats.output.graphite.prefix", "", "Prefix to append to statistics going to Graphite", "", NULL, NULL, 0);

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/9ba50f30/utils/src/org/apache/cloudstack/utils/graphite/GraphiteClient.java
----------------------------------------------------------------------
diff --git a/utils/src/org/apache/cloudstack/utils/graphite/GraphiteClient.java b/utils/src/org/apache/cloudstack/utils/graphite/GraphiteClient.java
new file mode 100644
index 0000000..5dca8cb
--- /dev/null
+++ b/utils/src/org/apache/cloudstack/utils/graphite/GraphiteClient.java
@@ -0,0 +1,125 @@
+//
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+//
+
+package org.apache.cloudstack.graphite;
+
+import java.io.IOException;
+import java.net.DatagramPacket;
+import java.net.DatagramSocket;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.HashMap;
+import java.util.Map;
+
+public class GraphiteClient {
+
+    private String graphiteHost;
+    private int graphitePort;
+
+    /**
+     * Create a new Graphite client
+     *
+     * @param graphiteHost Hostname of the Graphite host
+     * @param graphitePort UDP port of the Graphite host
+     */
+    public GraphiteClient(String graphiteHost, int graphitePort) {
+        this.graphiteHost = graphiteHost;
+        this.graphitePort = graphitePort;
+    }
+
+    /**
+     * Create a new Graphite client
+     *
+     * @param graphiteHost Hostname of the Graphite host. Will default to port 2003
+     */
+    public GraphiteClient(String graphiteHost) {
+        this.graphiteHost = graphiteHost;
+        this.graphitePort = 2003;
+    }
+
+    /**
+     * Get the current system timestamp to pass to Graphite
+     *
+     * @return Seconds passed since epoch (01-01-1970)
+     */
+    protected long getCurrentSystemTime() {
+        return System.currentTimeMillis() / 1000;
+    }
+
+    /**
+     * Send a array of metrics to graphite.
+     *
+     * @param metrics the metrics as key-value-pairs
+     */
+    public void sendMetrics(Map<String, Integer> metrics) {
+        sendMetrics(metrics, this.getCurrentSystemTime());
+    }
+
+    /**
+     * Send a array of metrics with a given timestamp to graphite.
+     *
+     * @param metrics the metrics as key-value-pairs
+     * @param timeStamp the timestamp
+     */
+    public void sendMetrics(Map<String, Integer> metrics, long timeStamp) {
+        try {
+            DatagramSocket sock = new DatagramSocket();
+            InetAddress addr = InetAddress.getByName(this.graphiteHost);
+
+            for (Map.Entry<String, Integer> metric: metrics.entrySet()) {
+                byte[] message = new String(metric.getKey() + " " + metric.getValue() + " " + timeStamp + "\n").getBytes();
+                DatagramPacket packet = new DatagramPacket(message, message.length, addr, this.graphitePort);
+                sock.send(packet);
+            }
+
+            sock.close();
+        } catch (UnknownHostException e) {
+            throw new GraphiteException("Unknown host: " + graphiteHost);
+        } catch (IOException e) {
+            throw new GraphiteException("Error while writing to graphite: " + e.getMessage(), e);
+        }
+    }
+
+    /**
+     * Send a single metric with the current time as timestamp to graphite.
+     *
+     * @param key The metric key
+     * @param value the metric value
+     *
+     * @throws GraphiteException if sending data to graphite failed
+     */
+    public void sendMetric(String key, int value) {
+        sendMetric(key, value, this.getCurrentSystemTime());
+    }
+
+    /**
+     * Send a single metric with a given timestamp to graphite.
+     *
+     * @param key The metric key
+     * @param value The metric value
+     * @param timeStamp the timestamp to use
+     *
+     * @throws GraphiteException if sending data to graphite failed
+     */
+    public void sendMetric(final String key, final int value, long timeStamp) {
+        HashMap metrics = new HashMap<String, Integer>();
+        metrics.put(key, value);
+        sendMetrics(metrics, timeStamp);
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/9ba50f30/utils/src/org/apache/cloudstack/utils/graphite/GraphiteException.java
----------------------------------------------------------------------
diff --git a/utils/src/org/apache/cloudstack/utils/graphite/GraphiteException.java b/utils/src/org/apache/cloudstack/utils/graphite/GraphiteException.java
new file mode 100644
index 0000000..b11532e
--- /dev/null
+++ b/utils/src/org/apache/cloudstack/utils/graphite/GraphiteException.java
@@ -0,0 +1,31 @@
+//
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+//
+
+package org.apache.cloudstack.graphite;
+
+public class GraphiteException extends RuntimeException {
+
+    public GraphiteException(String message) {
+        super(message);
+    }
+
+    public GraphiteException(String message, Throwable cause) {
+        super(message, cause);
+    }
+}
\ No newline at end of file