Refactor: move telemetry config from componentCfg to configs.xml

1. Support to export the metrics to multiple targets
2. Add a set of properties to kafka config (key, topic, etc.)
3. Add distributedStore to manage telemetry configs
4. Add CLI to query stored telemetry configs
5. Add a set of telemetry loaders to import xml definitions
6. Add unit tests for telemetry cfg, xml cfg loader and dist store
7. Add missing javadoc for a set of implementation classes

Change-Id: I39480c9a6ac07357184d2e1094b9c9f4d36fd8b1
diff --git a/apps/openstacktelemetry/app/src/test/java/org/onosproject/openstacktelemetry/config/DefaultGrpcTelemetryConfigTest.java b/apps/openstacktelemetry/app/src/test/java/org/onosproject/openstacktelemetry/config/DefaultGrpcTelemetryConfigTest.java
index 1a7f752..c56247f 100644
--- a/apps/openstacktelemetry/app/src/test/java/org/onosproject/openstacktelemetry/config/DefaultGrpcTelemetryConfigTest.java
+++ b/apps/openstacktelemetry/app/src/test/java/org/onosproject/openstacktelemetry/config/DefaultGrpcTelemetryConfigTest.java
@@ -15,17 +15,27 @@
  */
 package org.onosproject.openstacktelemetry.config;
 
+import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Maps;
 import com.google.common.testing.EqualsTester;
 import org.junit.Before;
 import org.junit.Test;
 import org.onosproject.openstacktelemetry.api.config.GrpcTelemetryConfig;
+import org.onosproject.openstacktelemetry.api.config.TelemetryConfig;
+import org.onosproject.openstacktelemetry.impl.DefaultTelemetryConfig;
 
 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.config.TelemetryConfig.ConfigType.GRPC;
+import static org.onosproject.openstacktelemetry.config.DefaultGrpcTelemetryConfig.ADDRESS;
+import static org.onosproject.openstacktelemetry.config.DefaultGrpcTelemetryConfig.MAX_INBOUND_MSG_SIZE;
+import static org.onosproject.openstacktelemetry.config.DefaultGrpcTelemetryConfig.PORT;
+import static org.onosproject.openstacktelemetry.config.DefaultGrpcTelemetryConfig.USE_PLAINTEXT;
+import static org.onosproject.openstacktelemetry.config.DefaultGrpcTelemetryConfig.fromTelemetryConfig;
 
 /**
  * Unit tests for DefaultGrpcTelemetryConfig class.
@@ -45,9 +55,11 @@
     private static final boolean USE_PLAIN_TEXT_2 = true;
 
     private static final Map<String, Object> CONFIG_MAP_1 =
-                                    ImmutableMap.of("key1", "value1");
+            ImmutableMap.of("key1", "value1");
     private static final Map<String, Object> CONFIG_MAP_2 =
-                                    ImmutableMap.of("key2", "value2");
+            ImmutableMap.of("key2", "value2");
+
+    private static final String DUMMY = "dummy";
 
     private GrpcTelemetryConfig config1;
     private GrpcTelemetryConfig sameAsConfig1;
@@ -122,4 +134,24 @@
         assertThat(config.usePlaintext(), is(USE_PLAIN_TEXT_1));
         assertThat(config.configMap(), is(CONFIG_MAP_1));
     }
-}
+
+    /**
+     * Tests props extraction.
+     */
+    @Test
+    public void testPropsExtraction() {
+        Map<String, String> props = Maps.newConcurrentMap();
+        props.put(ADDRESS, IP_ADDRESS_1);
+        props.put(PORT, String.valueOf(PORT_1));
+        props.put(MAX_INBOUND_MSG_SIZE, String.valueOf(MSG_SIZE_1));
+        props.put(USE_PLAINTEXT, String.valueOf(USE_PLAIN_TEXT_1));
+        TelemetryConfig config = new DefaultTelemetryConfig(DUMMY, GRPC,
+                ImmutableList.of(), DUMMY, DUMMY, false, props);
+
+        GrpcTelemetryConfig grpcConfig = fromTelemetryConfig(config);
+        assertThat(grpcConfig.address(), is(IP_ADDRESS_1));
+        assertThat(grpcConfig.port(), is(PORT_1));
+        assertThat(grpcConfig.maxInboundMsgSize(), is(MSG_SIZE_1));
+        assertThat(grpcConfig.usePlaintext(), is(USE_PLAIN_TEXT_1));
+    }
+}
\ No newline at end of file
diff --git a/apps/openstacktelemetry/app/src/test/java/org/onosproject/openstacktelemetry/config/DefaultInfluxDbTelemetryConfigTest.java b/apps/openstacktelemetry/app/src/test/java/org/onosproject/openstacktelemetry/config/DefaultInfluxDbTelemetryConfigTest.java
index 2357fec..cd4a973 100644
--- a/apps/openstacktelemetry/app/src/test/java/org/onosproject/openstacktelemetry/config/DefaultInfluxDbTelemetryConfigTest.java
+++ b/apps/openstacktelemetry/app/src/test/java/org/onosproject/openstacktelemetry/config/DefaultInfluxDbTelemetryConfigTest.java
@@ -15,17 +15,30 @@
  */
 package org.onosproject.openstacktelemetry.config;
 
+import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Maps;
 import com.google.common.testing.EqualsTester;
 import org.junit.Before;
 import org.junit.Test;
 import org.onosproject.openstacktelemetry.api.config.InfluxDbTelemetryConfig;
