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 <rohit.yadav@shapeblue.com>
This commit is contained in:
Rohit Yadav 2017-10-11 17:24:22 +05:30 committed by GitHub
parent ed7811a9a2
commit 0fedbdd7a9
13 changed files with 915 additions and 0 deletions

View File

@ -41,4 +41,11 @@ public interface ImageStore extends Identity, InternalIdentity {
* @return data store protocol
*/
String getProtocol();
/**
*
* @return uri
*/
String getUrl();
}

View File

@ -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>

View File

@ -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);
/**

View File

@ -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);
@ -718,6 +725,14 @@ public class VMInstanceDaoImpl extends GenericDaoBase<VMInstanceVO, Long> implem
return customSearch(sc, null).get(0);
}
@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) {

View File

@ -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);

View File

@ -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>

View File

@ -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

View File

@ -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>

View File

@ -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();
}

View File

@ -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);
}
}
}

View File

@ -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);
}

View File

@ -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
};
}
}

View File

@ -107,6 +107,7 @@
<module>network-elements/vxlan</module>
<module>network-elements/globodns</module>
<module>database/quota</module>
<module>integrations/prometheus</module>
</modules>
<dependencies>