Initial import of CFM and SOAM api 1.10

Change-Id: Icf5cc2d5fb34b75460e80e8cced0d70265bcd33b

Minor changes to get CFM and SOAM patch work on 1.10

Change-Id: Ibd90fdeaf143f4cfdc98f7e6eb28bfa55f471e4b
diff --git a/apps/cfm/src/test/java/org/onosproject/cfm/CfmCodecContext.java b/apps/cfm/src/test/java/org/onosproject/cfm/CfmCodecContext.java
new file mode 100644
index 0000000..0f1de5e
--- /dev/null
+++ b/apps/cfm/src/test/java/org/onosproject/cfm/CfmCodecContext.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2017-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.cfm;
+
+import org.onosproject.codec.CodecContext;
+import org.onosproject.codec.CodecService;
+import org.onosproject.codec.JsonCodec;
+import org.onosproject.codec.impl.CodecManager;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+/**
+ * Mock codec context for use in codec unit tests.
+ */
+public class CfmCodecContext implements CodecContext {
+
+    private final ObjectMapper mapper = new ObjectMapper();
+    private final CodecManager codecManager = new CodecManager();
+    private final CfmWebComponent manager = new CfmWebComponent();
+
+    /**
+     * Constructs a new mock codec context.
+     */
+    public CfmCodecContext() {
+        codecManager.activate();
+        manager.codecService = codecManager;
+        manager.activate();
+    }
+
+    @Override
+    public ObjectMapper mapper() {
+        return mapper;
+    }
+
+    @Override
+    public <T> T getService(Class<T> serviceClass) {
+        return null;
+    }
+
+    @Override
+    public <T> JsonCodec<T> codec(Class<T> entityClass) {
+        return codecManager.getCodec(entityClass);
+    }
+
+    /**
+     * Get the codec manager.
+     *
+     * @return instance of codec manager
+     */
+    public CodecService codecManager() {
+        return codecManager;
+    }
+}
diff --git a/apps/cfm/src/test/java/org/onosproject/cfm/impl/CfmResourceTest.java b/apps/cfm/src/test/java/org/onosproject/cfm/impl/CfmResourceTest.java
new file mode 100644
index 0000000..bcbaa9a
--- /dev/null
+++ b/apps/cfm/src/test/java/org/onosproject/cfm/impl/CfmResourceTest.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2017-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.cfm.impl;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.onosproject.rest.resources.ResourceTest;
+
+/**
+ * Base class for CFM REST API tests.  Performs common configuration operations.
+ */
+public class CfmResourceTest extends ResourceTest {
+
+    /**
+     * Creates a new web-resource test.
+     */
+    public CfmResourceTest() {
+        super(ResourceConfig.forApplicationClass(CfmWebApplication.class));
+    }
+}
diff --git a/apps/cfm/src/test/java/org/onosproject/cfm/impl/MaWebResourceTest.java b/apps/cfm/src/test/java/org/onosproject/cfm/impl/MaWebResourceTest.java
new file mode 100644
index 0000000..6378ea9
--- /dev/null
+++ b/apps/cfm/src/test/java/org/onosproject/cfm/impl/MaWebResourceTest.java
@@ -0,0 +1,199 @@
+/*
+ * Copyright 2017-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.cfm.impl;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import org.junit.Before;
+import org.junit.Test;
+import org.onlab.osgi.ServiceDirectory;
+import org.onlab.osgi.TestServiceDirectory;
+import org.onlab.packet.VlanId;
+import org.onlab.rest.BaseResource;
+import org.onosproject.cfm.CfmCodecContext;
+import org.onosproject.codec.CodecService;
+import org.onosproject.incubator.net.l2monitoring.cfm.Component;
+import org.onosproject.incubator.net.l2monitoring.cfm.DefaultComponent;
+import org.onosproject.incubator.net.l2monitoring.cfm.DefaultMaintenanceAssociation;
+import org.onosproject.incubator.net.l2monitoring.cfm.DefaultMaintenanceDomain;
+import org.onosproject.incubator.net.l2monitoring.cfm.MaintenanceAssociation;
+import org.onosproject.incubator.net.l2monitoring.cfm.MaintenanceDomain;
+import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MaIdCharStr;
+import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MaIdShort;
+import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MdId;
+import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MdIdCharStr;
+import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MepId;
+import org.onosproject.incubator.net.l2monitoring.cfm.service.CfmConfigException;
+import org.onosproject.incubator.net.l2monitoring.cfm.service.CfmMdService;
+
+import javax.ws.rs.InternalServerErrorException;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.Response;
+
+import java.io.BufferedReader;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.util.Optional;
+
+import static junit.framework.TestCase.fail;
+import static org.easymock.EasyMock.createMock;
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.replay;
+import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+
+public class MaWebResourceTest extends CfmResourceTest {
+    private final CfmMdService mdService = createMock(CfmMdService.class);
+
+    private static final MdId MDNAME1 = MdIdCharStr.asMdId("md-1");
+    private static final MaIdShort MANAME1 = MaIdCharStr.asMaId("ma-1-1");
+
+    private MaintenanceAssociation ma1;
+
+    @Before
+    public void setUpTest() throws CfmConfigException {
+        CfmCodecContext context = new CfmCodecContext();
+        ServiceDirectory testDirectory = new TestServiceDirectory()
+                .add(CfmMdService.class, mdService)
+                .add(CodecService.class, context.codecManager());
+        BaseResource.setServiceDirectory(testDirectory);
+
+        ma1 = DefaultMaintenanceAssociation
+                .builder(MANAME1, MDNAME1.getNameLength())
+                .addToRemoteMepIdList(MepId.valueOf((short) 101))
+                .addToRemoteMepIdList(MepId.valueOf((short) 102))
+                .ccmInterval(MaintenanceAssociation.CcmInterval.INTERVAL_3MS)
+                .maNumericId((short) 1)
+                .addToComponentList(
+                        DefaultComponent.builder(1)
+                                .tagType(Component.TagType.VLAN_STAG)
+                                .mhfCreationType(Component.MhfCreationType.NONE)
+                                .idPermission(Component.IdPermissionType.MANAGE)
+                                .addToVidList(VlanId.vlanId((short) 1010))
+                                .build())
+                .build();
+    }
+
+    @Test
+    public void testGetMa() {
+
+        expect(mdService.getMaintenanceAssociation(MDNAME1, MANAME1))
+                .andReturn(Optional.ofNullable(ma1)).anyTimes();
+        replay(mdService);
+
+        final WebTarget wt = target();
+        final String response = wt.path("md/" + MDNAME1.mdName()
+                + "/ma/" + MANAME1.maName()).request().get(String.class);
+
+        assertThat(response, is("{\"ma\":" +
+                "{\"maName\":\"ma-1-1\"," +
+                "\"maNameType\":\"CHARACTERSTRING\"," +
+                "\"maNumericId\":1," +
+                "\"ccm-interval\":\"INTERVAL_3MS\"," +
+                "\"component-list\":[{\"component\":" +
+                    "{\"component-id\":1," +
+                    "\"vid-list\":[{\"vid\":\"1010\"}]," +
+                    "\"mhf-creation-type\":\"NONE\"," +
+                    "\"id-permission\":\"MANAGE\"," +
+                    "\"tag-type\":\"VLAN_STAG\"}}]," +
+                "\"rmep-list\":" +
+                    "[{\"rmep\":101}," +
+                    "{\"rmep\":102}]}}"));
+    }
+
+    @Test
+    public void testGetMaEmpty() throws IOException {
+        MaIdShort maId2 = MaIdCharStr.asMaId("ma-2");
+        expect(mdService
+                .getMaintenanceAssociation(MDNAME1, maId2))
+                .andReturn(Optional.empty()).anyTimes();
+        replay(mdService);
+
+        final WebTarget wt = target();
+        try {
+            final String response = wt.path("md/" + MDNAME1.mdName()
+                    + "/ma/" + maId2.maName()).request().get(String.class);
+            fail("Expected InternalServerErrorException, as MA is unknown");
+        } catch (InternalServerErrorException e) {
+            ByteArrayInputStream is = (ByteArrayInputStream) e.getResponse().getEntity();
+            BufferedReader br = new BufferedReader(new InputStreamReader(is));
+            String line = null;
+            StringBuffer sb = new StringBuffer();
+            while ((line = br.readLine()) != null) {
+                sb.append(line);
+            }
+
+            assertThat(sb.toString(), is("{ \"failure\":" +
+                    "\"java.lang.IllegalArgumentException: MA ma-2 not Found\" }"));
+        }
+    }
+
+    @Test
+    public void testDeleteMa() throws CfmConfigException {
+
+        expect(mdService.deleteMaintenanceAssociation(MDNAME1, MANAME1))
+                .andReturn(true).anyTimes();
+        replay(mdService);
+
+        final WebTarget wt = target();
+        final Response response = wt.path("md/" + MDNAME1.mdName()
+                + "/ma/" + MANAME1.maName()).request().delete();
+
+        assertEquals(200, response.getStatus());
+    }
+
+    @Test
+    public void testDeleteMaEmpty() throws CfmConfigException {
+        MaIdShort maId2 = MaIdCharStr.asMaId("ma-2");
+
+        expect(mdService.deleteMaintenanceAssociation(MDNAME1, maId2))
+                .andReturn(false).anyTimes();
+        replay(mdService);
+
+        final WebTarget wt = target();
+        final Response response = wt.path("md/" + MDNAME1.mdName()
+                + "/ma/" + maId2.maName()).request().delete();
+
+        assertEquals(304, response.getStatus());
+    }
+
+    @Test
+    public void testCreateMa() throws CfmConfigException {
+        MaintenanceDomain md1 = DefaultMaintenanceDomain
+                .builder(MDNAME1).mdLevel(MaintenanceDomain.MdLevel.LEVEL2).build();
+
+        expect(mdService.getMaintenanceDomain(MDNAME1))
+                .andReturn(Optional.ofNullable(md1)).anyTimes();
+        expect(mdService.createMaintenanceAssociation(MDNAME1, ma1))
+                .andReturn(false).anyTimes();
+        replay(mdService);
+
+        ObjectMapper mapper = new ObjectMapper();
+        CfmCodecContext context = new CfmCodecContext();
+        ObjectNode node = mapper.createObjectNode();
+        node.set("ma", context.codec(MaintenanceAssociation.class)
+                .encode(ma1, context));
+
+        final WebTarget wt = target();
+        final Response response = wt.path("md/" + MDNAME1.mdName()
+                + "/ma").request().post(Entity.json(node.toString()));
+
+        assertEquals(201, response.getStatus());
+    }
+}
diff --git a/apps/cfm/src/test/java/org/onosproject/cfm/impl/MdWebResourceTest.java b/apps/cfm/src/test/java/org/onosproject/cfm/impl/MdWebResourceTest.java
new file mode 100644
index 0000000..f4c9b5a
--- /dev/null
+++ b/apps/cfm/src/test/java/org/onosproject/cfm/impl/MdWebResourceTest.java
@@ -0,0 +1,193 @@
+/*
+ * Copyright 2017-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.cfm.impl;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import org.junit.Before;
+import org.junit.Test;
+import org.onlab.osgi.ServiceDirectory;
+import org.onlab.osgi.TestServiceDirectory;
+import org.onlab.rest.BaseResource;
+import org.onosproject.cfm.CfmCodecContext;
+import org.onosproject.codec.CodecService;
+import org.onosproject.incubator.net.l2monitoring.cfm.DefaultMaintenanceDomain;
+import org.onosproject.incubator.net.l2monitoring.cfm.MaintenanceDomain;
+import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MdId;
+import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MdIdCharStr;
+import org.onosproject.incubator.net.l2monitoring.cfm.service.CfmConfigException;
+import org.onosproject.incubator.net.l2monitoring.cfm.service.CfmMdService;
+
+import javax.ws.rs.InternalServerErrorException;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.Response;
+import java.io.BufferedReader;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+
+import static junit.framework.TestCase.fail;
+import static org.easymock.EasyMock.createMock;
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.replay;
+import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+
+public class MdWebResourceTest extends CfmResourceTest {
+    private final CfmMdService mdService = createMock(CfmMdService.class);
+
+    private static final MdId MDNAME1 = MdIdCharStr.asMdId("md-1");
+    private static final MdId MDNAME2 = MdIdCharStr.asMdId("md-2");
+
+    private List<MaintenanceDomain> mdList;
+
+    @Before
+    public void setUpTest() throws CfmConfigException {
+        CfmCodecContext context = new CfmCodecContext();
+        ServiceDirectory testDirectory = new TestServiceDirectory()
+                .add(CfmMdService.class, mdService)
+                .add(CodecService.class, context.codecManager());
+        BaseResource.setServiceDirectory(testDirectory);
+
+        mdList = new ArrayList<>();
+
+        mdList.add(DefaultMaintenanceDomain.builder(MDNAME1)
+                .mdLevel(MaintenanceDomain.MdLevel.LEVEL1).build());
+        mdList.add(DefaultMaintenanceDomain.builder(MDNAME2)
+                .mdLevel(MaintenanceDomain.MdLevel.LEVEL2).build());
+    }
+
+    @Test
+    public void testGetMds() {
+        expect(mdService.getAllMaintenanceDomain()).andReturn(mdList).anyTimes();
+        replay(mdService);
+
+        final WebTarget wt = target();
+        final String response = wt.path("md").request().get(String.class);
+
+        assertThat(response, is("{\"mds\":[[" +
+                "{\"mdName\":\"md-1\",\"mdNameType\":\"CHARACTERSTRING\"," +
+                "\"mdLevel\":\"LEVEL1\",\"maList\":[]}," +
+                "{\"mdName\":\"md-2\",\"mdNameType\":\"CHARACTERSTRING\"," +
+                "\"mdLevel\":\"LEVEL2\",\"maList\":[]}]]}"));
+    }
+
+    @Test
+    public void testGetMdsEmpty() {
+        expect(mdService.getAllMaintenanceDomain())
+                            .andReturn(new ArrayList<>()).anyTimes();
+        replay(mdService);
+
+        final WebTarget wt = target();
+        final String response = wt.path("md").request().get(String.class);
+
+        assertThat(response, is("{\"mds\":[[]]}"));
+    }
+
+    @Test
+    public void testGetMd() {
+        expect(mdService.getMaintenanceDomain(MDNAME1))
+                .andReturn(Optional.ofNullable(mdList.get(0))).anyTimes();
+        replay(mdService);
+
+        final WebTarget wt = target();
+        final String response = wt.path("md/" + MDNAME1).request().get(String.class);
+
+        assertThat(response, is("{\"md\":" +
+                "{\"mdName\":\"md-1\",\"mdNameType\":\"CHARACTERSTRING\"," +
+                    "\"mdLevel\":\"LEVEL1\",\"maList\":[]}}"));
+    }
+
+    @Test
+    public void testGetMdEmpty() throws IOException {
+        final MdId mdName3 = MdIdCharStr.asMdId("md-3");
+        expect(mdService.getMaintenanceDomain(mdName3))
+                .andReturn(Optional.empty()).anyTimes();
+        replay(mdService);
+
+        final WebTarget wt = target();
+        try {
+            final String response = wt.path("md/" + mdName3).request().get(String.class);
+            fail("Expected InternalServerErrorException, as MD is unknown");
+        } catch (InternalServerErrorException e) {
+            ByteArrayInputStream is = (ByteArrayInputStream) e.getResponse().getEntity();
+            BufferedReader br = new BufferedReader(new InputStreamReader(is));
+            String line = null;
+            StringBuffer sb = new StringBuffer();
+            while ((line = br.readLine()) != null) {
+                sb.append(line);
+            }
+
+            assertThat(sb.toString(), is("{ \"failure\":" +
+                    "\"java.lang.IllegalArgumentException: MD md-3 not Found\" }"));
+        }
+    }
+
+    @Test
+    public void testDeleteMd() throws CfmConfigException {
+        expect(mdService.deleteMaintenanceDomain(MDNAME1))
+                .andReturn(true).anyTimes();
+        replay(mdService);
+
+        final WebTarget wt = target();
+        final Response response = wt.path("md/" + MDNAME1).request().delete();
+
+        assertEquals(200, response.getStatus());
+    }
+
+    @Test
+    public void testDeleteMdNotPresent() throws CfmConfigException {
+        expect(mdService.deleteMaintenanceDomain(MDNAME1))
+                .andReturn(false).anyTimes();
+        replay(mdService);
+
+        final WebTarget wt = target();
+        final Response response = wt.path("md/" + MDNAME1).request().delete();
+
+        assertEquals(304, response.getStatus());
+    }
+
+    @Test
+    public void testCreateMd() throws CfmConfigException {
+        MaintenanceDomain md3 = DefaultMaintenanceDomain
+                .builder(MdIdCharStr.asMdId("md-3"))
+                .mdLevel(MaintenanceDomain.MdLevel.LEVEL3)
+                .mdNumericId((short) 3)
+                .build();
+
+        expect(mdService.createMaintenanceDomain(mdList.get(1)))
+                .andReturn(false).anyTimes();
+        replay(mdService);
+
+        ObjectMapper mapper = new ObjectMapper();
+        CfmCodecContext context = new CfmCodecContext();
+        ObjectNode node = mapper.createObjectNode();
+        node.set("md", context.codec(MaintenanceDomain.class)
+                    .encode(mdList.get(1), context));
+
+
+        final WebTarget wt = target();
+        final Response response = wt.path("md")
+                .request().post(Entity.json(node.toString()));
+
+        assertEquals(201, response.getStatus());
+    }
+}
diff --git a/apps/cfm/src/test/java/org/onosproject/cfm/impl/MepWebResourceTest.java b/apps/cfm/src/test/java/org/onosproject/cfm/impl/MepWebResourceTest.java
new file mode 100644
index 0000000..4dd83db
--- /dev/null
+++ b/apps/cfm/src/test/java/org/onosproject/cfm/impl/MepWebResourceTest.java
@@ -0,0 +1,378 @@
+/*
+ * Copyright 2017-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.cfm.impl;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import org.junit.Before;
+import org.junit.Test;
+import org.onlab.osgi.ServiceDirectory;
+import org.onlab.osgi.TestServiceDirectory;
+import org.onlab.packet.VlanId;
+import org.onlab.rest.BaseResource;
+import org.onosproject.cfm.CfmCodecContext;
+import org.onosproject.codec.CodecService;
+import org.onosproject.incubator.net.l2monitoring.cfm.DefaultMaintenanceAssociation;
+import org.onosproject.incubator.net.l2monitoring.cfm.DefaultMaintenanceDomain;
+import org.onosproject.incubator.net.l2monitoring.cfm.DefaultMep;
+import org.onosproject.incubator.net.l2monitoring.cfm.DefaultMepEntry;
+import org.onosproject.incubator.net.l2monitoring.cfm.DefaultMepLbCreate;
+import org.onosproject.incubator.net.l2monitoring.cfm.DefaultMepLtCreate;
+import org.onosproject.incubator.net.l2monitoring.cfm.MaintenanceAssociation;
+import org.onosproject.incubator.net.l2monitoring.cfm.MaintenanceDomain;
+import org.onosproject.incubator.net.l2monitoring.cfm.Mep;
+import org.onosproject.incubator.net.l2monitoring.cfm.MepEntry;
+import org.onosproject.incubator.net.l2monitoring.cfm.MepLbCreate;
+import org.onosproject.incubator.net.l2monitoring.cfm.MepLtCreate;
+import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MaIdCharStr;
+import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MaIdShort;
+import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MdId;
+import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MdIdCharStr;
+import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MepId;
+import org.onosproject.incubator.net.l2monitoring.cfm.service.CfmConfigException;
+import org.onosproject.incubator.net.l2monitoring.cfm.service.CfmMdService;
+import org.onosproject.incubator.net.l2monitoring.cfm.service.CfmMepService;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.PortNumber;
+
+import javax.ws.rs.InternalServerErrorException;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.Response;
+
+import java.io.BufferedReader;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.time.Duration;
+import java.util.ArrayList;
+import java.util.BitSet;
+import java.util.Collection;
+import java.util.Optional;
+
+import static junit.framework.TestCase.fail;
+import static org.easymock.EasyMock.*;
+import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+
+public class MepWebResourceTest extends CfmResourceTest {
+    private final CfmMepService mepService = createMock(CfmMepService.class);
+    private final CfmMdService mdService = createMock(CfmMdService.class);
+
+    private static final MdId MDNAME1 = MdIdCharStr.asMdId("md-1");
+    private static final MaIdShort MANAME1 = MaIdCharStr.asMaId("ma-1-1");
+    private static final MepId MEPID1 = MepId.valueOf((short) 1);
+
+    private MepEntry mepEntry1 = null;
+
+    @Before
+    public void setUpTest() throws CfmConfigException {
+        CfmCodecContext context = new CfmCodecContext();
+        ServiceDirectory testDirectory = new TestServiceDirectory()
+                .add(CfmMepService.class, mepService)
+                .add(CfmMdService.class, mdService)
+                .add(CodecService.class, context.codecManager());
+        BaseResource.setServiceDirectory(testDirectory);
+
+        mepEntry1 = DefaultMepEntry.builder(
+                    MEPID1,
+                    DeviceId.deviceId("netconf:1.2.3.4:830"),
+                    PortNumber.portNumber(1),
+                    Mep.MepDirection.UP_MEP, MDNAME1, MANAME1)
+                .buildEntry();
+
+    }
+
+    @Test
+    public void testGetAllMepsForMaEmpty() throws CfmConfigException {
+
+        expect(mepService.getAllMeps(MDNAME1, MANAME1)).andReturn(null).anyTimes();
+        replay(mepService);
+
+        final WebTarget wt = target();
+        final String response = wt.path("md/" + MDNAME1.mdName() + "/ma/" +
+                MANAME1.maName() + "/mep").request().get(String.class);
+        assertThat(response, is("{\"meps\":[[]]}"));
+    }
+
+    @Test
+    public void testGetAllMepsForMa1Mep() throws CfmConfigException {
+        Collection<MepEntry> meps = new ArrayList<>();
+        meps.add(mepEntry1);
+
+        expect(mepService.getAllMeps(MDNAME1, MANAME1)).andReturn(meps).anyTimes();
+        replay(mepService);
+
+        final WebTarget wt = target();
+        final String response = wt.path("md/" + MDNAME1.mdName() + "/ma/" +
+                MANAME1.maName() + "/mep").request().get(String.class);
+
+        assertThat(response, is("{\"meps\":" +
+                "[[{" +
+                "\"mepId\":" + MEPID1.value() + "," +
+                "\"deviceId\":\"netconf:1.2.3.4:830\"," +
+                "\"port\":1," +
+                "\"direction\":\"UP_MEP\"," +
+                "\"mdName\":\"" + MDNAME1.mdName() + "\"," +
+                "\"maName\":\"" + MANAME1.maName() + "\"," +
+                "\"administrative-state\":false," +
+                "\"cci-enabled\":false," +
+                "\"remoteMeps\":[]}]]}"));
+    }
+
+    @Test
+    public void testGetMepValid() throws CfmConfigException {
+
+        expect(mepService.getMep(MDNAME1, MANAME1, MepId.valueOf((short) 1)))
+                .andReturn(mepEntry1).anyTimes();
+        replay(mepService);
+
+        final WebTarget wt = target();
+        final String response = wt.path("md/" + MDNAME1.mdName() + "/ma/" +
+                MANAME1.maName() + "/mep/" + MEPID1.value()).request().get(String.class);
+
+        assertThat(response, is("{\"mep\":" +
+                "{" +
+                "\"mepId\":" + MEPID1.value() + "," +
+                "\"deviceId\":\"netconf:1.2.3.4:830\"," +
+                "\"port\":1," +
+                "\"direction\":\"UP_MEP\"," +
+                "\"mdName\":\"" + MDNAME1.mdName() + "\"," +
+                "\"maName\":\"" + MANAME1.maName() + "\"," +
+                "\"administrative-state\":false," +
+                "\"cci-enabled\":false," +
+                "\"remoteMeps\":[]}}"));
+    }
+
+    @Test
+    public void testGetMepNotFound() throws CfmConfigException, IOException {
+
+        expect(mepService.getMep(MDNAME1, MANAME1, MepId.valueOf((short) 2)))
+                .andReturn(null).anyTimes();
+        replay(mepService);
+
+        final WebTarget wt = target();
+
+        try {
+            final String response = wt.path("md/" + MDNAME1.mdName() + "/ma/" +
+                    MANAME1.maName() + "/mep/" + 2).request().get(String.class);
+            fail("Expected exception to be thrown");
+        } catch (InternalServerErrorException e) {
+            ByteArrayInputStream is = (ByteArrayInputStream) e.getResponse().getEntity();
+            BufferedReader br = new BufferedReader(new InputStreamReader(is));
+            String line = null;
+            StringBuffer sb = new StringBuffer();
+            while ((line = br.readLine()) != null) {
+                sb.append(line);
+            }
+            assertEquals("{ \"failure\":\"MEP md-1/ma-1-1/2 not found\" }", sb.toString());
+        }
+    }
+
+    @Test
+    public void testDeleteMepValid() throws CfmConfigException {
+
+        expect(mepService.deleteMep(MDNAME1, MANAME1, MepId.valueOf((short) 1)))
+                .andReturn(true).anyTimes();
+        replay(mepService);
+
+        final WebTarget wt = target();
+        final Response response = wt.path("md/" + MDNAME1.mdName() + "/ma/" +
+                MANAME1.maName() + "/mep/" + MEPID1.value()).request().delete();
+
+        assertEquals("Expecting 200", 200, response.getStatus());
+    }
+
+    @Test
+    public void testDeleteMepNotFound() throws CfmConfigException {
+
+        expect(mepService.deleteMep(MDNAME1, MANAME1, MepId.valueOf((short) 2)))
+                .andReturn(false).anyTimes();
+        replay(mepService);
+
+        final WebTarget wt = target();
+        final Response response = wt.path("md/" + MDNAME1.mdName() + "/ma/" +
+                MANAME1.maName() + "/mep/2").request().delete();
+
+        assertEquals("Expecting 304", 304, response.getStatus());
+    }
+
+    @Test
+    public void testCreateMep() throws CfmConfigException, IOException {
+        MepId mepId2 = MepId.valueOf((short) 2);
+        Mep mep2 = DefaultMep.builder(
+                mepId2,
+                DeviceId.deviceId("netconf:2.2.3.4:830"),
+                PortNumber.portNumber(2),
+                Mep.MepDirection.UP_MEP, MDNAME1, MANAME1).build();
+
+        MaintenanceAssociation ma1 = DefaultMaintenanceAssociation
+                .builder(MANAME1, MDNAME1.getNameLength()).build();
+
+        expect(mdService.getMaintenanceAssociation(MDNAME1, MANAME1))
+                                .andReturn(Optional.ofNullable(ma1)).anyTimes();
+        replay(mdService);
+        expect(mepService.createMep(MDNAME1, MANAME1, mep2))
+                                .andReturn(true).anyTimes();
+        replay(mepService);
+
+        ObjectMapper mapper = new ObjectMapper();
+        CfmCodecContext context = new CfmCodecContext();
+        ObjectNode node = mapper.createObjectNode();
+        node.set("mep", context.codec(Mep.class).encode(mep2, context));
+
+        final WebTarget wt = target();
+        final Response response = wt.path("md/" + MDNAME1.mdName() + "/ma/" +
+                MANAME1.maName() + "/mep")
+                .request()
+                .post(Entity.json(node.toString()));
+
+        assertEquals("Expecting 201", 201, response.getStatus());
+    }
+
+    @Test
+    public void testCreateMepAlreadyExists() throws CfmConfigException, IOException {
+        MepId mepId3 = MepId.valueOf((short) 3);
+        Mep mep3 = DefaultMep.builder(
+                    mepId3,
+                    DeviceId.deviceId("netconf:3.2.3.4:830"),
+                    PortNumber.portNumber(3),
+                    Mep.MepDirection.UP_MEP, MDNAME1, MANAME1)
+                .cciEnabled(true)
+                .ccmLtmPriority(Mep.Priority.PRIO3)
+                .administrativeState(false)
+                .primaryVid(VlanId.vlanId((short) 3))
+                .defectAbsentTime(Duration.ofMinutes(2))
+                .defectPresentTime(Duration.ofMinutes(3))
+                .fngAddress(Mep.FngAddress.notSpecified())
+                .lowestFaultPriorityDefect(Mep.LowestFaultDefect.ALL_DEFECTS)
+                .build();
+
+        MaintenanceAssociation ma1 = DefaultMaintenanceAssociation
+                .builder(MANAME1, MDNAME1.getNameLength()).build();
+
+        expect(mdService.getMaintenanceAssociation(MDNAME1, MANAME1))
+                .andReturn(Optional.ofNullable(ma1)).anyTimes();
+        replay(mdService);
+        expect(mepService.createMep(MDNAME1, MANAME1, mep3))
+                .andReturn(false).anyTimes();
+        replay(mepService);
+
+        ObjectMapper mapper = new ObjectMapper();
+        CfmCodecContext context = new CfmCodecContext();
+        ObjectNode node = mapper.createObjectNode();
+        node.set("mep", context.codec(Mep.class).encode(mep3, context));
+
+        final WebTarget wt = target();
+        final Response response = wt.path("md/" + MDNAME1.mdName() + "/ma/" +
+                MANAME1.maName() + "/mep")
+                .request()
+                .post(Entity.json(node.toString()));
+
+        assertEquals("Expecting 304", 304, response.getStatus());
+    }
+
+    @Test
+    public void testTransmitLoopback() throws CfmConfigException {
+        MepLbCreate mepLbCreate1 = DefaultMepLbCreate
+                .builder(MEPID1)
+                .numberMessages(20)
+                .dataTlvHex("AA:BB:CC:DD")
+                .vlanDropEligible(true)
+                .build();
+
+        MaintenanceAssociation ma1 = DefaultMaintenanceAssociation
+                .builder(MANAME1, MDNAME1.getNameLength()).build();
+        MaintenanceDomain md1 = DefaultMaintenanceDomain.builder(MDNAME1)
+                .addToMaList(ma1).build();
+        expect(mdService.getMaintenanceDomain(MDNAME1))
+                        .andReturn(Optional.ofNullable(md1)).anyTimes();
+        expect(mdService.getMaintenanceAssociation(MDNAME1, MANAME1))
+                        .andReturn(Optional.ofNullable(ma1)).anyTimes();
+        replay(mdService);
+
+        ObjectMapper mapper = new ObjectMapper();
+        CfmCodecContext context = new CfmCodecContext();
+        ObjectNode node = mapper.createObjectNode();
+        node.set("loopback", context.codec(MepLbCreate.class).encode(mepLbCreate1, context));
+
+        final WebTarget wt = target();
+        final Response response = wt.path("md/" + MDNAME1.mdName() + "/ma/" +
+                MANAME1.maName() + "/mep/" + MEPID1.value() + "/transmit-loopback")
+                .request()
+                .put(Entity.json(node.toString()));
+
+        assertEquals("Expecting 202", 202, response.getStatus());
+    }
+
+    @Test
+    public void testAbortLoopback() throws CfmConfigException {
+
+        MepLbCreate mepLbCreate1 = DefaultMepLbCreate.builder(MEPID1).build();
+
+        MaintenanceAssociation ma1 = DefaultMaintenanceAssociation
+                .builder(MANAME1, MDNAME1.getNameLength()).build();
+        MaintenanceDomain md1 = DefaultMaintenanceDomain.builder(MDNAME1)
+                .addToMaList(ma1).build();
+        expect(mdService.getMaintenanceDomain(MDNAME1))
+                .andReturn(Optional.ofNullable(md1)).anyTimes();
+        expect(mdService.getMaintenanceAssociation(MDNAME1, MANAME1))
+                .andReturn(Optional.ofNullable(ma1)).anyTimes();
+        replay(mdService);
+
+        final WebTarget wt = target();
+        final Response response = wt.path("md/" + MDNAME1.mdName() + "/ma/" +
+                MANAME1.maName() + "/mep/" + MEPID1.value() + "/abort-loopback")
+                .request()
+                .put(Entity.json(""));
+
+        assertEquals("Expecting 202", 202, response.getStatus());
+
+    }
+
+    @Test
+    public void testTransmitLinktrace() throws CfmConfigException {
+        MepLtCreate mepLtCreate1 = DefaultMepLtCreate
+                .builder(MEPID1)
+                .defaultTtl((short) 20)
+                .transmitLtmFlags(BitSet.valueOf(new byte[]{1}))
+                .build();
+
+        MaintenanceAssociation ma1 = DefaultMaintenanceAssociation
+                .builder(MANAME1, MDNAME1.getNameLength()).build();
+        MaintenanceDomain md1 = DefaultMaintenanceDomain.builder(MDNAME1)
+                .addToMaList(ma1).build();
+        expect(mdService.getMaintenanceDomain(MDNAME1))
+                .andReturn(Optional.ofNullable(md1)).anyTimes();
+        expect(mdService.getMaintenanceAssociation(MDNAME1, MANAME1))
+                .andReturn(Optional.ofNullable(ma1)).anyTimes();
+        replay(mdService);
+
+        ObjectMapper mapper = new ObjectMapper();
+        CfmCodecContext context = new CfmCodecContext();
+        ObjectNode node = mapper.createObjectNode();
+        node.set("linktrace", context.codec(MepLtCreate.class).encode(mepLtCreate1, context));
+
+        final WebTarget wt = target();
+        final Response response = wt.path("md/" + MDNAME1.mdName() + "/ma/" +
+                MANAME1.maName() + "/mep/" + MEPID1.value() + "/transmit-linktrace")
+                .request()
+                .put(Entity.json(node.toString()));
+
+        assertEquals("Expecting 202", 202, response.getStatus());
+    }
+}
diff --git a/apps/cfm/src/test/java/org/onosproject/cfm/web/MaintenanceAssociationCodecTest.java b/apps/cfm/src/test/java/org/onosproject/cfm/web/MaintenanceAssociationCodecTest.java
new file mode 100644
index 0000000..9712c7d
--- /dev/null
+++ b/apps/cfm/src/test/java/org/onosproject/cfm/web/MaintenanceAssociationCodecTest.java
@@ -0,0 +1,248 @@
+/*
+ * Copyright 2017-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.cfm.web;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import org.junit.Before;
+import org.junit.Test;
+import org.onosproject.cfm.CfmCodecContext;
+import org.onosproject.incubator.net.l2monitoring.cfm.DefaultMaintenanceAssociation;
+import org.onosproject.incubator.net.l2monitoring.cfm.MaintenanceAssociation;
+import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MaId2Octet;
+import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MaIdCharStr;
+import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MaIdIccY1731;
+import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MaIdPrimaryVid;
+import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MaIdRfc2685VpnId;
+import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MaIdShort;
+import org.onosproject.incubator.net.l2monitoring.cfm.service.CfmConfigException;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Test that the MaintenanceAssociationCodec can successfully parse Json in to a MaintenanceAssociation.
+ */
+public class MaintenanceAssociationCodecTest {
+    private static final MaIdShort MAID1_CHAR = MaIdCharStr.asMaId("ma-1");
+    private static final MaIdShort MAID2_VID = MaIdPrimaryVid.asMaId(1234);
+    private static final MaIdShort MAID3_OCTET = MaId2Octet.asMaId(12467);
+    private static final MaIdShort MAID4_RFC = MaIdRfc2685VpnId.asMaIdHex("aa:bb:cc:dd:ee:ff:99");
+    private static final MaIdShort MAID5_Y1731 = MaIdIccY1731.asMaId("abc", "defghij");
+
+
+    private ObjectMapper mapper;
+    private CfmCodecContext context;
+
+    @Before
+    public void setUp() throws Exception, CfmConfigException {
+        mapper = new ObjectMapper();
+        context = new CfmCodecContext();
+    }
+
+    @Test
+    public void testDecodeMa1() throws IOException {
+        String mdString = "{\"ma\": {    \"maName\": \"ma-1\"," +
+                "\"maNameType\": \"CHARACTERSTRING\"," +
+                "\"component-list\": [], " +
+                "\"rmep-list\": [], " +
+                "\"maNumericId\": 1}}";
+
+        InputStream input = new ByteArrayInputStream(
+                mdString.getBytes(StandardCharsets.UTF_8));
+        JsonNode cfg = mapper.readTree(input);
+        MaintenanceAssociation maDecode1 = ((MaintenanceAssociationCodec) context
+                .codec(MaintenanceAssociation.class))
+                .decode((ObjectNode) cfg, context, 10);
+        assertEquals(MAID1_CHAR, maDecode1.maId());
+        assertEquals(1, maDecode1.maNumericId());
+    }
+
+    @Test
+    public void testDecodeMa1NoTypeGiven() throws IOException {
+        String mdString = "{\"ma\": {    \"maName\": \"ma-1\"," +
+                "\"component-list\": [], " +
+                "\"rmep-list\": [], " +
+                "\"maNumericId\": 1}}";
+
+        InputStream input = new ByteArrayInputStream(
+                mdString.getBytes(StandardCharsets.UTF_8));
+        JsonNode cfg = mapper.readTree(input);
+        MaintenanceAssociation maDecode1 = ((MaintenanceAssociationCodec) context
+                .codec(MaintenanceAssociation.class))
+                .decode((ObjectNode) cfg, context, 10);
+        assertEquals(MAID1_CHAR, maDecode1.maId());
+        assertEquals(1, maDecode1.maNumericId());
+    }
+
+    @Test
+    public void testDecodeMa2() throws IOException {
+        String mdString = "{\"ma\": {    \"maName\": 1234," +
+                "\"maNameType\": \"PRIMARYVID\"," +
+                "\"component-list\": [], " +
+                "\"rmep-list\": [], " +
+                "\"maNumericId\": 2}}";
+
+        InputStream input = new ByteArrayInputStream(
+                mdString.getBytes(StandardCharsets.UTF_8));
+        JsonNode cfg = mapper.readTree(input);
+        MaintenanceAssociation maDecode2 = ((MaintenanceAssociationCodec) context
+                .codec(MaintenanceAssociation.class))
+                .decode((ObjectNode) cfg, context, 10);
+        assertEquals(MAID2_VID, maDecode2.maId());
+    }
+
+    @Test
+    public void testDecodeMa3() throws IOException {
+        String mdString = "{\"ma\": {    \"maName\": 12467," +
+                "\"maNameType\": \"TWOOCTET\"," +
+                "\"component-list\": [], " +
+                "\"rmep-list\": [], " +
+                "\"maNumericId\": 3}}";
+
+        InputStream input = new ByteArrayInputStream(
+                mdString.getBytes(StandardCharsets.UTF_8));
+        JsonNode cfg = mapper.readTree(input);
+        MaintenanceAssociation maDecode3 = ((MaintenanceAssociationCodec) context
+                .codec(MaintenanceAssociation.class))
+                .decode((ObjectNode) cfg, context, 10);
+        assertEquals(MAID3_OCTET, maDecode3.maId());
+    }
+
+    @Test
+    public void testDecodeMa4() throws IOException {
+        String mdString = "{\"ma\": {    \"maName\": \"aa:bb:cc:dd:ee:ff:99\"," +
+                "\"maNameType\": \"RFC2685VPNID\"," +
+                "\"component-list\": [], " +
+                "\"rmep-list\": [], " +
+                "\"maNumericId\": 4}}";
+
+        InputStream input = new ByteArrayInputStream(
+                mdString.getBytes(StandardCharsets.UTF_8));
+        JsonNode cfg = mapper.readTree(input);
+        MaintenanceAssociation maDecode4 = ((MaintenanceAssociationCodec) context
+                .codec(MaintenanceAssociation.class))
+                .decode((ObjectNode) cfg, context, 10);
+        assertEquals(MAID4_RFC, maDecode4.maId());
+    }
+
+    @Test
+    public void testDecodeMa5() throws IOException {
+        String mdString = "{\"ma\": {    \"maName\": \"abc:defghij\"," +
+                "\"maNameType\": \"ICCY1731\"," +
+                "\"component-list\": [], " +
+                "\"rmep-list\": [], " +
+                "\"maNumericId\": 5}}";
+
+        InputStream input = new ByteArrayInputStream(
+                mdString.getBytes(StandardCharsets.UTF_8));
+        JsonNode cfg = mapper.readTree(input);
+        MaintenanceAssociation maDecode5 = ((MaintenanceAssociationCodec) context
+                .codec(MaintenanceAssociation.class))
+                .decode((ObjectNode) cfg, context, 10);
+        assertEquals(MAID5_Y1731, maDecode5.maId());
+    }
+
+    @Test
+    public void testEncodeMa1() throws CfmConfigException {
+        MaintenanceAssociation ma1 = DefaultMaintenanceAssociation.builder(MAID1_CHAR, 10)
+                .maNumericId((short) 1)
+                .build();
+
+        ObjectNode node = mapper.createObjectNode();
+        node.set("ma", context.codec(MaintenanceAssociation.class).encode(ma1, context));
+
+        assertEquals("{\"ma\":{" +
+                "\"maName\":\"ma-1\"," +
+                "\"maNameType\":\"CHARACTERSTRING\"," +
+                "\"maNumericId\":1," +
+                "\"component-list\":[]," +
+                "\"rmep-list\":[]}}", node.toString());
+    }
+
+    @Test
+    public void testEncodeMa2() throws CfmConfigException {
+        MaintenanceAssociation ma1 = DefaultMaintenanceAssociation.builder(MAID2_VID, 10)
+                .maNumericId((short) 2)
+                .build();
+
+        ObjectNode node = mapper.createObjectNode();
+        node.set("ma", context.codec(MaintenanceAssociation.class).encode(ma1, context));
+
+        assertEquals("{\"ma\":{" +
+                "\"maName\":\"1234\"," +
+                "\"maNameType\":\"PRIMARYVID\"," +
+                "\"maNumericId\":2," +
+                "\"component-list\":[]," +
+                "\"rmep-list\":[]}}", node.toString());
+    }
+
+    @Test
+    public void testEncodeMa3() throws CfmConfigException {
+        MaintenanceAssociation ma1 = DefaultMaintenanceAssociation.builder(MAID3_OCTET, 10)
+                .maNumericId((short) 3)
+                .build();
+
+        ObjectNode node = mapper.createObjectNode();
+        node.set("ma", context.codec(MaintenanceAssociation.class).encode(ma1, context));
+
+        assertEquals("{\"ma\":{" +
+                "\"maName\":\"12467\"," +
+                "\"maNameType\":\"TWOOCTET\"," +
+                "\"maNumericId\":3," +
+                "\"component-list\":[]," +
+                "\"rmep-list\":[]}}", node.toString());
+    }
+
+    @Test
+    public void testEncodeMa4() throws CfmConfigException {
+        MaintenanceAssociation ma1 = DefaultMaintenanceAssociation.builder(MAID4_RFC, 10)
+                .maNumericId((short) 4)
+                .build();
+
+        ObjectNode node = mapper.createObjectNode();
+        node.set("ma", context.codec(MaintenanceAssociation.class).encode(ma1, context));
+
+        assertEquals("{\"ma\":{" +
+                "\"maName\":\"aa:bb:cc:dd:ee:ff:99\"," +
+                "\"maNameType\":\"RFC2685VPNID\"," +
+                "\"maNumericId\":4," +
+                "\"component-list\":[]," +
+                "\"rmep-list\":[]}}", node.toString());
+    }
+
+    @Test
+    public void testEncodeMa5() throws CfmConfigException {
+        MaintenanceAssociation ma1 = DefaultMaintenanceAssociation.builder(MAID5_Y1731, 10)
+                .maNumericId((short) 5)
+                .build();
+
+        ObjectNode node = mapper.createObjectNode();
+        node.set("ma", context.codec(MaintenanceAssociation.class).encode(ma1, context));
+
+        assertEquals("{\"ma\":{" +
+                "\"maName\":\"abc:defghij\"," +
+                "\"maNameType\":\"ICCY1731\"," +
+                "\"maNumericId\":5," +
+                "\"component-list\":[]," +
+                "\"rmep-list\":[]}}", node.toString());
+    }
+}
diff --git a/apps/cfm/src/test/java/org/onosproject/cfm/web/MaintenanceDomainCodecTest.java b/apps/cfm/src/test/java/org/onosproject/cfm/web/MaintenanceDomainCodecTest.java
new file mode 100644
index 0000000..b1fff52
--- /dev/null
+++ b/apps/cfm/src/test/java/org/onosproject/cfm/web/MaintenanceDomainCodecTest.java
@@ -0,0 +1,197 @@
+/*
+ * Copyright 2017-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.cfm.web;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import com.google.common.net.InternetDomainName;
+import org.junit.Before;
+import org.junit.Test;
+import org.onlab.packet.MacAddress;
+import org.onosproject.cfm.CfmCodecContext;
+import org.onosproject.incubator.net.l2monitoring.cfm.DefaultMaintenanceDomain;
+import org.onosproject.incubator.net.l2monitoring.cfm.MaintenanceDomain;
+import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MdId;
+import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MdIdCharStr;
+import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MdIdDomainName;
+import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MdIdMacUint;
+import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MdIdNone;
+import org.onosproject.incubator.net.l2monitoring.cfm.service.CfmConfigException;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Test that the MaintenanceDomainCodec can successfully parse Json in to a MaintenanceDomain.
+ */
+public class MaintenanceDomainCodecTest {
+    private static final MdId MDID1_CHAR = MdIdCharStr.asMdId("test-1");
+    private static final MdId MDID2_DOMAIN = MdIdDomainName.asMdId(
+                        InternetDomainName.from("test.opennetworking.org"));
+    private static final MdId MDID3_MACUINT =
+            MdIdMacUint.asMdId(MacAddress.valueOf("aa:bb:cc:dd:ee:ff"), 181);
+    private static final MdId MDID4_NONE = MdIdNone.asMdId();
+
+    private ObjectMapper mapper;
+    private CfmCodecContext context;
+
+    @Before
+    public void setUp() throws Exception, CfmConfigException {
+        mapper = new ObjectMapper();
+        context = new CfmCodecContext();
+    }
+
+    @Test
+    public void testDecodeMd1() throws IOException {
+        String mdString = "{\"md\": {    \"mdName\": \"test-1\"," +
+                "\"mdNameType\": \"CHARACTERSTRING\"," +
+                "\"mdLevel\": \"LEVEL1\", \"mdNumericId\": 1}}";
+
+        InputStream input = new ByteArrayInputStream(
+                mdString.getBytes(StandardCharsets.UTF_8));
+        JsonNode cfg = mapper.readTree(input);
+        MaintenanceDomain mdDecode1 = context
+                .codec(MaintenanceDomain.class).decode((ObjectNode) cfg, context);
+        assertEquals(MDID1_CHAR, mdDecode1.mdId());
+        assertEquals(MaintenanceDomain.MdLevel.LEVEL1, mdDecode1.mdLevel());
+        assertEquals(1, mdDecode1.mdNumericId());
+    }
+
+    @Test
+    public void testDecodeMd1NoTypeGiven() throws IOException {
+        String mdString = "{\"md\": {    \"mdName\": \"test-1\"," +
+                "\"mdLevel\": \"LEVEL1\", \"mdNumericId\": 1}}";
+
+        InputStream input = new ByteArrayInputStream(
+                mdString.getBytes(StandardCharsets.UTF_8));
+        JsonNode cfg = mapper.readTree(input);
+        MaintenanceDomain mdDecode1 = context
+                .codec(MaintenanceDomain.class).decode((ObjectNode) cfg, context);
+        assertEquals(MDID1_CHAR, mdDecode1.mdId());
+        assertEquals(MaintenanceDomain.MdLevel.LEVEL1, mdDecode1.mdLevel());
+        assertEquals(1, mdDecode1.mdNumericId());
+    }
+
+
+    @Test
+    public void testDecodeMd2() throws IOException {
+        String mdString = "{\"md\": {    \"mdName\": \"test.opennetworking.org\"," +
+                "\"mdNameType\": \"DOMAINNAME\"}}";
+
+        InputStream input = new ByteArrayInputStream(
+                mdString.getBytes(StandardCharsets.UTF_8));
+        JsonNode cfg = mapper.readTree(input);
+        MaintenanceDomain mdDecode1 = context
+                .codec(MaintenanceDomain.class).decode((ObjectNode) cfg, context);
+        assertEquals(MDID2_DOMAIN, mdDecode1.mdId());
+        assertEquals(MaintenanceDomain.MdLevel.LEVEL0, mdDecode1.mdLevel());
+        assertEquals(0, mdDecode1.mdNumericId());
+    }
+
+    @Test
+    public void testDecodeMd3() throws IOException {
+        String mdString = "{\"md\": {    \"mdName\": \"aa:bb:cc:dd:ee:ff:181\"," +
+                "\"mdNameType\": \"MACANDUINT\"}}";
+
+        InputStream input = new ByteArrayInputStream(
+                mdString.getBytes(StandardCharsets.UTF_8));
+        JsonNode cfg = mapper.readTree(input);
+        MaintenanceDomain mdDecode1 = context
+                .codec(MaintenanceDomain.class).decode((ObjectNode) cfg, context);
+        assertEquals(MDID3_MACUINT, mdDecode1.mdId());
+    }
+
+    @Test
+    public void testDecodeMd4() throws IOException {
+        String mdString = "{\"md\": {    \"mdName\": \"\"," +
+                "\"mdNameType\": \"NONE\"}}";
+
+        InputStream input = new ByteArrayInputStream(
+                mdString.getBytes(StandardCharsets.UTF_8));
+        JsonNode cfg = mapper.readTree(input);
+        MaintenanceDomain mdDecode1 = context
+                .codec(MaintenanceDomain.class).decode((ObjectNode) cfg, context);
+        assertEquals(MDID4_NONE, mdDecode1.mdId());
+    }
+
+    @Test
+    public void testEncodeMd1() throws CfmConfigException {
+        MaintenanceDomain md1 = DefaultMaintenanceDomain.builder(MDID1_CHAR)
+                .mdLevel(MaintenanceDomain.MdLevel.LEVEL1)
+                .mdNumericId((short) 1)
+                .build();
+
+        ObjectNode node = mapper.createObjectNode();
+        node.set("md", context.codec(MaintenanceDomain.class).encode(md1, context));
+
+        assertEquals("{\"md\":{" +
+                "\"mdName\":\"test-1\"," +
+                "\"mdNameType\":\"CHARACTERSTRING\"," +
+                "\"mdLevel\":\"LEVEL1\"," +
+                "\"mdNumericId\":1," +
+                "\"maList\":[]}}", node.toString());
+    }
+
+    @Test
+    public void testEncodeMd2() throws CfmConfigException {
+        MaintenanceDomain md2 = DefaultMaintenanceDomain.builder(MDID2_DOMAIN)
+                .mdLevel(MaintenanceDomain.MdLevel.LEVEL2).build();
+
+        ObjectNode node = mapper.createObjectNode();
+        node.set("md", context.codec(MaintenanceDomain.class).encode(md2, context));
+
+        assertEquals("{\"md\":{" +
+                "\"mdName\":\"test.opennetworking.org\"," +
+                "\"mdNameType\":\"DOMAINNAME\"," +
+                "\"mdLevel\":\"LEVEL2\"," +
+                "\"maList\":[]}}", node.toString());
+    }
+
+    @Test
+    public void testEncodeMd3() throws CfmConfigException {
+        MaintenanceDomain md3 = DefaultMaintenanceDomain.builder(MDID3_MACUINT)
+                .mdLevel(MaintenanceDomain.MdLevel.LEVEL3).build();
+
+        ObjectNode node = mapper.createObjectNode();
+        node.set("md", context.codec(MaintenanceDomain.class).encode(md3, context));
+
+        assertEquals("{\"md\":{" +
+                "\"mdName\":\"AA:BB:CC:DD:EE:FF:181\"," +
+                "\"mdNameType\":\"MACANDUINT\"," +
+                "\"mdLevel\":\"LEVEL3\"," +
+                "\"maList\":[]}}", node.toString());
+    }
+
+    @Test
+    public void testEncodeMd4() throws CfmConfigException {
+        MaintenanceDomain md4 = DefaultMaintenanceDomain.builder(MDID4_NONE)
+                .mdLevel(MaintenanceDomain.MdLevel.LEVEL4).build();
+
+        ObjectNode node = mapper.createObjectNode();
+        node.set("md", context.codec(MaintenanceDomain.class).encode(md4, context));
+
+        assertEquals("{\"md\":{" +
+                "\"mdName\":\"\"," +
+                "\"mdNameType\":\"NONE\"," +
+                "\"mdLevel\":\"LEVEL4\"," +
+                "\"maList\":[]}}", node.toString());
+    }
+}
diff --git a/apps/cfm/src/test/java/org/onosproject/cfm/web/MepEntryCodecTest.java b/apps/cfm/src/test/java/org/onosproject/cfm/web/MepEntryCodecTest.java
new file mode 100644
index 0000000..ea07b8e
--- /dev/null
+++ b/apps/cfm/src/test/java/org/onosproject/cfm/web/MepEntryCodecTest.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright 2017-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.cfm.web;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.onlab.packet.MacAddress;
+import org.onosproject.cfm.CfmCodecContext;
+import org.onosproject.incubator.net.l2monitoring.cfm.DefaultMepEntry;
+import org.onosproject.incubator.net.l2monitoring.cfm.Mep.MepDirection;
+import org.onosproject.incubator.net.l2monitoring.cfm.Mep.Priority;
+import org.onosproject.incubator.net.l2monitoring.cfm.MepEntry;
+import org.onosproject.incubator.net.l2monitoring.cfm.MepEntry.MepEntryBuilder;
+import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MaIdCharStr;
+import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MdIdCharStr;
+import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MepId;
+import org.onosproject.incubator.net.l2monitoring.cfm.service.CfmConfigException;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.PortNumber;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+
+/**
+ * Test that the MepEntryCodec can successfully parse Json in to a Mep.
+ */
+public class MepEntryCodecTest {
+    ObjectMapper mapper;
+    CfmCodecContext context;
+    MepEntry mepEntry1;
+
+    @Before
+    public void setUp() throws Exception, CfmConfigException {
+        mapper = new ObjectMapper();
+        context = new CfmCodecContext();
+        MepEntryBuilder builder = DefaultMepEntry.builder(
+                MepId.valueOf((short) 22),
+                DeviceId.deviceId("netconf:1234:830"),
+                PortNumber.portNumber(2),
+                MepDirection.UP_MEP,
+                MdIdCharStr.asMdId("md-1"),
+                MaIdCharStr.asMaId("ma-1-1"))
+            .macAddress(MacAddress.valueOf("aa:bb:cc:dd:ee:ff"));
+        builder = (MepEntryBuilder) builder
+                .administrativeState(true)
+                .cciEnabled(true)
+                .ccmLtmPriority(Priority.PRIO1);
+        mepEntry1 = builder.buildEntry();
+
+    }
+
+    @Test
+    public void testEncodeMepEntryCodecContext() {
+        ObjectNode node = mapper.createObjectNode();
+        node.set("mep", context.codec(MepEntry.class).encode(mepEntry1, context));
+
+        assertEquals(22, node.get("mep").get("mepId").asInt());
+        assertEquals("aa:bb:cc:dd:ee:ff".toUpperCase(),
+                node.get("mep").get("macAddress").asText());
+        assertTrue(node.get("mep").get("administrative-state").asBoolean());
+        assertTrue(node.get("mep").get("cci-enabled").asBoolean());
+        assertEquals(Priority.PRIO1.ordinal(),
+                node.get("mep").get("ccm-ltm-priority").asInt());
+    }
+
+    @Test
+    public void testEncodeIterableOfMepEntryCodecContext() throws CfmConfigException {
+        MepEntry mepEntry2 = DefaultMepEntry.builder(
+                MepId.valueOf((short) 33),
+                DeviceId.deviceId("netconf:4321:830"),
+                PortNumber.portNumber(1),
+                MepDirection.DOWN_MEP,
+                MdIdCharStr.asMdId("md-2"),
+                MaIdCharStr.asMaId("ma-2-2"))
+            .buildEntry();
+
+        ArrayList<MepEntry> meps = new ArrayList<>();
+        meps.add(mepEntry1);
+        meps.add(mepEntry2);
+
+        ObjectNode node = mapper.createObjectNode();
+        node.set("mep", context.codec(MepEntry.class)
+                .encode(meps, context));
+
+        Iterator<JsonNode> an = node.get("mep").elements();
+        while (an.hasNext()) {
+            JsonNode jn = an.next();
+            assertEquals("md-", jn.get("mdName").asText().substring(0, 3));
+        }
+    }
+
+}
diff --git a/apps/cfm/src/test/java/org/onosproject/cfm/web/MepLbCreateCodecTest.java b/apps/cfm/src/test/java/org/onosproject/cfm/web/MepLbCreateCodecTest.java
new file mode 100644
index 0000000..e2c853a
--- /dev/null
+++ b/apps/cfm/src/test/java/org/onosproject/cfm/web/MepLbCreateCodecTest.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2017-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.cfm.web;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.onosproject.cfm.CfmCodecContext;
+import org.onosproject.incubator.net.l2monitoring.cfm.MepLbCreate;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+
+public class MepLbCreateCodecTest {
+    ObjectMapper mapper;
+    CfmCodecContext context;
+
+    @Before
+    public void setUp() throws Exception {
+        mapper = new ObjectMapper();
+        context = new CfmCodecContext();
+    }
+
+    @Test
+    public void testDecodeMepLbCreateMepId() throws JsonProcessingException, IOException {
+        String loopbackString = "{\"loopback\": {    \"remoteMepId\": 20," +
+                "\"numberMessages\": 10,    \"vlanDropEligible\": true," +
+                "\"vlanPriority\": 6,    \"dataTlvHex\": \"0A:BB:CC\" }}";
+
+        InputStream input = new ByteArrayInputStream(
+                loopbackString.getBytes(StandardCharsets.UTF_8));
+        JsonNode cfg = mapper.readTree(input);
+        MepLbCreate mepLbCreate = context
+                .codec(MepLbCreate.class).decode((ObjectNode) cfg, context);
+
+        assertNull(mepLbCreate.remoteMepAddress());
+        assertEquals(20, mepLbCreate.remoteMepId().id().shortValue());
+        assertEquals(10, mepLbCreate.numberMessages().intValue());
+        assertEquals(6, mepLbCreate.vlanPriority().ordinal());
+        assertEquals(true, mepLbCreate.vlanDropEligible());
+        assertEquals("0A:BB:CC".toLowerCase(), mepLbCreate.dataTlvHex());
+    }
+
+    @Test
+    public void testDecodeMepLbCreateMepMac() throws JsonProcessingException, IOException {
+        String loopbackString = "{\"loopback\": {    " +
+                "\"remoteMepMac\": \"AA:BB:CC:DD:EE:FF\" }}";
+        InputStream input = new ByteArrayInputStream(
+                loopbackString.getBytes(StandardCharsets.UTF_8));
+        JsonNode cfg = mapper.readTree(input);
+        MepLbCreate mepLbCreate = context
+                .codec(MepLbCreate.class).decode((ObjectNode) cfg, context);
+
+        assertNull(mepLbCreate.remoteMepId());
+        assertEquals("AA:BB:CC:DD:EE:FF", mepLbCreate.remoteMepAddress().toString());
+        assertNull(mepLbCreate.dataTlvHex());
+    }
+}
diff --git a/apps/cfm/src/test/java/org/onosproject/cfm/web/MepLbEntryCodecTest.java b/apps/cfm/src/test/java/org/onosproject/cfm/web/MepLbEntryCodecTest.java
new file mode 100644
index 0000000..844e4a2
--- /dev/null
+++ b/apps/cfm/src/test/java/org/onosproject/cfm/web/MepLbEntryCodecTest.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2017-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.cfm.web;
+
+import static org.junit.Assert.assertEquals;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.onosproject.cfm.CfmCodecContext;
+import org.onosproject.incubator.net.l2monitoring.cfm.DefaultMepLbEntry;
+import org.onosproject.incubator.net.l2monitoring.cfm.MepLbEntry;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+public class MepLbEntryCodecTest {
+    ObjectMapper mapper;
+    CfmCodecContext context;
+
+    @Before
+    public void setUp() throws Exception {
+        mapper = new ObjectMapper();
+        context = new CfmCodecContext();
+    }
+
+    @Test
+    public void testEncodeMepLbEntryCodecContext() {
+        MepLbEntry mepLbEntry1 = DefaultMepLbEntry.builder()
+                .countLbrMacMisMatch(987654321L)
+                .countLbrReceived(987654322L)
+                .countLbrTransmitted(987654323L)
+                .countLbrValidInOrder(987654324L)
+                .countLbrValidOutOfOrder(987654325L)
+                .nextLbmIdentifier(987654326L)
+                .build();
+
+        assertEquals(987654321L, mepLbEntry1.countLbrMacMisMatch());
+        assertEquals(987654322L, mepLbEntry1.countLbrReceived());
+        assertEquals(987654323L, mepLbEntry1.countLbrTransmitted());
+        assertEquals(987654324L, mepLbEntry1.countLbrValidInOrder());
+        assertEquals(987654325L, mepLbEntry1.countLbrValidOutOfOrder());
+        assertEquals(987654326L, mepLbEntry1.nextLbmIdentifier());
+    }
+
+}
diff --git a/apps/cfm/src/test/java/org/onosproject/cfm/web/MepLtCreateCodecTest.java b/apps/cfm/src/test/java/org/onosproject/cfm/web/MepLtCreateCodecTest.java
new file mode 100644
index 0000000..3e0ec46
--- /dev/null
+++ b/apps/cfm/src/test/java/org/onosproject/cfm/web/MepLtCreateCodecTest.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright 2017-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.cfm.web;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import org.junit.Before;
+import org.junit.Test;
+import org.onosproject.cfm.CfmCodecContext;
+import org.onosproject.incubator.net.l2monitoring.cfm.DefaultMepLtCreate;
+import org.onosproject.incubator.net.l2monitoring.cfm.MepLtCreate;
+import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MepId;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+import java.util.BitSet;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+
+public class MepLtCreateCodecTest {
+    ObjectMapper mapper;
+    CfmCodecContext context;
+
+    @Before
+    public void setUp() throws Exception {
+        mapper = new ObjectMapper();
+        context = new CfmCodecContext();
+    }
+
+    @Test
+    public void testDecodeMepLtCreateMepId() throws JsonProcessingException, IOException {
+        String linktraceString = "{\"linktrace\": {    " +
+                "\"remoteMepId\": 20," +
+                "\"defaultTtl\": 21," +
+                "\"transmitLtmFlags\": \"use-fdb-only\"}}";
+
+        InputStream input = new ByteArrayInputStream(
+                linktraceString.getBytes(StandardCharsets.UTF_8));
+        JsonNode cfg = mapper.readTree(input);
+        MepLtCreate mepLtCreate = context
+                .codec(MepLtCreate.class).decode((ObjectNode) cfg, context);
+
+        assertNull(mepLtCreate.remoteMepAddress());
+        assertEquals(20, mepLtCreate.remoteMepId().id().shortValue());
+        assertEquals(21, mepLtCreate.defaultTtl().intValue());
+        assertEquals(BitSet.valueOf(new byte[]{1}), mepLtCreate.transmitLtmFlags());
+    }
+
+    @Test
+    public void testDecodeMepLtCreateInvalidTransmitLtmFlags()
+            throws JsonProcessingException, IOException {
+        String linktraceString = "{\"linktrace\": {    " +
+                "\"remoteMepId\": 20," +
+                "\"transmitLtmFlags\": \"1\"}}";
+
+        InputStream input = new ByteArrayInputStream(
+                linktraceString.getBytes(StandardCharsets.UTF_8));
+        JsonNode cfg = mapper.readTree(input);
+        try {
+            context.codec(MepLtCreate.class).decode((ObjectNode) cfg, context);
+        } catch (IllegalArgumentException e) {
+            assertEquals("Expecting value 'use-fdb-only' or '' " +
+                    "for transmitLtmFlags", e.getMessage());
+        }
+    }
+
+    @Test
+    public void testEncodeMepLtCreate() {
+        MepId mepId1 = MepId.valueOf((short) 1);
+        MepLtCreate mepLtCreate1 = DefaultMepLtCreate
+                .builder(mepId1)
+                .defaultTtl((short) 20)
+                .transmitLtmFlags(BitSet.valueOf(new byte[]{1}))
+                .build();
+
+        ObjectMapper mapper = new ObjectMapper();
+        CfmCodecContext context = new CfmCodecContext();
+        ObjectNode node = mapper.createObjectNode();
+        node.set("linktrace", context.codec(MepLtCreate.class).encode(mepLtCreate1, context));
+
+        assertEquals("{\"linktrace\":{" +
+                "\"remoteMepId\":1," +
+                "\"defaultTtl\":20," +
+                "\"transmitLtmFlags\":\"use-fdb-only\"}}", node.toString());
+    }
+}
diff --git a/apps/cfm/src/test/java/org/onosproject/cfm/web/RemoteMepEntryCodecTest.java b/apps/cfm/src/test/java/org/onosproject/cfm/web/RemoteMepEntryCodecTest.java
new file mode 100644
index 0000000..7ad77ac
--- /dev/null
+++ b/apps/cfm/src/test/java/org/onosproject/cfm/web/RemoteMepEntryCodecTest.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright 2017-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.cfm.web;
+
+import static org.junit.Assert.assertEquals;
+
+import java.time.Duration;
+import java.util.ArrayList;
+import java.util.Iterator;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.onlab.packet.MacAddress;
+import org.onosproject.cfm.CfmCodecContext;
+import org.onosproject.incubator.net.l2monitoring.cfm.DefaultRemoteMepEntry;
+import org.onosproject.incubator.net.l2monitoring.cfm.RemoteMepEntry;
+import org.onosproject.incubator.net.l2monitoring.cfm.RemoteMepEntry.InterfaceStatusTlvType;
+import org.onosproject.incubator.net.l2monitoring.cfm.RemoteMepEntry.PortStatusTlvType;
+import org.onosproject.incubator.net.l2monitoring.cfm.RemoteMepEntry.RemoteMepState;
+import org.onosproject.incubator.net.l2monitoring.cfm.SenderIdTlv.SenderIdTlvType;
+import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MepId;
+import org.onosproject.incubator.net.l2monitoring.cfm.service.CfmConfigException;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+
+public class RemoteMepEntryCodecTest {
+    ObjectMapper mapper;
+    CfmCodecContext context;
+    RemoteMepEntry remoteMep1;
+
+    @Before
+    public void setUp() throws Exception, CfmConfigException {
+        mapper = new ObjectMapper();
+        context = new CfmCodecContext();
+        remoteMep1 = DefaultRemoteMepEntry
+                .builder(MepId.valueOf((short) 10), RemoteMepState.RMEP_OK)
+                .failedOrOkTime(Duration.ofMillis(546546546L))
+                .interfaceStatusTlvType(InterfaceStatusTlvType.IS_LOWERLAYERDOWN)
+                .macAddress(MacAddress.IPV4_MULTICAST)
+                .portStatusTlvType(PortStatusTlvType.PS_NO_STATUS_TLV)
+                .rdi(true)
+                .senderIdTlvType(SenderIdTlvType.SI_NETWORK_ADDRESS)
+                .build();
+    }
+
+    @Test
+    public void testEncodeRemoteMepEntryCodecContext() {
+        ObjectNode node = mapper.createObjectNode();
+        node.set("remoteMep", context.codec(RemoteMepEntry.class)
+                .encode(remoteMep1, context));
+
+        assertEquals(10, node.get("remoteMep").get("remoteMepId").asInt());
+    }
+
+    @Test
+    public void testEncodeIterableOfRemoteMepEntryCodecContext()
+            throws CfmConfigException {
+        RemoteMepEntry remoteMep2 = DefaultRemoteMepEntry
+                .builder(MepId.valueOf((short) 20), RemoteMepState.RMEP_IDLE)
+                .build();
+
+        ArrayList<RemoteMepEntry> remoteMeps = new ArrayList<>();
+        remoteMeps.add(remoteMep1);
+        remoteMeps.add(remoteMep2);
+
+        ObjectNode node = mapper.createObjectNode();
+        node.set("remoteMep", context.codec(RemoteMepEntry.class)
+                .encode(remoteMeps, context));
+
+        Iterator<JsonNode> an = node.get("remoteMep").elements();
+        while (an.hasNext()) {
+            JsonNode jn = an.next();
+            assertEquals("RMEP_", jn.get("remoteMepState").asText().substring(0, 5));
+        }
+    }
+}
diff --git a/apps/cfm/src/test/java/org/onosproject/soam/impl/DmWebResourceTest.java b/apps/cfm/src/test/java/org/onosproject/soam/impl/DmWebResourceTest.java
new file mode 100644
index 0000000..702bc09
--- /dev/null
+++ b/apps/cfm/src/test/java/org/onosproject/soam/impl/DmWebResourceTest.java
@@ -0,0 +1,299 @@
+/*
+ * Copyright 2017-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.soam.impl;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import org.junit.Before;
+import org.junit.Test;
+import org.onlab.osgi.ServiceDirectory;
+import org.onlab.osgi.TestServiceDirectory;
+import org.onlab.rest.BaseResource;
+import org.onosproject.cfm.CfmCodecContext;
+import org.onosproject.cfm.impl.CfmResourceTest;
+import org.onosproject.codec.CodecService;
+import org.onosproject.incubator.net.l2monitoring.cfm.DefaultMepEntry;
+import org.onosproject.incubator.net.l2monitoring.cfm.Mep;
+import org.onosproject.incubator.net.l2monitoring.cfm.MepEntry;
+import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MaIdCharStr;
+import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MaIdShort;
+import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MdId;
+import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MdIdCharStr;
+import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MepId;
+import org.onosproject.incubator.net.l2monitoring.cfm.service.CfmConfigException;
+import org.onosproject.incubator.net.l2monitoring.cfm.service.CfmMepService;
+import org.onosproject.incubator.net.l2monitoring.soam.SoamConfigException;
+import org.onosproject.incubator.net.l2monitoring.soam.SoamId;
+import org.onosproject.incubator.net.l2monitoring.soam.SoamService;
+import org.onosproject.incubator.net.l2monitoring.soam.delay.DefaultDelayMeasurementEntry;
+import org.onosproject.incubator.net.l2monitoring.soam.delay.DefaultDelayMeasurementStatCurrent;
+import org.onosproject.incubator.net.l2monitoring.soam.delay.DelayMeasurementCreate;
+import org.onosproject.incubator.net.l2monitoring.soam.delay.DelayMeasurementEntry;
+import org.onosproject.incubator.net.l2monitoring.soam.delay.DelayMeasurementStatCurrent;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.PortNumber;
+
+import javax.ws.rs.InternalServerErrorException;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.Response;
+
+import java.io.BufferedReader;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.time.Duration;
+import java.time.Instant;
+import java.util.ArrayList;
+import java.util.List;
+
+import static junit.framework.TestCase.fail;
+import static org.easymock.EasyMock.*;
+import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+
+public class DmWebResourceTest extends CfmResourceTest {
+    private final CfmMepService mepService = createMock(CfmMepService.class);
+    private final SoamService soamService = createMock(SoamService.class);
+
+    private static final MdId MDNAME1 = MdIdCharStr.asMdId("md-1");
+    private static final MaIdShort MANAME1 = MaIdCharStr.asMaId("ma-1-1");
+    private static final MepId MEPID1 = MepId.valueOf((short) 1);
+    private static final SoamId DM1 = SoamId.valueOf(1);
+    private static final SoamId DM2 = SoamId.valueOf(2);
+
+    private DelayMeasurementEntry dm1;
+    private DelayMeasurementEntry dm2;
+
+    private final Instant now = Instant.now();
+
+    @Before
+    public void setUpTest() throws CfmConfigException, SoamConfigException {
+        CfmCodecContext context = new CfmCodecContext();
+        ServiceDirectory testDirectory = new TestServiceDirectory()
+                .add(CfmMepService.class, mepService)
+                .add(SoamService.class, soamService)
+                .add(CodecService.class, context.codecManager());
+        BaseResource.setServiceDirectory(testDirectory);
+
+        DelayMeasurementStatCurrent.DmStatCurrentBuilder dmCurrBuilder1 =
+                (DelayMeasurementStatCurrent.DmStatCurrentBuilder)
+                        DefaultDelayMeasurementStatCurrent
+                        .builder(Duration.ofMinutes(1), false)
+                        .startTime(now)
+                        .frameDelayBackwardAvg(Duration.ofMillis(10))
+                        .frameDelayForwardAvg(Duration.ofMillis(11))
+                        .frameDelayRangeBackwardAvg(Duration.ofMillis(12));
+
+        dm1 = DefaultDelayMeasurementEntry.builder(DM1,
+                    DelayMeasurementCreate.DmType.DMDMM,
+                    DelayMeasurementCreate.Version.Y17312008,
+                    MepId.valueOf((short) 2),
+                    Mep.Priority.PRIO1)
+                .sessionStatus(DelayMeasurementEntry.SessionStatus.ACTIVE)
+                .frameDelayTwoWay(Duration.ofMillis(40))
+                .frameDelayBackward(Duration.ofMillis(30))
+                .frameDelayForward(Duration.ofMillis(10))
+                .interFrameDelayVariationTwoWay(Duration.ofMillis(8))
+                .interFrameDelayVariationBackward(Duration.ofMillis(3))
+                .interFrameDelayVariationForward(Duration.ofMillis(5))
+                .currentResult((DelayMeasurementStatCurrent) dmCurrBuilder1.build())
+                .build();
+
+        dm2 = DefaultDelayMeasurementEntry.builder(DM2,
+                    DelayMeasurementCreate.DmType.DMDMM,
+                    DelayMeasurementCreate.Version.Y17312011,
+                    MepId.valueOf((short) 2),
+                    Mep.Priority.PRIO2)
+                .build();
+    }
+
+    @Test
+    public void testGetAllDmsForMep() throws CfmConfigException, SoamConfigException {
+
+        List<DelayMeasurementEntry> dmList = new ArrayList<>();
+        dmList.add(dm1);
+        dmList.add(dm2);
+
+        expect(soamService.getAllDms(MDNAME1, MANAME1, MEPID1)).andReturn(dmList).anyTimes();
+        replay(soamService);
+
+        final WebTarget wt = target();
+        final String response = wt.path("md/" + MDNAME1.mdName() + "/ma/" +
+                MANAME1.maName() + "/mep/" + MEPID1.value() + "/dm")
+                .request().get(String.class);
+
+        assertThat(response, is("{\"dms\":[[" +
+                "{" +
+                    "\"dmId\":\"1\"," +
+                    "\"sessionStatus\":\"ACTIVE\"," +
+                    "\"frameDelayTwoWay\":\"PT0.04S\"," +
+                    "\"frameDelayForward\":\"PT0.01S\"," +
+                    "\"frameDelayBackward\":\"PT0.03S\"," +
+                    "\"interFrameDelayVariationTwoWay\":\"PT0.008S\"," +
+                    "\"interFrameDelayVariationForward\":\"PT0.005S\"," +
+                    "\"interFrameDelayVariationBackward\":\"PT0.003S\"," +
+                    "\"dmCfgType\":\"DMDMM\"," +
+                    "\"version\":\"Y17312008\"," +
+                    "\"remoteMepId\":2," +
+                    "\"priority\":\"PRIO1\"," +
+                    "\"measurementsEnabled\":[]," +
+                    "\"current\":{" +
+                        "\"startTime\":\"" + now + "\"," +
+                        "\"elapsedTime\":\"PT1M\"," +
+                        "\"suspectStatus\":\"false\"," +
+                        "\"frameDelayForwardAvg\":\"PT0.011S\"," +
+                        "\"frameDelayBackwardAvg\":\"PT0.01S\"," +
+                        "\"frameDelayRangeBackwardAvg\":\"PT0.012S\"" +
+                    "}," +
+                    "\"historic\":[]" +
+                "},{" +
+                    "\"dmId\":\"2\"," +
+                    "\"dmCfgType\":\"DMDMM\"," +
+                    "\"version\":\"Y17312011\"," +
+                    "\"remoteMepId\":2," +
+                    "\"priority\":\"PRIO2\"," +
+                    "\"measurementsEnabled\":[]," +
+                    "\"historic\":[]}]]" +
+                "}"));
+    }
+
+    @Test
+    public void testGetAllDmsForMepEmpty() throws CfmConfigException, SoamConfigException {
+
+        List<DelayMeasurementEntry> dmListEmpty = new ArrayList<>();
+
+        expect(soamService.getAllDms(MDNAME1, MANAME1, MEPID1)).andReturn(dmListEmpty).anyTimes();
+        replay(soamService);
+
+        final WebTarget wt = target();
+        final String response = wt.path("md/" + MDNAME1.mdName() + "/ma/" +
+                MANAME1.maName() + "/mep/" + MEPID1.value() + "/dm")
+                .request().get(String.class);
+
+        assertThat(response, is("{\"dms\":[[]]}"));
+    }
+
+    @Test
+    public void testGetDm() throws CfmConfigException, SoamConfigException {
+
+        expect(soamService.getDm(MDNAME1, MANAME1, MEPID1, DM1)).andReturn(dm1).anyTimes();
+        replay(soamService);
+
+        final WebTarget wt = target();
+        final String response = wt.path("md/" + MDNAME1.mdName() + "/ma/" +
+                MANAME1.maName() + "/mep/" + MEPID1.value() + "/dm/" + DM1.value())
+                .request().get(String.class);
+
+        assertThat(response, is("{\"dm\":" +
+                "{" +
+                    "\"dmId\":\"1\"," +
+                    "\"sessionStatus\":\"ACTIVE\"," +
+                    "\"frameDelayTwoWay\":\"PT0.04S\"," +
+                    "\"frameDelayForward\":\"PT0.01S\"," +
+                    "\"frameDelayBackward\":\"PT0.03S\"," +
+                    "\"interFrameDelayVariationTwoWay\":\"PT0.008S\"," +
+                    "\"interFrameDelayVariationForward\":\"PT0.005S\"," +
+                    "\"interFrameDelayVariationBackward\":\"PT0.003S\"," +
+                    "\"dmCfgType\":\"DMDMM\"," +
+                    "\"version\":\"Y17312008\"," +
+                    "\"remoteMepId\":2," +
+                    "\"priority\":\"PRIO1\"," +
+                    "\"measurementsEnabled\":[]," +
+                    "\"current\":{" +
+                        "\"startTime\":\"" + now + "\"," +
+                        "\"elapsedTime\":\"PT1M\"," +
+                        "\"suspectStatus\":\"false\"," +
+                        "\"frameDelayForwardAvg\":\"PT0.011S\"," +
+                        "\"frameDelayBackwardAvg\":\"PT0.01S\"," +
+                        "\"frameDelayRangeBackwardAvg\":\"PT0.012S\"" +
+                    "}," +
+                    "\"historic\":[]" +
+                "}}"));
+    }
+
+    @Test
+    public void testGetDmInvalid() throws CfmConfigException, SoamConfigException, IOException {
+
+        SoamId dm3 = SoamId.valueOf(3);
+
+        expect(soamService.getDm(MDNAME1, MANAME1, MEPID1, dm3)).andReturn(null).anyTimes();
+        replay(soamService);
+
+        final WebTarget wt = target();
+        try {
+            final String response = wt.path("md/" + MDNAME1.mdName() + "/ma/" +
+                    MANAME1.maName() + "/mep/" + MEPID1.value() + "/dm/" + dm3.value())
+                    .request().get(String.class);
+            fail("Expecting excpetion");
+        } catch (InternalServerErrorException e) {
+            ByteArrayInputStream is = (ByteArrayInputStream) e.getResponse().getEntity();
+            BufferedReader br = new BufferedReader(new InputStreamReader(is));
+            String line = null;
+            StringBuffer sb = new StringBuffer();
+            while ((line = br.readLine()) != null) {
+                sb.append(line);
+            }
+            assertEquals("{ \"failure\":\"DM md-1/ma-1-1/1/3 not found\" }", sb.toString());
+        }
+    }
+
+    @Test
+    public void testAbortDm() {
+
+        final WebTarget wt = target();
+        final Response response = wt.path("md/" + MDNAME1.mdName() + "/ma/" +
+                MANAME1.maName() + "/mep/" + MEPID1.value() + "/dm/" + DM1.value())
+                .request().delete();
+
+        assertEquals(200, response.getStatus());
+    }
+
+    @Test
+    public void testCreateDm() throws CfmConfigException, SoamConfigException {
+        MepEntry mep1 = DefaultMepEntry.builder(MEPID1, DeviceId.deviceId("netconf:1.2.3.4:830"),
+                PortNumber.portNumber(1), Mep.MepDirection.UP_MEP, MDNAME1, MANAME1).buildEntry();
+
+        expect(mepService.getMep(MDNAME1, MANAME1, MEPID1)).andReturn(mep1).anyTimes();
+        replay(mepService);
+
+        ObjectMapper mapper = new ObjectMapper();
+        CfmCodecContext context = new CfmCodecContext();
+        ObjectNode node = mapper.createObjectNode();
+        node.set("dm", context.codec(DelayMeasurementCreate.class).encode(dm1, context));
+
+        final WebTarget wt = target();
+        final Response response = wt.path("md/" + MDNAME1.mdName() + "/ma/" +
+                MANAME1.maName() + "/mep/" + MEPID1.value() + "/dm")
+                .request().post(Entity.json(node.toString()));
+
+        assertEquals(201, response.getStatus());
+    }
+
+    @Test
+    public void testClearDmHistory() {
+
+        final WebTarget wt = target();
+        final Response response = wt.path("md/" + MDNAME1.mdName() + "/ma/" +
+                MANAME1.maName() + "/mep/" + MEPID1.value() + "/dm/" + DM1.value() +
+                "/clear-history")
+                .request().put(Entity.json(""));
+
+        assertEquals(200, response.getStatus());
+    }
+
+}
diff --git a/apps/cfm/src/test/java/org/onosproject/soam/impl/LmWebResourceTest.java b/apps/cfm/src/test/java/org/onosproject/soam/impl/LmWebResourceTest.java
new file mode 100644
index 0000000..9dd70e6
--- /dev/null
+++ b/apps/cfm/src/test/java/org/onosproject/soam/impl/LmWebResourceTest.java
@@ -0,0 +1,262 @@
+/*
+ * Copyright 2017-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.soam.impl;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import org.junit.Before;
+import org.junit.Test;
+import org.onlab.osgi.ServiceDirectory;
+import org.onlab.osgi.TestServiceDirectory;
+import org.onlab.rest.BaseResource;
+import org.onosproject.cfm.CfmCodecContext;
+import org.onosproject.cfm.impl.CfmResourceTest;
+import org.onosproject.codec.CodecService;
+import org.onosproject.incubator.net.l2monitoring.cfm.DefaultMepEntry;
+import org.onosproject.incubator.net.l2monitoring.cfm.Mep;
+import org.onosproject.incubator.net.l2monitoring.cfm.MepEntry;
+import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MaIdCharStr;
+import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MaIdShort;
+import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MdId;
+import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MdIdCharStr;
+import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MepId;
+import org.onosproject.incubator.net.l2monitoring.cfm.service.CfmConfigException;
+import org.onosproject.incubator.net.l2monitoring.cfm.service.CfmMepService;
+import org.onosproject.incubator.net.l2monitoring.soam.MilliPct;
+import org.onosproject.incubator.net.l2monitoring.soam.SoamConfigException;
+import org.onosproject.incubator.net.l2monitoring.soam.SoamId;
+import org.onosproject.incubator.net.l2monitoring.soam.SoamService;
+import org.onosproject.incubator.net.l2monitoring.soam.delay.DelayMeasurementCreate;
+import org.onosproject.incubator.net.l2monitoring.soam.loss.DefaultLmEntry;
+import org.onosproject.incubator.net.l2monitoring.soam.loss.LossMeasurementCreate;
+import org.onosproject.incubator.net.l2monitoring.soam.loss.LossMeasurementEntry;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.PortNumber;
+
+import javax.ws.rs.InternalServerErrorException;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.Response;
+import java.io.BufferedReader;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.time.Instant;
+import java.util.ArrayList;
+import java.util.List;
+
+import static org.easymock.EasyMock.createMock;
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.replay;
+import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+
+public class LmWebResourceTest extends CfmResourceTest {
+    private final CfmMepService mepService = createMock(CfmMepService.class);
+    private final SoamService soamService = createMock(SoamService.class);
+
+    private static final MdId MDNAME1 = MdIdCharStr.asMdId("md-1");
+    private static final MaIdShort MANAME1 = MaIdCharStr.asMaId("ma-1-1");
+    private static final MepId MEPID1 = MepId.valueOf((short) 1);
+    private static final SoamId LMID1 = SoamId.valueOf(1);
+    private static final SoamId LMID2 = SoamId.valueOf(2);
+
+    private LossMeasurementEntry lm1;
+    private LossMeasurementEntry lm2;
+
+    private final Instant now = Instant.now();
+
+    @Before
+    public void setUpTest() throws CfmConfigException, SoamConfigException {
+        CfmCodecContext context = new CfmCodecContext();
+        ServiceDirectory testDirectory = new TestServiceDirectory()
+                .add(CfmMepService.class, mepService)
+                .add(SoamService.class, soamService)
+                .add(CodecService.class, context.codecManager());
+        BaseResource.setServiceDirectory(testDirectory);
+
+        lm1 = DefaultLmEntry.builder(
+                    DelayMeasurementCreate.Version.Y17312008,
+                    MepId.valueOf((short) 10),
+                    Mep.Priority.PRIO1,
+                    LossMeasurementCreate.LmType.LMLMM,
+                LMID1)
+                .build();
+        lm2 = DefaultLmEntry.builder(
+                    DelayMeasurementCreate.Version.Y17312011,
+                    MepId.valueOf((short) 10),
+                    Mep.Priority.PRIO2,
+                    LossMeasurementCreate.LmType.LMLMM,
+                LMID2)
+                .measuredAvailabilityBackwardStatus(LossMeasurementEntry.AvailabilityType.AVAILABLE)
+                .measuredAvailabilityForwardStatus(LossMeasurementEntry.AvailabilityType.UNKNOWN)
+                .measuredBackwardFlr(MilliPct.ofPercent(49.9f))
+                .measuredForwardFlr(MilliPct.ofRatio(0.51f))
+                .measuredBackwardLastTransitionTime(now)
+                .measuredForwardLastTransitionTime(now)
+                .build();
+
+    }
+
+    @Test
+    public void testGetAllLmsForMep() throws CfmConfigException, SoamConfigException {
+        List<LossMeasurementEntry> lmList = new ArrayList<>();
+        lmList.add(lm1);
+        lmList.add(lm2);
+
+        expect(soamService.getAllLms(MDNAME1, MANAME1, MEPID1)).andReturn(lmList).anyTimes();
+        replay(soamService);
+
+        final WebTarget wt = target();
+        final String response = wt.path("md/" + MDNAME1.mdName() + "/ma/" +
+                MANAME1.maName() + "/mep/" + MEPID1.value() + "/lm")
+                .request().get(String.class);
+
+        assertThat(response, is("{\"lms\":[[" +
+                "{" +
+                    "\"lmId\":\"1\"," +
+                    "\"lmCfgType\":\"LMLMM\"," +
+                    "\"version\":\"Y17312008\"," +
+                    "\"remoteMepId\":10," +
+                    "\"priority\":\"PRIO1\"," +
+                    "\"countersEnabled\":[]," +
+                    "\"measurementHistories\":[]," +
+                    "\"availabilityHistories\":[]" +
+                "},{" +
+                    "\"lmId\":\"2\"," +
+                    "\"measuredForwardFlr\":51.0," +
+                    "\"measuredBackwardFlr\":49.9," +
+                    "\"measuredAvailabilityForwardStatus\":\"UNKNOWN\"," +
+                    "\"measuredAvailabilityBackwardStatus\":\"AVAILABLE\"," +
+                    "\"measuredForwardLastTransitionTime\":\"" + now + "\"," +
+                    "\"measuredBackwardLastTransitionTime\":\"" + now + "\"," +
+                    "\"lmCfgType\":\"LMLMM\"," +
+                    "\"version\":\"Y17312011\"," +
+                    "\"remoteMepId\":10," +
+                    "\"priority\":\"PRIO2\"," +
+                    "\"countersEnabled\":[]," +
+                    "\"measurementHistories\":[]," +
+                    "\"availabilityHistories\":[]" +
+                "}]]}"));
+    }
+
+    @Test
+    public void testGetAllLmsForMepEmpty() throws CfmConfigException, SoamConfigException {
+        List<LossMeasurementEntry> lmList = new ArrayList<>();
+
+        expect(soamService.getAllLms(MDNAME1, MANAME1, MEPID1)).andReturn(lmList).anyTimes();
+        replay(soamService);
+
+        final WebTarget wt = target();
+        final String response = wt.path("md/" + MDNAME1.mdName() + "/ma/" +
+                MANAME1.maName() + "/mep/" + MEPID1.value() + "/lm")
+                .request().get(String.class);
+
+        assertThat(response, is("{\"lms\":[[]]}"));
+    }
+
+    @Test
+    public void testGetLm() throws CfmConfigException, SoamConfigException {
+
+        expect(soamService.getLm(MDNAME1, MANAME1, MEPID1, LMID1)).andReturn(lm1).anyTimes();
+        replay(soamService);
+
+        final WebTarget wt = target();
+        final String response = wt.path("md/" + MDNAME1.mdName() + "/ma/" +
+                MANAME1.maName() + "/mep/" + MEPID1.value() + "/lm/" + LMID1.value())
+                .request().get(String.class);
+
+        assertThat(response, is("{\"lm\":" +
+                "{" +
+                "\"lmId\":\"1\"," +
+                "\"lmCfgType\":\"LMLMM\"," +
+                "\"version\":\"Y17312008\"," +
+                "\"remoteMepId\":10," +
+                "\"priority\":\"PRIO1\"," +
+                "\"countersEnabled\":[]," +
+                "\"measurementHistories\":[]," +
+                "\"availabilityHistories\":[]" +
+                "}}"));
+    }
+
+    @Test
+    public void testGetLmEmpty() throws CfmConfigException, SoamConfigException, IOException {
+        SoamId lmId3 = SoamId.valueOf(3);
+        expect(soamService.getLm(MDNAME1, MANAME1, MEPID1, lmId3))
+                .andReturn(null).anyTimes();
+        replay(soamService);
+
+        final WebTarget wt = target();
+        try {
+            final String response = wt.path("md/" + MDNAME1.mdName() + "/ma/" +
+                    MANAME1.maName() + "/mep/" + MEPID1.value() + "/lm/" + lmId3.value())
+                    .request().get(String.class);
+        } catch (InternalServerErrorException e) {
+            ByteArrayInputStream is = (ByteArrayInputStream) e.getResponse().getEntity();
+            BufferedReader br = new BufferedReader(new InputStreamReader(is));
+            String line = null;
+            StringBuffer sb = new StringBuffer();
+            while ((line = br.readLine()) != null) {
+                sb.append(line);
+            }
+            assertEquals("{ \"failure\":\"LM md-1/ma-1-1/1/3 not found\" }",
+                    sb.toString());
+        }
+    }
+
+    @Test
+    public void testAbortLm() {
+
+        final WebTarget wt = target();
+        final Response response = wt.path("md/" + MDNAME1.mdName() + "/ma/" +
+                MANAME1.maName() + "/mep/" + MEPID1.value() + "/lm/" + LMID1.value())
+                .request().delete();
+        assertEquals(200, response.getStatus());
+    }
+
+    @Test
+    public void testCreateLm() throws CfmConfigException, SoamConfigException {
+        MepEntry mep1 = DefaultMepEntry.builder(MEPID1, DeviceId.deviceId("netconf:1.2.3.4:830"),
+                PortNumber.portNumber(1), Mep.MepDirection.UP_MEP, MDNAME1, MANAME1).buildEntry();
+
+        expect(mepService.getMep(MDNAME1, MANAME1, MEPID1)).andReturn(mep1).anyTimes();
+        replay(mepService);
+
+        ObjectMapper mapper = new ObjectMapper();
+        CfmCodecContext context = new CfmCodecContext();
+        ObjectNode node = mapper.createObjectNode();
+        node.set("lm", context.codec(LossMeasurementCreate.class).encode(lm1, context));
+
+        final WebTarget wt = target();
+        final Response response = wt.path("md/" + MDNAME1.mdName() + "/ma/" +
+                MANAME1.maName() + "/mep/" + MEPID1.value() + "/lm")
+                .request().post(Entity.json(node.toString()));
+        assertEquals(201, response.getStatus());
+    }
+
+    @Test
+    public void testClearLmHistory() {
+
+        final WebTarget wt = target();
+        final Response response = wt.path("md/" + MDNAME1.mdName() + "/ma/" +
+                MANAME1.maName() + "/mep/" + MEPID1.value() + "/lm/" + LMID1.value() +
+                "/clear-history")
+                .request().put(Entity.json(""));
+
+        assertEquals(200, response.getStatus());
+    }
+}
diff --git a/apps/cfm/src/test/java/org/onosproject/soam/web/DmCreateCodecTest.java b/apps/cfm/src/test/java/org/onosproject/soam/web/DmCreateCodecTest.java
new file mode 100644
index 0000000..2f31310
--- /dev/null
+++ b/apps/cfm/src/test/java/org/onosproject/soam/web/DmCreateCodecTest.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright 2017-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.soam.web;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+import java.time.Duration;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.onosproject.cfm.CfmCodecContext;
+import org.onosproject.incubator.net.l2monitoring.cfm.Mep.Priority;
+import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MepId;
+import org.onosproject.incubator.net.l2monitoring.soam.SoamConfigException;
+import org.onosproject.incubator.net.l2monitoring.soam.delay.DefaultDelayMeasurementCreate;
+import org.onosproject.incubator.net.l2monitoring.soam.delay.DelayMeasurementCreate;
+import org.onosproject.incubator.net.l2monitoring.soam.delay.DelayMeasurementCreate.DmCreateBuilder;
+import org.onosproject.incubator.net.l2monitoring.soam.delay.DelayMeasurementCreate.DmType;
+import org.onosproject.incubator.net.l2monitoring.soam.delay.DelayMeasurementCreate.MeasurementOption;
+import org.onosproject.incubator.net.l2monitoring.soam.delay.DelayMeasurementCreate.Version;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+
+public class DmCreateCodecTest {
+    ObjectMapper mapper;
+    CfmCodecContext context;
+
+    @Before
+    public void setUp() throws Exception {
+        mapper = new ObjectMapper();
+        context = new CfmCodecContext();
+    }
+
+    @Test
+    public void testDecodeObjectNodeCodecContext1()
+            throws JsonProcessingException, IOException {
+        String moStr = "{\"dm\": {}}";
+
+        InputStream input = new ByteArrayInputStream(
+                moStr.getBytes(StandardCharsets.UTF_8));
+        JsonNode cfg = mapper.readTree(input);
+
+        try {
+            context.codec(DelayMeasurementCreate.class)
+                .decode((ObjectNode) cfg, context);
+            fail("Expecting an exception");
+        } catch (IllegalArgumentException e) {
+            assertEquals("remoteMepId is required", e.getMessage());
+        }
+    }
+
+    @Test
+    public void testDecodeObjectNodeCodecContext2()
+            throws JsonProcessingException, IOException {
+        String moStr = "{\"dm\": {" +
+            "\"version\":\"Y17312008\"," +
+            "\"dmType\":\"DMDMM\"," +
+            "\"remoteMepId\":12," +
+            "\"priority\":\"PRIO6\"," +
+            "\"measurementsEnabled\" :" +
+            "[\"FRAME_DELAY_RANGE_BACKWARD_AVERAGE\", " +
+            "\"INTER_FRAME_DELAY_VARIATION_FORWARD_AVERAGE\"]" +
+            "}}";
+
+        InputStream input = new ByteArrayInputStream(
+                moStr.getBytes(StandardCharsets.UTF_8));
+        JsonNode cfg = mapper.readTree(input);
+
+        DelayMeasurementCreate dmCreate = context
+                .codec(DelayMeasurementCreate.class)
+                .decode((ObjectNode) cfg, context);
+
+        assertEquals(Version.Y17312008, dmCreate.version());
+        assertEquals(DmType.DMDMM, dmCreate.dmCfgType());
+        assertEquals(12, dmCreate.remoteMepId().id().shortValue());
+    }
+
+    @Test
+    public void testEncodeDelayMeasurementCreateCodecContext()
+            throws SoamConfigException {
+        DmCreateBuilder builder = DefaultDelayMeasurementCreate
+                .builder(DmType.DM1DMRX, Version.Y17312011,
+                        MepId.valueOf((short) 16), Priority.PRIO5);
+        builder.addToMeasurementsEnabled(
+                MeasurementOption.FRAME_DELAY_BACKWARD_MAX);
+        builder.addToMeasurementsEnabled(
+                MeasurementOption.FRAME_DELAY_TWO_WAY_MAX);
+        builder.addToMeasurementsEnabled(
+                MeasurementOption.INTER_FRAME_DELAY_VARIATION_BACKWARD_BINS);
+        builder = (DmCreateBuilder) builder.messagePeriod(Duration.ofMillis(100));
+        builder = (DmCreateBuilder) builder.frameSize((short) 1200);
+
+        ObjectNode node = mapper.createObjectNode();
+        node.set("dm", context.codec(DelayMeasurementCreate.class)
+                .encode(builder.build(), context));
+
+        assertEquals(DmType.DM1DMRX.name(), node.get("dm").get("dmCfgType").asText());
+        assertEquals(Version.Y17312011.name(), node.get("dm").get("version").asText());
+        assertEquals(16, node.get("dm").get("remoteMepId").asInt());
+        assertEquals(Priority.PRIO5.name(), node.get("dm").get("priority").asText());
+        assertEquals(100, node.get("dm").get("messagePeriodMs").asInt());
+        assertEquals(1200, node.get("dm").get("frameSize").asInt());
+
+        assertEquals(3, ((ArrayNode) node.get("dm").get("measurementsEnabled")).size());
+    }
+
+}
diff --git a/apps/cfm/src/test/java/org/onosproject/soam/web/DmEntryCodecTest.java b/apps/cfm/src/test/java/org/onosproject/soam/web/DmEntryCodecTest.java
new file mode 100644
index 0000000..028fad8
--- /dev/null
+++ b/apps/cfm/src/test/java/org/onosproject/soam/web/DmEntryCodecTest.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright 2017-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.soam.web;
+
+import static org.junit.Assert.assertEquals;
+
+import java.io.IOException;
+import java.time.Duration;
+import java.util.ArrayList;
+import java.util.Collection;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.onosproject.cfm.CfmCodecContext;
+import org.onosproject.incubator.net.l2monitoring.cfm.Mep.Priority;
+import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MepId;
+import org.onosproject.incubator.net.l2monitoring.soam.SoamConfigException;
+import org.onosproject.incubator.net.l2monitoring.soam.SoamId;
+import org.onosproject.incubator.net.l2monitoring.soam.delay.DefaultDelayMeasurementEntry;
+import org.onosproject.incubator.net.l2monitoring.soam.delay.DelayMeasurementCreate.DmType;
+import org.onosproject.incubator.net.l2monitoring.soam.delay.DelayMeasurementCreate.MeasurementOption;
+import org.onosproject.incubator.net.l2monitoring.soam.delay.DelayMeasurementCreate.Version;
+import org.onosproject.incubator.net.l2monitoring.soam.delay.DelayMeasurementEntry;
+import org.onosproject.incubator.net.l2monitoring.soam.delay.DelayMeasurementEntry.DmEntryBuilder;
+import org.onosproject.incubator.net.l2monitoring.soam.delay.DelayMeasurementEntry.SessionStatus;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+
+public class DmEntryCodecTest {
+    ObjectMapper mapper;
+    CfmCodecContext context;
+    DelayMeasurementEntry dmEntry1;
+
+    @Before
+    public void setUp() throws Exception, SoamConfigException {
+        mapper = new ObjectMapper();
+        context = new CfmCodecContext();
+        DmEntryBuilder builder = DefaultDelayMeasurementEntry
+                .builder(SoamId.valueOf(12), DmType.DM1DMTX,
+                        Version.Y17312008, MepId.valueOf((short) 10), Priority.PRIO4);
+        builder = builder.sessionStatus(SessionStatus.NOT_ACTIVE);
+        builder = builder.frameDelayTwoWay(Duration.ofNanos(101 * 1000));
+        builder = builder.frameDelayForward(Duration.ofNanos(102 * 1000));
+        builder = builder.frameDelayBackward(Duration.ofNanos(103 * 1000));
+        builder = builder.interFrameDelayVariationTwoWay(Duration.ofNanos(104 * 1000));
+        builder = builder.interFrameDelayVariationForward(Duration.ofNanos(105 * 1000));
+        builder = builder.interFrameDelayVariationBackward(Duration.ofNanos(106 * 1000));
+        builder.addToMeasurementsEnabled(MeasurementOption.FRAME_DELAY_BACKWARD_MAX);
+        builder.addToMeasurementsEnabled(MeasurementOption.FRAME_DELAY_TWO_WAY_MAX);
+        builder.addToMeasurementsEnabled(MeasurementOption.INTER_FRAME_DELAY_VARIATION_BACKWARD_BINS);
+
+        dmEntry1 = builder.build();
+    }
+
+    @Test
+    public void testEncodeDelayMeasurementEntryCodecContext()
+            throws JsonProcessingException, IOException {
+        ObjectNode node = mapper.createObjectNode();
+        node.set("dm", context.codec(DelayMeasurementEntry.class)
+                .encode(dmEntry1, context));
+
+        assertEquals(12, node.get("dm").get("dmId").asInt());
+        assertEquals(DmType.DM1DMTX.name(), node.get("dm").get("dmCfgType").asText());
+        assertEquals(Version.Y17312008.name(), node.get("dm").get("version").asText());
+        assertEquals(10, node.get("dm").get("remoteMepId").asInt());
+        assertEquals(3, ((ArrayNode) node.get("dm").get("measurementsEnabled")).size());
+
+        assertEquals(SessionStatus.NOT_ACTIVE.name(),
+                node.get("dm").get("sessionStatus").asText());
+        assertEquals("PT0.000101S",
+                node.get("dm").get("frameDelayTwoWay").asText());
+        assertEquals("PT0.000102S",
+                node.get("dm").get("frameDelayForward").asText());
+        assertEquals("PT0.000103S",
+                node.get("dm").get("frameDelayBackward").asText());
+        assertEquals("PT0.000104S",
+                node.get("dm").get("interFrameDelayVariationTwoWay").asText());
+        assertEquals("PT0.000105S",
+                node.get("dm").get("interFrameDelayVariationForward").asText());
+        assertEquals("PT0.000106S",
+                node.get("dm").get("interFrameDelayVariationBackward").asText());
+
+    }
+
+    @Test
+    public void testEncodeIterableOfDelayMeasurementEntryCodecContext()
+            throws SoamConfigException {
+        DmEntryBuilder builder2 = DefaultDelayMeasurementEntry
+                .builder(SoamId.valueOf(14), DmType.DM1DMRX,
+                        Version.Y17312011, MepId.valueOf((short) 16), Priority.PRIO5);
+        builder2.addToMeasurementsEnabled(MeasurementOption.FRAME_DELAY_BACKWARD_MIN);
+        builder2.addToMeasurementsEnabled(MeasurementOption.FRAME_DELAY_TWO_WAY_MIN);
+        builder2.addToMeasurementsEnabled(MeasurementOption.INTER_FRAME_DELAY_VARIATION_BACKWARD_MIN);
+
+        Collection<DelayMeasurementEntry> dmEntries = new ArrayList<>();
+        dmEntries.add(dmEntry1);
+        dmEntries.add(builder2.build());
+        ObjectNode node = mapper.createObjectNode();
+        node.set("dm", context.codec(DelayMeasurementEntry.class)
+                .encode(dmEntries, context));
+
+        assertEquals(2, ((ArrayNode) node.get("dm")).size());
+    }
+
+}
diff --git a/apps/cfm/src/test/java/org/onosproject/soam/web/DmMeasurementOptionCodecTest.java b/apps/cfm/src/test/java/org/onosproject/soam/web/DmMeasurementOptionCodecTest.java
new file mode 100644
index 0000000..4986d5a
--- /dev/null
+++ b/apps/cfm/src/test/java/org/onosproject/soam/web/DmMeasurementOptionCodecTest.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2017-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.soam.web;
+
+import static org.junit.Assert.assertEquals;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.onosproject.cfm.CfmCodecContext;
+import org.onosproject.incubator.net.l2monitoring.soam.delay.DelayMeasurementCreate.MeasurementOption;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+
+public class DmMeasurementOptionCodecTest {
+    ObjectMapper mapper;
+    CfmCodecContext context;
+
+    @Before
+    public void setUp() throws Exception {
+        mapper = new ObjectMapper();
+        context = new CfmCodecContext();
+    }
+
+    @Test
+    public void testEncodeIterableOfMeasurementOptionCodecContext() {
+        List<MeasurementOption> moList = new ArrayList<>();
+        moList.add(MeasurementOption.FRAME_DELAY_BACKWARD_MAX);
+        moList.add(MeasurementOption.FRAME_DELAY_FORWARD_BINS);
+
+        ArrayNode an =
+                context.codec(MeasurementOption.class).encode(moList, context);
+
+        assertEquals(MeasurementOption.FRAME_DELAY_BACKWARD_MAX.toString(),
+                an.get(0).asText());
+        assertEquals(MeasurementOption.FRAME_DELAY_FORWARD_BINS.toString(),
+                an.get(1).asText());
+    }
+
+    @Test
+    public void testDecodeArrayNodeCodecContext()
+            throws JsonProcessingException, IOException {
+        String moStr = "{\"measurementsEnabled\": " +
+                "[\"FRAME_DELAY_RANGE_BACKWARD_AVERAGE\", " +
+                "\"INTER_FRAME_DELAY_VARIATION_FORWARD_AVERAGE\"]}";
+        InputStream input = new ByteArrayInputStream(
+                moStr.getBytes(StandardCharsets.UTF_8));
+        JsonNode cfg = mapper.readTree(input);
+        Iterable<MeasurementOption> moIter = context
+                .codec(MeasurementOption.class)
+                .decode((ArrayNode) cfg.get("measurementsEnabled"), context);
+
+        Iterator<MeasurementOption> source = moIter.iterator();
+        List<MeasurementOption> moList = new ArrayList<>();
+        source.forEachRemaining(moList::add);
+
+        assertEquals(MeasurementOption.FRAME_DELAY_RANGE_BACKWARD_AVERAGE.toString(),
+                moList.get(0).name());
+        assertEquals(MeasurementOption.INTER_FRAME_DELAY_VARIATION_FORWARD_AVERAGE.toString(),
+                moList.get(1).name());
+    }
+
+}
diff --git a/apps/cfm/src/test/resources/examples/CreateDM_Mep10.json b/apps/cfm/src/test/resources/examples/CreateDM_Mep10.json
new file mode 100644
index 0000000..4b8a962
--- /dev/null
+++ b/apps/cfm/src/test/resources/examples/CreateDM_Mep10.json
@@ -0,0 +1,18 @@
+{
+  "dm": {
+    "remoteMepId":20,
+    "dmCfgType": "DMDMM",
+    "version": "Y17312008",
+    "priority": "PRIO1",
+    "messagePeriodMs": 100,
+    "startTime": {
+      "immediate":true
+    },
+    "stopTime": {
+      "none":true
+    },
+    "frameSize": 1500,
+    "measurementIntervalMins": 1,
+    "measurementsEnabled": ["FRAME_DELAY_TWO_WAY_BINS","FRAME_DELAY_TWO_WAY_AVERAGE"]
+  }
+}
\ No newline at end of file
diff --git a/apps/cfm/src/test/resources/examples/CreateMa1InDomainA.json b/apps/cfm/src/test/resources/examples/CreateMa1InDomainA.json
new file mode 100644
index 0000000..0790aee
--- /dev/null
+++ b/apps/cfm/src/test/resources/examples/CreateMa1InDomainA.json
@@ -0,0 +1,23 @@
+{
+  "ma": {
+    "maName": "ma-vlan-1",
+    "maNameType": "CHARACTERSTRING",
+    "maNumericId": 1,
+    "ccm-interval": "INTERVAL_1S",
+    "component-list": [
+      { "component": {
+        "component-id":"1",
+        "tag-type": "VLAN_STAG",
+        "vid-list": [
+          {"vid":1}
+        ]
+      }
+      }
+    ],
+    "rmep-list": [
+      { "rmep":10 },
+      { "rmep":20 },
+      { "rmep":30 }
+    ]
+  }
+}
\ No newline at end of file
diff --git a/apps/cfm/src/test/resources/examples/CreateMa2InDomainA.json b/apps/cfm/src/test/resources/examples/CreateMa2InDomainA.json
new file mode 100644
index 0000000..67afdba
--- /dev/null
+++ b/apps/cfm/src/test/resources/examples/CreateMa2InDomainA.json
@@ -0,0 +1,22 @@
+{
+  "ma": {
+    "maName": "ma-vlan-2",
+    "maNameType": "CHARACTERSTRING",
+    "maNumericId": 2,
+    "ccm-interval": "INTERVAL_1S",
+    "component-list": [
+      { "component": {
+        "component-id":"1",
+        "tag-type": "VLAN_STAG",
+        "vid-list": [
+          {"vid":2}
+        ]
+      }
+      }
+    ],
+    "rmep-list": [
+      { "rmep":10 },
+      { "rmep":20 }
+    ]
+  }
+}
\ No newline at end of file
diff --git a/apps/cfm/src/test/resources/examples/CreateMdDomainA.json b/apps/cfm/src/test/resources/examples/CreateMdDomainA.json
new file mode 100644
index 0000000..2dbb761
--- /dev/null
+++ b/apps/cfm/src/test/resources/examples/CreateMdDomainA.json
@@ -0,0 +1,7 @@
+{"md": {
+  "mdName": "DomainA",
+  "mdNameType": "CHARACTERSTRING",
+  "mdLevel": "LEVEL3",
+  "mdNumeridId": 1
+}
+}
\ No newline at end of file
diff --git a/apps/cfm/src/test/resources/examples/CreateMep10.json b/apps/cfm/src/test/resources/examples/CreateMep10.json
new file mode 100644
index 0000000..3b393c2
--- /dev/null
+++ b/apps/cfm/src/test/resources/examples/CreateMep10.json
@@ -0,0 +1,14 @@
+{
+  "mep": {
+    "mepId": 10,
+    "deviceId": "netconf:192.168.56.10:830",
+    "port": 0,
+    "direction": "DOWN_MEP",
+    "mdName": "DomainA",
+    "maName": "ma-vlan-1",
+    "primary-vid": 1,
+    "administrative-state": true,
+    "ccm-ltm-priority": 4,
+    "cci-enabled" :true
+  }
+}
\ No newline at end of file
diff --git a/apps/cfm/src/test/resources/examples/README.md b/apps/cfm/src/test/resources/examples/README.md
new file mode 100644
index 0000000..3557cff
--- /dev/null
+++ b/apps/cfm/src/test/resources/examples/README.md
@@ -0,0 +1,12 @@
+Run:
+
+* curl --user onos:rocks -d @CreateMdDomainA.json http://localhost:8181/onos/cfm/md --header "Content-Type:application/json"
+* curl --user onos:rocks -d @CreateMa1InDomainA.json http://localhost:8181/onos/cfm/md/DomainA/ma --header "Content-Type:application/json"
+* curl --user onos:rocks -d @CreateMa2InDomainA.json http://localhost:8181/onos/cfm/md/DomainA/ma --header "Content-Type:application/json"
+* curl --user onos:rocks http://localhost:8181/onos/cfm/md 
+
+Create a Device that supports CFM Programmable, say at netconf:192.168.56.10:830
+* curl --user onos:rocks -d @CreateMep10.json http://localhost:8181/onos/cfm/md/DomainA/ma/ma-vlan-1/mep --header "Content-Type:application/json"
+
+Now call the RPC to create a Delay Measurement on that device
+* curl --user onos:rocks -X PUT -d @CreateDM_Mep10.json http://localhost:8181/onos/cfm/md/DomainA/ma/ma-vlan-1/mep/10/dm --header "Content-Type:application/json"