+import org.onosproject.openstacktelemetry.api.config.TelemetryConfig;
+import org.onosproject.openstacktelemetry.impl.DefaultTelemetryConfig;
 
 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.config.TelemetryConfig.ConfigType.INFLUXDB;
+import static org.onosproject.openstacktelemetry.config.DefaultInfluxDbTelemetryConfig.ADDRESS;
+import static org.onosproject.openstacktelemetry.config.DefaultInfluxDbTelemetryConfig.DATABASE;
+import static org.onosproject.openstacktelemetry.config.DefaultInfluxDbTelemetryConfig.ENABLE_BATCH;
+import static org.onosproject.openstacktelemetry.config.DefaultInfluxDbTelemetryConfig.MEASUREMENT;
+import static org.onosproject.openstacktelemetry.config.DefaultInfluxDbTelemetryConfig.PASSWORD;
+import static org.onosproject.openstacktelemetry.config.DefaultInfluxDbTelemetryConfig.PORT;
+import static org.onosproject.openstacktelemetry.config.DefaultInfluxDbTelemetryConfig.USERNAME;
+import static org.onosproject.openstacktelemetry.config.DefaultInfluxDbTelemetryConfig.fromTelemetryConfig;
 
 /**
  * Unit tests for DefaultInfluxDbTelemetryConfig class.
@@ -58,6 +71,8 @@
     private static final Map<String, Object> CONFIG_MAP_2 =
             ImmutableMap.of("key2", "value2");
 
+    private static final String DUMMY = "dummy";
+
     private InfluxDbTelemetryConfig config1;
     private InfluxDbTelemetryConfig sameAsConfig1;
     private InfluxDbTelemetryConfig config2;
@@ -143,4 +158,31 @@
         assertThat(config.enableBatch(), is(ENABLE_BATCH_1));
         assertThat(config.configMap(), is(CONFIG_MAP_1));
     }
-}
+
+    /**
+     * Tests props extraction.
+     */
+    @Test
+    public void testPropsExtraction() {
+        Map<String, String> props = Maps.newConcurrentMap();
+        props.put(ADDRESS, IP_ADDRESS_1);
+        props.put(PORT, String.valueOf(PORT_1));
+        props.put(USERNAME, USERNAME_1);
+        props.put(PASSWORD, PASSWORD_1);
+        props.put(DATABASE, DATABASE_1);
+        props.put(ENABLE_BATCH, String.valueOf(ENABLE_BATCH_1));
+        props.put(MEASUREMENT, MEASUREMENT_1);
+
+        TelemetryConfig config = new DefaultTelemetryConfig(DUMMY, INFLUXDB,
+                ImmutableList.of(), DUMMY, DUMMY, false, props);
+
+        InfluxDbTelemetryConfig influxDbConfig = fromTelemetryConfig(config);
+        assertThat(influxDbConfig.address(), is(IP_ADDRESS_1));
+        assertThat(influxDbConfig.port(), is(PORT_1));
+        assertThat(influxDbConfig.database(), is(DATABASE_1));
+        assertThat(influxDbConfig.measurement(), is(MEASUREMENT_1));
+        assertThat(influxDbConfig.username(), is(USERNAME_1));
+        assertThat(influxDbConfig.password(), is(PASSWORD_1));
+        assertThat(influxDbConfig.enableBatch(), is(ENABLE_BATCH_1));
+    }
+}
\ No newline at end of file
diff --git a/apps/openstacktelemetry/app/src/test/java/org/onosproject/openstacktelemetry/config/DefaultKafkaTelemetryConfigTest.java b/apps/openstacktelemetry/app/src/test/java/org/onosproject/openstacktelemetry/config/DefaultKafkaTelemetryConfigTest.java
index d3958fe..ba9b34b 100644
--- a/apps/openstacktelemetry/app/src/test/java/org/onosproject/openstacktelemetry/config/DefaultKafkaTelemetryConfigTest.java
+++ b/apps/openstacktelemetry/app/src/test/java/org/onosproject/openstacktelemetry/config/DefaultKafkaTelemetryConfigTest.java
@@ -15,17 +15,35 @@
  */
 package org.onosproject.openstacktelemetry.config;
 
+import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Maps;
 import com.google.common.testing.EqualsTester;
 import org.junit.Before;
 import org.junit.Test;
 import org.onosproject.openstacktelemetry.api.config.KafkaTelemetryConfig;
+import org.onosproject.openstacktelemetry.api.config.TelemetryConfig;
+import org.onosproject.openstacktelemetry.impl.DefaultTelemetryConfig;
 
 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.config.TelemetryConfig.ConfigType.KAFKA;
