This is an automated email from the ASF dual-hosted git repository.
bhaisaab pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/cloudstack.git
The following commit(s) were added to refs/heads/master by this push:
new 0fedbdd CLOUDSTACK-9998: Prometheus Exporter for CloudStack (#2287)
0fedbdd is described below
commit 0fedbdd7a90113b7a631b9a4e98fff6b37026627
Author: Rohit Yadav <[email protected]>
AuthorDate: Wed Oct 11 17:24:22 2017 +0530
CLOUDSTACK-9998: Prometheus Exporter for CloudStack (#2287)
This implements a CloudStack Prometheus exporter as a plugin, that serves
metrics on a HTTP port.
New global settings:
1. prometheus.exporter.enable - (default: false), Enable the prometheus
exporter plugin, management server restart needed.
2. prometheus.exporter.port - (default: 9595), The prometheus exporter
server port.
3. prometheus.exporter.allowed.ips - (default: 127.0.0.1), List of comma
separated prometheus server ips (with no spaces) that should be allowed to
access the URLs.
The following list of metrics are provided per pop (zone) with the
exporter:
• Per host:
o CPU cores: used, total
o CPU usage: used, total (in MHz)
o Memory usage: used, total (in MiBs)
o Total VMs running on the host
• CPU cores: allocated (per zone)
• CPU usage: allocated (per zone, in MHz)
• Memory usage: allocated (per zone, in MiBs)
• Hosts: online, offline, total
• VMs: in all states -- starting, running, stopping, stopped, destroyed,
expunging, migrating, error, unknown
• Volumes: ready, destroyed, total
• Primary Storage Pool: (Disk size) used, allocated, unallocated, total
(in GiBs)
• Secondary Storage Pool: (Disk size) used, allocated, unallocated, total
(in GiBs)
• Private IPs: allocated, total
• Public IPs: allocated, total
• Shared Network IPs: allocated, total
• VLANs: allocated, total
Additional metrics for the environment:
• Summed domain (level=1) limit for CPU cores
• Summed domain (level=1) limit for memory/ram
Signed-off-by: Rohit Yadav <[email protected]>
---
api/src/com/cloud/storage/ImageStore.java | 7 +
client/pom.xml | 5 +
.../schema/src/com/cloud/vm/dao/VMInstanceDao.java | 2 +
.../src/com/cloud/vm/dao/VMInstanceDaoImpl.java | 15 +
.../storage/image/store/ImageStoreImpl.java | 5 +
plugins/integrations/prometheus/pom.xml | 48 ++
.../cloudstack/prometheus/module.properties | 18 +
.../prometheus/spring-prometheus-context.xml | 27 +
.../cloudstack/metrics/PrometheusExporter.java | 28 +-
.../cloudstack/metrics/PrometheusExporterImpl.java | 612 +++++++++++++++++++++
.../metrics/PrometheusExporterServer.java | 33 ++
.../metrics/PrometheusExporterServerImpl.java | 118 ++++
plugins/pom.xml | 1 +
13 files changed, 895 insertions(+), 24 deletions(-)
diff --git a/api/src/com/cloud/storage/ImageStore.java
b/api/src/com/cloud/storage/ImageStore.java
index ec693c4..c019b17 100644
--- a/api/src/com/cloud/storage/ImageStore.java
+++ b/api/src/com/cloud/storage/ImageStore.java
@@ -41,4 +41,11 @@ public interface ImageStore extends Identity,
InternalIdentity {
* @return data store protocol
*/
String getProtocol();
+
+ /**
+ *
+ * @return uri
+ */
+ String getUrl();
+
}
diff --git a/client/pom.xml b/client/pom.xml
index 3a0c5a5..ae0fcaa 100644
--- a/client/pom.xml
+++ b/client/pom.xml
@@ -414,6 +414,11 @@
<artifactId>cloud-plugin-database-quota</artifactId>
<version>${project.version}</version>
</dependency>
+ <dependency>
+ <groupId>org.apache.cloudstack</groupId>
+ <artifactId>cloud-plugin-integrations-prometheus-exporter</artifactId>
+ <version>${project.version}</version>
+ </dependency>
</dependencies>
<build>
<plugins>
diff --git a/engine/schema/src/com/cloud/vm/dao/VMInstanceDao.java
b/engine/schema/src/com/cloud/vm/dao/VMInstanceDao.java
index 8d457fa..3c5024b 100644
--- a/engine/schema/src/com/cloud/vm/dao/VMInstanceDao.java
+++ b/engine/schema/src/com/cloud/vm/dao/VMInstanceDao.java
@@ -116,6 +116,8 @@ public interface VMInstanceDao extends
GenericDao<VMInstanceVO, Long>, StateDao<
Long countRunningByAccount(long accountId);
+ Long countByZoneAndState(long zoneId, State state);
+
List<VMInstanceVO> listNonRemovedVmsByTypeAndNetwork(long networkId,
VirtualMachine.Type... types);
/**
diff --git a/engine/schema/src/com/cloud/vm/dao/VMInstanceDaoImpl.java
b/engine/schema/src/com/cloud/vm/dao/VMInstanceDaoImpl.java
index df5e60e..7065350 100644
--- a/engine/schema/src/com/cloud/vm/dao/VMInstanceDaoImpl.java
+++ b/engine/schema/src/com/cloud/vm/dao/VMInstanceDaoImpl.java
@@ -87,6 +87,7 @@ public class VMInstanceDaoImpl extends
GenericDaoBase<VMInstanceVO, Long> implem
protected GenericSearchBuilder<VMInstanceVO, Long>
FindIdsOfVirtualRoutersByAccount;
protected GenericSearchBuilder<VMInstanceVO, Long> CountActiveByHost;
protected GenericSearchBuilder<VMInstanceVO, Long> CountRunningByAccount;
+ protected GenericSearchBuilder<VMInstanceVO, Long> CountByZoneAndState;
protected SearchBuilder<VMInstanceVO> NetworkTypeSearch;
protected GenericSearchBuilder<VMInstanceVO, String>
DistinctHostNameSearch;
protected SearchBuilder<VMInstanceVO> HostAndStateSearch;
@@ -242,6 +243,12 @@ public class VMInstanceDaoImpl extends
GenericDaoBase<VMInstanceVO, Long> implem
CountRunningByAccount.and("state",
CountRunningByAccount.entity().getState(), SearchCriteria.Op.EQ);
CountRunningByAccount.done();
+ CountByZoneAndState = createSearchBuilder(Long.class);
+ CountByZoneAndState.select(null, Func.COUNT, null);
+ CountByZoneAndState.and("zone",
CountByZoneAndState.entity().getDataCenterId(), SearchCriteria.Op.EQ);
+ CountByZoneAndState.and("state",
CountByZoneAndState.entity().getState(), SearchCriteria.Op.EQ);
+ CountByZoneAndState.done();
+
HostAndStateSearch = createSearchBuilder();
HostAndStateSearch.and("host",
HostAndStateSearch.entity().getHostId(), Op.EQ);
HostAndStateSearch.and("states",
HostAndStateSearch.entity().getState(), Op.IN);
@@ -719,6 +726,14 @@ public class VMInstanceDaoImpl extends
GenericDaoBase<VMInstanceVO, Long> implem
}
@Override
+ public Long countByZoneAndState(long zoneId, State state) {
+ SearchCriteria<Long> sc = CountByZoneAndState.create();
+ sc.setParameters("zone", zoneId);
+ sc.setParameters("state", state);
+ return customSearch(sc, null).get(0);
+ }
+
+ @Override
public List<VMInstanceVO> listNonRemovedVmsByTypeAndNetwork(long
networkId, VirtualMachine.Type... types) {
if (NetworkTypeSearch == null) {
diff --git
a/engine/storage/image/src/org/apache/cloudstack/storage/image/store/ImageStoreImpl.java
b/engine/storage/image/src/org/apache/cloudstack/storage/image/store/ImageStoreImpl.java
index 182a8ec..41ce5a2 100644
---
a/engine/storage/image/src/org/apache/cloudstack/storage/image/store/ImageStoreImpl.java
+++
b/engine/storage/image/src/org/apache/cloudstack/storage/image/store/ImageStoreImpl.java
@@ -181,6 +181,11 @@ public class ImageStoreImpl implements ImageStoreEntity {
}
@Override
+ public String getUrl() {
+ return imageDataStoreVO.getUrl();
+ }
+
+ @Override
public DataStoreTO getTO() {
DataStoreTO to = getDriver().getStoreTO(this);
if (to == null) {
diff --git a/plugins/integrations/prometheus/pom.xml
b/plugins/integrations/prometheus/pom.xml
new file mode 100644
index 0000000..66dbebb
--- /dev/null
+++ b/plugins/integrations/prometheus/pom.xml
@@ -0,0 +1,48 @@
+<!--
+ 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.
+-->
+<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>
+ <artifactId>cloud-plugin-integrations-prometheus-exporter</artifactId>
+ <name>Apache CloudStack Plugin - Prometheus Exporter</name>
+ <parent>
+ <groupId>org.apache.cloudstack</groupId>
+ <artifactId>cloudstack-plugins</artifactId>
+ <version>4.11.0.0-SNAPSHOT</version>
+ <relativePath>../../pom.xml</relativePath>
+ </parent>
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.cloudstack</groupId>
+ <artifactId>cloud-utils</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.cloudstack</groupId>
+ <artifactId>cloud-api</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.cloudstack</groupId>
+ <artifactId>cloud-engine-schema</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ </dependencies>
+</project>
diff --git
a/plugins/integrations/prometheus/resources/META-INF/cloudstack/prometheus/module.properties
b/plugins/integrations/prometheus/resources/META-INF/cloudstack/prometheus/module.properties
new file mode 100644
index 0000000..cb70ec8
--- /dev/null
+++
b/plugins/integrations/prometheus/resources/META-INF/cloudstack/prometheus/module.properties
@@ -0,0 +1,18 @@
+# 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.
+name=prometheus
+parent=api
diff --git
a/plugins/integrations/prometheus/resources/META-INF/cloudstack/prometheus/spring-prometheus-context.xml
b/plugins/integrations/prometheus/resources/META-INF/cloudstack/prometheus/spring-prometheus-context.xml
new file mode 100644
index 0000000..06fb92c
--- /dev/null
+++
b/plugins/integrations/prometheus/resources/META-INF/cloudstack/prometheus/spring-prometheus-context.xml
@@ -0,0 +1,27 @@
+<!--
+ 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.
+-->
+<beans xmlns="http://www.springframework.org/schema/beans"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://www.springframework.org/schema/beans
+
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
+
+ <bean id="prometheusExporterServer"
class="org.apache.cloudstack.metrics.PrometheusExporterServerImpl" />
+ <bean id="prometheusExporter"
class="org.apache.cloudstack.metrics.PrometheusExporterImpl" />
+
+</beans>
diff --git a/api/src/com/cloud/storage/ImageStore.java
b/plugins/integrations/prometheus/src/org/apache/cloudstack/metrics/PrometheusExporter.java
similarity index 60%
copy from api/src/com/cloud/storage/ImageStore.java
copy to
plugins/integrations/prometheus/src/org/apache/cloudstack/metrics/PrometheusExporter.java
index ec693c4..6361f0e 100644
--- a/api/src/com/cloud/storage/ImageStore.java
+++
b/plugins/integrations/prometheus/src/org/apache/cloudstack/metrics/PrometheusExporter.java
@@ -14,31 +14,11 @@
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
-package com.cloud.storage;
+package org.apache.cloudstack.metrics;
-import org.apache.cloudstack.api.Identity;
-import org.apache.cloudstack.api.InternalIdentity;
+public interface PrometheusExporter {
-public interface ImageStore extends Identity, InternalIdentity {
+ void updateMetrics();
- /**
- * @return name of the object store.
- */
- String getName();
-
- /**
- * @return availability zone.
- */
- Long getDataCenterId();
-
- /**
- * @return object store provider name
- */
- String getProviderName();
-
- /**
- *
- * @return data store protocol
- */
- String getProtocol();
+ String getMetrics();
}
diff --git
a/plugins/integrations/prometheus/src/org/apache/cloudstack/metrics/PrometheusExporterImpl.java
b/plugins/integrations/prometheus/src/org/apache/cloudstack/metrics/PrometheusExporterImpl.java
new file mode 100644
index 0000000..a51b296
--- /dev/null
+++
b/plugins/integrations/prometheus/src/org/apache/cloudstack/metrics/PrometheusExporterImpl.java
@@ -0,0 +1,612 @@
+// 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.metrics;
+
+import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.inject.Inject;
+
+import org.apache.cloudstack.engine.subsystem.api.storage.ZoneScope;
+import org.apache.cloudstack.storage.datastore.db.ImageStoreDao;
+import org.apache.log4j.Logger;
+
+import com.cloud.alert.AlertManager;
+import com.cloud.api.ApiDBUtils;
+import com.cloud.api.query.dao.DomainJoinDao;
+import com.cloud.api.query.dao.HostJoinDao;
+import com.cloud.api.query.dao.StoragePoolJoinDao;
+import com.cloud.api.query.vo.DomainJoinVO;
+import com.cloud.api.query.vo.HostJoinVO;
+import com.cloud.api.query.vo.StoragePoolJoinVO;
+import com.cloud.capacity.Capacity;
+import com.cloud.capacity.CapacityManager;
+import com.cloud.capacity.CapacityVO;
+import com.cloud.capacity.dao.CapacityDao;
+import com.cloud.capacity.dao.CapacityDaoImpl;
+import com.cloud.configuration.Resource;
+import com.cloud.dc.DataCenterVO;
+import com.cloud.dc.Vlan;
+import com.cloud.dc.dao.DataCenterDao;
+import com.cloud.dc.dao.DataCenterIpAddressDao;
+import com.cloud.host.Host;
+import com.cloud.host.Status;
+import com.cloud.network.dao.IPAddressDao;
+import com.cloud.storage.ImageStore;
+import com.cloud.storage.StorageStats;
+import com.cloud.storage.Volume;
+import com.cloud.storage.VolumeVO;
+import com.cloud.storage.dao.VolumeDao;
+import com.cloud.utils.component.Manager;
+import com.cloud.utils.component.ManagerBase;
+import com.cloud.vm.VirtualMachine.State;
+import com.cloud.vm.dao.VMInstanceDao;
+import com.google.common.base.Strings;
+
+public class PrometheusExporterImpl extends ManagerBase implements
PrometheusExporter, Manager {
+ private static final Logger LOG =
Logger.getLogger(PrometheusExporterImpl.class);
+
+ private static final String USED = "used";
+ private static final String ALLOCATED = "allocated";
+ private static final String UNALLOCATED = "unallocated";
+ private static final String TOTAL = "total";
+ private static final String ONLINE = "online";
+ private static final String OFFLINE = "offline";
+
+ private static List<Item> metricsItems = new ArrayList<>();
+
+ @Inject
+ private DataCenterDao dcDao;
+ @Inject
+ private HostJoinDao hostJoinDao;
+ @Inject
+ private VMInstanceDao vmDao;
+ @Inject
+ private VolumeDao volumeDao;
+ @Inject
+ private IPAddressDao publicIpAddressDao;
+ @Inject
+ private DataCenterIpAddressDao privateIpAddressDao;
+ @Inject
+ private CapacityDao capacityDao;
+ @Inject
+ private StoragePoolJoinDao storagePoolJoinDao;
+ @Inject
+ private ImageStoreDao imageStoreDao;
+ @Inject
+ private DomainJoinDao domainDao;
+ @Inject
+ private AlertManager alertManager;
+
+ public PrometheusExporterImpl() {
+ super();
+ }
+
+ private void addHostMetrics(final List<Item> metricsList, final long dcId,
final String zoneName, final String zoneUuid) {
+ int total = 0;
+ int up = 0;
+ int down = 0;
+ for (final HostJoinVO host : hostJoinDao.listAll()) {
+ if (host == null || host.getType() != Host.Type.Routing ||
host.getZoneId() != dcId) {
+ continue;
+ }
+ total++;
+ if (host.getStatus() == Status.Up) {
+ up++;
+ } else if (host.getStatus() == Status.Disconnected ||
host.getStatus() == Status.Down) {
+ down++;
+ }
+
+ final String cpuFactor =
String.valueOf(CapacityManager.CpuOverprovisioningFactor.valueIn(host.getClusterId()));
+ final CapacityVO cpuCapacity =
capacityDao.findByHostIdType(host.getId(), Capacity.CAPACITY_TYPE_CPU);
+ metricsList.add(new ItemHostCpu(zoneName, zoneUuid,
host.getName(), host.getUuid(), host.getPrivateIpAddress(), cpuFactor, USED,
cpuCapacity.getUsedCapacity()));
+ metricsList.add(new ItemHostCpu(zoneName, zoneUuid,
host.getName(), host.getUuid(), host.getPrivateIpAddress(), cpuFactor, TOTAL,
cpuCapacity.getTotalCapacity()));
+
+ final String memoryFactor =
String.valueOf(CapacityManager.MemOverprovisioningFactor.valueIn(host.getClusterId()));
+ final CapacityVO memCapacity =
capacityDao.findByHostIdType(host.getId(), Capacity.CAPACITY_TYPE_MEMORY);
+ metricsList.add(new ItemHostMemory(zoneName, zoneUuid,
host.getName(), host.getUuid(), host.getPrivateIpAddress(), memoryFactor, USED,
memCapacity.getUsedCapacity()));
+ metricsList.add(new ItemHostMemory(zoneName, zoneUuid,
host.getName(), host.getUuid(), host.getPrivateIpAddress(), memoryFactor,
TOTAL, memCapacity.getTotalCapacity()));
+
+ metricsList.add(new ItemHostVM(zoneName, zoneUuid, host.getName(),
host.getUuid(), host.getPrivateIpAddress(),
vmDao.listByHostId(host.getId()).size()));
+
+ final CapacityVO coreCapacity =
capacityDao.findByHostIdType(host.getId(), Capacity.CAPACITY_TYPE_CPU_CORE);
+ if (coreCapacity != null) {
+ metricsList.add(new ItemVMCore(zoneName, zoneUuid,
host.getName(), host.getUuid(), host.getPrivateIpAddress(), USED,
coreCapacity.getUsedCapacity()));
+ metricsList.add(new ItemVMCore(zoneName, zoneUuid,
host.getName(), host.getUuid(), host.getPrivateIpAddress(), TOTAL,
coreCapacity.getTotalCapacity()));
+ }
+ }
+
+ final List<CapacityDaoImpl.SummedCapacity> cpuCapacity =
capacityDao.findCapacityBy((int) Capacity.CAPACITY_TYPE_CPU, dcId, null, null);
+ if (cpuCapacity != null && cpuCapacity.size() > 0) {
+ metricsList.add(new ItemHostCpu(zoneName, zoneUuid, null, null,
null, null, ALLOCATED, cpuCapacity.get(0).getAllocatedCapacity() != null ?
cpuCapacity.get(0).getAllocatedCapacity() : 0));
+ }
+
+ final List<CapacityDaoImpl.SummedCapacity> memCapacity =
capacityDao.findCapacityBy((int) Capacity.CAPACITY_TYPE_MEMORY, dcId, null,
null);
+ if (memCapacity != null && memCapacity.size() > 0) {
+ metricsList.add(new ItemHostMemory(zoneName, zoneUuid, null, null,
null, null, ALLOCATED, memCapacity.get(0).getAllocatedCapacity() != null ?
memCapacity.get(0).getAllocatedCapacity() : 0));
+ }
+
+ final List<CapacityDaoImpl.SummedCapacity> coreCapacity =
capacityDao.findCapacityBy((int) Capacity.CAPACITY_TYPE_CPU_CORE, dcId, null,
null);
+ if (coreCapacity != null && coreCapacity.size() > 0) {
+ metricsList.add(new ItemVMCore(zoneName, zoneUuid, null, null,
null, ALLOCATED, coreCapacity.get(0).getAllocatedCapacity() != null ?
coreCapacity.get(0).getAllocatedCapacity() : 0));
+ }
+
+ metricsList.add(new ItemHost(zoneName, zoneUuid, ONLINE, up));
+ metricsList.add(new ItemHost(zoneName, zoneUuid, OFFLINE, down));
+ metricsList.add(new ItemHost(zoneName, zoneUuid, TOTAL, total));
+ }
+
+ private void addVMMetrics(final List<Item> metricsList, final long dcId,
final String zoneName, final String zoneUuid) {
+ for (final State state : State.values()) {
+ final Long count = vmDao.countByZoneAndState(dcId, state);
+ if (count == null) {
+ continue;
+ }
+ metricsList.add(new ItemVM(zoneName, zoneUuid,
state.name().toLowerCase(), count));
+ }
+ }
+
+ private void addVolumeMetrics(final List<Item> metricsList, final long
dcId, final String zoneName, final String zoneUuid) {
+ int total = 0;
+ int ready = 0;
+ int destroyed = 0;
+ for (final VolumeVO volume : volumeDao.findByDc(dcId)) {
+ if (volume == null) {
+ continue;
+ }
+ total++;
+ if (volume.getState() == Volume.State.Ready) {
+ ready++;
+ } else if (volume.getState() == Volume.State.Destroy) {
+ destroyed++;
+ }
+ }
+ metricsList.add(new ItemVolume(zoneName, zoneUuid,
Volume.State.Ready.name().toLowerCase(), ready));
+ metricsList.add(new ItemVolume(zoneName, zoneUuid,
Volume.State.Destroy.name().toLowerCase(), destroyed));
+ metricsList.add(new ItemVolume(zoneName, zoneUuid, TOTAL, total));
+ }
+
+ private void addStorageMetrics(final List<Item> metricsList, final long
dcId, final String zoneName, final String zoneUuid) {
+ for (final StoragePoolJoinVO pool: storagePoolJoinDao.listAll()) {
+ if (pool == null || pool.getZoneId() != dcId) {
+ continue;
+ }
+ final String poolName = pool.getName();
+ final String poolPath = pool.getHostAddress() + ":" +
pool.getPath();
+
+ long usedCapacity = 0L;
+ long allocatedCapacity = pool.getUsedCapacity() +
pool.getReservedCapacity();
+ final long totalCapacity = pool.getCapacityBytes();
+
+ final StorageStats stats =
ApiDBUtils.getStoragePoolStatistics(pool.getId());
+ if (stats != null) {
+ usedCapacity = stats.getByteUsed();
+ }
+
+ final BigDecimal poolOverProvisioningFactor =
BigDecimal.valueOf(CapacityManager.StorageOverprovisioningFactor.valueIn(pool.getId()));
+ final String poolFactor = poolOverProvisioningFactor.toString();
+
+ metricsList.add(new ItemPool(zoneName, zoneUuid, poolName,
poolPath, "primary", poolFactor, USED, usedCapacity));
+ metricsList.add(new ItemPool(zoneName, zoneUuid, poolName,
poolPath, "primary", poolFactor, ALLOCATED, allocatedCapacity));
+ metricsList.add(new ItemPool(zoneName, zoneUuid, poolName,
poolPath, "primary", poolFactor, UNALLOCATED,
poolOverProvisioningFactor.multiply(BigDecimal.valueOf(totalCapacity)).longValue()
- allocatedCapacity));
+ metricsList.add(new ItemPool(zoneName, zoneUuid, poolName,
poolPath, "primary", poolFactor, TOTAL, totalCapacity));
+ }
+
+ for (final ImageStore imageStore : imageStoreDao.findByScope(new
ZoneScope(dcId))) {
+ final StorageStats stats =
ApiDBUtils.getSecondaryStorageStatistics(imageStore.getId());
+ metricsList.add(new ItemPool(zoneName, zoneUuid,
imageStore.getName(), imageStore.getUrl(), "secondary", null, USED, stats !=
null ? stats.getByteUsed() : 0));
+ metricsList.add(new ItemPool(zoneName, zoneUuid,
imageStore.getName(), imageStore.getUrl(), "secondary", null, TOTAL, stats !=
null ? stats.getCapacityBytes() : 0));
+ }
+ }
+
+ private void addIpAddressMetrics(final List<Item> metricsList, final long
dcId, final String zoneName, final String zoneUuid) {
+ metricsList.add(new ItemPrivateIp(zoneName, zoneUuid, ALLOCATED,
privateIpAddressDao.countIPs(dcId, true)));
+ metricsList.add(new ItemPrivateIp(zoneName, zoneUuid, TOTAL,
privateIpAddressDao.countIPs(dcId, false)));
+ metricsList.add(new ItemPublicIp(zoneName, zoneUuid, ALLOCATED,
publicIpAddressDao.countIPsForNetwork(dcId, true,
Vlan.VlanType.VirtualNetwork)));
+ metricsList.add(new ItemPublicIp(zoneName, zoneUuid, TOTAL,
publicIpAddressDao.countIPsForNetwork(dcId, false,
Vlan.VlanType.VirtualNetwork)));
+ metricsList.add(new ItemSharedNetworkIp(zoneName, zoneUuid, ALLOCATED,
publicIpAddressDao.countIPsForNetwork(dcId, true,
Vlan.VlanType.DirectAttached)));
+ metricsList.add(new ItemSharedNetworkIp(zoneName, zoneUuid, TOTAL,
publicIpAddressDao.countIPsForNetwork(dcId, false,
Vlan.VlanType.DirectAttached)));
+ }
+
+ private void addVlanMetrics(final List<Item> metricsList, final long dcId,
final String zoneName, final String zoneUuid) {
+ metricsList.add(new ItemVlan(zoneName, zoneUuid, ALLOCATED,
dcDao.countZoneVlans(dcId, true)));
+ metricsList.add(new ItemVlan(zoneName, zoneUuid, TOTAL,
dcDao.countZoneVlans(dcId, false)));
+ }
+
+ private void addDomainLimits(final List<Item> metricsList) {
+ Long totalCpuLimit = 0L;
+ Long totalMemoryLimit = 0L;
+
+ for (final DomainJoinVO domain: domainDao.listAll()) {
+ if (domain == null || domain.getLevel() != 1) {
+ continue;
+ }
+ long cpuLimit =
ApiDBUtils.findCorrectResourceLimitForDomain(domain.getCpuLimit(), false,
+ Resource.ResourceType.cpu, domain.getId());
+ if (cpuLimit > 0) {
+ totalCpuLimit += cpuLimit;
+ }
+
+ long memoryLimit =
ApiDBUtils.findCorrectResourceLimitForDomain(domain.getMemoryLimit(), false,
+ Resource.ResourceType.memory, domain.getId());
+ if (memoryLimit > 0) {
+ totalMemoryLimit += memoryLimit;
+ }
+ }
+ metricsList.add(new ItemDomainLimitCpu(totalCpuLimit));
+ metricsList.add(new ItemDomainLimitMemory(totalMemoryLimit));
+ }
+
+ @Override
+ public void updateMetrics() {
+ final List<Item> latestMetricsItems = new ArrayList<Item>();
+ try {
+ for (final DataCenterVO dc : dcDao.listAll()) {
+ final String zoneName = dc.getName();
+ final String zoneUuid = dc.getUuid();
+ alertManager.recalculateCapacity();
+ addHostMetrics(latestMetricsItems, dc.getId(), zoneName,
zoneUuid);
+ addVMMetrics(latestMetricsItems, dc.getId(), zoneName,
zoneUuid);
+ addVolumeMetrics(latestMetricsItems, dc.getId(), zoneName,
zoneUuid);
+ addStorageMetrics(latestMetricsItems, dc.getId(), zoneName,
zoneUuid);
+ addIpAddressMetrics(latestMetricsItems, dc.getId(), zoneName,
zoneUuid);
+ addVlanMetrics(latestMetricsItems, dc.getId(), zoneName,
zoneUuid);
+ }
+ addDomainLimits(latestMetricsItems);
+ } catch (Exception e) {
+ LOG.warn("Getting metrics failed ", e);
+ }
+ metricsItems = latestMetricsItems;
+ }
+
+ @Override
+ public String getMetrics() {
+ final StringBuilder stringBuilder = new StringBuilder();
+ stringBuilder.append("# Cloudstack Prometheus Metrics\n");
+ for (final Item item : metricsItems) {
+ stringBuilder.append(item.toMetricsString()).append("\n");
+ }
+ return stringBuilder.toString();
+ }
+
+ private abstract class Item {
+ String name;
+
+ public Item(final String nm) {
+ name = nm;
+ }
+
+ public abstract String toMetricsString();
+ }
+
+ class ItemVM extends Item {
+ String zoneName;
+ String zoneUuid;
+ String filter;
+ long total;
+
+ public ItemVM(final String zn, final String zu, final String st, long
cnt) {
+ super("cloudstack_vms_total");
+ zoneName = zn;
+ zoneUuid = zu;
+ filter = st;
+ total = cnt;
+ }
+
+ @Override
+ public String toMetricsString() {
+ return String.format("%s{zone=\"%s\",filter=\"%s\"} %d", name,
zoneName, filter, total);
+ }
+ }
+
+ class ItemVolume extends Item {
+ String zoneName;
+ String zoneUuid;
+ String filter;
+ int total;
+
+ public ItemVolume(final String zn, final String zu, final String st,
int cnt) {
+ super("cloudstack_volumes_total");
+ zoneName = zn;
+ zoneUuid = zu;
+ filter = st;
+ total = cnt;
+ }
+
+ @Override
+ public String toMetricsString() {
+ return String.format("%s{zone=\"%s\",filter=\"%s\"} %d", name,
zoneName, filter, total);
+ }
+ }
+
+ class ItemHost extends Item {
+ String zoneName;
+ String zoneUuid;
+ String state;
+ int total;
+
+ public ItemHost(final String zn, final String zu, final String st, int
cnt) {
+ super("cloudstack_hosts_total");
+ zoneName = zn;
+ zoneUuid = zu;
+ state = st;
+ total = cnt;
+ }
+
+ @Override
+ public String toMetricsString() {
+ return String.format("%s{zone=\"%s\",filter=\"%s\"} %d", name,
zoneName, state, total);
+ }
+ }
+
+ class ItemVMCore extends Item {
+ String zoneName;
+ String zoneUuid;
+ String hostName;
+ String uuid;
+ String ip;
+ String filter;
+ long core = 0;
+
+ public ItemVMCore(final String zn, final String zu, final String hn,
final String hu, final String hip, final String fl, final Long cr) {
+ super("cloudstack_host_vms_cores_total");
+ zoneName = zn;
+ zoneUuid = zu;
+ hostName = hn;
+ uuid = hu;
+ ip = hip;
+ filter = fl;
+ if (cr != null) {
+ core = cr;
+ }
+ }
+
+ @Override
+ public String toMetricsString() {
+ if (Strings.isNullOrEmpty(hostName) && Strings.isNullOrEmpty(ip)) {
+ return String.format("%s{zone=\"%s\",filter=\"%s\"} %d", name,
zoneName, filter, core);
+ }
+ return
String.format("%s{zone=\"%s\",hostname=\"%s\",ip=\"%s\",filter=\"%s\"} %d",
name, zoneName, hostName, ip, filter, core);
+ }
+ }
+
+ class ItemHostCpu extends Item {
+ String zoneName;
+ String zoneUuid;
+ String hostName;
+ String uuid;
+ String ip;
+ String overProvisioningFactor;
+ String filter;
+ double mhertz;
+
+ public ItemHostCpu(final String zn, final String zu, final String hn,
final String hu, final String hip, final String of, final String fl, final
double mh) {
+ super("cloudstack_host_cpu_usage_mhz_total");
+ zoneName = zn;
+ zoneUuid = zu;
+ hostName = hn;
+ uuid = hu;
+ ip = hip;
+ overProvisioningFactor = of;
+ filter = fl;
+ mhertz = mh;
+ }
+
+ @Override
+ public String toMetricsString() {
+ if (Strings.isNullOrEmpty(hostName) && Strings.isNullOrEmpty(ip)) {
+ return String.format("%s{zone=\"%s\",filter=\"%s\"} %.2f",
name, zoneName, filter, mhertz);
+ }
+ return
String.format("%s{zone=\"%s\",hostname=\"%s\",ip=\"%s\",overprovisioningfactor=\"%s\",filter=\"%s\"}
%.2f", name, zoneName, hostName, ip, overProvisioningFactor, filter, mhertz);
+ }
+ }
+
+ class ItemHostMemory extends Item {
+ String zoneName;
+ String zoneUuid;
+ String hostName;
+ String uuid;
+ String ip;
+ String overProvisioningFactor;
+ String filter;
+ double miBytes;
+
+ public ItemHostMemory(final String zn, final String zu, final String
hn, final String hu, final String hip, final String of, final String fl, final
double membytes) {
+ super("cloudstack_host_memory_usage_mibs_total");
+ zoneName = zn;
+ zoneUuid = zu;
+ hostName = hn;
+ uuid = hu;
+ ip = hip;
+ overProvisioningFactor = of;
+ filter = fl;
+ miBytes = membytes / (1024.0 * 1024.0);
+ }
+
+ @Override
+ public String toMetricsString() {
+ if (Strings.isNullOrEmpty(hostName) && Strings.isNullOrEmpty(ip)) {
+ return String.format("%s{zone=\"%s\",filter=\"%s\"} %.2f",
name, zoneName, filter, miBytes);
+ }
+ return
String.format("%s{zone=\"%s\",hostname=\"%s\",ip=\"%s\",overprovisioningfactor=\"%s\",filter=\"%s\"}
%.2f", name, zoneName, hostName, ip, overProvisioningFactor, filter, miBytes);
+ }
+ }
+
+ class ItemHostVM extends Item {
+ String zoneName;
+ String zoneUuid;
+ String hostName;
+ String hostUuid;
+ String hostIp;
+ int total;
+
+ public ItemHostVM(final String zoneName, final String zoneUuid, final
String hostName, final String hostUuid, final String hostIp, final int total) {
+ super("cloudstack_host_vms_total");
+ this.zoneName = zoneName;
+ this.zoneUuid = zoneUuid;
+ this.hostName = hostName;
+ this.hostUuid = hostUuid;
+ this.hostIp = hostIp;
+ this.total = total;
+ }
+
+ @Override
+ public String toMetricsString() {
+ return
String.format("%s{zone=\"%s\",hostname=\"%s\",address=\"%s\"} %d", name,
zoneName, hostName, hostIp, total);
+ }
+ }
+
+ class ItemPool extends Item {
+ String zoneName;
+ String zoneUuid;
+ String type;
+ String overProvisioningFactor;
+ String filter;
+ String pname;
+ String address;
+ double total;
+
+ public ItemPool(final String zn, final String zu, final String pn,
final String pa, final String typ, final String of, final String fl, double
cnt) {
+ super("cloudstack_storage_pool_gibs_total");
+ zoneName = zn;
+ zoneUuid = zu;
+ pname = pn;
+ address = pa;
+ type = typ;
+ overProvisioningFactor = of;
+ filter = fl;
+ total = cnt / (1024.0 * 1024.0 * 1024.0);
+ }
+
+ @Override
+ public String toMetricsString() {
+ if (Strings.isNullOrEmpty(overProvisioningFactor)) {
+ return
String.format("%s{zone=\"%s\",name=\"%s\",address=\"%s\",type=\"%s\",filter=\"%s\"}
%.2f", name, zoneName, pname, address, type, filter, total);
+ }
+ return
String.format("%s{zone=\"%s\",name=\"%s\",address=\"%s\",type=\"%s\",overprovisioningfactor=\"%s\",filter=\"%s\"}
%.2f", name, zoneName, pname, address, type, overProvisioningFactor, filter,
total);
+ }
+ }
+
+ class ItemPrivateIp extends Item {
+ String zoneName;
+ String zoneUuid;
+ String filter;
+ int total;
+
+ public ItemPrivateIp(final String zn, final String zu, final String
fl, int cnt) {
+ super("cloudstack_private_ips_total");
+ zoneName = zn;
+ zoneUuid = zu;
+ filter = fl;
+ total = cnt;
+ }
+
+ @Override
+ public String toMetricsString() {
+ return String.format("%s{zone=\"%s\",filter=\"%s\"} %d", name,
zoneName, filter, total);
+ }
+ }
+
+ class ItemPublicIp extends Item {
+ String zoneName;
+ String zoneUuid;
+ String filter;
+ int total;
+
+ public ItemPublicIp(final String zn, final String zu, final String fl,
int cnt) {
+ super("cloudstack_public_ips_total");
+ zoneName = zn;
+ zoneUuid = zu;
+ filter = fl;
+ total = cnt;
+ }
+
+ @Override
+ public String toMetricsString() {
+ return String.format("%s{zone=\"%s\",filter=\"%s\"} %d", name,
zoneName, filter, total);
+ }
+ }
+
+ class ItemSharedNetworkIp extends Item {
+ String zoneName;
+ String zoneUuid;
+ String filter;
+ int total;
+
+ public ItemSharedNetworkIp(final String zn, final String zu, final
String fl, int cnt) {
+ super("cloudstack_shared_network_ips_total");
+ zoneName = zn;
+ zoneUuid = zu;
+ filter = fl;
+ total = cnt;
+ }
+
+ @Override
+ public String toMetricsString() {
+ return String.format("%s{zone=\"%s\",filter=\"%s\"} %d", name,
zoneName, filter, total);
+ }
+ }
+
+ class ItemVlan extends Item {
+ String zoneName;
+ String zoneUuid;
+ String filter;
+ int total;
+
+ public ItemVlan(final String zn, final String zu, final String fl, int
cnt) {
+ super("cloudstack_vlans_total");
+ zoneName = zn;
+ zoneUuid = zu;
+ filter = fl;
+ total = cnt;
+ }
+
+ @Override
+ public String toMetricsString() {
+ return String.format("%s{zone=\"%s\",filter=\"%s\"} %d", name,
zoneName, filter, total);
+ }
+ }
+
+ class ItemDomainLimitCpu extends Item {
+ long cores;
+
+ public ItemDomainLimitCpu(final long c) {
+ super("cloudstack_domain_limit_cpu_cores_total");
+ cores = c;
+ }
+
+ @Override
+ public String toMetricsString() {
+ return String.format("%s %d", name, cores);
+ }
+ }
+
+ class ItemDomainLimitMemory extends Item {
+ long miBytes;
+
+ public ItemDomainLimitMemory(final long mb) {
+ super("cloudstack_domain_limit_memory_mibs_total");
+ miBytes = mb;
+ }
+
+ @Override
+ public String toMetricsString() {
+ return String.format("%s %d", name, miBytes);
+ }
+ }
+}
diff --git
a/plugins/integrations/prometheus/src/org/apache/cloudstack/metrics/PrometheusExporterServer.java
b/plugins/integrations/prometheus/src/org/apache/cloudstack/metrics/PrometheusExporterServer.java
new file mode 100644
index 0000000..e030352
--- /dev/null
+++
b/plugins/integrations/prometheus/src/org/apache/cloudstack/metrics/PrometheusExporterServer.java
@@ -0,0 +1,33 @@
+// 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.metrics;
+
+import org.apache.cloudstack.framework.config.ConfigKey;
+
+import com.cloud.utils.component.Manager;
+
+public interface PrometheusExporterServer extends Manager {
+
+ ConfigKey<Boolean> EnablePrometheusExporter = new ConfigKey<>("Advanced",
Boolean.class, "prometheus.exporter.enable", "false",
+ "Enable the prometheus exporter plugin, management server restart
needed.", true);
+
+ ConfigKey<Integer> PrometheusExporterServerPort = new
ConfigKey<>("Advanced", Integer.class, "prometheus.exporter.port", "9595",
+ "The prometheus exporter server port", true);
+
+ ConfigKey<String> PrometheusExporterAllowedAddresses = new
ConfigKey<>("Advanced", String.class, "prometheus.exporter.allowed.ips",
"127.0.0.1",
+ "List of comma separated prometheus server ips (with no spaces)
that should be allowed to access the URLs", true);
+}
diff --git
a/plugins/integrations/prometheus/src/org/apache/cloudstack/metrics/PrometheusExporterServerImpl.java
b/plugins/integrations/prometheus/src/org/apache/cloudstack/metrics/PrometheusExporterServerImpl.java
new file mode 100644
index 0000000..a615c65
--- /dev/null
+++
b/plugins/integrations/prometheus/src/org/apache/cloudstack/metrics/PrometheusExporterServerImpl.java
@@ -0,0 +1,118 @@
+// 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.metrics;
+
+import com.cloud.utils.component.ManagerBase;
+import com.sun.net.httpserver.HttpExchange;
+import com.sun.net.httpserver.HttpHandler;
+import com.sun.net.httpserver.HttpServer;
+import org.apache.cloudstack.framework.config.ConfigKey;
+import org.apache.cloudstack.framework.config.Configurable;
+import org.apache.log4j.Logger;
+
+import javax.inject.Inject;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.net.InetSocketAddress;
+import java.util.Arrays;
+
+public class PrometheusExporterServerImpl extends ManagerBase implements
PrometheusExporterServer, Configurable {
+ private static final Logger LOG =
Logger.getLogger(PrometheusExporterServerImpl.class);
+
+ private static HttpServer httpServer;
+
+ @Inject
+ private PrometheusExporter prometheusExporter;
+
+ private final static class ExporterHandler implements HttpHandler {
+ private PrometheusExporter prometheusExporter;
+
+ ExporterHandler(final PrometheusExporter prometheusExporter) {
+ super();
+ this.prometheusExporter = prometheusExporter;
+ }
+
+ @Override
+ public void handle(final HttpExchange httpExchange) throws IOException
{
+ final String remoteClientAddress =
httpExchange.getRemoteAddress().getAddress().toString().replace("/", "");
+ LOG.debug("Prometheus exporter received client request from: " +
remoteClientAddress);
+ String response = "Forbidden";
+ int responseCode = 403;
+ if
(Arrays.asList(PrometheusExporterAllowedAddresses.value().split(",")).contains(remoteClientAddress))
{
+ prometheusExporter.updateMetrics();
+ response = prometheusExporter.getMetrics();
+ responseCode = 200;
+ }
+ httpExchange.getResponseHeaders().set("Content-Type",
"text/plain");
+ httpExchange.sendResponseHeaders(responseCode, response.length());
+ final OutputStream os = httpExchange.getResponseBody();
+ os.write(response.getBytes());
+ os.close();
+ }
+ }
+
+ @Override
+ public boolean start() {
+ if (EnablePrometheusExporter.value()) {
+ try {
+ httpServer = HttpServer.create(new
InetSocketAddress(PrometheusExporterServerPort.value()), 0);
+ httpServer.createContext("/metrics", new
ExporterHandler(prometheusExporter));
+ httpServer.createContext("/", new HttpHandler() {
+ @Override
+ public void handle(HttpExchange httpExchange) throws
IOException {
+ final String response = "<html><head><title>CloudStack
Exporter</title></head>" +
+ "<body><h1>CloudStack Exporter</h1>" +
+ "<p><a href=\"/metrics\">Metrics</a></p>" +
+ "</body></html>";
+ httpExchange.sendResponseHeaders(200,
response.length());
+ final OutputStream os = httpExchange.getResponseBody();
+ os.write(response.getBytes());
+ os.close();
+ }
+ });
+ httpServer.start();
+ LOG.debug("Started prometheus exporter http server");
+ } catch (final IOException e) {
+ LOG.info("Failed to start prometheus exporter http server due
to: ", e);
+ }
+ }
+ return true;
+ }
+
+ @Override
+ public boolean stop() {
+ if (httpServer != null) {
+ httpServer.stop(0);
+ LOG.debug("Stopped Prometheus exporter http server");
+ }
+ return true;
+ }
+
+ @Override
+ public String getConfigComponentName() {
+ return PrometheusExporter.class.getSimpleName();
+ }
+
+ @Override
+ public ConfigKey<?>[] getConfigKeys() {
+ return new ConfigKey<?>[] {
+ EnablePrometheusExporter,
+ PrometheusExporterServerPort,
+ PrometheusExporterAllowedAddresses
+ };
+ }
+}
diff --git a/plugins/pom.xml b/plugins/pom.xml
index 28104b4..1ee7af5 100755
--- a/plugins/pom.xml
+++ b/plugins/pom.xml
@@ -107,6 +107,7 @@
<module>network-elements/vxlan</module>
<module>network-elements/globodns</module>
<module>database/quota</module>
+ <module>integrations/prometheus</module>
</modules>
<dependencies>
--
To stop receiving notification emails like this one, please contact
['"[email protected]" <[email protected]>'].