diff --git a/api/src/com/cloud/storage/ImageStore.java b/api/src/com/cloud/storage/ImageStore.java
index ec693c4e493..c019b17421d 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 3a0c5a5a720..ae0fcaa20a5 100644
--- a/client/pom.xml
+++ b/client/pom.xml
@@ -414,6 +414,11 @@
cloud-plugin-database-quota
${project.version}
+
+ org.apache.cloudstack
+ cloud-plugin-integrations-prometheus-exporter
+ ${project.version}
+
diff --git a/engine/schema/src/com/cloud/vm/dao/VMInstanceDao.java b/engine/schema/src/com/cloud/vm/dao/VMInstanceDao.java
index 8d457fadff5..3c5024b833e 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, StateDao<
Long countRunningByAccount(long accountId);
+ Long countByZoneAndState(long zoneId, State state);
+
List 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 df5e60e7a1a..7065350a57e 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 implem
protected GenericSearchBuilder FindIdsOfVirtualRoutersByAccount;
protected GenericSearchBuilder CountActiveByHost;
protected GenericSearchBuilder CountRunningByAccount;
+ protected GenericSearchBuilder CountByZoneAndState;
protected SearchBuilder NetworkTypeSearch;
protected GenericSearchBuilder DistinctHostNameSearch;
protected SearchBuilder HostAndStateSearch;
@@ -242,6 +243,12 @@ public class VMInstanceDaoImpl extends GenericDaoBase 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);
@@ -718,6 +725,14 @@ public class VMInstanceDaoImpl extends GenericDaoBase implem
return customSearch(sc, null).get(0);
}
+ @Override
+ public Long countByZoneAndState(long zoneId, State state) {
+ SearchCriteria sc = CountByZoneAndState.create();
+ sc.setParameters("zone", zoneId);
+ sc.setParameters("state", state);
+ return customSearch(sc, null).get(0);
+ }
+
@Override
public List 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 182a8ec4cab..41ce5a230b0 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
@@ -180,6 +180,11 @@ public class ImageStoreImpl implements ImageStoreEntity {
return imageDataStoreVO.getProtocol();
}
+ @Override
+ public String getUrl() {
+ return imageDataStoreVO.getUrl();
+ }
+
@Override
public DataStoreTO getTO() {
DataStoreTO to = getDriver().getStoreTO(this);
diff --git a/plugins/integrations/prometheus/pom.xml b/plugins/integrations/prometheus/pom.xml
new file mode 100644
index 00000000000..66dbebbf1d1
--- /dev/null
+++ b/plugins/integrations/prometheus/pom.xml
@@ -0,0 +1,48 @@
+
+
+ 4.0.0
+ cloud-plugin-integrations-prometheus-exporter
+ Apache CloudStack Plugin - Prometheus Exporter
+
+ org.apache.cloudstack
+ cloudstack-plugins
+ 4.11.0.0-SNAPSHOT
+ ../../pom.xml
+
+
+
+ org.apache.cloudstack
+ cloud-utils
+ ${project.version}
+
+
+ org.apache.cloudstack
+ cloud-api
+ ${project.version}
+
+
+ org.apache.cloudstack
+ cloud-engine-schema
+ ${project.version}
+
+
+
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 00000000000..cb70ec81e54
--- /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 00000000000..06fb92c1617
--- /dev/null
+++ b/plugins/integrations/prometheus/resources/META-INF/cloudstack/prometheus/spring-prometheus-context.xml
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
diff --git a/plugins/integrations/prometheus/src/org/apache/cloudstack/metrics/PrometheusExporter.java b/plugins/integrations/prometheus/src/org/apache/cloudstack/metrics/PrometheusExporter.java
new file mode 100644
index 00000000000..6361f0edc6b
--- /dev/null
+++ b/plugins/integrations/prometheus/src/org/apache/cloudstack/metrics/PrometheusExporter.java
@@ -0,0 +1,24 @@
+// 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;
+
+public interface PrometheusExporter {
+
+ void updateMetrics();
+
+ 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 00000000000..a51b2966631
--- /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- 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
- 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 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 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 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
- 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
- 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
- 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
- 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
- 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
- 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
- latestMetricsItems = new ArrayList
- ();
+ 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 00000000000..e0303524235
--- /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 EnablePrometheusExporter = new ConfigKey<>("Advanced", Boolean.class, "prometheus.exporter.enable", "false",
+ "Enable the prometheus exporter plugin, management server restart needed.", true);
+
+ ConfigKey PrometheusExporterServerPort = new ConfigKey<>("Advanced", Integer.class, "prometheus.exporter.port", "9595",
+ "The prometheus exporter server port", true);
+
+ ConfigKey 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 00000000000..a615c65766b
--- /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 = "CloudStack Exporter" +
+ "
CloudStack Exporter
" +
+ "Metrics
" +
+ "";
+ 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 28104b4047c..1ee7af58c60 100755
--- a/plugins/pom.xml
+++ b/plugins/pom.xml
@@ -107,6 +107,7 @@
network-elements/vxlan
network-elements/globodns
database/quota
+ integrations/prometheus