+import static org.onosproject.openstacktelemetry.config.DefaultKafkaTelemetryConfig.ADDRESS;
+import static org.onosproject.openstacktelemetry.config.DefaultKafkaTelemetryConfig.BATCH_SIZE;
+import static org.onosproject.openstacktelemetry.config.DefaultKafkaTelemetryConfig.CODEC;
+import static org.onosproject.openstacktelemetry.config.DefaultKafkaTelemetryConfig.KEY;
+import static org.onosproject.openstacktelemetry.config.DefaultKafkaTelemetryConfig.KEY_SERIALIZER;
+import static org.onosproject.openstacktelemetry.config.DefaultKafkaTelemetryConfig.LINGER_MS;
+import static org.onosproject.openstacktelemetry.config.DefaultKafkaTelemetryConfig.MEMORY_BUFFER;
+import static org.onosproject.openstacktelemetry.config.DefaultKafkaTelemetryConfig.PORT;
+import static org.onosproject.openstacktelemetry.config.DefaultKafkaTelemetryConfig.REQUIRED_ACKS;
+import static org.onosproject.openstacktelemetry.config.DefaultKafkaTelemetryConfig.RETRIES;
+import static org.onosproject.openstacktelemetry.config.DefaultKafkaTelemetryConfig.TOPIC;
+import static org.onosproject.openstacktelemetry.config.DefaultKafkaTelemetryConfig.VALUE_SERIALIZER;
+import static org.onosproject.openstacktelemetry.config.DefaultKafkaTelemetryConfig.fromTelemetryConfig;
 
 public final class DefaultKafkaTelemetryConfigTest {
 
@@ -55,11 +73,22 @@
     private static final String VALUE_SERIALIZER_1 = "valueserializer1";
     private static final String VALUE_SERIALIZER_2 = "valueserializer2";
 
+    private static final String KEY_1 = "key1";
+    private static final String KEY_2 = "key2";
+
+    private static final String TOPIC_1 = "topic1";
+    private static final String TOPIC_2 = "topic2";
+
+    private static final String CODEC_1 = "codec1";
+    private static final String CODEC_2 = "codec2";
+
     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 static final String DUMMY = "dummy";
+
     private KafkaTelemetryConfig config1;
     private KafkaTelemetryConfig sameAsConfig1;
     private KafkaTelemetryConfig config2;
@@ -87,6 +116,9 @@
                 .withLingerMs(LINGER_MS_1)
                 .withKeySerializer(KEY_SERIALIZER_1)
                 .withValueSerializer(VALUE_SERIALIZER_1)
+                .withKey(KEY_1)
+                .withTopic(TOPIC_1)
+                .withCodec(CODEC_1)
                 .withConfigMap(CONFIG_MAP_1)
                 .build();
 
@@ -100,6 +132,9 @@
                 .withLingerMs(LINGER_MS_1)
                 .withKeySerializer(KEY_SERIALIZER_1)
                 .withValueSerializer(VALUE_SERIALIZER_1)
+                .withKey(KEY_1)
+                .withTopic(TOPIC_1)
+                .withCodec(CODEC_1)
                 .withConfigMap(CONFIG_MAP_1)
                 .build();
 
@@ -113,6 +148,9 @@
                 .withLingerMs(LINGER_MS_2)
                 .withKeySerializer(KEY_SERIALIZER_2)
                 .withValueSerializer(VALUE_SERIALIZER_2)
+                .withKey(KEY_2)
+                .withTopic(TOPIC_2)
+                .withCodec(CODEC_2)
                 .withConfigMap(CONFIG_MAP_2)
                 .build();
     }
@@ -151,6 +189,46 @@
         assertThat(config.lingerMs(), is(LINGER_MS_1));
         assertThat(config.keySerializer(), is(KEY_SERIALIZER_1));
         assertThat(config.valueSerializer(), is(VALUE_SERIALIZER_1));
+        assertThat(config.key(), is(KEY_1));
+        assertThat(config.topic(), is(TOPIC_1));
+        assertThat(config.codec(), is(CODEC_1));
         assertThat(config.configMap(), is(CONFIG_MAP_1));
     }
-}
+
+    /**
+     * Tests props extraction.
+     */
+    @Test
+    public void testPropsExtraction() {
+        Map<String, String> props = Maps.newConcurrentMap();
+        props.put(ADDRESS, IP_ADDRESS_1);
+        props.put(PORT, String.valueOf(PORT_1));
+        props.put(RETRIES, String.valueOf(RETRIES_1));
+        props.put(BATCH_SIZE, String.valueOf(BATCH_SIZE_1));
+        props.put(MEMORY_BUFFER, String.valueOf(MEMORY_BUFFER_1));
+        props.put(REQUIRED_ACKS, REQUIRED_ACKS_1);
+        props.put(LINGER_MS, String.valueOf(LINGER_MS_1));
+        props.put(KEY_SERIALIZER, KEY_SERIALIZER_1);
+        props.put(VALUE_SERIALIZER, VALUE_SERIALIZER_1);
+        props.put(KEY, KEY_1);
+        props.put(TOPIC, TOPIC_1);
+        props.put(CODEC, CODEC_1);
+
+        TelemetryConfig config = new DefaultTelemetryConfig(DUMMY, KAFKA,
+                ImmutableList.of(), DUMMY, DUMMY, false, props);
+
+        KafkaTelemetryConfig kafkaConfig = fromTelemetryConfig(config);
+        assertThat(kafkaConfig.address(), is(IP_ADDRESS_1));
+        assertThat(kafkaConfig.port(), is(PORT_1));
+        assertThat(kafkaConfig.retries(), is(RETRIES_1));
+        assertThat(kafkaConfig.batchSize(), is(BATCH_SIZE_1));
+        assertThat(kafkaConfig.memoryBuffer(), is(MEMORY_BUFFER_1));
+        assertThat(kafkaConfig.requiredAcks(), is(REQUIRED_ACKS_1));
+        assertThat(kafkaConfig.lingerMs(), is(LINGER_MS_1));
+        assertThat(kafkaConfig.keySerializer(), is(KEY_SERIALIZER_1));
+        assertThat(kafkaConfig.valueSerializer(), is(VALUE_SERIALIZER_1));
+        assertThat(kafkaConfig.key(), is(KEY_1));
+        assertThat(kafkaConfig.topic(), is(TOPIC_1));
+        assertThat(kafkaConfig.codec(), is(CODEC_1));
+    }
+}
\ No newline at end of file
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
index 7012ee8..de94f83 100644
--- 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
@@ -15,36 +15,43 @@
  */
 package org.onosproject.openstacktelemetry.config;
 
