Initial import of CFM and SOAM api

Change-Id: Icf5cc2d5fb34b75460e80e8cced0d70265bcd33b
diff --git a/apps/cfm/src/main/java/org/onosproject/cfm/CfmWebComponent.java b/apps/cfm/src/main/java/org/onosproject/cfm/CfmWebComponent.java
new file mode 100644
index 0000000..6ead985
--- /dev/null
+++ b/apps/cfm/src/main/java/org/onosproject/cfm/CfmWebComponent.java
@@ -0,0 +1,195 @@
+/*
+ * 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.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.onlab.packet.VlanId;
+import org.onosproject.cfm.web.ComponentCodec;
+import org.onosproject.cfm.web.FngAddressCodec;
+import org.onosproject.cfm.web.MaintenanceAssociationCodec;
+import org.onosproject.cfm.web.MaintenanceDomainCodec;
+import org.onosproject.cfm.web.MepCodec;
+import org.onosproject.cfm.web.MepEntryCodec;
+import org.onosproject.cfm.web.MepLbCreateCodec;
+import org.onosproject.cfm.web.MepLbEntryCodec;
+import org.onosproject.cfm.web.MepLtCreateCodec;
+import org.onosproject.cfm.web.RemoteMepEntryCodec;
+import org.onosproject.cfm.web.VidCodec;
+import org.onosproject.codec.CodecService;
+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.Mep.FngAddress;
+import org.onosproject.incubator.net.l2monitoring.cfm.MepEntry;
+import org.onosproject.incubator.net.l2monitoring.cfm.MepLbCreate;
+import org.onosproject.incubator.net.l2monitoring.cfm.MepLbEntry;
+import org.onosproject.incubator.net.l2monitoring.cfm.MepLtCreate;
+import org.onosproject.incubator.net.l2monitoring.cfm.RemoteMepEntry;
+import org.onosproject.incubator.net.l2monitoring.soam.StartTime;
+import org.onosproject.incubator.net.l2monitoring.soam.StopTime;
+import org.onosproject.incubator.net.l2monitoring.soam.delay.DelayMeasurementCreate;
+import org.onosproject.incubator.net.l2monitoring.soam.delay.DelayMeasurementCreate.MeasurementOption;
+import org.onosproject.incubator.net.l2monitoring.soam.delay.DelayMeasurementEntry;
+import org.onosproject.incubator.net.l2monitoring.soam.delay.DelayMeasurementStat;
+import org.onosproject.incubator.net.l2monitoring.soam.delay.DelayMeasurementStatCurrent;
+import org.onosproject.incubator.net.l2monitoring.soam.delay.DelayMeasurementStatHistory;
+import org.onosproject.incubator.net.l2monitoring.soam.loss.LossAvailabilityStat;
+import org.onosproject.incubator.net.l2monitoring.soam.loss.LossAvailabilityStatCurrent;
+import org.onosproject.incubator.net.l2monitoring.soam.loss.LossAvailabilityStatHistory;
+import org.onosproject.incubator.net.l2monitoring.soam.loss.LossMeasurementCreate;
+import org.onosproject.incubator.net.l2monitoring.soam.loss.LossMeasurementCreate.CounterOption;
+import org.onosproject.incubator.net.l2monitoring.soam.loss.LossMeasurementEntry;
+import org.onosproject.incubator.net.l2monitoring.soam.loss.LossMeasurementStat;
+import org.onosproject.incubator.net.l2monitoring.soam.loss.LossMeasurementStatCurrent;
+import org.onosproject.incubator.net.l2monitoring.soam.loss.LossMeasurementStatHistory;
+import org.onosproject.incubator.net.l2monitoring.soam.loss.LossMeasurementThreshold;
+import org.onosproject.soam.web.DelayMeasurementStatCodec;
+import org.onosproject.soam.web.DelayMeasurementStatCurrentCodec;
+import org.onosproject.soam.web.DelayMeasurementStatHistoryCodec;
+import org.onosproject.soam.web.DmCreateCodec;
+import org.onosproject.soam.web.DmEntryCodec;
+import org.onosproject.soam.web.DmMeasurementOptionCodec;
+import org.onosproject.soam.web.LmCounterOptionCodec;
+import org.onosproject.soam.web.LmCreateCodec;
+import org.onosproject.soam.web.LmEntryCodec;
+import org.onosproject.soam.web.LmThresholdOptionCodec;
+import org.onosproject.soam.web.LossAvailabilityStatCodec;
+import org.onosproject.soam.web.LossAvailabilityStatCurrentCodec;
+import org.onosproject.soam.web.LossAvailabilityStatHistoryCodec;
+import org.onosproject.soam.web.LossMeasurementStatCodec;
+import org.onosproject.soam.web.LossMeasurementStatCurrentCodec;
+import org.onosproject.soam.web.LossMeasurementStatHistoryCodec;
+import org.onosproject.soam.web.LossMeasurementThresholdCodec;
+import org.onosproject.soam.web.StartTimeCodec;
+import org.onosproject.soam.web.StopTimeCodec;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import static org.onosproject.incubator.net.l2monitoring.soam.loss.LossMeasurementThreshold.*;
+
+/**
+ * Enables the CFM REST Web Service component at /onos/cfm.
+ * Each codec for the rest interfaces should be registered here.
+ */
+@Component(immediate = true)
+public class CfmWebComponent {
+    private final Logger log = LoggerFactory.getLogger(getClass());
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected CodecService codecService;
+
+    @Activate
+    public void activate() {
+        codecService.registerCodec(MaintenanceDomain.class,
+                                        new MaintenanceDomainCodec());
+        codecService.registerCodec(MaintenanceAssociation.class,
+                                        new MaintenanceAssociationCodec());
+        codecService.registerCodec(org.onosproject.incubator.net.l2monitoring.cfm.Component.class,
+                                        new ComponentCodec());
+        codecService.registerCodec(VlanId.class, new VidCodec());
+        codecService.registerCodec(Mep.class, new MepCodec());
+        codecService.registerCodec(MepEntry.class, new MepEntryCodec());
+        codecService.registerCodec(MepLbCreate.class, new MepLbCreateCodec());
+        codecService.registerCodec(MepLbEntry.class, new MepLbEntryCodec());
+        codecService.registerCodec(MepLtCreate.class, new MepLtCreateCodec());
+        codecService.registerCodec(RemoteMepEntry.class, new RemoteMepEntryCodec());
+        codecService.registerCodec(FngAddress.class, new FngAddressCodec());
+
+
+        codecService.registerCodec(DelayMeasurementCreate.class,
+                                        new DmCreateCodec());
+        codecService.registerCodec(DelayMeasurementEntry.class,
+                                        new DmEntryCodec());
+        codecService.registerCodec(DelayMeasurementStat.class,
+                                        new DelayMeasurementStatCodec());
+        codecService.registerCodec(DelayMeasurementStatCurrent.class,
+                                        new DelayMeasurementStatCurrentCodec());
+        codecService.registerCodec(DelayMeasurementStatHistory.class,
+                                        new DelayMeasurementStatHistoryCodec());
+        codecService.registerCodec(MeasurementOption.class,
+                                        new DmMeasurementOptionCodec());
+
+        codecService.registerCodec(LossMeasurementCreate.class,
+                                        new LmCreateCodec());
+        codecService.registerCodec(LossMeasurementThreshold.class,
+                                        new LossMeasurementThresholdCodec());
+        codecService.registerCodec(LossMeasurementEntry.class,
+                                        new LmEntryCodec());
+        codecService.registerCodec(LossMeasurementStat.class,
+                                        new LossMeasurementStatCodec());
+        codecService.registerCodec(LossMeasurementStatCurrent.class,
+                                        new LossMeasurementStatCurrentCodec());
+        codecService.registerCodec(LossMeasurementStatHistory.class,
+                                        new LossMeasurementStatHistoryCodec());
+        codecService.registerCodec(LossAvailabilityStat.class,
+                                        new LossAvailabilityStatCodec());
+        codecService.registerCodec(LossAvailabilityStatCurrent.class,
+                                        new LossAvailabilityStatCurrentCodec());
+        codecService.registerCodec(LossAvailabilityStatHistory.class,
+                                        new LossAvailabilityStatHistoryCodec());
+        codecService.registerCodec(CounterOption.class,
+                                        new LmCounterOptionCodec());
+        codecService.registerCodec(ThresholdOption.class,
+                                        new LmThresholdOptionCodec());
+
+        codecService.registerCodec(StartTime.class, new StartTimeCodec());
+        codecService.registerCodec(StopTime.class, new StopTimeCodec());
+
+        log.info("CFM Web Component Started");
+    }
+
+    @Deactivate
+    public void deactivate() {
+        log.info("CFM Web Component Stopped");
+        codecService.unregisterCodec(MaintenanceDomain.class);
+        codecService.unregisterCodec(MaintenanceAssociation.class);
+        codecService.unregisterCodec(org.onosproject.incubator.net.l2monitoring.cfm.Component.class);
+        codecService.unregisterCodec(VlanId.class);
+        codecService.unregisterCodec(Mep.class);
+        codecService.unregisterCodec(MepEntry.class);
+        codecService.unregisterCodec(MepLbCreate.class);
+        codecService.unregisterCodec(MepLbEntry.class);
+        codecService.unregisterCodec(MepLtCreate.class);
+        codecService.unregisterCodec(RemoteMepEntry.class);
+        codecService.unregisterCodec(FngAddress.class);
+
+        codecService.unregisterCodec(DelayMeasurementCreate.class);
+        codecService.unregisterCodec(DelayMeasurementEntry.class);
+        codecService.unregisterCodec(DelayMeasurementStat.class);
+        codecService.unregisterCodec(DelayMeasurementStatCurrent.class);
+        codecService.unregisterCodec(DelayMeasurementStatHistory.class);
+        codecService.unregisterCodec(MeasurementOption.class);
+        codecService.unregisterCodec(StartTime.class);
+        codecService.unregisterCodec(StopTime.class);
+
+        codecService.unregisterCodec(LossMeasurementCreate.class);
+        codecService.unregisterCodec(LossMeasurementThreshold.class);
+        codecService.unregisterCodec(LossMeasurementEntry.class);
+        codecService.unregisterCodec(LossMeasurementStat.class);
+        codecService.unregisterCodec(LossMeasurementStatCurrent.class);
+        codecService.unregisterCodec(LossMeasurementStatHistory.class);
+        codecService.unregisterCodec(LossAvailabilityStat.class);
+        codecService.unregisterCodec(LossAvailabilityStatCurrent.class);
+        codecService.unregisterCodec(LossAvailabilityStatHistory.class);
+        codecService.unregisterCodec(CounterOption.class);
+        codecService.unregisterCodec(ThresholdOption.class);
+
+    }
+}
diff --git a/apps/cfm/src/main/java/org/onosproject/cfm/cli/CfmMaAddCommand.java b/apps/cfm/src/main/java/org/onosproject/cfm/cli/CfmMaAddCommand.java
new file mode 100644
index 0000000..d449cce
--- /dev/null
+++ b/apps/cfm/src/main/java/org/onosproject/cfm/cli/CfmMaAddCommand.java
@@ -0,0 +1,184 @@
+/*
+ * 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.cli;
+
+import org.apache.karaf.shell.commands.Argument;
+import org.apache.karaf.shell.commands.Command;
+import org.onlab.packet.VlanId;
+import org.onosproject.cli.AbstractShellCommand;
+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.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.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.identifier.MepId;
+import org.onosproject.incubator.net.l2monitoring.cfm.service.CfmConfigException;
+import org.onosproject.incubator.net.l2monitoring.cfm.service.CfmMdService;
+
+/**
+ * Adds a Maintenance Association to a Maintenance Domain.
+ */
+@Command(scope = "onos", name = "cfm-ma-add",
+        description = "Add a CFM Maintenance Association to a Maintenance Domain.")
+public class CfmMaAddCommand extends AbstractShellCommand {
+    @Argument(index = 0, name = "name",
+            description = "Maintenance Domain name and type (in brackets)",
+            required = true, multiValued = false)
+    String mdName = null;
+
+    @Argument(index = 1, name = "name-type",
+            description = "Maintenance Assocation name type",
+            required = true, multiValued = false)
+    String nameType = null;
+
+    @Argument(index = 2, name = "name",
+            description = "Maintenance Assocation name. Restrictions apply depending " +
+                    "on name-type",
+            required = true, multiValued = false)
+    String name = null;
+
+    @Argument(index = 3, name = "ccm-interval",
+            description = "CCM Interval values from list",
+            required = true, multiValued = false)
+    String ccmInterval = null;
+
+    @Argument(index = 4, name = "numeric-id",
+            description = "An optional numeric id for Maintenance Association [1-65535]",
+            required = false, multiValued = false)
+    short numericId = 0;
+
+    @Argument(index = 5, name = "component-id",
+            description = "An id for a Component in the Component List [1-65535]." +
+                    "The CLI allows creation of only 1 component. If more are " +
+                    "required use the REST interface",
+            required = false, multiValued = false)
+    short componentId = 0;
+
+    @Argument(index = 6, name = "component-tag-type",
+            description = "Tag Type value for the component",
+            required = false, multiValued = false)
+    String tagType = null;
+
+    @Argument(index = 7, name = "component-mhf-creation",
+            description = "MEP Half function creation type for the component",
+            required = false, multiValued = false)
+    String mhfCreationType = null;
+
+    @Argument(index = 8, name = "component-vid",
+            description = "A VID for the component [1-4095]. This CLI allows " +
+                    "only the specification of 1 VID. If more are required use " +
+                    "the REST interface",
+            required = false, multiValued = false)
+    short vid = 0;
+
+    @Argument(index = 9, name = "rmep",
+            description = "Remote Mep numeric identifier [1-8192]",
+            required = true, multiValued = true)
+    String[] rmepArray = null;
+
+    @Override
+    protected void execute() {
+        CfmMdService service = get(CfmMdService.class);
+
+        String[] mdNameParts = mdName.split("[()]");
+        if (mdNameParts.length != 2) {
+            throw new IllegalArgumentException("Invalid name format. " +
+                    "Must be in the format of <identifier(name-type)>");
+        }
+
+        MdId mdId = null;
+        MdId.MdNameType mdNameTypeEnum = MdId.MdNameType.valueOf(mdNameParts[1]);
+        switch (mdNameTypeEnum) {
+            case DOMAINNAME:
+                mdId = MdIdDomainName.asMdId(mdNameParts[0]);
+                break;
+            case MACANDUINT:
+                mdId = MdIdMacUint.asMdId(mdNameParts[0]);
+                break;
+            case NONE:
+                mdId = MdIdNone.asMdId();
+                break;
+            case CHARACTERSTRING:
+            default:
+                mdId = MdIdCharStr.asMdId(mdNameParts[0]);
+        }
+
+        MaIdShort maId = null;
+        MaIdShort.MaIdType maNameTypeEnum = MaIdShort.MaIdType.valueOf(nameType);
+        switch (maNameTypeEnum) {
+            case TWOOCTET:
+                maId = MaId2Octet.asMaId(name);
+                break;
+            case ICCY1731:
+                maId = MaIdIccY1731.asMaId(name);
+                break;
+            case PRIMARYVID:
+                maId = MaIdPrimaryVid.asMaId(name);
+                break;
+            case RFC2685VPNID:
+                maId = MaIdRfc2685VpnId.asMaIdHex(name);
+                break;
+            case CHARACTERSTRING:
+            default:
+                maId = MaIdCharStr.asMaId(name);
+        }
+
+        MaintenanceAssociation.MaBuilder builder = null;
+        try {
+            builder = DefaultMaintenanceAssociation.builder(maId, mdId.getNameLength());
+            if (ccmInterval != null && !ccmInterval.isEmpty()) {
+                builder = builder.ccmInterval(MaintenanceAssociation.CcmInterval.valueOf(ccmInterval));
+            }
+            for (String rmep:rmepArray) {
+                builder = builder.addToRemoteMepIdList(MepId.valueOf(Short.parseShort(rmep)));
+            }
+            if (numericId > 0) {
+                builder = builder.maNumericId(numericId);
+            }
+            if (componentId > 0) {
+                Component.ComponentBuilder compBuilder =
+                        DefaultComponent.builder(componentId);
+                if (tagType != null && !tagType.isEmpty()) {
+                    compBuilder = compBuilder.tagType(
+                            Component.TagType.valueOf(tagType));
+                }
+                if (mhfCreationType != null && !mhfCreationType.isEmpty()) {
+                    compBuilder = compBuilder.mhfCreationType(
+                            Component.MhfCreationType.valueOf(mhfCreationType));
+                }
+                if (vid > 0) {
+                    compBuilder = compBuilder.addToVidList(VlanId.vlanId(vid));
+                }
+                builder = builder.addToComponentList(compBuilder.build());
+            }
+            boolean created = service.createMaintenanceAssociation(mdId, builder.build());
+            print("Maintenance Association %s is successfully %s on MD %s",
+                    maId, created ? "updated" : "created", mdId);
+        } catch (CfmConfigException e) {
+            throw new IllegalArgumentException(e);
+        }
+    }
+}
diff --git a/apps/cfm/src/main/java/org/onosproject/cfm/cli/CfmMaDeleteCommand.java b/apps/cfm/src/main/java/org/onosproject/cfm/cli/CfmMaDeleteCommand.java
new file mode 100644
index 0000000..4228b78
--- /dev/null
+++ b/apps/cfm/src/main/java/org/onosproject/cfm/cli/CfmMaDeleteCommand.java
@@ -0,0 +1,103 @@
+/*
+ * 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.cli;
+
+import org.apache.karaf.shell.commands.Argument;
+import org.apache.karaf.shell.commands.Command;
+import org.onosproject.cli.AbstractShellCommand;
+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.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 org.onosproject.incubator.net.l2monitoring.cfm.service.CfmMdService;
+
+/**
+ * Delete a Maintenance Association from the existing list of a Maintenance Domain.
+ */
+@Command(scope = "onos", name = "cfm-ma-delete",
+        description = "Delete a CFM Maintenance Association and its children.")
+public class CfmMaDeleteCommand extends AbstractShellCommand {
+
+    @Argument(index = 0, name = "name",
+            description = "Maintenance Domain name and type (in brackets) " +
+                    "and the Maintenance Association name and type (in brackets)",
+            required = true, multiValued = false)
+    String name = null;
+
+    @Override
+    protected void execute() {
+        CfmMdService service = get(CfmMdService.class);
+
+        String[] nameParts = name.split("[()]");
+        if (nameParts.length != 4) {
+            throw new IllegalArgumentException("Invalid name format. Must be in " +
+                    "the format of <identifier(name-type)identifier(name-type)>");
+        }
+
+        MdId mdId = null;
+        MdId.MdNameType nameTypeEnum = MdId.MdNameType.valueOf(nameParts[1]);
+        switch (nameTypeEnum) {
+            case DOMAINNAME:
+                mdId = MdIdDomainName.asMdId(nameParts[0]);
+                break;
+            case MACANDUINT:
+                mdId = MdIdMacUint.asMdId(nameParts[0]);
+                break;
+            case NONE:
+                mdId = MdIdNone.asMdId();
+                break;
+            case CHARACTERSTRING:
+            default:
+                mdId = MdIdCharStr.asMdId(nameParts[0]);
+        }
+
+        MaIdShort maId = null;
+        MaIdShort.MaIdType maNameTypeEnum = MaIdShort.MaIdType.valueOf(nameParts[3]);
+        switch (maNameTypeEnum) {
+            case TWOOCTET:
+                maId = MaId2Octet.asMaId(nameParts[2]);
+                break;
+            case ICCY1731:
+                maId = MaIdIccY1731.asMaId(nameParts[2]);
+                break;
+            case PRIMARYVID:
+                maId = MaIdPrimaryVid.asMaId(nameParts[2]);
+                break;
+            case RFC2685VPNID:
+                maId = MaIdRfc2685VpnId.asMaIdHex(nameParts[2]);
+                break;
+            case CHARACTERSTRING:
+            default:
+                maId = MaIdCharStr.asMaId(nameParts[2]);
+        }
+
+        try {
+            boolean deleted = service.deleteMaintenanceAssociation(mdId, maId);
+            print("Maintenance Association %s-%s is %ssuccessfully deleted.",
+                    mdId, maId, deleted ? "" : "NOT ");
+        } catch (CfmConfigException e) {
+            throw new IllegalArgumentException(e);
+        }
+    }
+}
diff --git a/apps/cfm/src/main/java/org/onosproject/cfm/cli/CfmMdAddCommand.java b/apps/cfm/src/main/java/org/onosproject/cfm/cli/CfmMdAddCommand.java
new file mode 100644
index 0000000..922ed64
--- /dev/null
+++ b/apps/cfm/src/main/java/org/onosproject/cfm/cli/CfmMdAddCommand.java
@@ -0,0 +1,100 @@
+/*
+ * 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.cli;
+
+import org.apache.karaf.shell.commands.Command;
+import org.apache.karaf.shell.commands.Argument;
+import org.onosproject.cli.AbstractShellCommand;
+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 org.onosproject.incubator.net.l2monitoring.cfm.service.CfmMdService;
+
+import java.util.Optional;
+
+/**
+ * Adds a Maintenance Domain to the existing list.
+ */
+@Command(scope = "onos", name = "cfm-md-add",
+        description = "Add a CFM Maintenance Domain.")
+public class CfmMdAddCommand extends AbstractShellCommand {
+
+    @Argument(index = 0, name = "name-type",
+            description = "Maintenance Domain name type",
+            required = true, multiValued = false)
+    String nameType = null;
+
+    @Argument(index = 1, name = "name",
+            description = "Maintenance Domain name. Restrictions apply depending " +
+                    "on name-type. Leave empty if name type is none",
+            required = true, multiValued = false)
+    String name = null;
+
+    @Argument(index = 2, name = "level",
+            description = "Maintenance Domain level LEVEL0-LEVEL7",
+            required = true, multiValued = false)
+    String level = null;
+
+    @Argument(index = 3, name = "numeric-id",
+            description = "An optional numeric id for Maintenance Domain [1-65535]",
+            required = false, multiValued = false)
+    short numericId = 0;
+
+    @Override
+    protected void execute() {
+        CfmMdService service = get(CfmMdService.class);
+        MdId mdId = null;
+        MdId.MdNameType nameTypeEnum = MdId.MdNameType.valueOf(nameType);
+        switch (nameTypeEnum) {
+            case DOMAINNAME:
+                mdId = MdIdDomainName.asMdId(name);
+                break;
+            case MACANDUINT:
+                mdId = MdIdMacUint.asMdId(name);
+                break;
+            case NONE:
+                mdId = MdIdNone.asMdId();
+                break;
+            case CHARACTERSTRING:
+            default:
+                mdId = MdIdCharStr.asMdId(name);
+        }
+        MaintenanceDomain.MdLevel levelEnum =
+                MaintenanceDomain.MdLevel.valueOf(level);
+        Optional<Short> numericIdOpt = Optional.empty();
+        if (numericId > 0) {
+            numericIdOpt = Optional.of(numericId);
+        }
+
+        MaintenanceDomain.MdBuilder builder = null;
+        try {
+            builder = DefaultMaintenanceDomain.builder(mdId).mdLevel(levelEnum);
+            if (numericIdOpt.isPresent()) {
+                builder = builder.mdNumericId(numericIdOpt.get());
+            }
+            boolean created = service.createMaintenanceDomain(builder.build());
+            print("Maintenance Domain with id %s is successfully %s.",
+                    mdId, created ? "updated" : "created");
+        } catch (CfmConfigException e) {
+            throw new IllegalArgumentException(e);
+        }
+    }
+}
diff --git a/apps/cfm/src/main/java/org/onosproject/cfm/cli/CfmMdDeleteCommand.java b/apps/cfm/src/main/java/org/onosproject/cfm/cli/CfmMdDeleteCommand.java
new file mode 100644
index 0000000..3cf3c26
--- /dev/null
+++ b/apps/cfm/src/main/java/org/onosproject/cfm/cli/CfmMdDeleteCommand.java
@@ -0,0 +1,76 @@
+/*
+ * 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.cli;
+
+import org.apache.karaf.shell.commands.Argument;
+import org.apache.karaf.shell.commands.Command;
+import org.onosproject.cli.AbstractShellCommand;
+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 org.onosproject.incubator.net.l2monitoring.cfm.service.CfmMdService;
+
+/**
+ * Deletes a Maintenance Domain from the existing list.
+ */
+@Command(scope = "onos", name = "cfm-md-delete",
+        description = "Delete a CFM Maintenance Domain and its children.")
+public class CfmMdDeleteCommand extends AbstractShellCommand {
+
+    @Argument(index = 0, name = "name",
+            description = "Maintenance Domain name and type (in brackets)",
+            required = true, multiValued = false)
+    String name = null;
+
+    @Override
+    protected void execute() {
+        CfmMdService service = get(CfmMdService.class);
+
+        String[] nameParts = name.split("[()]");
+        if (nameParts.length != 2) {
+            throw new IllegalArgumentException("Invalid name format. " +
+                    "Must be in the format of <identifier(name-type)>");
+        }
+
+        MdId mdId = null;
+        MdId.MdNameType nameTypeEnum = MdId.MdNameType.valueOf(nameParts[1]);
+        switch (nameTypeEnum) {
+            case DOMAINNAME:
+                mdId = MdIdDomainName.asMdId(nameParts[0]);
+                break;
+            case MACANDUINT:
+                mdId = MdIdMacUint.asMdId(nameParts[0]);
+                break;
+            case NONE:
+                mdId = MdIdNone.asMdId();
+                break;
+            case CHARACTERSTRING:
+            default:
+                mdId = MdIdCharStr.asMdId(nameParts[0]);
+        }
+
+        try {
+            boolean deleted = service.deleteMaintenanceDomain(mdId);
+            print("Maintenance Domain %s is %ssuccessfully deleted.",
+                    mdId, deleted ? "" : "NOT ");
+        } catch (CfmConfigException e) {
+            throw new IllegalArgumentException(e);
+        }
+    }
+}
diff --git a/apps/cfm/src/main/java/org/onosproject/cfm/cli/CfmMdListMdCommand.java b/apps/cfm/src/main/java/org/onosproject/cfm/cli/CfmMdListMdCommand.java
new file mode 100644
index 0000000..341bd7d
--- /dev/null
+++ b/apps/cfm/src/main/java/org/onosproject/cfm/cli/CfmMdListMdCommand.java
@@ -0,0 +1,134 @@
+/*
+ * 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.cli;
+
+import org.apache.karaf.shell.commands.Argument;
+import org.apache.karaf.shell.commands.Command;
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.incubator.net.l2monitoring.cfm.MaintenanceAssociation;
+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.CfmMdService;
+
+import java.util.Optional;
+
+/**
+ * Lists a particular Maintenance Domain.
+ */
+@Command(scope = "onos", name = "cfm-md-list",
+        description = "Lists a single CFM Maintenance Domain or all if none specified.")
+public class CfmMdListMdCommand extends AbstractShellCommand {
+    @Argument(index = 0, name = "name",
+            description = "Maintenance Domain name and type (in brackets)",
+            required = false, multiValued = false)
+    String name = null;
+
+    @Override
+    protected void execute() {
+        CfmMdService service = get(CfmMdService.class);
+
+        if (name != null) {
+            String[] nameParts = name.split("[()]");
+            if (nameParts.length != 2) {
+                throw new IllegalArgumentException("Invalid name format. " +
+                        "Must be in the format of <identifier(name-type)>");
+            }
+
+            MdId mdId = null;
+            MdId.MdNameType nameTypeEnum = MdId.MdNameType.valueOf(nameParts[1]);
+            switch (nameTypeEnum) {
+                case DOMAINNAME:
+                    mdId = MdIdDomainName.asMdId(nameParts[0]);
+                    break;
+                case MACANDUINT:
+                    mdId = MdIdMacUint.asMdId(nameParts[0]);
+                    break;
+                case NONE:
+                    mdId = MdIdNone.asMdId();
+                    break;
+                case CHARACTERSTRING:
+                default:
+                    mdId = MdIdCharStr.asMdId(nameParts[0]);
+            }
+
+            print("Maintenance Domain:");
+            Optional<MaintenanceDomain> md = service.getMaintenanceDomain(mdId);
+            print(printMd(md));
+            md.get().maintenanceAssociationList().forEach(ma -> printMa(Optional.of(ma)));
+        } else {
+            service.getAllMaintenanceDomain().forEach(md -> {
+                print(printMd(Optional.of(md)));
+            });
+        }
+    }
+
+    public static String printMd(Optional<MaintenanceDomain> md) {
+        if (!md.isPresent()) {
+            return new String("MD not found");
+        } else {
+            StringBuffer sb = new StringBuffer("\tMD: ");
+            sb.append(md.get().mdId().mdName());
+            sb.append("(" + md.get().mdId().nameType());
+            sb.append(") Lvl:" + md.get().mdLevel().ordinal());
+            sb.append(", Num: " + md.get().mdNumericId());
+
+            md.get().maintenanceAssociationList().
+                    forEach(ma -> sb.append(printMa(Optional.of(ma))));
+            return sb.toString();
+        }
+    }
+
+    public static String printMa(Optional<MaintenanceAssociation> ma) {
+        if (!ma.isPresent()) {
+            return "\n\tNo MA found";
+        }
+
+        StringBuilder sb = new StringBuilder("\n\t\tMA: ");
+        sb.append(ma.get().maId().maName());
+        sb.append("(");
+        sb.append(ma.get().maId().nameType());
+        sb.append(") CCM: ");
+        sb.append(ma.get().ccmInterval());
+        sb.append(" Num: ");
+        sb.append(ma.get().maNumericId());
+
+        ma.get().remoteMepIdList().forEach(rmep -> {
+                    sb.append("\n\t\t\tRmep: ");
+                    sb.append(rmep);
+                });
+        ma.get().componentList().forEach(comp -> {
+            sb.append("\n\t\t\tComponent: ");
+            sb.append(comp.componentId());
+            sb.append(" Perm: ");
+            sb.append(comp.idPermission());
+            sb.append(" MHF: ");
+            sb.append(comp.mhfCreationType());
+            sb.append(" Tag: ");
+            sb.append(comp.tagType());
+
+            comp.vidList().forEach(vid -> {
+                    sb.append("\n\t\t\t\tVID: ");
+                    sb.append(vid);
+            });
+        });
+
+        return sb.toString();
+    }
+}
diff --git a/apps/cfm/src/main/java/org/onosproject/cfm/cli/completer/CfmCompMhfCompleter.java b/apps/cfm/src/main/java/org/onosproject/cfm/cli/completer/CfmCompMhfCompleter.java
new file mode 100644
index 0000000..499b84d
--- /dev/null
+++ b/apps/cfm/src/main/java/org/onosproject/cfm/cli/completer/CfmCompMhfCompleter.java
@@ -0,0 +1,39 @@
+/*
+ * 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.cli.completer;
+
+import org.onosproject.cli.AbstractChoicesCompleter;
+import org.onosproject.incubator.net.l2monitoring.cfm.Component;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * CLI completer for Component MEP Half Function creation.
+ */
+public class CfmCompMhfCompleter extends AbstractChoicesCompleter {
+    @Override
+    public List<String> choices() {
+        List<String> choices = new ArrayList<>();
+
+        for (Component.MhfCreationType mhfCreationType: Component.MhfCreationType.values()) {
+            choices.add(String.valueOf(mhfCreationType.toString()));
+        }
+
+        return choices;
+    }
+
+}
diff --git a/apps/cfm/src/main/java/org/onosproject/cfm/cli/completer/CfmCompTagTypeCompleter.java b/apps/cfm/src/main/java/org/onosproject/cfm/cli/completer/CfmCompTagTypeCompleter.java
new file mode 100644
index 0000000..d919780
--- /dev/null
+++ b/apps/cfm/src/main/java/org/onosproject/cfm/cli/completer/CfmCompTagTypeCompleter.java
@@ -0,0 +1,39 @@
+/*
+ * 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.cli.completer;
+
+import org.onosproject.cli.AbstractChoicesCompleter;
+import org.onosproject.incubator.net.l2monitoring.cfm.Component;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * CLI completer for Component TagType creation.
+ */
+public class CfmCompTagTypeCompleter extends AbstractChoicesCompleter {
+    @Override
+    public List<String> choices() {
+        List<String> choices = new ArrayList<>();
+
+        for (Component.TagType tagType: Component.TagType.values()) {
+            choices.add(String.valueOf(tagType.toString()));
+        }
+
+        return choices;
+    }
+
+}
diff --git a/apps/cfm/src/main/java/org/onosproject/cfm/cli/completer/CfmMaCcmIntervalCompleter.java b/apps/cfm/src/main/java/org/onosproject/cfm/cli/completer/CfmMaCcmIntervalCompleter.java
new file mode 100644
index 0000000..6599ed3
--- /dev/null
+++ b/apps/cfm/src/main/java/org/onosproject/cfm/cli/completer/CfmMaCcmIntervalCompleter.java
@@ -0,0 +1,39 @@
+/*
+ * 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.cli.completer;
+
+import org.onosproject.cli.AbstractChoicesCompleter;
+import org.onosproject.incubator.net.l2monitoring.cfm.MaintenanceAssociation;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * CLI completer for Ccm Interval creation.
+ */
+public class CfmMaCcmIntervalCompleter extends AbstractChoicesCompleter {
+    @Override
+    public List<String> choices() {
+        List<String> choices = new ArrayList<>();
+
+        for (MaintenanceAssociation.CcmInterval ccmInterval: MaintenanceAssociation.CcmInterval.values()) {
+            choices.add(ccmInterval.toString());
+        }
+
+        return choices;
+    }
+
+}
diff --git a/apps/cfm/src/main/java/org/onosproject/cfm/cli/completer/CfmMaNameCompleter.java b/apps/cfm/src/main/java/org/onosproject/cfm/cli/completer/CfmMaNameCompleter.java
new file mode 100644
index 0000000..b484212
--- /dev/null
+++ b/apps/cfm/src/main/java/org/onosproject/cfm/cli/completer/CfmMaNameCompleter.java
@@ -0,0 +1,51 @@
+/*
+ * 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.cli.completer;
+
+import org.onosproject.cli.AbstractChoicesCompleter;
+import org.onosproject.incubator.net.l2monitoring.cfm.service.CfmMdService;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import static org.onosproject.cli.AbstractShellCommand.get;
+
+/**
+ * CLI completer for MA Name creation.
+ */
+public class CfmMaNameCompleter extends AbstractChoicesCompleter {
+    @Override
+    public List<String> choices() {
+        List<String> choices = new ArrayList<>();
+
+        CfmMdService service = get(CfmMdService.class);
+        service.getAllMaintenanceDomain().forEach(md ->
+            md.maintenanceAssociationList().forEach(ma ->
+                choices.add(new StringBuilder(md.mdId().mdName())
+                .append("(")
+                .append(md.mdId().nameType())
+                .append(")")
+                .append(ma.maId().maName())
+                .append("(")
+                .append(ma.maId().nameType())
+                .append(")").toString())
+            )
+        );
+
+        return choices;
+    }
+
+}
diff --git a/apps/cfm/src/main/java/org/onosproject/cfm/cli/completer/CfmMaNameTypeCompleter.java b/apps/cfm/src/main/java/org/onosproject/cfm/cli/completer/CfmMaNameTypeCompleter.java
new file mode 100644
index 0000000..d59a5c8
--- /dev/null
+++ b/apps/cfm/src/main/java/org/onosproject/cfm/cli/completer/CfmMaNameTypeCompleter.java
@@ -0,0 +1,43 @@
+/*
+ * 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.cli.completer;
+
+import org.apache.karaf.shell.console.Completer;
+import org.apache.karaf.shell.console.completer.StringsCompleter;
+import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MaIdShort;
+
+import java.util.List;
+import java.util.SortedSet;
+
+/**
+ * CLI completer for MA name type creation.
+ */
+public class CfmMaNameTypeCompleter implements Completer {
+    @Override
+    public int complete(String buffer, int cursor, List<String> candidates) {
+        // Delegate string completer
+        StringsCompleter delegate = new StringsCompleter();
+        SortedSet<String> strings = delegate.getStrings();
+
+        for (MaIdShort.MaIdType nameType : MaIdShort.MaIdType.values()) {
+            strings.add(nameType.toString());
+        }
+
+        // Now let the completer do the work for figuring out what to offer.
+        return delegate.complete(buffer, cursor, candidates);
+    }
+
+}
diff --git a/apps/cfm/src/main/java/org/onosproject/cfm/cli/completer/CfmMdLevelCompleter.java b/apps/cfm/src/main/java/org/onosproject/cfm/cli/completer/CfmMdLevelCompleter.java
new file mode 100644
index 0000000..374e549
--- /dev/null
+++ b/apps/cfm/src/main/java/org/onosproject/cfm/cli/completer/CfmMdLevelCompleter.java
@@ -0,0 +1,39 @@
+/*
+ * 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.cli.completer;
+
+import org.onosproject.cli.AbstractChoicesCompleter;
+import org.onosproject.incubator.net.l2monitoring.cfm.MaintenanceDomain;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * CLI completer for MD Level creation.
+ */
+public class CfmMdLevelCompleter extends AbstractChoicesCompleter {
+    @Override
+    public List<String> choices() {
+        List<String> choices = new ArrayList<>();
+
+        for (MaintenanceDomain.MdLevel mdLevel:MaintenanceDomain.MdLevel.values()) {
+            choices.add(String.valueOf(mdLevel.toString()));
+        }
+
+        return choices;
+    }
+
+}
diff --git a/apps/cfm/src/main/java/org/onosproject/cfm/cli/completer/CfmMdNameCompleter.java b/apps/cfm/src/main/java/org/onosproject/cfm/cli/completer/CfmMdNameCompleter.java
new file mode 100644
index 0000000..7a336e7
--- /dev/null
+++ b/apps/cfm/src/main/java/org/onosproject/cfm/cli/completer/CfmMdNameCompleter.java
@@ -0,0 +1,40 @@
+/*
+ * 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.cli.completer;
+
+import org.onosproject.cli.AbstractChoicesCompleter;
+import static org.onosproject.cli.AbstractShellCommand.get;
+import org.onosproject.incubator.net.l2monitoring.cfm.service.CfmMdService;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * CLI completer for MD Name creation.
+ */
+public class CfmMdNameCompleter extends AbstractChoicesCompleter {
+    @Override
+    public List<String> choices() {
+        List<String> choices = new ArrayList<>();
+
+        CfmMdService service = get(CfmMdService.class);
+        service.getAllMaintenanceDomain().forEach(md ->
+                choices.add(md.mdId().mdName() + "(" + md.mdId().nameType() + ")"));
+
+        return choices;
+    }
+
+}
diff --git a/apps/cfm/src/main/java/org/onosproject/cfm/cli/completer/CfmMdNameTypeCompleter.java b/apps/cfm/src/main/java/org/onosproject/cfm/cli/completer/CfmMdNameTypeCompleter.java
new file mode 100644
index 0000000..6e24fc7
--- /dev/null
+++ b/apps/cfm/src/main/java/org/onosproject/cfm/cli/completer/CfmMdNameTypeCompleter.java
@@ -0,0 +1,43 @@
+/*
+ * 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.cli.completer;
+
+import org.apache.karaf.shell.console.Completer;
+import org.apache.karaf.shell.console.completer.StringsCompleter;
+import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MdId;
+
+import java.util.List;
+import java.util.SortedSet;
+
+/**
+ * CLI completer for MD Name Type creation.
+ */
+public class CfmMdNameTypeCompleter implements Completer {
+    @Override
+    public int complete(String buffer, int cursor, List<String> candidates) {
+        // Delegate string completer
+        StringsCompleter delegate = new StringsCompleter();
+        SortedSet<String> strings = delegate.getStrings();
+
+        for (MdId.MdNameType nameType : MdId.MdNameType.values()) {
+            strings.add(nameType.toString());
+        }
+
+        // Now let the completer do the work for figuring out what to offer.
+        return delegate.complete(buffer, cursor, candidates);
+    }
+
+}
diff --git a/apps/cfm/src/main/java/org/onosproject/cfm/cli/completer/package-info.java b/apps/cfm/src/main/java/org/onosproject/cfm/cli/completer/package-info.java
new file mode 100644
index 0000000..419084d
--- /dev/null
+++ b/apps/cfm/src/main/java/org/onosproject/cfm/cli/completer/package-info.java
@@ -0,0 +1,19 @@
+/*
+ * 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.
+ */
+/**
+ * Completion of Commands for creating and deleting Maintenance Domains and Maintenance Associations.
+ */
+package org.onosproject.cfm.cli.completer;
\ No newline at end of file
diff --git a/apps/cfm/src/main/java/org/onosproject/cfm/cli/package-info.java b/apps/cfm/src/main/java/org/onosproject/cfm/cli/package-info.java
new file mode 100644
index 0000000..4c57e22
--- /dev/null
+++ b/apps/cfm/src/main/java/org/onosproject/cfm/cli/package-info.java
@@ -0,0 +1,19 @@
+/*
+ * 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.
+ */
+/**
+ * Commands for creating and deleting Maintenance Domains and Maintenance Associations.
+ */
+package org.onosproject.cfm.cli;
\ No newline at end of file
diff --git a/apps/cfm/src/main/java/org/onosproject/cfm/impl/CfmWebApplication.java b/apps/cfm/src/main/java/org/onosproject/cfm/impl/CfmWebApplication.java
new file mode 100644
index 0000000..f21b66d
--- /dev/null
+++ b/apps/cfm/src/main/java/org/onosproject/cfm/impl/CfmWebApplication.java
@@ -0,0 +1,38 @@
+/*
+ * 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 java.util.Set;
+
+import org.onlab.rest.AbstractWebApplication;
+import org.onosproject.soam.impl.DmWebResource;
+import org.onosproject.soam.impl.LmWebResource;
+
+/**
+ * CFM REST API web application.
+ */
+public class CfmWebApplication extends AbstractWebApplication {
+    @Override
+    public Set<Class<?>> getClasses() {
+        return getClasses(
+                MdWebResource.class,
+                MaWebResource.class,
+                MepWebResource.class,
+                DmWebResource.class,
+                LmWebResource.class);
+    }
+}
diff --git a/apps/cfm/src/main/java/org/onosproject/cfm/impl/MaWebResource.java b/apps/cfm/src/main/java/org/onosproject/cfm/impl/MaWebResource.java
new file mode 100644
index 0000000..c861314
--- /dev/null
+++ b/apps/cfm/src/main/java/org/onosproject/cfm/impl/MaWebResource.java
@@ -0,0 +1,177 @@
+/*
+ * 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 java.io.InputStream;
+import java.net.URI;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import org.onosproject.cfm.web.MaintenanceAssociationCodec;
+import org.onosproject.codec.JsonCodec;
+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.service.CfmConfigException;
+import org.onosproject.incubator.net.l2monitoring.cfm.service.CfmMdService;
+import org.onosproject.rest.AbstractWebResource;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+
+/**
+ * Layer 2 CFM Maintenance Association web resource.
+ */
+@Path("md/{md_name}/ma")
+public class MaWebResource extends AbstractWebResource {
+    private final Logger log = LoggerFactory.getLogger(getClass());
+
+    /**
+     * Get Maintenance Association by MD and MA name.
+     *
+     * @param mdName The name of a Maintenance Domain
+     * @param maName The name of a Maintenance Association belonging to the MD
+     * @return 200 OK with details of MA or 500 on Error
+     */
+    @GET
+    @Path("{ma_name}")
+    @Produces(MediaType.APPLICATION_JSON)
+    @Consumes(MediaType.APPLICATION_JSON)
+    public Response getMa(@PathParam("md_name") String mdName,
+                          @PathParam("ma_name") String maName) {
+        log.debug("GET called for MA {}/{}", mdName, maName);
+        try {
+            MdId mdId = MdIdCharStr.asMdId(mdName);
+            MaIdShort maId = MaIdCharStr.asMaId(maName);
+            MaintenanceAssociation ma = get(CfmMdService.class)
+                .getMaintenanceAssociation(mdId, maId)
+                .orElseThrow(() -> new IllegalArgumentException(
+                        "MA " + maName + " not Found"));
+            ObjectNode node = mapper().createObjectNode();
+            node.set("ma", codec(MaintenanceAssociation.class).encode(ma, this));
+            return ok(node).build();
+        } catch (IllegalArgumentException e) {
+            log.error("Get MA {} failed", mdName + "/" + maName, e);
+            return Response.serverError()
+                    .entity("{ \"failure\":\"" + e.toString() + "\" }").build();
+        }
+    }
+
+    /**
+     * Delete the Maintenance Association by MD and MA name.
+     *
+     * @param mdName The name of a Maintenance Domain
+     * @param maName The name of a Maintenance Association belonging to the MD
+     * @return 200 OK if removed, 304 if item was not found or 500 on Error
+     */
+    @DELETE
+    @Path("{ma_name}")
+    @Consumes(MediaType.APPLICATION_JSON)
+    @Produces(MediaType.APPLICATION_JSON)
+    public Response deleteMa(@PathParam("md_name") String mdName,
+                             @PathParam("ma_name") String maName) {
+        log.debug("DELETE called for MA {}/{}", mdName, maName);
+        try {
+            MdId mdId = MdIdCharStr.asMdId(mdName);
+            MaIdShort maId = MaIdCharStr.asMaId(maName);
+            boolean deleted = get(CfmMdService.class)
+                                        .deleteMaintenanceAssociation(mdId, maId);
+            if (!deleted) {
+                return Response.notModified(mdName + "/"
+                                            + maName + " did not exist").build();
+            } else {
+                return ok("{ \"success\":\"deleted " + mdName
+                                                + "/" + maName + "\" }").build();
+            }
+        } catch (CfmConfigException e) {
+            log.error("Delete Maintenance Association {} failed",
+                    mdName + "/" + maName, e);
+            return Response.serverError().entity("{ \"failure\":\"" +
+                                                e.toString() + "\" }").build();
+        }
+    }
+
+    /**
+     * Create Maintenance Association by MD and MA name.
+     *
+     * @param mdName The name of a Maintenance Domain
+     * @param input A JSON formatted input stream specifying the MA parameters
+     * @return 200 OK or 500 on error
+     */
+    @POST
+    @Consumes(MediaType.APPLICATION_JSON)
+    @Produces(MediaType.APPLICATION_JSON)
+    public Response createMaintenanceAssociation(@PathParam("md_name") String mdName,
+                                                 InputStream input) {
+        log.debug("POST called to Create MA");
+        try {
+            MdId mdId = MdIdCharStr.asMdId(mdName);
+            MaintenanceDomain md = get(CfmMdService.class)
+                                            .getMaintenanceDomain(mdId).get();
+            if (md == null) {
+                return Response.serverError().entity("{ \"failure\":\"md "
+                                        + mdName + " does not exist\" }").build();
+            }
+
+            ObjectMapper mapper = new ObjectMapper();
+            JsonNode cfg = mapper.readTree(input);
+            JsonCodec<MaintenanceAssociation> maCodec =
+                                            codec(MaintenanceAssociation.class);
+
+            MaintenanceAssociation ma;
+            try {
+                ma = ((MaintenanceAssociationCodec) maCodec)
+                        .decode((ObjectNode) cfg, this, mdId.getNameLength());
+            } catch (Exception e) {
+                log.error("Create MaintenanceAssociation on MD {} failed", mdName, e);
+                return Response.serverError()
+                        .entity("{ \"failure\":\"" + e.toString() + "\" }")
+                        .build();
+            }
+
+            Boolean alreadyExists = get(CfmMdService.class)
+                                        .createMaintenanceAssociation(mdId, ma);
+            if (alreadyExists) {
+                return Response.notModified(mdName + "/" + ma.maId() +
+                                                    " already exists").build();
+            }
+            return Response
+                    .created(new URI("md/" + mdName + "/ma/" + ma.maId()))
+                    .entity("{ \"success\":\"" + mdName + "/" + ma.maId() + " created\" }")
+                    .build();
+
+        } catch (Exception | CfmConfigException e) {
+            log.error("Create MaintenanceAssociation on MD {} failed", mdName, e);
+            return Response.serverError()
+                    .entity("{ \"failure\":\"" + e.toString() + "\" }")
+                    .build();
+        }
+    }
+}
diff --git a/apps/cfm/src/main/java/org/onosproject/cfm/impl/MdWebResource.java b/apps/cfm/src/main/java/org/onosproject/cfm/impl/MdWebResource.java
new file mode 100644
index 0000000..ecf1fe9
--- /dev/null
+++ b/apps/cfm/src/main/java/org/onosproject/cfm/impl/MdWebResource.java
@@ -0,0 +1,156 @@
+/*
+ * 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 java.io.InputStream;
+import java.net.URI;
+import java.util.Collection;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+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 org.onosproject.rest.AbstractWebResource;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+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;
+
+/**
+ * Layer 2 CFM Maintenance Domain web resource.
+ */
+@Path("md")
+public class MdWebResource extends AbstractWebResource {
+    private final Logger log = LoggerFactory.getLogger(getClass());
+    public static final String JSON_NOT_NULL = "JsonNode can not be null";
+
+    /**
+     * Get all Maintenance Domains.
+     *
+     * @return 200 OK with a list of MDs and their children
+     */
+    @GET
+    @Produces(MediaType.APPLICATION_JSON)
+    @Consumes(MediaType.APPLICATION_JSON)
+    public Response getMds() {
+        log.debug("GET called for all MDs");
+        Collection<MaintenanceDomain> mdMap =
+                                get(CfmMdService.class).getAllMaintenanceDomain();
+        ArrayNode arrayNode = mapper().createArrayNode();
+        arrayNode.add(codec(MaintenanceDomain.class).encode(mdMap, this));
+        return ok(mapper().createObjectNode().set("mds", arrayNode)).build();
+    }
+
+    /**
+     * Get Maintenance Domain by name.
+     *
+     * @param mdName The name of a Maintenance Domain
+     * @return 200 OK with the details of the MD and its children or 500 on error
+     */
+    @GET
+    @Path("{md_name}")
+    @Produces(MediaType.APPLICATION_JSON)
+    @Consumes(MediaType.APPLICATION_JSON)
+    public Response getMd(@PathParam("md_name") String mdName) {
+        log.debug("GET called for MD {}", mdName);
+        try {
+            MaintenanceDomain md = get(CfmMdService.class)
+                    .getMaintenanceDomain(MdIdCharStr.asMdId(mdName))
+                    .orElseThrow(() -> new IllegalArgumentException(
+                            "MD " + mdName + " not Found"));
+            ObjectNode result = mapper().createObjectNode();
+            result.set("md", codec(MaintenanceDomain.class).encode(md, this));
+            return ok(result.toString()).build();
+        } catch (IllegalArgumentException e) {
+            log.error("Get MD {} failed", mdName, e);
+            return Response.serverError()
+                    .entity("{ \"failure\":\"" + e.toString() + "\" }").build();
+        }
+    }
+
+    /**
+     * Delete Maintenance Domain by name.
+     *
+     * @param mdName The name of a Maintenance Domain
+     * @return 200 OK, or 304 if not found or 500 on error
+     */
+    @DELETE
+    @Path("{md_name}")
+    @Consumes(MediaType.APPLICATION_JSON)
+    @Produces(MediaType.APPLICATION_JSON)
+    public Response deleteMd(@PathParam("md_name") String mdName) {
+        log.debug("DELETE called for MD {}", mdName);
+        try {
+            MdId mdId = MdIdCharStr.asMdId(mdName);
+            boolean deleted = get(CfmMdService.class).deleteMaintenanceDomain(mdId);
+            if (!deleted) {
+                return Response.notModified(mdName + " did not exist").build();
+            } else {
+                return ok("{ \"success\":\"deleted " + mdName + "\" }").build();
+            }
+        } catch (CfmConfigException e) {
+            log.error("Delete Maintenance Domain {} failed", mdName, e);
+            return Response.serverError()
+                    .entity("{ \"failure\":\"" + e.toString() + "\" }").build();
+        }
+    }
+
+    /**
+     * Create Maintenance Domain.
+     *
+     * @param input A JSON formatted input stream specifying the MA parameters
+     * @return 200 OK, 304 if MD already exists or 500 on error
+     */
+    @POST
+    @Consumes(MediaType.APPLICATION_JSON)
+    @Produces(MediaType.APPLICATION_JSON)
+    public Response createMaintenanceDomain(InputStream input) {
+        log.debug("POST called to Create MD");
+        try {
+            ObjectMapper mapper = new ObjectMapper();
+            JsonNode cfg = mapper.readTree(input);
+            MaintenanceDomain md = codec(MaintenanceDomain.class).decode((ObjectNode) cfg, this);
+
+            if (get(CfmMdService.class).createMaintenanceDomain(md)) {
+                return Response.notModified(md.mdId().toString() + " already exists").build();
+            }
+            return Response
+                    .created(new URI("md/" + md.mdId()))
+                    .entity("{ \"success\":\"" + md.mdId() + " created\" }")
+                    .build();
+
+        } catch (Exception | CfmConfigException e) {
+            log.error("Create MaintenanceDomain", e);
+            return Response.serverError()
+                    .entity("{ \"failure\":\"" + e.toString() + "\" }")
+                    .build();
+        }
+    }
+}
diff --git a/apps/cfm/src/main/java/org/onosproject/cfm/impl/MepWebResource.java b/apps/cfm/src/main/java/org/onosproject/cfm/impl/MepWebResource.java
new file mode 100644
index 0000000..2486df0
--- /dev/null
+++ b/apps/cfm/src/main/java/org/onosproject/cfm/impl/MepWebResource.java
@@ -0,0 +1,343 @@
+/*
+ * 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 java.io.InputStream;
+import java.net.URI;
+import java.util.Collection;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import org.onosproject.cfm.web.MepCodec;
+import org.onosproject.codec.JsonCodec;
+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.rest.AbstractWebResource;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+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;
+
+/**
+ * Layer 2 CFM Maintenance Association Endpoint (MEP) web resource.
+ */
+@Path("md/{md_name}/ma/{ma_name}/mep")
+public class MepWebResource extends AbstractWebResource {
+    private final Logger log = LoggerFactory.getLogger(getClass());
+
+    /**
+     * Get all MEPs by MD name, MA name.
+     *
+     * @param mdName The name of a Maintenance Domain
+     * @param maName The name of a Maintenance Association belonging to the MD
+     * @return 200 OK with a list of MEPS or 500 on error
+     */
+    @GET
+    @Produces(MediaType.APPLICATION_JSON)
+    @Consumes(MediaType.APPLICATION_JSON)
+    public Response getAllMepsForMa(@PathParam("md_name") String mdName,
+            @PathParam("ma_name") String maName) {
+        log.debug("GET all Meps called for MA {}", mdName + "/" + maName);
+        try {
+            MdId mdId = MdIdCharStr.asMdId(mdName);
+            MaIdShort maId = MaIdCharStr.asMaId(maName);
+            Collection<MepEntry> mepCollection = get(CfmMepService.class).getAllMeps(mdId, maId);
+            ArrayNode an = mapper().createArrayNode();
+            an.add(codec(MepEntry.class).encode(mepCollection, this));
+            return ok(mapper().createObjectNode().set("meps", an)).build();
+        } catch (CfmConfigException e) {
+            log.error("Get all Meps on {} failed because of exception",
+                    mdName + "/" + maName, e);
+            return Response.serverError().entity("{ \"failure\":\"" + e.toString() + "\" }").build();
+        }
+
+    }
+
+    /**
+     * Get MEP by MD name, MA name and Mep Id.
+     *
+     * @param mdName The name of a Maintenance Domain
+     * @param maName The name of a Maintenance Association belonging to the MD
+     * @param mepId The Id of the MEP
+     * @return 200 OK with details of the MEP or 500 on error
+     */
+    @GET
+    @Path("{mep_id}")
+    @Produces(MediaType.APPLICATION_JSON)
+    @Consumes(MediaType.APPLICATION_JSON)
+    public Response getMep(@PathParam("md_name") String mdName,
+            @PathParam("ma_name") String maName,
+            @PathParam("mep_id") short mepId) {
+        log.debug("GET called for MEP {}", mdName + "/" + maName + "/" + mepId);
+        try {
+            MdId mdId = MdIdCharStr.asMdId(mdName);
+            MaIdShort maId = MaIdCharStr.asMaId(maName);
+            MepEntry mepEntry = get(CfmMepService.class)
+                    .getMep(mdId, maId, MepId.valueOf(mepId));
+            if (mepEntry == null) {
+                return Response.serverError().entity("{ \"failure\":\"MEP " +
+                        mdName + "/" + maName + "/" + mepId + " not found\" }").build();
+            }
+            ObjectNode node = mapper().createObjectNode();
+            node.set("mep", codec(MepEntry.class).encode(mepEntry, this));
+            return ok(node).build();
+        } catch (CfmConfigException e) {
+            log.error("Get Mep {} failed because of exception",
+                    mdName + "/" + maName + "/" + mepId, e);
+            return Response.serverError().entity("{ \"failure\":\"" + e.toString() + "\" }").build();
+        }
+    }
+
+    /**
+     * Delete MEP by MD name, MA name and Mep Id.
+     *
+     * @param mdName The name of a Maintenance Domain
+     * @param maName The name of a Maintenance Association belonging to the MD
+     * @param mepIdShort The Id of the MEP
+     * @return 200 OK or 304 if not found, or 500 on error
+     */
+    @DELETE
+    @Path("{mep_id}")
+    @Consumes(MediaType.APPLICATION_JSON)
+    @Produces(MediaType.APPLICATION_JSON)
+    public Response deleteMep(@PathParam("md_name") String mdName,
+            @PathParam("ma_name") String maName,
+            @PathParam("mep_id") short mepIdShort) {
+        log.debug("DELETE called for MEP " + mdName + "/" + maName + "/" + mepIdShort);
+        try {
+            MdId mdId = MdIdCharStr.asMdId(mdName);
+            MaIdShort maId = MaIdCharStr.asMaId(maName);
+            boolean deleted = get(CfmMepService.class)
+                    .deleteMep(mdId, maId, MepId.valueOf(mepIdShort));
+            if (!deleted) {
+                return Response.notModified(mdName + "/" + maName + "/" +
+                        mepIdShort + " did not exist").build();
+            } else {
+                return ok("{ \"success\":\"deleted " + mdName + "/" + maName +
+                        "/" + mepIdShort + "\" }").build();
+            }
+        } catch (CfmConfigException e) {
+            log.error("Delete Mep {} failed because of exception ",
+                    mdName + "/" + maName + "/" + mepIdShort, e);
+            return Response.serverError().entity("{ \"failure\":\"" +
+                    e.toString() + "\" }").build();
+        }
+    }
+
+    /**
+     * Create MEP with MD name, MA name and Mep Json.
+     *
+     * @param mdName The name of a Maintenance Domain
+     * @param maName The name of a Maintenance Association belonging to the MD
+     * @param input A JSON formatted input stream specifying the Mep parameters
+     * @return 201 Created or 304 if already exists or 500 on error
+     */
+    @POST
+    @Consumes(MediaType.APPLICATION_JSON)
+    @Produces(MediaType.APPLICATION_JSON)
+    public Response createMep(@PathParam("md_name") String mdName,
+                              @PathParam("ma_name") String maName, InputStream input) {
+        log.debug("POST called to Create Mep");
+        try {
+            MdId mdId = MdIdCharStr.asMdId(mdName);
+            MaIdShort maId = MaIdCharStr.asMaId(maName);
+            MaintenanceAssociation ma = get(CfmMdService.class).getMaintenanceAssociation(mdId, maId).get();
+
+            ObjectMapper mapper = new ObjectMapper();
+            JsonNode cfg = mapper.readTree(input);
+            JsonCodec<Mep> mepCodec = codec(Mep.class);
+
+            Mep mep = ((MepCodec) mepCodec).decode((ObjectNode) cfg, this, mdName, maName);
+
+            Boolean issuccess = get(CfmMepService.class).createMep(mdId, maId, mep);
+            if (!issuccess) {
+                return Response.notModified(mdName + "/" + ma.maId() + "/" + mep.mepId() +
+                        " already exists").build();
+            }
+            return Response
+                    .created(new URI("md/" + mdName + "/ma/" + ma.maId() + "/mep/" + mep.mepId()))
+                    .entity("{ \"success\":\"mep " + mdName + "/" + ma.maId() + "/" + mep.mepId() + " created\" }")
+                    .build();
+        } catch (Exception | CfmConfigException e) {
+            log.error("Create Mep on " + mdName + "/" + maName + " failed because of exception {}",
+                      e.toString());
+            return Response.serverError()
+                    .entity("{ \"failure\":\"" + e.toString() + "\" }")
+                    .build();
+        }
+    }
+
+    /**
+     * Transmit Loopback on MEP with MD name, MA name and Mep Id.
+     *
+     * @param mdName The name of a Maintenance Domain
+     * @param maName The name of a Maintenance Association belonging to the MD
+     * @param mepIdShort The id of a MEP belonging to the MA
+     * @param input A JSON formatted input stream specifying the Mep parameters
+     * @return 202 Received with success message or 500 on error
+     */
+    @PUT
+    @Path("{mep_id}/transmit-loopback")
+    @Consumes(MediaType.APPLICATION_JSON)
+    @Produces(MediaType.APPLICATION_JSON)
+    public Response transmitLoopback(
+            @PathParam("md_name") String mdName,
+            @PathParam("ma_name") String maName,
+            @PathParam("mep_id") short mepIdShort,
+            InputStream input) {
+        log.debug("PUT called to Transmit Loopback on Mep");
+
+        MdId mdId = MdIdCharStr.asMdId(mdName);
+        MaIdShort maId = MaIdCharStr.asMaId(maName);
+        MaintenanceDomain md = get(CfmMdService.class).getMaintenanceDomain(mdId).get();
+        MaintenanceAssociation ma = get(CfmMdService.class)
+                .getMaintenanceAssociation(mdId, maId).get();
+
+        MepId mepId = MepId.valueOf(mepIdShort);
+
+        try {
+            ObjectMapper mapper = new ObjectMapper();
+            JsonNode cfg = mapper.readTree(input);
+            JsonCodec<MepLbCreate> mepLbCreateCodec = codec(MepLbCreate.class);
+
+            MepLbCreate lbCreate = mepLbCreateCodec.decode((ObjectNode) cfg, this);
+            get(CfmMepService.class).transmitLoopback(md.mdId(), ma.maId(), mepId, lbCreate);
+        } catch (Exception | CfmConfigException e) {
+            log.error("Transmit Loopback on " + mdName + "/" + maName +
+                    "/{} failed", String.valueOf(mepIdShort), e);
+            return Response.serverError()
+                  .entity("{ \"failure\":\"" + e.toString() + "\" }")
+                  .build();
+        }
+
+        return Response.accepted()
+            .entity("{ \"success\":\"Loopback on MEP " + mdName + "/" + ma.maId() + "/"
+                    + mepId.id() + " started\" }").build();
+    }
+
+    /**
+     * Abort Loopback on MEP with MD name, MA name and Mep Id.
+     *
+     * @param mdName The name of a Maintenance Domain
+     * @param maName The name of a Maintenance Association belonging to the MD
+     * @param mepIdShort The id of a MEP belonging to the MA
+     * @return 202 Received with success message or 500 on error
+     */
+    @PUT
+    @Path("{mep_id}/abort-loopback")
+    @Consumes(MediaType.APPLICATION_JSON)
+    @Produces(MediaType.APPLICATION_JSON)
+    public Response abortLoopback(
+            @PathParam("md_name") String mdName,
+            @PathParam("ma_name") String maName,
+            @PathParam("mep_id") short mepIdShort) {
+        log.debug("PUT called to Abort Loopback on Mep");
+
+        MdId mdId = MdIdCharStr.asMdId(mdName);
+        MaIdShort maId = MaIdCharStr.asMaId(maName);
+        MaintenanceDomain md = get(CfmMdService.class).getMaintenanceDomain(mdId).get();
+        MaintenanceAssociation ma = get(CfmMdService.class)
+                .getMaintenanceAssociation(mdId, maId).get();
+
+        MepId mepId = MepId.valueOf(mepIdShort);
+
+        try {
+            get(CfmMepService.class).abortLoopback(md.mdId(), ma.maId(), mepId);
+        } catch (CfmConfigException e) {
+            log.error("Abort Loopback on " + mdName + "/" + maName +
+                    "/{} failed", String.valueOf(mepIdShort), e);
+            return Response.serverError()
+                  .entity("{ \"failure\":\"" + e.toString() + "\" }")
+                  .build();
+        }
+
+        return Response.accepted()
+            .entity("{ \"success\":\"Loopback on MEP " + mdName + "/" + ma.maId() + "/"
+                    + mepId.id() + " aborted\" }").build();
+    }
+
+    /**
+     * Transmit Linktrace on MEP with MD name, MA name and Mep Id.
+     *
+     * @param mdName The name of a Maintenance Domain
+     * @param maName The name of a Maintenance Association belonging to the MD
+     * @param mepIdShort The id of a MEP belonging to the MA
+     * @param input A JSON formatted input stream specifying the Linktrace parameters
+     * @return 202 Received with success message or 500 on error
+     */
+    @PUT
+    @Path("{mep_id}/transmit-linktrace")
+    @Consumes(MediaType.APPLICATION_JSON)
+    @Produces(MediaType.APPLICATION_JSON)
+    public Response transmitLinktrace(
+            @PathParam("md_name") String mdName,
+            @PathParam("ma_name") String maName,
+            @PathParam("mep_id") short mepIdShort,
+            InputStream input) {
+        log.debug("PUT called to Transmit Linktrace on Mep");
+
+        MdId mdId = MdIdCharStr.asMdId(mdName);
+        MaIdShort maId = MaIdCharStr.asMaId(maName);
+        MaintenanceDomain md = get(CfmMdService.class).getMaintenanceDomain(mdId).get();
+        MaintenanceAssociation ma = get(CfmMdService.class)
+                .getMaintenanceAssociation(mdId, maId).get();
+
+        MepId mepId = MepId.valueOf(mepIdShort);
+
+        try {
+            ObjectMapper mapper = new ObjectMapper();
+            JsonNode cfg = mapper.readTree(input);
+            JsonCodec<MepLtCreate> mepLtCreateCodec = codec(MepLtCreate.class);
+
+            MepLtCreate ltCreate = mepLtCreateCodec.decode((ObjectNode) cfg, this);
+            get(CfmMepService.class).transmitLinktrace(md.mdId(), ma.maId(), mepId, ltCreate);
+        } catch (Exception | CfmConfigException e) {
+            log.error("Transmit Linktrace on " + mdName + "/" + maName +
+                    "/{} failed", String.valueOf(mepIdShort), e);
+            return Response.serverError()
+                    .entity("{ \"failure\":\"" + e.toString() + "\" }")
+                    .build();
+        }
+
+        return Response.accepted()
+                .entity("{ \"success\":\"Linktrace on MEP " + mdName + "/" + ma.maId() + "/"
+                        + mepId.id() + " started\" }").build();
+    }
+}
diff --git a/apps/cfm/src/main/java/org/onosproject/cfm/impl/package-info.java b/apps/cfm/src/main/java/org/onosproject/cfm/impl/package-info.java
new file mode 100644
index 0000000..798317d
--- /dev/null
+++ b/apps/cfm/src/main/java/org/onosproject/cfm/impl/package-info.java
@@ -0,0 +1,19 @@
+/*
+ * 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.
+ */
+/**
+ * REST Web Application for CFM.
+ */
+package org.onosproject.cfm.impl;
\ No newline at end of file
diff --git a/apps/cfm/src/main/java/org/onosproject/cfm/package-info.java b/apps/cfm/src/main/java/org/onosproject/cfm/package-info.java
new file mode 100644
index 0000000..2522fb4
--- /dev/null
+++ b/apps/cfm/src/main/java/org/onosproject/cfm/package-info.java
@@ -0,0 +1,19 @@
+/*
+ * 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.
+ */
+/**
+ * REST Web Component for CFM and SOAM.
+ */
+package org.onosproject.cfm;
\ No newline at end of file
diff --git a/apps/cfm/src/main/java/org/onosproject/cfm/web/ComponentCodec.java b/apps/cfm/src/main/java/org/onosproject/cfm/web/ComponentCodec.java
new file mode 100644
index 0000000..6a53497
--- /dev/null
+++ b/apps/cfm/src/main/java/org/onosproject/cfm/web/ComponentCodec.java
@@ -0,0 +1,123 @@
+/*
+ * 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.onlab.util.Tools.nullIsIllegal;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.onlab.packet.VlanId;
+import org.onosproject.codec.CodecContext;
+import org.onosproject.codec.JsonCodec;
+import org.onosproject.incubator.net.l2monitoring.cfm.Component;
+import org.onosproject.incubator.net.l2monitoring.cfm.DefaultComponent;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+
+/**
+ * Encode and decode to/from JSON to Component object.
+ */
+public class ComponentCodec extends JsonCodec<Component> {
+
+    private static final String COMPONENT_ID = "component-id";
+    private static final String COMPONENT = "component";
+    private static final String VID_LIST = "vid-list";
+    private static final String TAG_TYPE = "tag-type";
+    private static final String MHF_CREATION_TYPE = "mhf-creation-type";
+    private static final String ID_PERMISSION = "id-permission";
+
+    @Override
+    public ObjectNode encode(Component component, CodecContext context) {
+
+        ObjectNode node = context.mapper().createObjectNode()
+                .put(COMPONENT_ID, component.componentId());
+
+        node.set(VID_LIST, new VidCodec().encode(component.vidList(), context));
+
+        if (component.mhfCreationType() != null) {
+            node.put(MHF_CREATION_TYPE, component.mhfCreationType().name());
+        }
+        if (component.idPermission() != null) {
+            node.put(ID_PERMISSION, component.idPermission().name());
+        }
+        if (component.tagType() != null) {
+            node.put(TAG_TYPE, component.tagType().name());
+        }
+
+        return (ObjectNode) context.mapper().createObjectNode().set(COMPONENT, node);
+    }
+
+    @Override
+    public ArrayNode encode(Iterable<Component> components, CodecContext context) {
+        ArrayNode an = context.mapper().createArrayNode();
+        components.forEach(component -> {
+            an.add(encode(component, context));
+        });
+        return an;
+    }
+
+    @Override
+    public Component decode(ObjectNode json, CodecContext context) {
+        if (json == null || !json.isObject()) {
+            return null;
+        }
+
+        JsonNode componentNode = json.get(COMPONENT);
+
+        int componentId = nullIsIllegal(componentNode.get(COMPONENT_ID),
+                "component-id is required").asInt();
+        Component.ComponentBuilder componentBuilder =
+                DefaultComponent.builder(componentId);
+
+        List<VlanId> vidList = (new VidCodec()).decode((ArrayNode)
+                nullIsIllegal(componentNode.get(VID_LIST), "vid-list is required"), context);
+        if (vidList == null || vidList.size() < 1) {
+            throw new IllegalArgumentException("A least one VID is required in component: " + componentId);
+        }
+        for (VlanId vid:vidList) {
+            componentBuilder = componentBuilder.addToVidList(vid);
+        }
+
+        if (componentNode.get(TAG_TYPE) != null) {
+            componentBuilder = componentBuilder
+                    .tagType(Component.TagType.valueOf(
+                            componentNode.get(TAG_TYPE).asText()));
+        }
+
+        if (componentNode.get(MHF_CREATION_TYPE) != null) {
+            componentBuilder = componentBuilder
+                    .mhfCreationType(Component.MhfCreationType.valueOf(
+                            componentNode.get(MHF_CREATION_TYPE).asText()));
+        }
+        if (componentNode.get(ID_PERMISSION) != null) {
+            componentBuilder = componentBuilder
+                    .idPermission(Component.IdPermissionType.valueOf(
+                            componentNode.get(ID_PERMISSION).asText()));
+        }
+
+        return componentBuilder.build();
+    }
+
+    @Override
+    public List<Component> decode(ArrayNode json, CodecContext context) {
+        List<Component> componentList = new ArrayList<>();
+        json.forEach(node -> componentList.add(decode((ObjectNode) node, context)));
+        return componentList;
+    }
+}
diff --git a/apps/cfm/src/main/java/org/onosproject/cfm/web/FngAddressCodec.java b/apps/cfm/src/main/java/org/onosproject/cfm/web/FngAddressCodec.java
new file mode 100644
index 0000000..47cc9b6
--- /dev/null
+++ b/apps/cfm/src/main/java/org/onosproject/cfm/web/FngAddressCodec.java
@@ -0,0 +1,75 @@
+/*
+ * 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.node.ObjectNode;
+import org.onlab.packet.Ip4Address;
+import org.onlab.packet.Ip6Address;
+import org.onlab.packet.IpAddress;
+import org.onosproject.codec.CodecContext;
+import org.onosproject.codec.JsonCodec;
+import org.onosproject.incubator.net.l2monitoring.cfm.Mep;
+import org.onosproject.incubator.net.l2monitoring.cfm.Mep.FngAddress;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.onlab.util.Tools.nullIsIllegal;
+
+/**
+ * Encode and decode to/from JSON to FngAddress object.
+ */
+public class FngAddressCodec extends JsonCodec<FngAddress> {
+
+    @Override
+    public ObjectNode encode(FngAddress fngAddress, CodecContext context) {
+        checkNotNull(fngAddress, "FngAddress cannot be null");
+        ObjectNode result = context.mapper().createObjectNode()
+                .put("address-type", fngAddress.addressType().name());
+
+        if (fngAddress.addressType().equals(Mep.FngAddressType.IPV4) ||
+                fngAddress.addressType().equals(Mep.FngAddressType.IPV6)) {
+            result.put("ip-address", fngAddress.ipAddress().toString());
+        }
+
+        return result;
+    }
+
+    @Override
+    public FngAddress decode(ObjectNode json, CodecContext context) {
+        if (json == null || !json.isObject()) {
+            return null;
+        }
+
+        JsonNode node = json.get("fng-address");
+
+        String addressType = nullIsIllegal(node.get("address-type"),
+                            "address type is required").asText();
+        Mep.FngAddressType type = Mep.FngAddressType.valueOf(addressType.toUpperCase());
+        JsonNode ipAddressNode = node.get("ipAddress");
+
+        switch (type) {
+            case IPV4:
+                return FngAddress.ipV4Address(Ip4Address.valueOf(ipAddressNode.asText()));
+            case IPV6:
+                return FngAddress.ipV6Address(Ip6Address.valueOf(ipAddressNode.asText()));
+            case NOT_TRANSMITTED:
+                return FngAddress.notTransmitted(IpAddress.valueOf(ipAddressNode.asText()));
+            case NOT_SPECIFIED:
+            default:
+                return FngAddress.notSpecified();
+        }
+    }
+}
diff --git a/apps/cfm/src/main/java/org/onosproject/cfm/web/MaintenanceAssociationCodec.java b/apps/cfm/src/main/java/org/onosproject/cfm/web/MaintenanceAssociationCodec.java
new file mode 100644
index 0000000..62be35d
--- /dev/null
+++ b/apps/cfm/src/main/java/org/onosproject/cfm/web/MaintenanceAssociationCodec.java
@@ -0,0 +1,146 @@
+/*
+ * 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 com.google.common.base.Preconditions.checkNotNull;
+import static org.onlab.util.Tools.nullIsIllegal;
+
+import java.util.List;
+
+import org.onosproject.codec.CodecContext;
+import org.onosproject.codec.JsonCodec;
+import org.onosproject.incubator.net.l2monitoring.cfm.Component;
+import org.onosproject.incubator.net.l2monitoring.cfm.DefaultMaintenanceAssociation;
+import org.onosproject.incubator.net.l2monitoring.cfm.MaintenanceAssociation;
+import org.onosproject.incubator.net.l2monitoring.cfm.MaintenanceAssociation.CcmInterval;
+import org.onosproject.incubator.net.l2monitoring.cfm.MaintenanceAssociation.MaBuilder;
+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.identifier.MepId;
+import org.onosproject.incubator.net.l2monitoring.cfm.service.CfmConfigException;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+
+/**
+ * Encode and decode to/from JSON to MaintenanceAssociation object.
+ */
+public class MaintenanceAssociationCodec extends JsonCodec<MaintenanceAssociation> {
+
+    private static final String MA_NAME_TYPE = "maNameType";
+    private static final String MA_NUMERIC_ID = "maNumericId";
+    private static final String MA_NAME = "maName";
+    private static final String CCM_INTERVAL = "ccm-interval";
+    private static final String COMPONENT_LIST = "component-list";
+    private static final String RMEP_LIST = "rmep-list";
+    private static final String MA = "ma";
+
+    @Override
+    public ObjectNode encode(MaintenanceAssociation ma, CodecContext context) {
+        checkNotNull(ma, "Maintenance Association cannot be null");
+        ObjectNode result = context.mapper().createObjectNode()
+                .put(MA_NAME, ma.maId().toString())
+                .put(MA_NAME_TYPE, ma.maId().nameType().name());
+        if (ma.maNumericId() > 0) {
+            result = result.put(MA_NUMERIC_ID, ma.maNumericId());
+        }
+        if (ma.ccmInterval() != null) {
+            result = result.put(CCM_INTERVAL, ma.ccmInterval().name());
+        }
+
+        result.set(COMPONENT_LIST, new ComponentCodec().encode(ma.componentList(), context));
+        result.set(RMEP_LIST, new RMepCodec().encode(ma.remoteMepIdList(), context));
+
+        return result;
+    }
+
+    public MaintenanceAssociation decode(ObjectNode json, CodecContext context, int mdNameLen) {
+        if (json == null || !json.isObject()) {
+            return null;
+        }
+
+        JsonNode maNode = json.get(MA);
+
+        String maName = nullIsIllegal(maNode.get(MA_NAME), "maName is required").asText();
+        String maNameType = MaIdShort.MaIdType.CHARACTERSTRING.name();
+        if (maNode.get(MA_NAME_TYPE) != null) {
+            maNameType = maNode.get(MA_NAME_TYPE).asText();
+        }
+
+        try {
+            MaIdShort maId = null;
+            MaIdShort.MaIdType maIdType = MaIdShort.MaIdType.valueOf(maNameType);
+            switch (maIdType) {
+                case PRIMARYVID:
+                    maId = MaIdPrimaryVid.asMaId(maName);
+                    break;
+                case TWOOCTET:
+                    maId = MaId2Octet.asMaId(maName);
+                    break;
+                case RFC2685VPNID:
+                    maId = MaIdRfc2685VpnId.asMaIdHex(maName);
+                    break;
+                case ICCY1731:
+                    maId = MaIdIccY1731.asMaId(maName);
+                    break;
+                case CHARACTERSTRING:
+                default:
+                    maId = MaIdCharStr.asMaId(maName);
+            }
+            MaBuilder builder =
+                    DefaultMaintenanceAssociation.builder(maId, mdNameLen);
+
+            JsonNode maNumericIdNode = maNode.get(MA_NUMERIC_ID);
+            if (maNumericIdNode != null) {
+                short mdNumericId = (short) maNumericIdNode.asInt();
+                builder = builder.maNumericId(mdNumericId);
+            }
+            if (maNode.get(CCM_INTERVAL) != null) {
+                builder.ccmInterval(CcmInterval.valueOf(maNode.get(CCM_INTERVAL).asText()));
+            }
+
+            List<Component> componentList = (new ComponentCodec()).decode((ArrayNode)
+                    nullIsIllegal(maNode.get(COMPONENT_LIST),
+                            "component-list is required"), context);
+            for (Component component:componentList) {
+                builder = builder.addToComponentList(component);
+            }
+
+            List<MepId> remoteMeps = (new RMepCodec()).decode(
+                    (ArrayNode) nullIsIllegal(maNode.get(RMEP_LIST), "rmep-list is required"), context);
+            for (MepId remoteMep:remoteMeps) {
+                builder = builder.addToRemoteMepIdList(remoteMep);
+            }
+
+            return builder.build();
+        } catch (CfmConfigException e) {
+            throw new IllegalArgumentException(e);
+        }
+
+    }
+
+    @Override
+    public ArrayNode encode(Iterable<MaintenanceAssociation> maEntities, CodecContext context) {
+        ArrayNode an = context.mapper().createArrayNode();
+        maEntities.forEach(ma -> an.add(encode(ma, context)));
+        return an;
+    }
+}
diff --git a/apps/cfm/src/main/java/org/onosproject/cfm/web/MaintenanceDomainCodec.java b/apps/cfm/src/main/java/org/onosproject/cfm/web/MaintenanceDomainCodec.java
new file mode 100644
index 0000000..a6bcf01
--- /dev/null
+++ b/apps/cfm/src/main/java/org/onosproject/cfm/web/MaintenanceDomainCodec.java
@@ -0,0 +1,127 @@
+/*
+ * 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 com.google.common.base.Preconditions.checkNotNull;
+import static org.onlab.util.Tools.nullIsIllegal;
+
+import org.onosproject.codec.CodecContext;
+import org.onosproject.codec.JsonCodec;
+import org.onosproject.incubator.net.l2monitoring.cfm.DefaultMaintenanceDomain;
+import org.onosproject.incubator.net.l2monitoring.cfm.MaintenanceDomain;
+import org.onosproject.incubator.net.l2monitoring.cfm.MaintenanceDomain.MdLevel;
+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 com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+
+/**
+ * Encode and decode to/from JSON to MaintenanceDomain object.
+ */
+public class MaintenanceDomainCodec extends JsonCodec<MaintenanceDomain> {
+
+    private static final String MD_LEVEL = "mdLevel";
+    private static final String MD_NUMERIC_ID = "mdNumericId";
+    private static final String MD = "md";
+    private static final String MD_NAME = "mdName";
+    private static final String MD_NAME_TYPE = "mdNameType";
+
+    public MaintenanceDomainCodec() {
+        super();
+
+    }
+
+    @Override
+    public ObjectNode encode(MaintenanceDomain md, CodecContext context) {
+        checkNotNull(md, "Maintenance Domain cannot be null");
+        ObjectNode result = context.mapper().createObjectNode()
+                .put(MD_NAME, md.mdId().toString())
+                .put(MD_NAME_TYPE, md.mdId().nameType().name())
+                .put(MD_LEVEL, md.mdLevel().name());
+        if (md.mdNumericId() > 0) {
+            result = result.put(MD_NUMERIC_ID, md.mdNumericId());
+        }
+        result.set("maList",
+                new MaintenanceAssociationCodec()
+                .encode(md.maintenanceAssociationList(), context));
+
+        return result;
+    }
+
+    @Override
+    public MaintenanceDomain decode(ObjectNode json, CodecContext context) {
+        if (json == null || !json.isObject()) {
+            return null;
+        }
+
+        JsonNode mdNode = json.get(MD);
+
+        String mdName = nullIsIllegal(mdNode.get(MD_NAME), "mdName is required").asText();
+        String mdNameType = MdId.MdNameType.CHARACTERSTRING.name();
+        if (mdNode.get(MD_NAME_TYPE) != null) {
+            mdNameType = mdNode.get(MD_NAME_TYPE).asText();
+        }
+
+        try {
+            MdId mdId = null;
+            MdId.MdNameType nameType =
+                    MdId.MdNameType.valueOf(mdNameType);
+            switch (nameType) {
+                case DOMAINNAME:
+                    mdId = MdIdDomainName.asMdId(mdName);
+                    break;
+                case MACANDUINT:
+                    mdId = MdIdMacUint.asMdId(mdName);
+                    break;
+                case NONE:
+                    mdId = MdIdNone.asMdId();
+                    break;
+                case CHARACTERSTRING:
+                default:
+                    mdId = MdIdCharStr.asMdId(mdName);
+            }
+
+            MaintenanceDomain.MdBuilder builder = DefaultMaintenanceDomain.builder(mdId);
+            JsonNode mdLevelNode = mdNode.get(MD_LEVEL);
+            if (mdLevelNode != null) {
+                MdLevel mdLevel = MdLevel.valueOf(mdLevelNode.asText());
+                builder = builder.mdLevel(mdLevel);
+            }
+            JsonNode mdNumericIdNode = mdNode.get(MD_NUMERIC_ID);
+            if (mdNumericIdNode != null) {
+                short mdNumericId = (short) mdNumericIdNode.asInt();
+                builder = builder.mdNumericId(mdNumericId);
+            }
+
+            return builder.build();
+        } catch (CfmConfigException e) {
+            throw new IllegalArgumentException(e);
+        }
+    }
+
+    @Override
+    public ArrayNode encode(Iterable<MaintenanceDomain> mdEntities, CodecContext context) {
+        ArrayNode an = context.mapper().createArrayNode();
+        mdEntities.forEach(md -> an.add(encode(md, context)));
+        return an;
+    }
+}
diff --git a/apps/cfm/src/main/java/org/onosproject/cfm/web/MepCodec.java b/apps/cfm/src/main/java/org/onosproject/cfm/web/MepCodec.java
new file mode 100644
index 0000000..73a58b6
--- /dev/null
+++ b/apps/cfm/src/main/java/org/onosproject/cfm/web/MepCodec.java
@@ -0,0 +1,163 @@
+/*
+ * 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 com.google.common.base.Preconditions.checkNotNull;
+import static org.onlab.util.Tools.nullIsIllegal;
+
+import org.onlab.packet.VlanId;
+import org.onosproject.codec.CodecContext;
+import org.onosproject.codec.JsonCodec;
+import org.onosproject.incubator.net.l2monitoring.cfm.DefaultMep;
+import org.onosproject.incubator.net.l2monitoring.cfm.Mep;
+import org.onosproject.incubator.net.l2monitoring.cfm.Mep.MepBuilder;
+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.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.net.DeviceId;
+import org.onosproject.net.PortNumber;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+
+import java.time.Duration;
+
+/**
+ * Encode and decode to/from JSON to Mep object.
+ */
+public class MepCodec extends JsonCodec<Mep> {
+    private static final String ADMINISTRATIVE_STATE = "administrative-state";
+    private static final String PRIMARY_VID = "primary-vid";
+    private static final String CCM_LTM_PRIORITY = "ccm-ltm-priority";
+    private static final String CCI_ENABLED = "cci-enabled";
+    private static final String FNG_ADDRESS = "fng-address";
+    private static final String LOWEST_FAULT_PRIORITY_DEFECT = "lowest-fault-priority-defect";
+    private static final String DEFECT_PRESENT_TIME = "defect-present-time";
+    private static final String DEFECT_ABSENT_TIME = "defect-absent-time";
+
+     public Mep decode(ObjectNode json, CodecContext context, String
+                        mdName, String maName) {
+        if (json == null || !json.isObject()) {
+            return null;
+        }
+
+        JsonNode mepNode = json.get("mep");
+
+        int mepId = Integer.parseInt(
+                nullIsIllegal(mepNode.get("mepId"), "mepId is required").asText());
+        DeviceId deviceId = DeviceId.deviceId(
+                nullIsIllegal(mepNode.get("deviceId"), "deviceId is required")
+                .asText());
+        PortNumber port = PortNumber
+                .portNumber(Long.parseLong(
+                        nullIsIllegal(mepNode.get("port"), "port is required")
+                        .asText()));
+        MepDirection direction = MepDirection.valueOf(
+                nullIsIllegal(mepNode.get("direction"), "direction is required").
+                asText());
+
+        try {
+            MdId mdId = MdIdCharStr.asMdId(mdName);
+            MaIdShort maId = MaIdCharStr.asMaId(maName);
+            MepBuilder mepBuilder = DefaultMep
+                    .builder(MepId.valueOf((short) mepId),
+                            deviceId, port, direction, mdId, maId);
+
+            if (mepNode.get(PRIMARY_VID) != null) {
+                mepBuilder.primaryVid(VlanId.vlanId(
+                        (short) mepNode.get(PRIMARY_VID).asInt(0)));
+            }
+
+            if (mepNode.get(ADMINISTRATIVE_STATE) != null) {
+                mepBuilder.administrativeState(mepNode.get(ADMINISTRATIVE_STATE)
+                        .asBoolean());
+            }
+
+            if (mepNode.get(CCM_LTM_PRIORITY) != null) {
+                mepBuilder.ccmLtmPriority(
+                        Priority.values()[mepNode.get(CCM_LTM_PRIORITY).asInt(0)]);
+            }
+
+            if (mepNode.get(CCI_ENABLED) != null) {
+                mepBuilder.cciEnabled(mepNode.get(CCI_ENABLED).asBoolean());
+            }
+
+            if (mepNode.get(LOWEST_FAULT_PRIORITY_DEFECT) != null) {
+                mepBuilder.lowestFaultPriorityDefect(
+                        Mep.LowestFaultDefect.values()[mepNode.get(LOWEST_FAULT_PRIORITY_DEFECT).asInt()]);
+            }
+
+            if (mepNode.get(DEFECT_ABSENT_TIME) != null) {
+                mepBuilder.defectAbsentTime(
+                        Duration.parse(mepNode.get(DEFECT_ABSENT_TIME).asText()));
+            }
+
+            if (mepNode.get(DEFECT_PRESENT_TIME) != null) {
+                mepBuilder.defectPresentTime(
+                        Duration.parse(mepNode.get(DEFECT_PRESENT_TIME).asText()));
+            }
+
+            if (mepNode.get(FNG_ADDRESS) != null) {
+                mepBuilder.fngAddress((new FngAddressCodec())
+                        .decode((ObjectNode) mepNode, context));
+            }
+
+
+            return mepBuilder.build();
+        } catch (CfmConfigException e) {
+            throw new IllegalArgumentException(e);
+        }
+    }
+
+    @Override
+    public ObjectNode encode(Mep mep, CodecContext context) {
+        checkNotNull(mep, "Mep cannot be null");
+        ObjectNode result = context.mapper().createObjectNode()
+                .put("mepId", mep.mepId().id())
+                .put("deviceId", mep.deviceId().toString())
+                .put("port", mep.port().toLong())
+                .put("direction", mep.direction().name())
+                .put("mdName", mep.mdId().toString())
+                .put("maName", mep.maId().toString())
+                .put(ADMINISTRATIVE_STATE, mep.administrativeState())
+                .put(CCI_ENABLED, mep.cciEnabled());
+        if (mep.ccmLtmPriority() != null) {
+            result.put(CCM_LTM_PRIORITY, mep.ccmLtmPriority().ordinal());
+        }
+        if (mep.primaryVid() != null) {
+            result.put(PRIMARY_VID, mep.primaryVid().toShort());
+        }
+        if (mep.fngAddress() != null) {
+            result.put(FNG_ADDRESS, new FngAddressCodec().encode(mep.fngAddress(), context));
+        }
+        if (mep.lowestFaultPriorityDefect() != null) {
+            result.put(LOWEST_FAULT_PRIORITY_DEFECT, mep.lowestFaultPriorityDefect().ordinal());
+        }
+        if (mep.defectPresentTime() != null) {
+            result.put(DEFECT_PRESENT_TIME, mep.defectPresentTime().toString());
+        }
+        if (mep.defectAbsentTime() != null) {
+            result.put(DEFECT_ABSENT_TIME, mep.defectAbsentTime().toString());
+        }
+
+        return result;
+    }
+}
diff --git a/apps/cfm/src/main/java/org/onosproject/cfm/web/MepEntryCodec.java b/apps/cfm/src/main/java/org/onosproject/cfm/web/MepEntryCodec.java
new file mode 100644
index 0000000..d1618a7
--- /dev/null
+++ b/apps/cfm/src/main/java/org/onosproject/cfm/web/MepEntryCodec.java
@@ -0,0 +1,93 @@
+/*
+ * 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 com.google.common.base.Preconditions.checkNotNull;
+
+import java.util.Iterator;
+import java.util.Map.Entry;
+
+import org.onosproject.codec.CodecContext;
+import org.onosproject.codec.JsonCodec;
+import org.onosproject.incubator.net.l2monitoring.cfm.Mep;
+import org.onosproject.incubator.net.l2monitoring.cfm.MepEntry;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+
+/**
+ * Encode and decode to/from JSON to MepEntry object.
+ */
+public class MepEntryCodec extends JsonCodec<MepEntry> {
+
+    @Override
+    public ObjectNode encode(MepEntry mepEntry, CodecContext context) {
+        checkNotNull(mepEntry, "Mep cannot be null");
+
+        ObjectNode result = context.mapper().createObjectNode();
+
+        //Get the common attributes
+        Mep mep = (Mep) mepEntry;
+        ObjectNode mepAttrs = new MepCodec().encode(mep, context);
+        Iterator<Entry<String, JsonNode>> elements = mepAttrs.fields();
+        while (elements.hasNext()) {
+            Entry<String, JsonNode> element = elements.next();
+            result.set(element.getKey(), element.getValue());
+        }
+
+        if (mepEntry.macAddress() != null) {
+            result.put("macAddress", mepEntry.macAddress().toString());
+        }
+
+        if (mepEntry.loopbackAttributes() != null) {
+            result.set("loopback", new MepLbEntryCodec()
+                    .encode(mepEntry.loopbackAttributes(), context));
+        }
+
+        if (mepEntry.activeRemoteMepList() != null) {
+            result.set("remoteMeps", new RemoteMepEntryCodec()
+                    .encode(mepEntry.activeRemoteMepList(), context));
+        }
+
+        if (mepEntry.activeErrorCcmDefect()) {
+            result.put("activeErrorCcmDefect", true);
+        }
+        if (mepEntry.activeMacStatusDefect()) {
+            result.put("activeMacStatusDefect", true);
+        }
+        if (mepEntry.activeRdiCcmDefect()) {
+            result.put("activeRdiCcmDefect", true);
+        }
+        if (mepEntry.activeRemoteCcmDefect()) {
+            result.put("activeRemoteCcmDefect", true);
+        }
+        if (mepEntry.activeXconCcmDefect()) {
+            result.put("activeXconCcmDefect", true);
+        }
+        return result;
+    }
+
+    @Override
+    public ArrayNode encode(Iterable<MepEntry> mepEntryEntities, CodecContext context) {
+        ArrayNode an = context.mapper().createArrayNode();
+        if (mepEntryEntities != null) {
+            mepEntryEntities.forEach(mepEntry -> an.add(encode(mepEntry, context)));
+        }
+        return an;
+    }
+
+}
diff --git a/apps/cfm/src/main/java/org/onosproject/cfm/web/MepLbCreateCodec.java b/apps/cfm/src/main/java/org/onosproject/cfm/web/MepLbCreateCodec.java
new file mode 100644
index 0000000..e29415c
--- /dev/null
+++ b/apps/cfm/src/main/java/org/onosproject/cfm/web/MepLbCreateCodec.java
@@ -0,0 +1,122 @@
+/*
+ * 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 org.onlab.packet.MacAddress;
+import org.onlab.util.HexString;
+import org.onosproject.codec.CodecContext;
+import org.onosproject.codec.JsonCodec;
+import org.onosproject.incubator.net.l2monitoring.cfm.DefaultMepLbCreate;
+import org.onosproject.incubator.net.l2monitoring.cfm.Mep.Priority;
+import org.onosproject.incubator.net.l2monitoring.cfm.MepLbCreate;
+import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MepId;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * Encode and decode to/from JSON to MepLbCreate object.
+ */
+public class MepLbCreateCodec extends JsonCodec<MepLbCreate> {
+
+    public static final String NUMBER_MESSAGES = "numberMessages";
+    public static final String REMOTE_MEP_ID = "remoteMepId";
+    public static final String REMOTE_MEP_MAC = "remoteMepMac";
+    public static final String DATA_TLV_HEX = "dataTlvHex";
+    public static final String VLAN_DROP_ELIGIBLE = "vlanDropEligible";
+    public static final String VLAN_PRIORITY = "vlanPriority";
+    public static final String LOOPBACK = "loopback";
+
+    @Override
+    public ObjectNode encode(MepLbCreate mepLbCreate, CodecContext context) {
+        checkNotNull(mepLbCreate, "Mep Lb Create cannot be null");
+        ObjectNode result = context.mapper().createObjectNode()
+                .put(NUMBER_MESSAGES, mepLbCreate.numberMessages());
+
+        if (mepLbCreate.remoteMepId() != null) {
+            result.put(REMOTE_MEP_ID, mepLbCreate.remoteMepId().value());
+        } else {
+            result.put(REMOTE_MEP_MAC, mepLbCreate.remoteMepAddress().toString());
+        }
+
+        if (mepLbCreate.dataTlvHex() != null) {
+            result.put(DATA_TLV_HEX, mepLbCreate.dataTlvHex());
+        }
+        if (mepLbCreate.vlanDropEligible() != null) {
+            result.put(VLAN_DROP_ELIGIBLE, mepLbCreate.vlanDropEligible());
+        }
+        if (mepLbCreate.vlanPriority() != null) {
+            result.put(VLAN_PRIORITY, mepLbCreate.vlanPriority().ordinal());
+        }
+        return result;
+    }
+
+
+    @Override
+    public MepLbCreate decode(ObjectNode json, CodecContext context) {
+        if (json == null || !json.isObject()) {
+            return null;
+        }
+
+        JsonNode loopbackNode = json.get(LOOPBACK);
+
+        JsonNode remoteMepIdNode = loopbackNode.get(REMOTE_MEP_ID);
+        JsonNode remoteMepMacNode = loopbackNode.get(REMOTE_MEP_MAC);
+
+        MepLbCreate.MepLbCreateBuilder lbCreateBuilder = null;
+        if (remoteMepIdNode != null) {
+            MepId remoteMepId = MepId.valueOf((short) remoteMepIdNode.asInt());
+            lbCreateBuilder = DefaultMepLbCreate.builder(remoteMepId);
+        } else if (remoteMepMacNode != null) {
+            MacAddress remoteMepMac = MacAddress.valueOf(
+                                            remoteMepMacNode.asText());
+            lbCreateBuilder = DefaultMepLbCreate.builder(remoteMepMac);
+        } else {
+            throw new IllegalArgumentException(
+                    "Either a remoteMepId or a remoteMepMac");
+        }
+
+        JsonNode numMessagesNode = loopbackNode.get(NUMBER_MESSAGES);
+        if (numMessagesNode != null) {
+            int numMessages = numMessagesNode.asInt();
+            lbCreateBuilder.numberMessages(numMessages);
+        }
+
+        JsonNode vlanDropEligibleNode = loopbackNode.get(VLAN_DROP_ELIGIBLE);
+        if (vlanDropEligibleNode != null) {
+            boolean vlanDropEligible = vlanDropEligibleNode.asBoolean();
+            lbCreateBuilder.vlanDropEligible(vlanDropEligible);
+        }
+
+        JsonNode vlanPriorityNode = loopbackNode.get(VLAN_PRIORITY);
+        if (vlanPriorityNode != null) {
+            short vlanPriority = (short) vlanPriorityNode.asInt();
+            lbCreateBuilder.vlanPriority(Priority.values()[vlanPriority]);
+        }
+
+        JsonNode dataTlvHexNode = loopbackNode.get(DATA_TLV_HEX);
+        if (dataTlvHexNode != null) {
+            String dataTlvHex = loopbackNode.get(DATA_TLV_HEX).asText();
+            if (!dataTlvHex.isEmpty()) {
+                lbCreateBuilder.dataTlv(HexString.fromHexString(dataTlvHex));
+            }
+        }
+
+        return lbCreateBuilder.build();
+    }
+}
diff --git a/apps/cfm/src/main/java/org/onosproject/cfm/web/MepLbEntryCodec.java b/apps/cfm/src/main/java/org/onosproject/cfm/web/MepLbEntryCodec.java
new file mode 100644
index 0000000..62409cd
--- /dev/null
+++ b/apps/cfm/src/main/java/org/onosproject/cfm/web/MepLbEntryCodec.java
@@ -0,0 +1,44 @@
+/*
+ * 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 com.google.common.base.Preconditions.checkNotNull;
+
+import org.onosproject.codec.CodecContext;
+import org.onosproject.codec.JsonCodec;
+import org.onosproject.incubator.net.l2monitoring.cfm.MepLbEntry;
+
+import com.fasterxml.jackson.databind.node.ObjectNode;
+
+/**
+ * Encode and decode to/from JSON to MepLbEntry object.
+ */
+public class MepLbEntryCodec extends JsonCodec<MepLbEntry> {
+
+    @Override
+    public ObjectNode encode(MepLbEntry mepLbEntry, CodecContext context) {
+        checkNotNull(mepLbEntry, "Mep Lb Entry cannot be null");
+        ObjectNode result = context.mapper().createObjectNode()
+                .put("nextLbmIdentifier", mepLbEntry.nextLbmIdentifier())
+                .put("countLbrTransmitted", mepLbEntry.countLbrTransmitted())
+                .put("countLbrReceived", mepLbEntry.countLbrReceived())
+                .put("countLbrValidInOrder", mepLbEntry.countLbrValidInOrder())
+                .put("countLbrValidOutOfOrder", mepLbEntry.countLbrValidOutOfOrder())
+                .put("countLbrMacMisMatch", mepLbEntry.countLbrMacMisMatch());
+
+        return result;
+    }
+}
diff --git a/apps/cfm/src/main/java/org/onosproject/cfm/web/MepLtCreateCodec.java b/apps/cfm/src/main/java/org/onosproject/cfm/web/MepLtCreateCodec.java
new file mode 100644
index 0000000..0141a73
--- /dev/null
+++ b/apps/cfm/src/main/java/org/onosproject/cfm/web/MepLtCreateCodec.java
@@ -0,0 +1,110 @@
+/*
+ * 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.node.ObjectNode;
+import org.onlab.packet.MacAddress;
+import org.onosproject.codec.CodecContext;
+import org.onosproject.codec.JsonCodec;
+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.util.BitSet;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * Encode and decode to/from JSON to MepLtCreate object.
+ */
+public class MepLtCreateCodec extends JsonCodec<MepLtCreate> {
+
+    private static final String REMOTE_MEP_ID = "remoteMepId";
+    private static final String REMOTE_MEP_MAC = "remoteMepMac";
+    private static final String DEFAULT_TTL = "defaultTtl";
+    private static final String TRANSMIT_LTM_FLAGS = "transmitLtmFlags";
+    private static final String LINKTRACE = "linktrace";
+    private static final String USE_FDB_ONLY = "use-fdb-only";
+
+    @Override
+    public ObjectNode encode(MepLtCreate mepLtCreate, CodecContext context) {
+        checkNotNull(mepLtCreate, "Mep Lt Create cannot be null");
+        ObjectNode result = context.mapper().createObjectNode();
+
+        if (mepLtCreate.remoteMepId() != null) {
+            result.put(REMOTE_MEP_ID, mepLtCreate.remoteMepId().value());
+        } else {
+            result.put(REMOTE_MEP_MAC, mepLtCreate.remoteMepAddress().toString());
+        }
+
+        if (mepLtCreate.defaultTtl() != null) {
+            result.put(DEFAULT_TTL, mepLtCreate.defaultTtl());
+        }
+        if (mepLtCreate.transmitLtmFlags() != null) {
+            result.put(TRANSMIT_LTM_FLAGS,
+                    mepLtCreate.transmitLtmFlags().get(0) ? USE_FDB_ONLY : "");
+        }
+
+        return result;
+    }
+
+
+    @Override
+    public MepLtCreate decode(ObjectNode json, CodecContext context) {
+        if (json == null || !json.isObject()) {
+            return null;
+        }
+
+        JsonNode linktraceNode = json.get(LINKTRACE);
+
+        JsonNode remoteMepIdNode = linktraceNode.get(REMOTE_MEP_ID);
+        JsonNode remoteMepMacNode = linktraceNode.get(REMOTE_MEP_MAC);
+
+        MepLtCreate.MepLtCreateBuilder ltCreateBuilder = null;
+        if (remoteMepIdNode != null) {
+            MepId remoteMepId = MepId.valueOf((short) remoteMepIdNode.asInt());
+            ltCreateBuilder = DefaultMepLtCreate.builder(remoteMepId);
+        } else if (remoteMepMacNode != null) {
+            MacAddress remoteMepMac = MacAddress.valueOf(
+                                            remoteMepMacNode.asText());
+            ltCreateBuilder = DefaultMepLtCreate.builder(remoteMepMac);
+        } else {
+            throw new IllegalArgumentException(
+                    "Either a remoteMepId or a remoteMepMac");
+        }
+
+        JsonNode defaultTtlNode = linktraceNode.get(DEFAULT_TTL);
+        if (defaultTtlNode != null) {
+            short defaultTtl = (short) defaultTtlNode.asInt();
+            ltCreateBuilder.defaultTtl(defaultTtl);
+        }
+
+        JsonNode transmitLtmFlagsNode = linktraceNode.get(TRANSMIT_LTM_FLAGS);
+        if (transmitLtmFlagsNode != null) {
+            if (transmitLtmFlagsNode.asText().isEmpty()) {
+                ltCreateBuilder.transmitLtmFlags(BitSet.valueOf(new long[]{0}));
+            } else if (transmitLtmFlagsNode.asText().equals(USE_FDB_ONLY)) {
+               ltCreateBuilder.transmitLtmFlags(BitSet.valueOf(new long[]{1}));
+            } else {
+                throw new IllegalArgumentException("Expecting value 'use-fdb-only' " +
+                        "or '' for " + TRANSMIT_LTM_FLAGS);
+            }
+        }
+
+        return ltCreateBuilder.build();
+    }
+}
diff --git a/apps/cfm/src/main/java/org/onosproject/cfm/web/RMepCodec.java b/apps/cfm/src/main/java/org/onosproject/cfm/web/RMepCodec.java
new file mode 100644
index 0000000..7edb204
--- /dev/null
+++ b/apps/cfm/src/main/java/org/onosproject/cfm/web/RMepCodec.java
@@ -0,0 +1,69 @@
+/*
+ * 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.onlab.util.Tools.nullIsIllegal;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.onosproject.codec.CodecContext;
+import org.onosproject.codec.JsonCodec;
+import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MepId;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+
+/**
+ * Encode and decode to/from JSON to RMep object.
+ */
+public class RMepCodec extends JsonCodec<MepId> {
+
+    @Override
+    public ObjectNode encode(MepId rmep, CodecContext context) {
+        return context.mapper().createObjectNode().put("rmep", rmep.id());
+    }
+
+    @Override
+    public ArrayNode encode(Iterable<MepId> rmeps, CodecContext context) {
+        ArrayNode an = context.mapper().createArrayNode();
+        rmeps.forEach(rmep -> {
+            an.add(encode(rmep, context));
+        });
+        return an;
+    }
+
+    @Override
+    public MepId decode(ObjectNode json, CodecContext context) {
+        if (json == null || !json.isObject()) {
+            return null;
+        }
+
+        JsonNode vidNode = json.get("rmep");
+
+        return MepId.valueOf(
+                    nullIsIllegal((short) vidNode.asInt(), "rmep is required"));
+
+    }
+
+    @Override
+    public List<MepId> decode(ArrayNode json, CodecContext context) {
+        List<MepId> rmepList = new ArrayList<>();
+        json.forEach(node -> rmepList.add(decode((ObjectNode) node, context)));
+        return rmepList;
+    }
+}
diff --git a/apps/cfm/src/main/java/org/onosproject/cfm/web/RemoteMepEntryCodec.java b/apps/cfm/src/main/java/org/onosproject/cfm/web/RemoteMepEntryCodec.java
new file mode 100644
index 0000000..4f8344a
--- /dev/null
+++ b/apps/cfm/src/main/java/org/onosproject/cfm/web/RemoteMepEntryCodec.java
@@ -0,0 +1,74 @@
+/*
+ * 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 com.google.common.base.Preconditions.checkNotNull;
+
+import org.onosproject.codec.CodecContext;
+import org.onosproject.codec.JsonCodec;
+import org.onosproject.incubator.net.l2monitoring.cfm.RemoteMepEntry;
+
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+
+/**
+ * Encode and decode to/from JSON to RemoteMepEntry object.
+ */
+public class RemoteMepEntryCodec extends JsonCodec<RemoteMepEntry> {
+
+    @Override
+    public ObjectNode encode(RemoteMepEntry remoteMepEntry, CodecContext context) {
+        checkNotNull(remoteMepEntry, "Mep cannot be null");
+        ObjectNode result = context.mapper().createObjectNode()
+                .put("remoteMepId", remoteMepEntry.remoteMepId().toString())
+                .put("remoteMepState", remoteMepEntry.state().name())
+                .put("rdi", remoteMepEntry.rdi());
+
+        if (remoteMepEntry.failedOrOkTime() != null) {
+            result = result.put("failedOrOkTime",
+                    remoteMepEntry.failedOrOkTime().toString());
+        }
+
+        if (remoteMepEntry.macAddress() != null) {
+            result = result.put("macAddress", remoteMepEntry.macAddress().toString());
+        }
+
+        if (remoteMepEntry.portStatusTlvType() != null) {
+            result = result.put("portStatusTlvType",
+                    remoteMepEntry.portStatusTlvType().name());
+        }
+        if (remoteMepEntry.interfaceStatusTlvType() != null) {
+            result = result.put("interfaceStatusTlvType",
+                    remoteMepEntry.interfaceStatusTlvType().name());
+        }
+        if (remoteMepEntry.senderIdTlvType() != null) {
+            result = result.put("senderIdTlvType",
+                    remoteMepEntry.senderIdTlvType().name());
+        }
+
+        return result;
+    }
+
+    @Override
+    public ArrayNode encode(Iterable<RemoteMepEntry> remoteMepEntries,
+            CodecContext context) {
+        ArrayNode an = context.mapper().createArrayNode();
+        remoteMepEntries.forEach(remoteMepEntry ->
+                        an.add(encode(remoteMepEntry, context)));
+        return an;
+    }
+
+}
diff --git a/apps/cfm/src/main/java/org/onosproject/cfm/web/VidCodec.java b/apps/cfm/src/main/java/org/onosproject/cfm/web/VidCodec.java
new file mode 100644
index 0000000..a673719
--- /dev/null
+++ b/apps/cfm/src/main/java/org/onosproject/cfm/web/VidCodec.java
@@ -0,0 +1,71 @@
+/*
+ * 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.onlab.util.Tools.nullIsIllegal;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.onlab.packet.VlanId;
+import org.onosproject.codec.CodecContext;
+import org.onosproject.codec.JsonCodec;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+
+/**
+ * Encode and decode to/from JSON to Vid object.
+ */
+public class VidCodec extends JsonCodec<VlanId> {
+
+    @Override
+    public ObjectNode encode(VlanId vid, CodecContext context) {
+        return context.mapper().createObjectNode().put("vid", vid.toString());
+    }
+
+    @Override
+    public ArrayNode encode(Iterable<VlanId> vids, CodecContext context) {
+        ArrayNode an = context.mapper().createArrayNode();
+        vids.forEach(vid -> {
+            an.add(encode(vid, context));
+        });
+        return an;
+    }
+
+    @Override
+    public VlanId decode(ObjectNode json, CodecContext context) {
+        if (json == null || !json.isObject()) {
+            return null;
+        }
+
+        JsonNode vidNode = json.get("vid");
+
+        int vid = (nullIsIllegal(vidNode.asInt(), "vid is required"));
+        if (vid < 0 || vid > 4095) {
+            throw new IllegalArgumentException("VID values must be between 0 and 4095");
+        }
+        return VlanId.vlanId((short) vid);
+    }
+
+    @Override
+    public List<VlanId> decode(ArrayNode json, CodecContext context) {
+        List<VlanId> vidList = new ArrayList<>();
+        json.forEach(node -> vidList.add(decode((ObjectNode) node, context)));
+        return vidList;
+    }
+}
diff --git a/apps/cfm/src/main/java/org/onosproject/cfm/web/package-info.java b/apps/cfm/src/main/java/org/onosproject/cfm/web/package-info.java
new file mode 100644
index 0000000..265a1fa
--- /dev/null
+++ b/apps/cfm/src/main/java/org/onosproject/cfm/web/package-info.java
@@ -0,0 +1,19 @@
+/*
+ * 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.
+ */
+/**
+ * Codecs for converting CFM objects to and from JSON.
+ */
+package org.onosproject.cfm.web;
\ No newline at end of file