Add a prometheus exporter
Change-Id: I2989d87c2a83eed31c6617694bdcb78bf9b38553
diff --git a/apps/openstacktelemetry/app/BUCK b/apps/openstacktelemetry/app/BUCK
index 4cc3d98..0d8faa4 100644
--- a/apps/openstacktelemetry/app/BUCK
+++ b/apps/openstacktelemetry/app/BUCK
@@ -15,6 +15,18 @@
'//apps/openstacktelemetry/api:onos-apps-openstacktelemetry-api',
'//lib:kafka-clients',
'//lib:influxdb-java',
+ '//lib:simpleclient',
+ '//lib:simpleclient_common',
+ '//lib:simpleclient_hotspot',
+ '//lib:simpleclient_servlet',
+ '//lib:jetty-http',
+ '//lib:jetty-servlet',
+ '//lib:jetty-server',
+ '//lib:jetty-security',
+ '//lib:jetty-websocket',
+ '//lib:jetty-util',
+ '//lib:jetty-io',
+ '//lib:servlet-api',
'//lib:protobuf-java-3.2.0',
'//lib:GRPC_1.3',
'//incubator/grpc-dependencies:grpc-core-repkg-' + GRPC_VER,
diff --git a/apps/openstacktelemetry/app/BUILD b/apps/openstacktelemetry/app/BUILD
index c3f3b36..06b9f73 100644
--- a/apps/openstacktelemetry/app/BUILD
+++ b/apps/openstacktelemetry/app/BUILD
@@ -2,6 +2,16 @@
"@kafka_clients//jar",
"@jersey_client//jar",
"@influxdb_java//jar",
+ "@simpleclient//jar",
+ "@simpleclient_common//jar",
+ "@simpleclient_hotspot//jar",
+ "@simpleclient_servlet//jar",
+ "@jetty_servlet//jar",
+ "@jetty_http//jar",
+ "@jetty_server//jar",
+ "@jetty_util//jar",
+ "@jetty_websocket//jar",
+ "@servlet_api//jar",
"@io_grpc_grpc_java//core",
"@io_grpc_grpc_java//protobuf-lite",
"//core/store/serializers:onos-core-serializers",
diff --git a/apps/openstacktelemetry/app/src/main/java/org/onosproject/openstacktelemetry/config/DefaultPrometheusTelemetryConfig.java b/apps/openstacktelemetry/app/src/main/java/org/onosproject/openstacktelemetry/config/DefaultPrometheusTelemetryConfig.java
new file mode 100644
index 0000000..b22f053
--- /dev/null
+++ b/apps/openstacktelemetry/app/src/main/java/org/onosproject/openstacktelemetry/config/DefaultPrometheusTelemetryConfig.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright 2018-present Open Networking Foundation
+ *
+ * Licensed 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.onosproject.openstacktelemetry.config;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Maps;
+import org.onosproject.openstacktelemetry.api.config.PrometheusTelemetryConfig;
+import org.onosproject.openstacktelemetry.api.config.TelemetryConfig;
+
+import java.util.Map;
+import java.util.Objects;
+
+import static com.google.common.base.MoreObjects.toStringHelper;
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * A configuration file contains Prometheus telemetry parameters.
+ */
+public final class DefaultPrometheusTelemetryConfig implements PrometheusTelemetryConfig {
+
+ private final String address;
+ private final int port;
+ private final Map<String, Object> configMap;
+
+ private DefaultPrometheusTelemetryConfig(String address,
+ int port,
+ Map<String, Object> configMap) {
+ this.address = address;
+ this.port = port;
+ this.configMap = configMap;
+ }
+
+ @Override
+ public String address() {
+ return address;
+ }
+
+ @Override
+ public int port() {
+ return port;
+ }
+
+ @Override
+ public Map<String, Object> configMap() {
+ if (configMap != null) {
+ return ImmutableMap.copyOf(configMap);
+ } else {
+ return Maps.newConcurrentMap();
+ }
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+
+ if (obj instanceof DefaultPrometheusTelemetryConfig) {
+ final DefaultPrometheusTelemetryConfig other = (DefaultPrometheusTelemetryConfig) obj;
+ return Objects.equals(this.address, other.address) &&
+ Objects.equals(this.port, other.port) &&
+ Objects.equals(this.configMap, other.configMap);
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(address, port, configMap);
+ }
+
+ @Override
+ public String toString() {
+ return toStringHelper(this)
+ .add("address", address)
+ .add("port", port)
+ .add("configMap", configMap)
+ .toString();
+ }
+
+ @Override
+ public TelemetryConfig.Builder createBuilder() {
+ return new DefaultBuilder();
+ }
+
+ /**
+ * Builder class of DefaultPrometheusTelemetryConfig.
+ */
+ public static final class DefaultBuilder implements Builder {
+ private String address;
+ private int port;
+ private Map<String, Object> configMap;
+
+ @Override
+ public Builder withAddress(String address) {
+ this.address = address;
+ return this;
+ }
+
+ @Override
+ public Builder withPort(int port) {
+ this.port = port;
+ return this;
+ }
+
+ @Override
+ public Builder withConfigMap(Map<String, Object> configMap) {
+ this.configMap = configMap;
+ return this;
+ }
+ @Override
+ public PrometheusTelemetryConfig build() {
+ checkNotNull(address, "Prometheus exporter binding address cannot be null");
+ checkNotNull(configMap, "Config map cannot be null");
+ return new DefaultPrometheusTelemetryConfig(address, port, configMap);
+ }
+ }
+}
diff --git a/apps/openstacktelemetry/app/src/main/java/org/onosproject/openstacktelemetry/impl/InfluxDbTelemetryManager.java b/apps/openstacktelemetry/app/src/main/java/org/onosproject/openstacktelemetry/impl/InfluxDbTelemetryManager.java
index e35d662..41a318b 100644
--- a/apps/openstacktelemetry/app/src/main/java/org/onosproject/openstacktelemetry/impl/InfluxDbTelemetryManager.java
+++ b/apps/openstacktelemetry/app/src/main/java/org/onosproject/openstacktelemetry/impl/InfluxDbTelemetryManager.java
@@ -58,8 +58,6 @@
private static final String SRC_PORT = "srcPort";
private static final String DST_PORT = "dstPort";
private static final String PROTOCOL = "protocol";
- private static final String SRC_MAC = "srcMac";
- private static final String DST_MAC = "dstMac";
private static final String STARTUP_TIME = "startupTime";
private static final String FST_PKT_ARR_TIME = "fstPktArrTime";
@@ -130,7 +128,7 @@
producer = null;
}
- log.info("InfluxDB producer has Stopped");
+ log.info("InfluxDB producer has stopped");
}
@Override
diff --git a/apps/openstacktelemetry/app/src/main/java/org/onosproject/openstacktelemetry/impl/OpenstackTelemetryManager.java b/apps/openstacktelemetry/app/src/main/java/org/onosproject/openstacktelemetry/impl/OpenstackTelemetryManager.java
index 761e01b..3e629cc 100644
--- a/apps/openstacktelemetry/app/src/main/java/org/onosproject/openstacktelemetry/impl/OpenstackTelemetryManager.java
+++ b/apps/openstacktelemetry/app/src/main/java/org/onosproject/openstacktelemetry/impl/OpenstackTelemetryManager.java
@@ -30,6 +30,7 @@
import org.onosproject.openstacktelemetry.api.InfluxDbTelemetryService;
import org.onosproject.openstacktelemetry.api.KafkaTelemetryService;
import org.onosproject.openstacktelemetry.api.OpenstackTelemetryService;
+import org.onosproject.openstacktelemetry.api.PrometheusTelemetryService;
import org.onosproject.openstacktelemetry.api.RestTelemetryService;
import org.onosproject.openstacktelemetry.api.TelemetryService;
import org.onosproject.openstacktelemetry.codec.TinaMessageByteBufferCodec;
@@ -96,6 +97,12 @@
invokeInfluxDbPublisher((InfluxDbTelemetryService) service, flowInfos);
}
+ if (service instanceof PrometheusTelemetryManager &&
+ getPropertyValueAsBoolean(componentConfigService.getProperties(
+ PrometheusTelemetryConfigManager.class.getName()), ENABLE_SERVICE)) {
+ invokePrometheusPublisher((PrometheusTelemetryService) service, flowInfos);
+ }
+
if (service instanceof KafkaTelemetryManager &&
getPropertyValueAsBoolean(componentConfigService.getProperties(
KafkaTelemetryConfigManager.class.getName()), ENABLE_SERVICE)) {
@@ -127,6 +134,10 @@
service.publish(influxRecord);
}
+ private void invokePrometheusPublisher(PrometheusTelemetryService service, Set<FlowInfo> flowInfos) {
+ service.publish(flowInfos);
+ }
+
private void invokeKafkaPublisher(KafkaTelemetryService service, Set<FlowInfo> flowInfos) {
TinaMessageByteBufferCodec codec = new TinaMessageByteBufferCodec();
ByteBuffer buffer = codec.encode(flowInfos);
diff --git a/apps/openstacktelemetry/app/src/main/java/org/onosproject/openstacktelemetry/impl/PrometheusTelemetryConfigManager.java b/apps/openstacktelemetry/app/src/main/java/org/onosproject/openstacktelemetry/impl/PrometheusTelemetryConfigManager.java
new file mode 100644
index 0000000..23cc240
--- /dev/null
+++ b/apps/openstacktelemetry/app/src/main/java/org/onosproject/openstacktelemetry/impl/PrometheusTelemetryConfigManager.java
@@ -0,0 +1,140 @@
+/*
+ * Copyright 2018-present Open Networking Foundation
+ *
+ * Licensed 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.onosproject.openstacktelemetry.impl;
+
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Modified;
+import org.apache.felix.scr.annotations.Property;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.apache.felix.scr.annotations.Service;
+import org.onlab.util.Tools;
+import org.onosproject.cfg.ComponentConfigService;
+import org.onosproject.openstacktelemetry.api.PrometheusTelemetryAdminService;
+import org.onosproject.openstacktelemetry.api.PrometheusTelemetryConfigService;
+import org.onosproject.openstacktelemetry.api.config.TelemetryConfig;
+import org.onosproject.openstacktelemetry.config.DefaultPrometheusTelemetryConfig;
+import org.osgi.service.component.ComponentContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Dictionary;
+
+import static org.onosproject.openstacktelemetry.api.Constants.DEFAULT_DISABLE;
+import static org.onosproject.openstacktelemetry.api.Constants.DEFAULT_ENABLE;
+import static org.onosproject.openstacktelemetry.api.Constants.DEFAULT_PROMETHEUS_EXPORTER_IP;
+import static org.onosproject.openstacktelemetry.api.Constants.DEFAULT_PROMETHEUS_EXPORTER_PORT;
+import static org.onosproject.openstacktelemetry.util.OpenstackTelemetryUtil.getBooleanProperty;
+import static org.onosproject.openstacktelemetry.util.OpenstackTelemetryUtil.initTelemetryService;
+
+/**
+ * Prometheus exporter configuration manager for publishing openstack telemetry.
+ */
+@Component(immediate = true)
+@Service
+public class PrometheusTelemetryConfigManager implements PrometheusTelemetryConfigService {
+
+ private final Logger log = LoggerFactory.getLogger(getClass());
+
+ private static final String ENABLE_SERVICE = "enableService";
+ private static final String ADDRESS = "address";
+ private static final String PORT = "port";
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected ComponentConfigService componentConfigService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected PrometheusTelemetryAdminService prometheusTelemetryAdminService;
+
+ @Property(name = ADDRESS, value = DEFAULT_PROMETHEUS_EXPORTER_IP,
+ label = "Default IP address of prometheus exporter")
+ protected String address = DEFAULT_PROMETHEUS_EXPORTER_IP;
+
+ @Property(name = PORT, intValue = DEFAULT_PROMETHEUS_EXPORTER_PORT,
+ label = "Default port number of prometheus exporter")
+ protected Integer port = DEFAULT_PROMETHEUS_EXPORTER_PORT;
+
+ @Property(name = ENABLE_SERVICE, boolValue = DEFAULT_ENABLE,
+ label = "Specify the default behavior of telemetry service")
+ protected Boolean enableService = DEFAULT_ENABLE;
+
+ @Activate
+ protected void activate(ComponentContext context) {
+ componentConfigService.registerProperties(getClass());
+ if (enableService) {
+ prometheusTelemetryAdminService.start(getConfig());
+ }
+ log.info("Started");
+ }
+
+ @Deactivate
+ protected void deactivate() {
+ componentConfigService.unregisterProperties(getClass(), false);
+ if (enableService) {
+ prometheusTelemetryAdminService.stop();
+ }
+ log.info("Stopped");
+ }
+
+ @Modified
+ private void modified(ComponentContext context) {
+ readComponentConfiguration(context);
+ initTelemetryService(prometheusTelemetryAdminService, getConfig(), enableService);
+ log.info("Modified");
+ }
+
+ @Override
+ public TelemetryConfig getConfig() {
+ return new DefaultPrometheusTelemetryConfig.DefaultBuilder()
+ .withAddress(address)
+ .withPort(port)
+ .build();
+ }
+
+ /**
+ * Extracts properties from the component configuration context.
+ *
+ * @param context the component context
+ */
+ private void readComponentConfiguration(ComponentContext context) {
+ Dictionary<?, ?> properties = context.getProperties();
+
+ String addressStr = Tools.get(properties, ADDRESS);
+ address = addressStr != null ? addressStr : DEFAULT_PROMETHEUS_EXPORTER_IP;
+ log.info("Configured. Prometheus exporter address is {}", address);
+
+ Integer portConfigured = Tools.getIntegerProperty(properties, PORT);
+ if (portConfigured == null) {
+ port = DEFAULT_PROMETHEUS_EXPORTER_PORT;
+ log.info("Prometheus exporter port is NOT configured, default value is {}", port);
+ } else {
+ port = portConfigured;
+ log.info("Configured. Prometheus exporter port is {}", port);
+ }
+
+ Boolean enableServiceConfigured = getBooleanProperty(properties, ENABLE_SERVICE);
+ if (enableServiceConfigured == null) {
+ enableService = DEFAULT_DISABLE;
+ log.info("Prometheus service enable flag is NOT " +
+ "configured, default value is {}", enableService);
+ } else {
+ enableService = enableServiceConfigured;
+ log.info("Configured. Prometheus service enable flag is {}", enableService);
+ }
+ }
+}
diff --git a/apps/openstacktelemetry/app/src/main/java/org/onosproject/openstacktelemetry/impl/PrometheusTelemetryManager.java b/apps/openstacktelemetry/app/src/main/java/org/onosproject/openstacktelemetry/impl/PrometheusTelemetryManager.java
new file mode 100644
index 0000000..f06a961
--- /dev/null
+++ b/apps/openstacktelemetry/app/src/main/java/org/onosproject/openstacktelemetry/impl/PrometheusTelemetryManager.java
@@ -0,0 +1,196 @@
+/*
+ * Copyright 2018-present Open Networking Foundation
+ *
+ * Licensed 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.onosproject.openstacktelemetry.impl;
+
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.apache.felix.scr.annotations.Service;
+import org.onosproject.openstacktelemetry.api.FlowInfo;
+import org.onosproject.openstacktelemetry.api.OpenstackTelemetryService;
+import org.onosproject.openstacktelemetry.api.PrometheusTelemetryAdminService;
+import org.onosproject.openstacktelemetry.api.config.PrometheusTelemetryConfig;
+import org.onosproject.openstacktelemetry.api.config.TelemetryConfig;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import io.prometheus.client.Counter;
+import io.prometheus.client.exporter.MetricsServlet;
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.servlet.ServletContextHandler;
+import org.eclipse.jetty.servlet.ServletHolder;
+import java.util.Set;
+
+/**
+ * Prometheus telemetry manager.
+ */
+@Component(immediate = true)
+@Service
+public class PrometheusTelemetryManager implements PrometheusTelemetryAdminService {
+
+ private final Logger log = LoggerFactory.getLogger(getClass());
+
+ private Server prometheusExporter;
+
+ private static final String BYTE_VM2VM = "byte_vm2vm";
+ private static final String BYTE_DEVICE = "byte_device";
+ private static final String BYTE_SRC_IP = "byte_src_ip";
+ private static final String BYTE_DST_IP = "byte_dst_ip";
+
+ private static final String PKT_VM2VM = "pkt_vm2vm";
+ private static final String PKT_DEVICE = "pkt_device";
+ private static final String PKT_SRC_IP = "pkt_src_ip";
+ private static final String PKT_DST_IP = "pkt_dst_ip";
+
+ private static final String PKT_ERROR = "pkt_error";
+ private static final String PKT_DROP = "pkt_drop";
+
+ private static final String LABEL_IP_5_TUPLE = "IP_5_TUPLE";
+ private static final String LABEL_DEV_ID = "DEVICE_ID";
+ private static final String LABEL_SRC_IP = "SOURCE_IP";
+ private static final String LABEL_DST_IP = "DESTINATION_IP";
+
+ private static final String HELP_MSG = "SONA Flow statistics";
+
+ private static Counter byteVM2VM = Counter.build().name(BYTE_VM2VM)
+ .help(HELP_MSG)
+ .labelNames(LABEL_IP_5_TUPLE).register();
+
+ private static Counter byteDevice = Counter.build().name(BYTE_DEVICE)
+ .help(HELP_MSG)
+ .labelNames(LABEL_DEV_ID).register();
+
+ private static Counter byteSrcIp = Counter.build().name(BYTE_SRC_IP)
+ .help(HELP_MSG)
+ .labelNames(LABEL_SRC_IP).register();
+
+ private static Counter byteDstIp = Counter.build().name(BYTE_DST_IP)
+ .help(HELP_MSG)
+ .labelNames(LABEL_DST_IP).register();
+
+ private static Counter pktVM2VM = Counter.build().name(PKT_VM2VM)
+ .help(HELP_MSG)
+ .labelNames(LABEL_IP_5_TUPLE).register();
+
+ private static Counter pktDevice = Counter.build().name(PKT_DEVICE)
+ .help(HELP_MSG)
+ .labelNames(LABEL_DEV_ID).register();
+
+ private static Counter pktSrcIp = Counter.build().name(PKT_SRC_IP)
+ .help(HELP_MSG)
+ .labelNames(LABEL_SRC_IP).register();
+
+ private static Counter pktDstIp = Counter.build().name(PKT_DST_IP)
+ .help(HELP_MSG)
+ .labelNames(LABEL_DST_IP).register();
+
+ private static Counter pktError = Counter.build().name(PKT_ERROR)
+ .help(HELP_MSG)
+ .register();
+ private static Counter pktDrop = Counter.build().name(PKT_DROP)
+ .help(HELP_MSG)
+ .register();
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected OpenstackTelemetryService openstackTelemetryService;
+
+ @Activate
+ protected void activate() {
+ openstackTelemetryService.addTelemetryService(this);
+ log.info("Started");
+ }
+
+ @Deactivate
+ protected void deactivate() {
+ stop();
+ openstackTelemetryService.removeTelemetryService(this);
+ log.info("Stopped");
+ }
+
+ @Override
+ public void start(TelemetryConfig config) {
+ log.info("Prometheus exporter starts.");
+
+ PrometheusTelemetryConfig prometheusConfig = (PrometheusTelemetryConfig) config;
+
+ try {
+ // TODO Offer a 'Authentication'
+ prometheusExporter = new Server(prometheusConfig.port());
+ ServletContextHandler context = new ServletContextHandler();
+ context.setContextPath("/");
+ prometheusExporter.setHandler(context);
+ context.addServlet(new ServletHolder(new MetricsServlet()), "/metrics");
+
+ log.info("Prometeus server start");
+
+ prometheusExporter.start();
+ } catch (Exception ex) {
+ log.warn("Exception: {}", ex.toString());
+ }
+ }
+
+ @Override
+ public void stop() {
+ try {
+ prometheusExporter.stop();
+ } catch (Exception ex) {
+ log.warn("Exception: {}", ex.toString());
+ }
+ log.info("Prometheus exporter has stopped");
+ }
+
+ @Override
+ public void restart(TelemetryConfig config) {
+ stop();
+ start(config);
+ }
+
+ @Override
+ public void publish(Set<FlowInfo> flowInfos) {
+ if (flowInfos.size() == 0) {
+ log.debug("No record to publish");
+ return;
+ }
+
+ long flowByte;
+ int flowPkt;
+ for (FlowInfo flowInfo: flowInfos) {
+ flowByte = flowInfo.statsInfo().currAccBytes() - flowInfo.statsInfo().prevAccBytes();
+ flowPkt = flowInfo.statsInfo().currAccPkts() - flowInfo.statsInfo().prevAccPkts();
+
+ byteVM2VM.labels(flowInfo.uniqueFlowInfoKey()).inc(flowByte);
+ byteDevice.labels(flowInfo.deviceId().toString()).inc(flowByte);
+ byteSrcIp.labels(flowInfo.srcIp().toString()).inc(flowByte);
+ byteDstIp.labels(flowInfo.dstIp().toString()).inc(flowByte);
+
+ pktVM2VM.labels(flowInfo.uniqueFlowInfoKey()).inc(flowPkt);
+ pktDevice.labels(flowInfo.deviceId().toString()).inc(flowPkt);
+ pktSrcIp.labels(flowInfo.srcIp().toString()).inc(flowPkt);
+ pktDstIp.labels(flowInfo.dstIp().toString()).inc(flowPkt);
+
+ pktError.inc(flowInfo.statsInfo().errorPkts());
+ pktDrop.inc(flowInfo.statsInfo().dropPkts());
+ }
+ }
+
+ @Override
+ public boolean isRunning() {
+ log.info("Prometheus Exporter State: {}", prometheusExporter.isRunning());
+ return prometheusExporter.isRunning();
+ }
+}
diff --git a/apps/openstacktelemetry/app/src/test/java/org/onosproject/openstacktelemetry/config/DefaultPrometheusTelemetryConfigTest.java b/apps/openstacktelemetry/app/src/test/java/org/onosproject/openstacktelemetry/config/DefaultPrometheusTelemetryConfigTest.java
new file mode 100644
index 0000000..3e291c2
--- /dev/null
+++ b/apps/openstacktelemetry/app/src/test/java/org/onosproject/openstacktelemetry/config/DefaultPrometheusTelemetryConfigTest.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright 2018-present Open Networking Foundation
+ *
+ * Licensed 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.onosproject.openstacktelemetry.config;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.common.testing.EqualsTester;
+import org.junit.Before;
+import org.junit.Test;
+import org.onosproject.openstacktelemetry.api.config.PrometheusTelemetryConfig;
+
+import java.util.Map;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.is;
+import static org.onlab.junit.ImmutableClassChecker.assertThatClassIsImmutable;
+import static org.onosproject.openstacktelemetry.api.Constants.DEFAULT_PROMETHEUS_EXPORTER_IP;
+import static org.onosproject.openstacktelemetry.api.Constants.DEFAULT_PROMETHEUS_EXPORTER_PORT;
+
+/**
+ * Unit tests for DefaultPrometheusTelemetryConfig class.
+ */
+public class DefaultPrometheusTelemetryConfigTest {
+
+ private static final String IP_ADDRESS_1 = DEFAULT_PROMETHEUS_EXPORTER_IP;
+ private static final String IP_ADDRESS_2 = "10.10.1.2";
+
+ private static final int PORT_1 = DEFAULT_PROMETHEUS_EXPORTER_PORT;
+ private static final int PORT_2 = DEFAULT_PROMETHEUS_EXPORTER_PORT + 1;
+
+ private static final Map<String, Object> CONFIG_MAP_1 =
+ ImmutableMap.of("key1", "value1");
+ private static final Map<String, Object> CONFIG_MAP_2 =
+ ImmutableMap.of("key2", "value2");
+
+ private PrometheusTelemetryConfig config1;
+ private PrometheusTelemetryConfig sameAsConfig1;
+ private PrometheusTelemetryConfig config2;
+
+ /**
+ * Initial setup for this unit test.
+ */
+ @Before
+ public void setup() {
+
+ PrometheusTelemetryConfig.Builder builder1 =
+ new DefaultPrometheusTelemetryConfig.DefaultBuilder();
+ PrometheusTelemetryConfig.Builder builder2 =
+ new DefaultPrometheusTelemetryConfig.DefaultBuilder();
+ PrometheusTelemetryConfig.Builder builder3 =
+ new DefaultPrometheusTelemetryConfig.DefaultBuilder();
+
+ config1 = builder1
+ .withAddress(IP_ADDRESS_1)
+ .withPort(PORT_1)
+ .withConfigMap(CONFIG_MAP_1)
+ .build();
+
+ sameAsConfig1 = builder2
+ .withAddress(IP_ADDRESS_1)
+ .withPort(PORT_1)
+ .withConfigMap(CONFIG_MAP_1)
+ .build();
+
+ config2 = builder3
+ .withAddress(IP_ADDRESS_2)
+ .withPort(PORT_2)
+ .withConfigMap(CONFIG_MAP_2)
+ .build();
+ }
+
+ /**
+ * Tests class immutability.
+ */
+ @Test
+ public void testImmutability() {
+ assertThatClassIsImmutable(DefaultPrometheusTelemetryConfig.class);
+ }
+
+ /**
+ * Tests object equality.
+ */
+ @Test
+ public void testEquality() {
+ new EqualsTester()
+ .addEqualityGroup(config1, sameAsConfig1)
+ .addEqualityGroup(config2).testEquals();
+ }
+
+ /**
+ * Tests object construction.
+ */
+ @Test
+ public void testConstruction() {
+ PrometheusTelemetryConfig config = config1;
+
+ assertThat(config.address(), is(IP_ADDRESS_1));
+ assertThat(config.port(), is(PORT_1));
+ assertThat(config.configMap(), is(CONFIG_MAP_1));
+ }
+}