+import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Maps;
 import com.google.common.testing.EqualsTester;
 import org.junit.Before;
 import org.junit.Test;
 import org.onosproject.openstacktelemetry.api.config.PrometheusTelemetryConfig;
+import org.onosproject.openstacktelemetry.api.config.TelemetryConfig;
+import org.onosproject.openstacktelemetry.impl.DefaultTelemetryConfig;
 
 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.impl.OsgiPropertyConstants.PROP_PROMETHEUS_EXPORTER_PORT_DEFAULT;
-import static org.onosproject.openstacktelemetry.impl.OsgiPropertyConstants.PROP_PROMETHEUS_EXPORTER_ADDRESS_DEFAULT;
+import static org.onosproject.openstacktelemetry.api.config.TelemetryConfig.ConfigType.PROMETHEUS;
+import static org.onosproject.openstacktelemetry.config.DefaultPrometheusTelemetryConfig.ADDRESS;
+import static org.onosproject.openstacktelemetry.config.DefaultPrometheusTelemetryConfig.PORT;
+import static org.onosproject.openstacktelemetry.config.DefaultPrometheusTelemetryConfig.fromTelemetryConfig;
 
 /**
  * Unit tests for DefaultPrometheusTelemetryConfig class.
  */
 public class DefaultPrometheusTelemetryConfigTest {
-
-    private static final String IP_ADDRESS_1 = PROP_PROMETHEUS_EXPORTER_ADDRESS_DEFAULT;
+    private static final String IP_ADDRESS_1 = "10.10.1.1";
     private static final String IP_ADDRESS_2 = "10.10.1.2";
 
-    private static final int PORT_1 = PROP_PROMETHEUS_EXPORTER_PORT_DEFAULT;
-    private static final int PORT_2 = PROP_PROMETHEUS_EXPORTER_PORT_DEFAULT + 1;
+    private static final int PORT_1 = 50050;
+    private static final int PORT_2 = 50051;
 
     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 static final String DUMMY = "dummy";
+
     private PrometheusTelemetryConfig config1;
     private PrometheusTelemetryConfig sameAsConfig1;
     private PrometheusTelemetryConfig config2;
@@ -110,4 +117,21 @@
         assertThat(config.port(), is(PORT_1));
         assertThat(config.configMap(), is(CONFIG_MAP_1));
     }
-}
+
+    /**
+     * Tests props extraction.
+     */
+    @Test
+    public void testPropsExtraction() {
+        Map<String, String> props = Maps.newConcurrentMap();
+        props.put(ADDRESS, IP_ADDRESS_1);
+        props.put(PORT, String.valueOf(PORT_1));
+
+        TelemetryConfig config = new DefaultTelemetryConfig(DUMMY, PROMETHEUS,
+                ImmutableList.of(), DUMMY, DUMMY, false, props);
+
+        PrometheusTelemetryConfig prometheusConfig = fromTelemetryConfig(config);
+        assertThat(prometheusConfig.address(), is(IP_ADDRESS_1));
+        assertThat(prometheusConfig.port(), is(PORT_1));
+    }
+}
\ No newline at end of file
diff --git a/apps/openstacktelemetry/app/src/test/java/org/onosproject/openstacktelemetry/config/DefaultRestTelemetryConfigTest.java b/apps/openstacktelemetry/app/src/test/java/org/onosproject/openstacktelemetry/config/DefaultRestTelemetryConfigTest.java
index 3e8bac06..c78bb63 100644
--- a/apps/openstacktelemetry/app/src/test/java/org/onosproject/openstacktelemetry/config/DefaultRestTelemetryConfigTest.java
+++ b/apps/openstacktelemetry/app/src/test/java/org/onosproject/openstacktelemetry/config/DefaultRestTelemetryConfigTest.java
@@ -15,17 +15,28 @@
  */
 package org.onosproject.openstacktelemetry.config;
 
+import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Maps;
 import com.google.common.testing.EqualsTester;
 import org.junit.Before;
 import org.junit.Test;
 import org.onosproject.openstacktelemetry.api.config.RestTelemetryConfig;
+import org.onosproject.openstacktelemetry.api.config.TelemetryConfig;
+import org.onosproject.openstacktelemetry.impl.DefaultTelemetryConfig;
 
 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.config.TelemetryConfig.ConfigType.REST;
+import static org.onosproject.openstacktelemetry.config.DefaultRestTelemetryConfig.ADDRESS;
+import static org.onosproject.openstacktelemetry.config.DefaultRestTelemetryConfig.ENDPOINT;
+import static org.onosproject.openstacktelemetry.config.DefaultRestTelemetryConfig.METHOD;
+import static org.onosproject.openstacktelemetry.config.DefaultRestTelemetryConfig.PORT;
+import static org.onosproject.openstacktelemetry.config.DefaultRestTelemetryConfig.REQUEST_MEDIA_TYPE;
+import static org.onosproject.openstacktelemetry.config.DefaultRestTelemetryConfig.RESPONSE_MEDIA_TYPE;
 
 public final class DefaultRestTelemetryConfigTest {
 
@@ -52,6 +63,8 @@
     private static final Map<String, Object> CONFIG_MAP_2 =
             ImmutableMap.of("key2", "value2");
 
+    private static final String DUMMY = "dummy";
+
     private RestTelemetryConfig config1;
     private RestTelemetryConfig sameAsConfig1;
     private RestTelemetryConfig config2;
@@ -133,4 +146,28 @@
         assertThat(config.responseMediaType(), is(RESPONSE_MEDIA_TYPE_1));
         assertThat(config.configMap(), is(CONFIG_MAP_1));
     }
-}
+
+    /**
+     * Tests props extraction.
+     */
+    @Test
+    public void testPropsExtraction() {
+        Map<String, String> props = Maps.newConcurrentMap();
+        props.put(ADDRESS, IP_ADDRESS_1);
+        props.put(PORT, String.valueOf(PORT_1));
+        props.put(ENDPOINT, ENDPOINT_1);
+        props.put(METHOD, METHOD_1);
+        props.put(REQUEST_MEDIA_TYPE, REQUEST_MEDIA_TYPE_1);
+        props.put(RESPONSE_MEDIA_TYPE, RESPONSE_MEDIA_TYPE_1);
+
+        TelemetryConfig config = new DefaultTelemetryConfig(DUMMY, REST,
+                ImmutableList.of(), DUMMY, DUMMY, false, props);
+        RestTelemetryConfig restConfig = DefaultRestTelemetryConfig.fromTelemetryConfig(config);
+        assertThat(restConfig.address(), is(IP_ADDRESS_1));
+        assertThat(restConfig.port(), is(PORT_1));
+        assertThat(restConfig.endpoint(), is(ENDPOINT_1));
+        assertThat(restConfig.method(), is(METHOD_1));
+        assertThat(restConfig.requestMediaType(), is(REQUEST_MEDIA_TYPE_1));
+        assertThat(restConfig.responseMediaType(), is(RESPONSE_MEDIA_TYPE_1));
+    }
+}
\ No newline at end of file
diff --git a/apps/openstacktelemetry/app/src/test/java/org/onosproject/openstacktelemetry/impl/DefaultTelemetryConfigTest.java b/apps/openstacktelemetry/app/src/test/java/org/onosproject/openstacktelemetry/impl/DefaultTelemetryConfigTest.java
new file mode 100644
index 0000000..637f9fc
--- /dev/null
+++ b/apps/openstacktelemetry/app/src/test/java/org/onosproject/openstacktelemetry/impl/DefaultTelemetryConfigTest.java
@@ -0,0 +1,116 @@
+/*
+ * 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 com.google.common.collect.Maps;
+import com.google.common.testing.EqualsTester;
+import org.junit.Before;
+import org.junit.Test;
+import org.onosproject.openstacktelemetry.api.config.TelemetryConfig;
+import org.onosproject.openstacktelemetry.api.config.TelemetryConfig.ConfigType;
+
+import java.util.Map;
+
+import static org.junit.Assert.assertEquals;
+import static org.onlab.junit.ImmutableClassChecker.assertThatClassIsImmutable;
+
+/**
+ * Unit tests for DefaultTelemetryConfig class.
+ */
+public final class DefaultTelemetryConfigTest {
+
+    private static final String NAME_1 = "grpc";
+    private static final String NAME_2 = "kafka";
+
+    private static final ConfigType TYPE_1 = ConfigType.GRPC;
+    private static final ConfigType TYPE_2 = ConfigType.KAFKA;
+
+    private static final String MANUFACTURER_1 = "grpc.io";
+    private static final String MANUFACTURER_2 = "kafka.apache.org";
+
+    private static final String SW_VERSION_1 = "1.0";
+    private static final String SW_VERSION_2 = "1.0";
+
+    private static final Map<String, String> PROP_1 = Maps.newConcurrentMap();
+    private static final Map<String, String> PROP_2 = Maps.newConcurrentMap();
+
+    private static final String PROP_1_KEY_1 = "key11";
+    private static final String PROP_1_KEY_2 = "key12";
+    private static final String PROP_1_VALUE_1 = "value11";
+    private static final String PROP_1_VALUE_2 = "value12";
+    private static final String PROP_2_KEY_1 = "key21";
+    private static final String PROP_2_KEY_2 = "key22";
+    private static final String PROP_2_VALUE_1 = "value21";
+    private static final String PROP_2_VALUE_2 = "value22";
+
+    private static final boolean ENABLED_1 = true;
+    private static final boolean ENABLED_2 = false;
+
+    private TelemetryConfig config1;
+    private TelemetryConfig sameAsConfig1;
+    private TelemetryConfig config2;
+
+    /**
+     * Initial setup for this unit test.
+     */
+    @Before
+    public void setup() {
+        PROP_1.put(PROP_1_KEY_1, PROP_1_VALUE_1);
+        PROP_1.put(PROP_1_KEY_2, PROP_1_VALUE_2);
+        PROP_2.put(PROP_2_KEY_1, PROP_2_VALUE_1);
+        PROP_2.put(PROP_2_KEY_2, PROP_2_VALUE_2);
+
+        config1 = new DefaultTelemetryConfig(NAME_1, TYPE_1, null,
+                MANUFACTURER_1, SW_VERSION_1, ENABLED_1, PROP_1);
+        sameAsConfig1 = new DefaultTelemetryConfig(NAME_1, TYPE_1, null,
+                MANUFACTURER_1, SW_VERSION_1, ENABLED_1, PROP_1);
+        config2 = new DefaultTelemetryConfig(NAME_2, TYPE_2, null,
+                MANUFACTURER_2, SW_VERSION_2, ENABLED_2, PROP_2);
+    }
+
+    /**
+     * Tests class immutability.
+     */
+    @Test
+    public void testImmutability() {
+        assertThatClassIsImmutable(DefaultTelemetryConfig.class);
+    }
+
+    /**
+     * Tests object equality.
+     */
+    @Test
+    public void testEquality() {
+        new EqualsTester()
+                .addEqualityGroup(config1, sameAsConfig1)
+                .addEqualityGroup(config2).testEquals();
+    }
+
+    /**
+     * Tests object construction.
+     */
+    @Test
+    public void testConstruction() {
+        TelemetryConfig config = config1;
+
+        assertEquals(config.name(), NAME_1);
+        assertEquals(config.type(), TYPE_1);
+        assertEquals(config.manufacturer(), MANUFACTURER_1);
+        assertEquals(config.swVersion(), SW_VERSION_1);
+        assertEquals(config.properties(), PROP_1);
+        assertEquals(config.enabled(), ENABLED_1);
+    }
+}
diff --git a/apps/openstacktelemetry/app/src/test/java/org/onosproject/openstacktelemetry/impl/DistributedTelemetryConfigStoreTest.java b/apps/openstacktelemetry/app/src/test/java/org/onosproject/openstacktelemetry/impl/DistributedTelemetryConfigStoreTest.java
new file mode 100644
index 0000000..511fa6d
--- /dev/null
+++ b/apps/openstacktelemetry/app/src/test/java/org/onosproject/openstacktelemetry/impl/DistributedTelemetryConfigStoreTest.java
@@ -0,0 +1,138 @@
+/*
+ * 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 com.google.common.collect.Maps;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.onosproject.TestApplicationId;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.CoreServiceAdapter;
+import org.onosproject.openstacktelemetry.api.config.TelemetryConfig;
+import org.onosproject.openstacktelemetry.api.config.TelemetryConfig.ConfigType;
+import org.onosproject.store.service.TestStorageService;
+
+import java.util.Map;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Distributed TelemetryConfig store test suite.
+ */
+public class DistributedTelemetryConfigStoreTest {
+
+    private static final String NAME_1 = "grpc";
+    private static final String NAME_2 = "kafka";
+
+    private static final ConfigType TYPE_1 = ConfigType.GRPC;
+    private static final ConfigType TYPE_2 = ConfigType.KAFKA;
+
+    private static final String MANUFACTURER_1 = "grpc.io";
+    private static final String MANUFACTURER_2 = "kafka.apache.org";
+
+    private static final String SW_VERSION_1 = "1.0";
+    private static final String SW_VERSION_2 = "1.0";
+
+    private static final Map<String, String> PROP_1 = Maps.newConcurrentMap();
+    private static final Map<String, String> PROP_2 = Maps.newConcurrentMap();
+
+    private static final String PROP_1_KEY_1 = "key11";
+    private static final String PROP_1_KEY_2 = "key12";
+    private static final String PROP_1_VALUE_1 = "value11";
+    private static final String PROP_1_VALUE_2 = "value12";
+    private static final String PROP_2_KEY_1 = "key21";
+    private static final String PROP_2_KEY_2 = "key22";
+    private static final String PROP_2_VALUE_1 = "value21";
+    private static final String PROP_2_VALUE_2 = "value22";
+
+    private static final boolean ENABLED_1 = true;
+    private static final boolean ENABLED_2 = false;
+
+    private TelemetryConfig config1;
+    private TelemetryConfig config2;
+
+    private DistributedTelemetryConfigStore configStore;
+
+    /**
+     * Sets up the telemetry config store and the storage service test harness.
+     */
+    @Before
+    public void setUp() {
+        configStore = new DistributedTelemetryConfigStore();
+        configStore.storageService = new TestStorageService();
+        configStore.coreService = new TestCoreService();
+        configStore.setDelegate(event -> {
+        });
+        configStore.activate();
+
+        initTelemetryConfigs();
+    }
+
+    /**
+     * Tears down the telemetry config store.
+     */
+    @After
+    public void tearDown() {
+        configStore.deactivate();
+    }
+
+    /**
+     * Tests adding, removing and getting.
+     */
+    @Test
+    public void basics() {
+        configStore.createTelemetryConfig(config1);
+        assertTrue("There should be one telemetry config in the set.",
+                configStore.telemetryConfigs().contains(config1));
+        assertTrue("The same telemetry config should be returned.",
+                configStore.telemetryConfigsByType(ConfigType.GRPC).contains(config1));
+        assertEquals("The telemetry config should be the same.",
+                configStore.telemetryConfig(NAME_1), config1);
+        configStore.removeTelemetryConfig(NAME_1);
+        assertFalse("There should be no telemetry config in the set.",
+                configStore.telemetryConfigs().contains(config1));
+
+        configStore.createTelemetryConfig(config1);
+        configStore.createTelemetryConfig(config2);
+        assertEquals("There should be two configs in the sets.",
+                configStore.telemetryConfigs().size(), 2);
+    }
+
+    /**
+     * Test core service; For generate test application ID.
+     */
+    public class TestCoreService extends CoreServiceAdapter {
+        @Override
+        public ApplicationId registerApplication(String name) {
+            return TestApplicationId.create(name);
+        }
+    }
+
+    private void initTelemetryConfigs() {
+        PROP_1.put(PROP_1_KEY_1, PROP_1_VALUE_1);
+        PROP_1.put(PROP_1_KEY_2, PROP_1_VALUE_2);
+        PROP_2.put(PROP_2_KEY_1, PROP_2_VALUE_1);
+        PROP_2.put(PROP_2_KEY_2, PROP_2_VALUE_2);
+
+        config1 = new DefaultTelemetryConfig(NAME_1, TYPE_1, null,
+                MANUFACTURER_1, SW_VERSION_1, ENABLED_1, PROP_1);
+        config2 = new DefaultTelemetryConfig(NAME_2, TYPE_2, null,
+                MANUFACTURER_2, SW_VERSION_2, ENABLED_2, PROP_2);
+    }
+}
diff --git a/apps/openstacktelemetry/app/src/test/java/org/onosproject/openstacktelemetry/impl/OpenstackTelemetryManagerTest.java b/apps/openstacktelemetry/app/src/test/java/org/onosproject/openstacktelemetry/impl/OpenstackTelemetryManagerTest.java
index 47ef6d9..0362492 100644
--- a/apps/openstacktelemetry/app/src/test/java/org/onosproject/openstacktelemetry/impl/OpenstackTelemetryManagerTest.java
+++ b/apps/openstacktelemetry/app/src/test/java/org/onosproject/openstacktelemetry/impl/OpenstackTelemetryManagerTest.java
@@ -18,7 +18,7 @@
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
-import org.onosproject.openstacktelemetry.api.TelemetryService;
+import org.onosproject.openstacktelemetry.api.TelemetryAdminService;
 
 import static org.junit.Assert.assertEquals;
 
@@ -27,11 +27,11 @@
  */
 public final class OpenstackTelemetryManagerTest {
 
-    private static final TelemetryService GRPC_SERVICE = new GrpcTelemetryManager();
-    private static final TelemetryService INFLUXDB_SERVICE = new InfluxDbTelemetryManager();
-    private static final TelemetryService KAFKA_SERVICE = new KafkaTelemetryManager();
-    private static final TelemetryService PROMETHEUS_SERVICE = new PrometheusTelemetryManager();
-    private static final TelemetryService REST_SERVICE = new PrometheusTelemetryManager();
+    private static final TelemetryAdminService GRPC_SERVICE = new GrpcTelemetryManager();
+    private static final TelemetryAdminService INFLUXDB_SERVICE = new InfluxDbTelemetryManager();
+    private static final TelemetryAdminService KAFKA_SERVICE = new KafkaTelemetryManager();
+    private static final TelemetryAdminService PROMETHEUS_SERVICE = new PrometheusTelemetryManager();
+    private static final TelemetryAdminService REST_SERVICE = new PrometheusTelemetryManager();
 
     private OpenstackTelemetryManager manager;
 
@@ -42,6 +42,8 @@
     public void setUp() {
         manager = new OpenstackTelemetryManager();
 
+        manager.telemetryConfigService = new TelemetryConfigManager();
+
         manager.activate();
     }
 
@@ -52,7 +54,7 @@
     public void testAddTelemetryService() {
         addDefaultServices();
 
-        TelemetryService kafkaService = new KafkaTelemetryManager();
+        TelemetryAdminService kafkaService = new KafkaTelemetryManager();
 
         assertEquals(5, manager.telemetryServices().size());
 
diff --git a/apps/openstacktelemetry/app/src/test/java/org/onosproject/openstacktelemetry/impl/OpenstackTelemetryServiceAdapter.java b/apps/openstacktelemetry/app/src/test/java/org/onosproject/openstacktelemetry/impl/OpenstackTelemetryServiceAdapter.java
index 129fec5..dafb059 100644
--- a/apps/openstacktelemetry/app/src/test/java/org/onosproject/openstacktelemetry/impl/OpenstackTelemetryServiceAdapter.java
+++ b/apps/openstacktelemetry/app/src/test/java/org/onosproject/openstacktelemetry/impl/OpenstackTelemetryServiceAdapter.java
@@ -19,7 +19,7 @@
 import com.google.common.collect.Sets;
 import org.onosproject.openstacktelemetry.api.FlowInfo;
 import org.onosproject.openstacktelemetry.api.OpenstackTelemetryService;
-import org.onosproject.openstacktelemetry.api.TelemetryService;
+import org.onosproject.openstacktelemetry.api.TelemetryAdminService;
 
 import java.util.Set;
 
@@ -28,15 +28,15 @@
  */
 public class OpenstackTelemetryServiceAdapter implements OpenstackTelemetryService {
 
-    Set<TelemetryService> services = Sets.newConcurrentHashSet();
+    Set<TelemetryAdminService> services = Sets.newConcurrentHashSet();
 
     @Override
-    public void addTelemetryService(TelemetryService telemetryService) {
+    public void addTelemetryService(TelemetryAdminService telemetryService) {
         services.add(telemetryService);
     }
 
     @Override
-    public void removeTelemetryService(TelemetryService telemetryService) {
+    public void removeTelemetryService(TelemetryAdminService telemetryService) {
         services.remove(telemetryService);
     }
 
@@ -46,7 +46,12 @@
     }
 
     @Override
-    public Set<TelemetryService> telemetryServices() {
+    public Set<TelemetryAdminService> telemetryServices() {
         return ImmutableSet.copyOf(services);
     }
+
+    @Override
+    public TelemetryAdminService telemetryService(String type) {
+        return null;
+    }
 }
diff --git a/apps/openstacktelemetry/app/src/test/java/org/onosproject/openstacktelemetry/impl/XmlTelemetryConfigLoaderTest.java b/apps/openstacktelemetry/app/src/test/java/org/onosproject/openstacktelemetry/impl/XmlTelemetryConfigLoaderTest.java
new file mode 100644
index 0000000..6188cef
--- /dev/null
+++ b/apps/openstacktelemetry/app/src/test/java/org/onosproject/openstacktelemetry/impl/XmlTelemetryConfigLoaderTest.java
@@ -0,0 +1,90 @@
+/*
+ * 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.junit.Test;
+import org.onosproject.openstacktelemetry.api.TelemetryConfigProvider;
+import org.onosproject.openstacktelemetry.api.config.TelemetryConfig;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Iterator;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Tests of the XML configuration loader implementation.
+ */
+public class XmlTelemetryConfigLoaderTest {
+
+    @Test
+    public void basics() throws IOException {
+        XmlTelemetryConfigLoader loader = new XmlTelemetryConfigLoader();
+        InputStream stream = getClass().getResourceAsStream("configs.singleInheritance.xml");
+        TelemetryConfigProvider provider = loader.loadTelemetryConfigs(stream);
+
+        assertEquals("incorrect config count", 2, provider.getTelemetryConfigs().size());
+
+        TelemetryConfig config = getConfig(provider, "foo.1");
+
+        assertEquals("incorrect config name", "foo.1", config.name());
+        assertEquals("incorrect config type", "grpc", config.type().name().toLowerCase());
+        assertEquals("incorrect config mfg", "Circus", config.manufacturer());
+        assertEquals("incorrect config sw", "2.2", config.swVersion());
+
+        assertEquals("incorrect config properties", 2, config.properties().size());
+        assertTrue("incorrect config property", config.properties().containsKey("p1"));
+    }
+
+    @Test
+    public void multipleDrivers() throws IOException {
+        XmlTelemetryConfigLoader loader = new XmlTelemetryConfigLoader();
+        InputStream stream = getClass().getResourceAsStream("configs.multipleInheritance.xml");
+        TelemetryConfigProvider provider = loader.loadTelemetryConfigs(stream);
+
+        TelemetryConfig config1 = getConfig(provider, "foo.1");
+
+        assertEquals("incorrect config mfg", "Circus", config1.manufacturer());
+        assertEquals("incorrect config sw", "2.2", config1.swVersion());
+
+        assertEquals("incorrect config type", "grpc", config1.type().name().toLowerCase());
+        assertEquals("incorrect config properties", 3, config1.properties().size());
+        assertTrue("incorrect config property", config1.properties().containsKey("p0"));
+        assertTrue("incorrect config property", config1.properties().containsKey("p1"));
+        assertTrue("incorrect config property", config1.properties().containsKey("p2"));
+
+        TelemetryConfig config2 = getConfig(provider, "foo.2");
+        assertEquals("incorrect config type", "grpc", config2.type().name().toLowerCase());
+        assertEquals("incorrect config mfg", "Big Top OEM", config2.manufacturer());
+        assertEquals("incorrect config sw", "2.2", config2.swVersion());
+
+        assertEquals("incorrect config properties", 4, config2.properties().size());
+        assertTrue("incorrect config property", config2.properties().containsKey("p0"));
+        assertTrue("incorrect config property", config2.properties().containsKey("p1"));
+        assertTrue("incorrect config property", config2.properties().containsKey("p2"));
+        assertTrue("incorrect config property", config2.properties().containsKey("p3"));
+    }
+
+    private TelemetryConfig getConfig(TelemetryConfigProvider provider, String name) {
+        Iterator<TelemetryConfig> iterator = provider.getTelemetryConfigs().iterator();
+        TelemetryConfig config;
+        do {
+            config = iterator.next();
+        } while (!name.equals(config.name()));
+        return config;
+    }
+}
diff --git a/apps/openstacktelemetry/app/src/test/resources/org/onosproject/openstacktelemetry/impl/configs.multipleInheritance.xml b/apps/openstacktelemetry/app/src/test/resources/org/onosproject/openstacktelemetry/impl/configs.multipleInheritance.xml
new file mode 100644
index 0000000..0c56b26
--- /dev/null
+++ b/apps/openstacktelemetry/app/src/test/resources/org/onosproject/openstacktelemetry/impl/configs.multipleInheritance.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ 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.
+  -->
+<configs>
+    <config name="foo.0" type="grpc" manufacturer="Circus" swVersion="2.0">
+        <property name="p0">v0</property>
+    </config>
+
+    <config name="foo.1" extends="foo.0" swVersion="2.2">
+        <fingerprint>ding</fingerprint>
+        <fingerprint>bat</fingerprint>
+
+        <property name="p1">v1</property>
+        <property name="p2">v2</property>
+    </config>
+
+    <config name="foo.2" extends="foo.1" manufacturer="Big Top OEM">
+        <property name="p3">v3</property>
+    </config>
+</configs>
\ No newline at end of file
diff --git a/apps/openstacktelemetry/app/src/test/resources/org/onosproject/openstacktelemetry/impl/configs.singleInheritance.xml b/apps/openstacktelemetry/app/src/test/resources/org/onosproject/openstacktelemetry/impl/configs.singleInheritance.xml
new file mode 100644
index 0000000..8b6e5be
--- /dev/null
+++ b/apps/openstacktelemetry/app/src/test/resources/org/onosproject/openstacktelemetry/impl/configs.singleInheritance.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ 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.
+  -->
+<configs>
+    <config name="foo.0" type="grpc" manufacturer="Circus" swVersion="2.0">
+    </config>
+
+    <config name="foo.1" extends="foo.0" swVersion="2.2">
+        <fingerprint>ding</fingerprint>
+        <fingerprint>bat</fingerprint>
+
+        <property name="p1">v1</property>
+        <property name="p2">v2</property>
+    </config>
+</configs>
\ No newline at end of file