Initial import of CFM and SOAM api
Change-Id: Icf5cc2d5fb34b75460e80e8cced0d70265bcd33b
diff --git a/apps/cfm/BUCK b/apps/cfm/BUCK
new file mode 100644
index 0000000..7c8cb71
--- /dev/null
+++ b/apps/cfm/BUCK
@@ -0,0 +1,28 @@
+COMPILE_DEPS = [
+ '//lib:CORE_DEPS',
+ '//lib:javax.ws.rs-api',
+ '//utils/rest:onlab-rest',
+ '//lib:JACKSON',
+ '//cli:onos-cli',
+ '//lib:org.apache.karaf.shell.console',
+ '//incubator/api:onos-incubator-api'
+]
+
+TEST_DEPS = [
+ '//lib:TEST_REST',
+ '//utils/osgi:onlab-osgi-tests',
+ '//web/api:onos-rest-tests',
+]
+
+osgi_jar_with_tests (
+ deps = COMPILE_DEPS,
+ test_deps = TEST_DEPS,
+ web_context = '/onos/cfm',
+)
+
+onos_app (
+ title = 'Layer 2 Monitoring CFM Application',
+ category = 'default',
+ url = 'http://onosproject.org',
+ description = 'Layer 2 Monitoring Connectivity Fault Management App',
+)
diff --git a/apps/cfm/README.md b/apps/cfm/README.md
new file mode 100644
index 0000000..c2c27fa
--- /dev/null
+++ b/apps/cfm/README.md
@@ -0,0 +1,3 @@
+# Layer 2 Monitoring with CFM and SOAM in ONOS
+Please see the README.md file in the API at
+incubator/api/src/main/java/org/onosproject/incubator/net/l2monitoring/README.md
\ No newline at end of file
diff --git a/apps/cfm/pom.xml b/apps/cfm/pom.xml
new file mode 100644
index 0000000..ad63ddd
--- /dev/null
+++ b/apps/cfm/pom.xml
@@ -0,0 +1,96 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- ~ Copyright 2017 Open Networking Laboratory ~ ~ 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. -->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.onosproject</groupId>
+ <artifactId>onos-apps</artifactId>
+ <version>1.12.0-SNAPSHOT</version>
+ </parent>
+
+ <artifactId>onos-app-cfm</artifactId>
+ <packaging>bundle</packaging>
+ <version>1.12.0-SNAPSHOT</version>
+
+ <description>ONOS OSGi bundle archetype</description>
+ <url>http://onosproject.org</url>
+
+ <properties>
+ <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+ <web.context>/onos/cfm</web.context>
+ <api.description>Layer 2 Monitoring CFM REST API</api.description>
+ <api.version>1.0.0</api.version>
+ <api.package>org.onosproject.cfm</api.package>
+ <api.title>Layer 2 Monitoring CFM REST API</api.title>
+ <onos.app.name>org.onosproject.cfm</onos.app.name>
+ <onos.app.title>Layer 2 Monitoring CFM App</onos.app.title>
+ <onos.app.origin>Microsemi Inc</onos.app.origin>
+ <onos.app.category>default</onos.app.category>
+ <onos.app.url>http://onosproject.org</onos.app.url>
+ <onos.app.readme>Layer 2 Monitoring Connectivity Fault Management App</onos.app.readme>
+ </properties>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.onosproject</groupId>
+ <artifactId>onos-api</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.onosproject</groupId>
+ <artifactId>onos-incubator-api</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.onosproject</groupId>
+ <artifactId>onos-core-common</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.onosproject</groupId>
+ <artifactId>onos-cli</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.karaf.shell</groupId>
+ <artifactId>org.apache.karaf.shell.console</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.onosproject</groupId>
+ <artifactId>onlab-junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <extensions>true</extensions>
+ <configuration>
+ <instructions>
+ <_wab>src/main/webapp/</_wab>
+ <Include-Resource>WEB-INF/classes/apidoc/swagger.json=target/swagger.json,
+ {maven-resources}</Include-Resource>
+ <Bundle-SymbolicName>${project.groupId}.${project.artifactId}</Bundle-SymbolicName>
+ <Import-Package>*,org.glassfish.jersey.servlet</Import-Package>
+ <Web-ContextPath>${web.context}</Web-ContextPath>
+ </instructions>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+
+</project>
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
diff --git a/apps/cfm/src/main/java/org/onosproject/soam/impl/DmWebResource.java b/apps/cfm/src/main/java/org/onosproject/soam/impl/DmWebResource.java
new file mode 100644
index 0000000..b732450
--- /dev/null
+++ b/apps/cfm/src/main/java/org/onosproject/soam/impl/DmWebResource.java
@@ -0,0 +1,257 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.soam.impl;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URI;
+import java.net.URISyntaxException;
+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.codec.JsonCodec;
+import org.onosproject.incubator.net.l2monitoring.cfm.Mep;
+import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MaIdCharStr;
+import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MaIdShort;
+import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MdId;
+import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MdIdCharStr;
+import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MepId;
+import org.onosproject.incubator.net.l2monitoring.cfm.service.CfmConfigException;
+import org.onosproject.incubator.net.l2monitoring.cfm.service.CfmMepService;
+import org.onosproject.incubator.net.l2monitoring.soam.SoamConfigException;
+import org.onosproject.incubator.net.l2monitoring.soam.SoamId;
+import org.onosproject.incubator.net.l2monitoring.soam.SoamService;
+import org.onosproject.incubator.net.l2monitoring.soam.delay.DelayMeasurementCreate;
+import org.onosproject.incubator.net.l2monitoring.soam.delay.DelayMeasurementEntry;
+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 SOAM Delay Measurement web resource.
+ */
+@Path("md/{md_name}/ma/{ma_name}/mep/{mep_id}/dm")
+public class DmWebResource extends AbstractWebResource {
+ private final Logger log = LoggerFactory.getLogger(getClass());
+
+ /**
+ * Get all DMs for a Mep.
+ *
+ * @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 a Mep belonging to the MA
+ * @return 200 OK with a list of DMs or 500 on error
+ */
+ @GET
+ @Produces(MediaType.APPLICATION_JSON)
+ @Consumes(MediaType.APPLICATION_JSON)
+ public Response getAllDmsForMep(@PathParam("md_name") String mdName,
+ @PathParam("ma_name") String maName,
+ @PathParam("mep_id") short mepId) {
+ log.debug("GET all DMs called for MEP {}", mdName + "/" + maName + "/" + mepId);
+ try {
+ MdId mdId = MdIdCharStr.asMdId(mdName);
+ MaIdShort maId = MaIdCharStr.asMaId(maName);
+ MepId mepIdObj = MepId.valueOf(mepId);
+ Collection<DelayMeasurementEntry> dmCollection =
+ get(SoamService.class).getAllDms(mdId, maId, mepIdObj);
+ ArrayNode an = mapper().createArrayNode();
+ an.add(codec(DelayMeasurementEntry.class).encode(dmCollection, this));
+ return ok(mapper().createObjectNode().set("dms", an)).build();
+ } catch (CfmConfigException | SoamConfigException e) {
+ log.error("Get DM {} failed because of exception {}",
+ mdName + "/" + maName + "/" + mepId, e.toString());
+ return Response.serverError().entity("{ \"failure\":\"" + e.toString() + "\" }").build();
+ }
+ }
+
+ /**
+ * Get DM by MD name, MA name, Mep Id and Dm 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
+ * @param dmId The Id of the DM
+ * @return 200 OK with details of the DM or 500 on error
+ */
+ @GET
+ @Path("{dm_id}")
+ @Produces(MediaType.APPLICATION_JSON)
+ @Consumes(MediaType.APPLICATION_JSON)
+ public Response getDm(@PathParam("md_name") String mdName,
+ @PathParam("ma_name") String maName,
+ @PathParam("mep_id") short mepId, @PathParam("dm_id") int dmId) {
+ log.debug("GET called for DM {}", mdName + "/" + maName + "/" + mepId + "/" + dmId);
+ try {
+ MdId mdId = MdIdCharStr.asMdId(mdName);
+ MaIdShort maId = MaIdCharStr.asMaId(maName);
+ MepId mepIdObj = MepId.valueOf(mepId);
+ SoamId dmIdObj = SoamId.valueOf(dmId);
+ DelayMeasurementEntry dm = get(SoamService.class)
+ .getDm(mdId, maId, mepIdObj, dmIdObj);
+ if (dm == null) {
+ return Response.serverError().entity("{ \"failure\":\"DM " +
+ mdName + "/" + maName + "/" + mepId + "/" + dmId + " not found\" }").build();
+ }
+ ObjectNode node = mapper().createObjectNode();
+ node.set("dm", codec(DelayMeasurementEntry.class).encode(dm, this));
+ return ok(node).build();
+ } catch (CfmConfigException | SoamConfigException e) {
+ log.error("Get DM {} failed because of exception {}",
+ mdName + "/" + maName + "/" + mepId + "/" + dmId, e.toString());
+ return Response.serverError().entity("{ \"failure\":\"" + e.toString() + "\" }").build();
+ }
+ }
+
+ /**
+ * Abort DM by MD name, MA name, Mep Id and DM Id.
+ * In the API the measurement is aborted, and not truly deleted. It still
+ * remains so that its results may be read. Depending on the device it will
+ * get overwritten on the creation of subsequent measurements.
+ * Use clear stats to delete old results
+ * measurements.
+ *
+ * @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
+ * @param dmId The Id of the DM
+ * @return 200 OK or 304 if not found, or 500 on error
+ */
+ @DELETE
+ @Path("{dm_id}")
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ public Response abortDm(@PathParam("md_name") String mdName,
+ @PathParam("ma_name") String maName,
+ @PathParam("mep_id") short mepId,
+ @PathParam("dm_id") int dmId) {
+ log.debug("DELETE called for DM {}", mdName + "/" + maName + "/" + mepId + "/" + dmId);
+ try {
+ MdId mdId = MdIdCharStr.asMdId(mdName);
+ MaIdShort maId = MaIdCharStr.asMaId(maName);
+ MepId mepIdObj = MepId.valueOf(mepId);
+ SoamId dmIdObj = SoamId.valueOf(dmId);
+
+ get(SoamService.class).abortDm(mdId, maId, mepIdObj, dmIdObj);
+ return ok("{ \"success\":\"deleted (aborted) " + mdName + "/" + maName +
+ "/" + mepId + "/" + dmId + "\" }").build();
+ } catch (CfmConfigException e) {
+ log.error("Delete (abort) DM {} failed because of exception {}",
+ mdName + "/" + maName + "/" + mepId + "/" + dmId, e.toString());
+ return Response.serverError().entity("{ \"failure\":\"" + e.toString() + "\" }").build();
+ }
+ }
+
+ /**
+ * Create DM with MD name, MA name, Mep id and DM Json.
+ *
+ * @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 belonging to the MEP
+ * @param input A JSON formatted input stream specifying the DM parameters
+ * @return 201 Created or 304 if already exists or 500 on error
+ */
+ @POST
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ public Response createDm(@PathParam("md_name") String mdName,
+ @PathParam("ma_name") String maName,
+ @PathParam("mep_id") short mepId, InputStream input) {
+ log.debug("POST called to Create Dm");
+ try {
+ MdId mdId = MdIdCharStr.asMdId(mdName);
+ MaIdShort maId = MaIdCharStr.asMaId(maName);
+ MepId mepIdObj = MepId.valueOf(mepId);
+
+ Mep mep = get(CfmMepService.class).getMep(mdId, maId, mepIdObj);
+ if (mep == null) {
+ return Response.serverError().entity("{ \"failure\":\"mep " +
+ mdName + "/" + maName + "/" + mepId + " does not exist\" }").build();
+ }
+
+ ObjectMapper mapper = new ObjectMapper();
+ JsonNode cfg = mapper.readTree(input);
+ JsonCodec<DelayMeasurementCreate> dmCodec = codec(DelayMeasurementCreate.class);
+
+ DelayMeasurementCreate dm = dmCodec.decode((ObjectNode) cfg, this);
+ get(SoamService.class).createDm(mdId, maId, mepIdObj, dm);
+ return Response
+ .created(new URI("md/" + mdName + "/ma/" + maName + "/mep/" +
+ mepId + "/dm"))
+ .entity("{ \"success\":\"dm " + mdName + "/" + maName + "/" +
+ mepId + " created\" }")
+ .build();
+ } catch (CfmConfigException | SoamConfigException | IllegalArgumentException |
+ IOException | URISyntaxException e) {
+ log.error("Create DM on " + mdName + "/" + maName + "/" + mepId +
+ " failed because of exception {}", e.toString());
+ return Response.serverError()
+ .entity("{ \"failure\":\"" + e.toString() + "\" }")
+ .build();
+ }
+ }
+
+ /**
+ * Clear DM history stats by MD name, MA name, Mep Id and DM 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
+ * @param dmId The Id of the DM
+ * @return 200 OK or 304 if not found, or 500 on error
+ */
+ @PUT
+ @Path("{dm_id}/clear-history")
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ public Response clearDmHistory(@PathParam("md_name") String mdName,
+ @PathParam("ma_name") String maName,
+ @PathParam("mep_id") short mepId,
+ @PathParam("dm_id") int dmId) {
+ log.debug("clear-history called for DM {}", mdName + "/" + maName +
+ "/" + mepId + "/" + dmId);
+ try {
+ MdId mdId = MdIdCharStr.asMdId(mdName);
+ MaIdShort maId = MaIdCharStr.asMaId(maName);
+ MepId mepIdObj = MepId.valueOf(mepId);
+ SoamId dmIdObj = SoamId.valueOf(dmId);
+
+ get(SoamService.class).clearDelayHistoryStats(mdId, maId, mepIdObj, dmIdObj);
+ return ok("{ \"success\":\"cleared DM history stats for " +
+ mdName + "/" + maName + "/" + mepId + "/" + dmId + "\" }").build();
+ } catch (CfmConfigException e) {
+ log.error("Clear history stats for DM {} failed because of exception {}",
+ mdName + "/" + maName + "/" + mepId + "/" + dmId, e.toString());
+ return Response.serverError().entity("{ \"failure\":\"" +
+ e.toString() + "\" }").build();
+ }
+ }
+}
diff --git a/apps/cfm/src/main/java/org/onosproject/soam/impl/LmWebResource.java b/apps/cfm/src/main/java/org/onosproject/soam/impl/LmWebResource.java
new file mode 100644
index 0000000..d889668
--- /dev/null
+++ b/apps/cfm/src/main/java/org/onosproject/soam/impl/LmWebResource.java
@@ -0,0 +1,256 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.soam.impl;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import org.onosproject.codec.JsonCodec;
+import org.onosproject.incubator.net.l2monitoring.cfm.Mep;
+import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MaIdCharStr;
+import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MaIdShort;
+import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MdId;
+import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MdIdCharStr;
+import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MepId;
+import org.onosproject.incubator.net.l2monitoring.cfm.service.CfmConfigException;
+import org.onosproject.incubator.net.l2monitoring.cfm.service.CfmMepService;
+import org.onosproject.incubator.net.l2monitoring.soam.SoamConfigException;
+import org.onosproject.incubator.net.l2monitoring.soam.SoamId;
+import org.onosproject.incubator.net.l2monitoring.soam.SoamService;
+import org.onosproject.incubator.net.l2monitoring.soam.loss.LossMeasurementCreate;
+import org.onosproject.incubator.net.l2monitoring.soam.loss.LossMeasurementEntry;
+import org.onosproject.rest.AbstractWebResource;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+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 java.io.IOException;
+import java.io.InputStream;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.Collection;
+
+/**
+ * Layer 2 SOAM Loss Measurement web resource.
+ */
+@Path("md/{md_name}/ma/{ma_name}/mep/{mep_id}/lm")
+public class LmWebResource extends AbstractWebResource {
+ private final Logger log = LoggerFactory.getLogger(getClass());
+
+ /**
+ * Get all LMs for a Mep.
+ *
+ * @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 a Mep belonging to the MA
+ * @return 200 OK with a list of LMs or 500 on error
+ */
+ @GET
+ @Produces(MediaType.APPLICATION_JSON)
+ @Consumes(MediaType.APPLICATION_JSON)
+ public Response getAllLmsForMep(@PathParam("md_name") String mdName,
+ @PathParam("ma_name") String maName,
+ @PathParam("mep_id") short mepId) {
+
+ log.debug("GET all LMs called for MEP {}", mdName + "/" + maName + "/" + mepId);
+ try {
+ MdId mdId = MdIdCharStr.asMdId(mdName);
+ MaIdShort maId = MaIdCharStr.asMaId(maName);
+ MepId mepIdObj = MepId.valueOf(mepId);
+ Collection<LossMeasurementEntry> lmCollection =
+ get(SoamService.class).getAllLms(mdId, maId, mepIdObj);
+ ArrayNode an = mapper().createArrayNode();
+ an.add(codec(LossMeasurementEntry.class).encode(lmCollection, this));
+ return ok(mapper().createObjectNode().set("lms", an)).build();
+ } catch (CfmConfigException | SoamConfigException e) {
+ log.error("Get LM {} failed because of exception {}",
+ mdName + "/" + maName + "/" + mepId, e.toString());
+ return Response.serverError().entity("{ \"failure\":\"" + e.toString() + "\" }").build();
+ }
+ }
+
+ /**
+ * Get LM by MD name, MA name, Mep Id and Dm 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
+ * @param lmId The Id of the LM
+ * @return 200 OK with details of the LM or 500 on error
+ */
+ @GET
+ @Path("{lm_id}")
+ @Produces(MediaType.APPLICATION_JSON)
+ @Consumes(MediaType.APPLICATION_JSON)
+ public Response getLm(@PathParam("md_name") String mdName,
+ @PathParam("ma_name") String maName,
+ @PathParam("mep_id") short mepId, @PathParam("lm_id") int lmId) {
+ log.debug("GET called for LM {}", mdName + "/" + maName + "/" + mepId + "/" + lmId);
+ try {
+ MdId mdId = MdIdCharStr.asMdId(mdName);
+ MaIdShort maId = MaIdCharStr.asMaId(maName);
+ MepId mepIdObj = MepId.valueOf(mepId);
+ SoamId lmIdObj = SoamId.valueOf(lmId);
+ LossMeasurementEntry lm = get(SoamService.class)
+ .getLm(mdId, maId, mepIdObj, lmIdObj);
+ if (lm == null) {
+ return Response.serverError().entity("{ \"failure\":\"LM " +
+ mdName + "/" + maName + "/" + mepId + "/" + lmId + " not found\" }").build();
+ }
+ ObjectNode node = mapper().createObjectNode();
+ node.set("lm", codec(LossMeasurementEntry.class).encode(lm, this));
+ return ok(node).build();
+ } catch (CfmConfigException | SoamConfigException e) {
+ log.error("Get LM {} failed because of exception {}",
+ mdName + "/" + maName + "/" + mepId + "/" + lmId, e.toString());
+ return Response.serverError().entity("{ \"failure\":\"" + e.toString() + "\" }").build();
+ }
+ }
+
+ /**
+ * Abort LM by MD name, MA name, Mep Id and LM Id.
+ * In the API the measurement is aborted, and not truly deleted. It still
+ * remains so that its results may be read. Depending on the device it will
+ * get overwritten on the creation of subsequent measurements.
+ * Use clear stats to delete old results measurements.
+ *
+ * @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
+ * @param lmId The Id of the LM
+ * @return 200 OK or 304 if not found, or 500 on error
+ */
+ @DELETE
+ @Path("{lm_id}")
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ public Response abortLm(@PathParam("md_name") String mdName,
+ @PathParam("ma_name") String maName,
+ @PathParam("mep_id") short mepId,
+ @PathParam("lm_id") int lmId) {
+ log.debug("DELETE called for LM {}", mdName + "/" + maName + "/" + mepId + "/" + lmId);
+ try {
+ MdId mdId = MdIdCharStr.asMdId(mdName);
+ MaIdShort maId = MaIdCharStr.asMaId(maName);
+ MepId mepIdObj = MepId.valueOf(mepId);
+ SoamId lmIdObj = SoamId.valueOf(lmId);
+
+ get(SoamService.class).abortLm(mdId, maId, mepIdObj, lmIdObj);
+ return ok("{ \"success\":\"deleted (aborted) " + mdName + "/" + maName +
+ "/" + mepId + "/" + lmId + "\" }").build();
+ } catch (CfmConfigException e) {
+ log.error("Delete (abort) LM {} failed because of exception {}",
+ mdName + "/" + maName + "/" + mepId + "/" + lmId, e.toString());
+ return Response.serverError().entity("{ \"failure\":\"" + e.toString() + "\" }").build();
+ }
+ }
+
+ /**
+ * Create LM with MD name, MA name, Mep id and LM Json.
+ *
+ * @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 belonging to the MEP
+ * @param input A JSON formatted input stream specifying the LM parameters
+ * @return 201 Created or 304 if already exists or 500 on error
+ */
+ @POST
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ public Response createLm(@PathParam("md_name") String mdName,
+ @PathParam("ma_name") String maName,
+ @PathParam("mep_id") short mepId, InputStream input) {
+ log.debug("POST called to Create Lm");
+ try {
+ MdId mdId = MdIdCharStr.asMdId(mdName);
+ MaIdShort maId = MaIdCharStr.asMaId(maName);
+ MepId mepIdObj = MepId.valueOf(mepId);
+
+ Mep mep = get(CfmMepService.class).getMep(mdId, maId, mepIdObj);
+ if (mep == null) {
+ return Response.serverError().entity("{ \"failure\":\"mep " +
+ mdName + "/" + maName + "/" + mepId + " does not exist\" }").build();
+ }
+
+ ObjectMapper mapper = new ObjectMapper();
+ JsonNode cfg = mapper.readTree(input);
+ JsonCodec<LossMeasurementCreate> lmCodec = codec(LossMeasurementCreate.class);
+
+ LossMeasurementCreate lm = lmCodec.decode((ObjectNode) cfg, this);
+ get(SoamService.class).createLm(mdId, maId, mepIdObj, lm);
+ return Response
+ .created(new URI("md/" + mdName + "/ma/" + maName + "/mep/" +
+ mepId + "/lm"))
+ .entity("{ \"success\":\"lm " + mdName + "/" + maName + "/" +
+ mepId + " created\" }")
+ .build();
+ } catch (CfmConfigException | SoamConfigException | IllegalArgumentException |
+ IOException | URISyntaxException e) {
+ log.error("Create LM on " + mdName + "/" + maName + "/" + mepId +
+ " failed because of exception {}", e.toString());
+ return Response.serverError()
+ .entity("{ \"failure\":\"" + e.toString() + "\" }")
+ .build();
+ }
+ }
+
+ /**
+ * Clear LM history stats by MD name, MA name, Mep Id and LM 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
+ * @param lmId The Id of the LM
+ * @return 200 OK or 304 if not found, or 500 on error
+ */
+ @PUT
+ @Path("{lm_id}/clear-history")
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ public Response clearLmHistory(@PathParam("md_name") String mdName,
+ @PathParam("ma_name") String maName,
+ @PathParam("mep_id") short mepId,
+ @PathParam("lm_id") int lmId) {
+ log.debug("clear-history called for LM {}", mdName + "/" + maName +
+ "/" + mepId + "/" + lmId);
+ try {
+ MdId mdId = MdIdCharStr.asMdId(mdName);
+ MaIdShort maId = MaIdCharStr.asMaId(maName);
+ MepId mepIdObj = MepId.valueOf(mepId);
+ SoamId lmIdObj = SoamId.valueOf(lmId);
+
+ get(SoamService.class).clearDelayHistoryStats(mdId, maId, mepIdObj, lmIdObj);
+ return ok("{ \"success\":\"cleared LM history stats for " +
+ mdName + "/" + maName + "/" + mepId + "/" + lmId + "\" }").build();
+ } catch (CfmConfigException e) {
+ log.error("Clear history stats for LM {} failed because of exception {}",
+ mdName + "/" + maName + "/" + mepId + "/" + lmId, e.toString());
+ return Response.serverError().entity("{ \"failure\":\"" +
+ e.toString() + "\" }").build();
+ }
+ }
+
+}
diff --git a/apps/cfm/src/main/java/org/onosproject/soam/impl/package-info.java b/apps/cfm/src/main/java/org/onosproject/soam/impl/package-info.java
new file mode 100644
index 0000000..370030f
--- /dev/null
+++ b/apps/cfm/src/main/java/org/onosproject/soam/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 SOAM.
+ */
+package org.onosproject.soam.impl;
\ No newline at end of file
diff --git a/apps/cfm/src/main/java/org/onosproject/soam/web/DelayMeasurementStatCodec.java b/apps/cfm/src/main/java/org/onosproject/soam/web/DelayMeasurementStatCodec.java
new file mode 100644
index 0000000..7bd0d7a
--- /dev/null
+++ b/apps/cfm/src/main/java/org/onosproject/soam/web/DelayMeasurementStatCodec.java
@@ -0,0 +1,205 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.soam.web;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.time.Duration;
+import java.util.Map;
+
+import org.onosproject.codec.CodecContext;
+import org.onosproject.codec.JsonCodec;
+import org.onosproject.incubator.net.l2monitoring.soam.delay.DelayMeasurementStat;
+
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+
+/**
+ * Encode and decode to/from JSON to DelayMeasurementStat object.
+ */
+public class DelayMeasurementStatCodec extends JsonCodec<DelayMeasurementStat> {
+
+ private static final String LOWER_LIMIT = "lowerLimit";
+ private static final String COUNT = "count";
+ private static final String BINS = "bins";
+ private static final String SOAM_PDUS_SENT = "soamPdusSent";
+ private static final String SOAM_PDUS_RECEIVED = "soamPdusReceived";
+
+ @Override
+ public ObjectNode encode(DelayMeasurementStat dmStat, CodecContext context) {
+ checkNotNull(dmStat, "DM stat cannot be null");
+ ObjectNode result = context.mapper().createObjectNode()
+ .put("elapsedTime", dmStat.elapsedTime().toString())
+ .put("suspectStatus", String.valueOf(dmStat.suspectStatus()));
+
+ if (dmStat.frameDelayTwoWayMin() != null) {
+ result = result.put("frameDelayTwoWayMin",
+ dmStat.frameDelayTwoWayMin().toString());
+ }
+ if (dmStat.frameDelayTwoWayMax() != null) {
+ result = result.put("frameDelayTwoWayMax",
+ dmStat.frameDelayTwoWayMax().toString());
+ }
+ if (dmStat.frameDelayTwoWayAvg() != null) {
+ result = result.put("frameDelayTwoWayAvg",
+ dmStat.frameDelayTwoWayAvg().toString());
+ }
+ if (dmStat.frameDelayTwoWayBins() != null) {
+ result = (ObjectNode) result.set("frameDelayTwoWayBins",
+ encode(dmStat.frameDelayTwoWayBins(), context));
+ }
+ if (dmStat.frameDelayForwardMin() != null) {
+ result = result.put("frameDelayForwardMin",
+ dmStat.frameDelayForwardMin().toString());
+ }
+ if (dmStat.frameDelayForwardMax() != null) {
+ result = result.put("frameDelayForwardMax",
+ dmStat.frameDelayForwardMax().toString());
+ }
+ if (dmStat.frameDelayForwardAvg() != null) {
+ result = result.put("frameDelayForwardAvg",
+ dmStat.frameDelayForwardAvg().toString());
+ }
+ if (dmStat.frameDelayForwardBins() != null) {
+ result = (ObjectNode) result.set("frameDelayForwardBins",
+ encode(dmStat.frameDelayForwardBins(), context));
+ }
+ if (dmStat.frameDelayBackwardMin() != null) {
+ result = result.put("frameDelayBackwardMin",
+ dmStat.frameDelayBackwardMin().toString());
+ }
+ if (dmStat.frameDelayBackwardMax() != null) {
+ result = result.put("frameDelayBackwardMax",
+ dmStat.frameDelayBackwardMax().toString());
+ }
+ if (dmStat.frameDelayBackwardAvg() != null) {
+ result = result.put("frameDelayBackwardAvg",
+ dmStat.frameDelayBackwardAvg().toString());
+ }
+ if (dmStat.frameDelayBackwardBins() != null) {
+ result = (ObjectNode) result.set("frameDelayBackwardBins",
+ encode(dmStat.frameDelayBackwardBins(), context));
+ }
+ if (dmStat.interFrameDelayVariationTwoWayMin() != null) {
+ result = result.put("interFrameDelayVariationTwoWayMin",
+ dmStat.interFrameDelayVariationTwoWayMin().toString());
+ }
+ if (dmStat.interFrameDelayVariationTwoWayMax() != null) {
+ result.put("interFrameDelayVariationTwoWayMax",
+ dmStat.interFrameDelayVariationTwoWayMax().toString());
+ }
+ if (dmStat.interFrameDelayVariationTwoWayAvg() != null) {
+ result.put("interFrameDelayVariationTwoWayAvg",
+ dmStat.interFrameDelayVariationTwoWayAvg().toString());
+ }
+ if (dmStat.interFrameDelayVariationTwoWayBins() != null) {
+ result = (ObjectNode) result.set("interFrameDelayVariationTwoWayBins",
+ encode(dmStat.interFrameDelayVariationTwoWayBins(), context));
+ }
+ if (dmStat.interFrameDelayVariationForwardMin() != null) {
+ result = result.put("interFrameDelayVariationForwardMin",
+ dmStat.interFrameDelayVariationForwardMin().toString());
+ }
+ if (dmStat.interFrameDelayVariationForwardMax() != null) {
+ result = result.put("interFrameDelayVariationForwardMax",
+ dmStat.interFrameDelayVariationForwardMax().toString());
+ }
+ if (dmStat.interFrameDelayVariationForwardAvg() != null) {
+ result = result.put("interFrameDelayVariationForwardAvg",
+ dmStat.interFrameDelayVariationForwardAvg().toString());
+ }
+ if (dmStat.interFrameDelayVariationForwardBins() != null) {
+ result = (ObjectNode) result.set("interFrameDelayVariationForwardBins",
+ encode(dmStat.interFrameDelayVariationForwardBins(), context));
+ }
+ if (dmStat.interFrameDelayVariationBackwardMin() != null) {
+ result = result.put("interFrameDelayVariationBackwardMin",
+ dmStat.interFrameDelayVariationBackwardMin().toString());
+ }
+ if (dmStat.interFrameDelayVariationBackwardMax() != null) {
+ result = result.put("interFrameDelayVariationBackwardMax",
+ dmStat.interFrameDelayVariationBackwardMax().toString());
+ }
+ if (dmStat.interFrameDelayVariationBackwardAvg() != null) {
+ result = result.put("interFrameDelayVariationBackwardAvg",
+ dmStat.interFrameDelayVariationBackwardAvg().toString());
+ }
+ if (dmStat.interFrameDelayVariationBackwardBins() != null) {
+ result = (ObjectNode) result.set("interFrameDelayVariationBackwardBins",
+ encode(dmStat.interFrameDelayVariationBackwardBins(), context));
+ }
+ if (dmStat.frameDelayRangeTwoWayMax() != null) {
+ result = result.put("frameDelayRangeTwoWayMax",
+ dmStat.frameDelayRangeTwoWayMax().toString());
+ }
+ if (dmStat.frameDelayRangeTwoWayAvg() != null) {
+ result = result.put("frameDelayRangeTwoWayAvg",
+ dmStat.frameDelayRangeTwoWayAvg().toString());
+ }
+ if (dmStat.frameDelayRangeTwoWayBins() != null) {
+ result = (ObjectNode) result.set("frameDelayRangeTwoWayBins",
+ encode(dmStat.frameDelayRangeTwoWayBins(), context));
+ }
+ if (dmStat.frameDelayRangeForwardMax() != null) {
+ result = result.put("frameDelayRangeForwardMax",
+ dmStat.frameDelayRangeForwardMax().toString());
+ }
+ if (dmStat.frameDelayRangeForwardAvg() != null) {
+ result = result.put("frameDelayRangeForwardAvg",
+ dmStat.frameDelayRangeForwardAvg().toString());
+ }
+ if (dmStat.frameDelayRangeForwardBins() != null) {
+ result = (ObjectNode) result.set("frameDelayRangeForwardBins",
+ encode(dmStat.frameDelayRangeForwardBins(), context));
+ }
+ if (dmStat.frameDelayRangeBackwardMax() != null) {
+ result = result.put("frameDelayRangeBackwardMax",
+ dmStat.frameDelayRangeBackwardMax().toString());
+ }
+ if (dmStat.frameDelayRangeBackwardAvg() != null) {
+ result = result.put("frameDelayRangeBackwardAvg",
+ dmStat.frameDelayRangeBackwardAvg().toString());
+ }
+ if (dmStat.frameDelayRangeBackwardBins() != null) {
+ result = (ObjectNode) result.set("frameDelayRangeBackwardBins",
+ encode(dmStat.frameDelayRangeBackwardBins(), context));
+ }
+
+ if (dmStat.soamPdusReceived() != null) {
+ result = result.put(SOAM_PDUS_RECEIVED, dmStat.soamPdusReceived().toString());
+ }
+
+ if (dmStat.soamPdusSent() != null) {
+ result = result.put(SOAM_PDUS_SENT, dmStat.soamPdusSent().toString());
+ }
+
+ return result;
+ }
+
+ private ObjectNode encode(Map<Duration, Integer> bins, CodecContext context) {
+ checkNotNull(bins, "Bins cannot be null");
+ ArrayNode binsResult = context.mapper().createArrayNode();
+ bins.keySet().forEach(lwrLimit -> binsResult.add(encode(lwrLimit, bins.get(lwrLimit), context)));
+
+ return (ObjectNode) context.mapper().createObjectNode().set(BINS, binsResult);
+ }
+
+ private ObjectNode encode(Duration duration, Integer count, CodecContext context) {
+ return context.mapper().createObjectNode()
+ .put(LOWER_LIMIT, duration.toString())
+ .put(COUNT, count);
+ }
+}
diff --git a/apps/cfm/src/main/java/org/onosproject/soam/web/DelayMeasurementStatCurrentCodec.java b/apps/cfm/src/main/java/org/onosproject/soam/web/DelayMeasurementStatCurrentCodec.java
new file mode 100644
index 0000000..a79cd90
--- /dev/null
+++ b/apps/cfm/src/main/java/org/onosproject/soam/web/DelayMeasurementStatCurrentCodec.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.soam.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.soam.delay.DelayMeasurementStatCurrent;
+
+import com.fasterxml.jackson.databind.node.ObjectNode;
+
+/**
+ * Encode and decode to/from JSON to DelayMeasurementStatCurrent object.
+ */
+public class DelayMeasurementStatCurrentCodec extends JsonCodec<DelayMeasurementStatCurrent> {
+
+ @Override
+ public ObjectNode encode(DelayMeasurementStatCurrent dmCurrent, CodecContext context) {
+ checkNotNull(dmCurrent, "DM current cannot be null");
+ ObjectNode result = context.mapper().createObjectNode()
+ .put("startTime", dmCurrent.startTime().toString())
+ .put("elapsedTime", dmCurrent.elapsedTime().toString());
+
+ ObjectNode resultAbstract = new DelayMeasurementStatCodec().encode(dmCurrent, context);
+ result.setAll(resultAbstract);
+
+ return result;
+ }
+}
diff --git a/apps/cfm/src/main/java/org/onosproject/soam/web/DelayMeasurementStatHistoryCodec.java b/apps/cfm/src/main/java/org/onosproject/soam/web/DelayMeasurementStatHistoryCodec.java
new file mode 100644
index 0000000..8901958
--- /dev/null
+++ b/apps/cfm/src/main/java/org/onosproject/soam/web/DelayMeasurementStatHistoryCodec.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.soam.web;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import org.onosproject.codec.CodecContext;
+import org.onosproject.codec.JsonCodec;
+import org.onosproject.incubator.net.l2monitoring.soam.delay.DelayMeasurementStatHistory;
+
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+
+/**
+ * Encode and decode to/from JSON to DelayMeasurementStatHistory object.
+ */
+public class DelayMeasurementStatHistoryCodec extends JsonCodec<DelayMeasurementStatHistory> {
+
+ @Override
+ public ObjectNode encode(DelayMeasurementStatHistory dmHistory, CodecContext context) {
+ checkNotNull(dmHistory, "DM history cannot be null");
+ ObjectNode result = context.mapper().createObjectNode()
+ .put("historyId", String.valueOf(dmHistory.historyStatsId()))
+ .put("endTime", dmHistory.endTime().toString());
+ ObjectNode resultAbstract = new DelayMeasurementStatCodec().encode(dmHistory, context);
+ result.setAll(resultAbstract);
+ return result;
+ }
+
+ @Override
+ public ArrayNode encode(Iterable<DelayMeasurementStatHistory> historyEntities, CodecContext context) {
+ ArrayNode an = context.mapper().createArrayNode();
+ historyEntities.forEach(history -> an.add(encode(history, context)));
+ return an;
+ }
+}
diff --git a/apps/cfm/src/main/java/org/onosproject/soam/web/DmCreateCodec.java b/apps/cfm/src/main/java/org/onosproject/soam/web/DmCreateCodec.java
new file mode 100644
index 0000000..8743eb4
--- /dev/null
+++ b/apps/cfm/src/main/java/org/onosproject/soam/web/DmCreateCodec.java
@@ -0,0 +1,174 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.soam.web;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.onlab.util.Tools.nullIsIllegal;
+
+import java.time.Duration;
+
+import org.onosproject.codec.CodecContext;
+import org.onosproject.codec.JsonCodec;
+import org.onosproject.incubator.net.l2monitoring.cfm.Mep.Priority;
+import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MepId;
+import org.onosproject.incubator.net.l2monitoring.soam.SoamConfigException;
+import org.onosproject.incubator.net.l2monitoring.soam.StartTime;
+import org.onosproject.incubator.net.l2monitoring.soam.StopTime;
+import org.onosproject.incubator.net.l2monitoring.soam.delay.DefaultDelayMeasurementCreate;
+import org.onosproject.incubator.net.l2monitoring.soam.delay.DelayMeasurementCreate;
+import org.onosproject.incubator.net.l2monitoring.soam.delay.DelayMeasurementCreate.DmCreateBuilder;
+import org.onosproject.incubator.net.l2monitoring.soam.delay.DelayMeasurementCreate.DmType;
+import org.onosproject.incubator.net.l2monitoring.soam.delay.DelayMeasurementCreate.MeasurementOption;
+import org.onosproject.incubator.net.l2monitoring.soam.delay.DelayMeasurementCreate.Version;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+
+/**
+ * Encode and decode to/from JSON to DelayMeasurementCreate object.
+ */
+public class DmCreateCodec extends JsonCodec<DelayMeasurementCreate> {
+
+ private static final String VERSION = "version";
+ private static final String DM = "dm";
+ private static final String DM_CFG_TYPE = "dmCfgType";
+ private static final String DMDMM = "DMDMM";
+ private static final String REMOTE_MEP_ID = "remoteMepId";
+ private static final String PRIORITY = "priority";
+ private static final String MEASUREMENTS_ENABLED = "measurementsEnabled";
+ private static final String BINS_PER_FD_INTERVAL = "binsPerFdInterval";
+ private static final String BINS_PER_IFDV_INTERVAL = "binsPerIfdvInterval";
+ private static final String IFDV_SELECTION_OFFSET = "ifdvSelectionOffset";
+ private static final String BINS_PER_FDR_INTERVAL = "binsPerFdrInterval";
+ private static final String FRAME_SIZE = "frameSize";
+ private static final String MESSAGE_PERIOD_MS = "messagePeriodMs";
+ private static final String MEASUREMENT_INTERVAL_MINS = "measurementIntervalMins";
+ private static final String ALIGN_MEASUREMENT_INTERVALS = "alignMeasurementIntervals";
+ private static final String ALIGN_MEASUREMENT_OFFSET_MINS = "alignMeasurementOffsetMins";
+ private static final String START_TIME = "startTime";
+ private static final String STOP_TIME = "stopTime";
+
+ @Override
+ public DelayMeasurementCreate decode(ObjectNode json,
+ CodecContext context) {
+
+ if (json == null || !json.isObject()) {
+ return null;
+ }
+
+ JsonNode dmNode = json.get(DM);
+ Version version = Version.Y17312011;
+ if (dmNode.get(VERSION) != null) {
+ version = Version.valueOf(dmNode.get(VERSION).asText());
+ }
+ DmType dmCfgType = DmType.DMDMM;
+ if (dmNode.get(DM_CFG_TYPE) != null) {
+ dmCfgType = DmType.valueOf(dmNode.get(DM_CFG_TYPE).asText(DMDMM));
+ }
+ MepId remoteMepId = MepId.valueOf(
+ nullIsIllegal(dmNode.get(REMOTE_MEP_ID), REMOTE_MEP_ID + " is required")
+ .shortValue());
+ Priority prio = Priority.valueOf(nullIsIllegal(dmNode.get(PRIORITY),
+ PRIORITY + " is required in the format 'PRIOn'").asText());
+
+ try {
+ DmCreateBuilder builder = DefaultDelayMeasurementCreate
+ .builder(dmCfgType, version, remoteMepId, prio);
+
+ if (dmNode.get(MEASUREMENTS_ENABLED) != null) {
+ context.codec(MeasurementOption.class)
+ .decode((ArrayNode) (dmNode.get(MEASUREMENTS_ENABLED)), context)
+ .forEach(builder::addToMeasurementsEnabled);
+ }
+
+ if (dmNode.get(BINS_PER_FD_INTERVAL) != null) {
+ builder = builder.binsPerFdInterval(
+ (short) dmNode.get(BINS_PER_FD_INTERVAL).asInt());
+ }
+ if (dmNode.get(BINS_PER_IFDV_INTERVAL) != null) {
+ builder = builder.binsPerIfdvInterval(
+ (short) dmNode.get(BINS_PER_IFDV_INTERVAL).asInt());
+ }
+ if (dmNode.get(IFDV_SELECTION_OFFSET) != null) {
+ builder = builder.ifdvSelectionOffset(
+ (short) dmNode.get(IFDV_SELECTION_OFFSET).asInt());
+ }
+ if (dmNode.get(BINS_PER_FDR_INTERVAL) != null) {
+ builder = builder.binsPerFdrInterval(
+ (short) dmNode.get(BINS_PER_FDR_INTERVAL).asInt());
+ }
+ if (dmNode.get(FRAME_SIZE) != null) {
+ builder = (DmCreateBuilder) builder.frameSize(
+ (short) dmNode.get(FRAME_SIZE).asInt());
+ }
+ if (dmNode.get(MESSAGE_PERIOD_MS) != null) {
+ builder = (DmCreateBuilder) builder.messagePeriod(Duration.ofMillis(
+ dmNode.get(MESSAGE_PERIOD_MS).asInt()));
+ }
+ if (dmNode.get(MEASUREMENT_INTERVAL_MINS) != null) {
+ builder = (DmCreateBuilder) builder.measurementInterval(
+ Duration.ofMinutes(
+ dmNode.get(MEASUREMENT_INTERVAL_MINS).asInt()));
+ }
+ if (dmNode.get(ALIGN_MEASUREMENT_INTERVALS) != null) {
+ builder = (DmCreateBuilder) builder.alignMeasurementIntervals(
+ dmNode.get(ALIGN_MEASUREMENT_INTERVALS).asBoolean());
+ }
+ if (dmNode.get(ALIGN_MEASUREMENT_OFFSET_MINS) != null) {
+ builder = (DmCreateBuilder) builder.alignMeasurementOffset(Duration.ofMinutes(
+ dmNode.get(ALIGN_MEASUREMENT_OFFSET_MINS).asInt()));
+ }
+ if (dmNode.get(START_TIME) != null) {
+ builder = (DmCreateBuilder) builder.startTime(context.codec(StartTime.class)
+ .decode((ObjectNode) dmNode.get(START_TIME), context));
+ }
+ if (dmNode.get(STOP_TIME) != null) {
+ builder = (DmCreateBuilder) builder.stopTime(context.codec(StopTime.class)
+ .decode((ObjectNode) dmNode.get(STOP_TIME), context));
+ }
+
+ return builder.build();
+ } catch (SoamConfigException e) {
+ throw new IllegalArgumentException(e);
+ }
+ }
+
+ @Override
+ public ObjectNode encode(DelayMeasurementCreate dm, CodecContext context) {
+ checkNotNull(dm, "DM cannot be null");
+ ObjectNode result = context.mapper().createObjectNode()
+ .put(DM_CFG_TYPE, dm.dmCfgType().name())
+ .put(VERSION, dm.version().name())
+ .put(REMOTE_MEP_ID, dm.remoteMepId().id())
+ .put(PRIORITY, dm.priority().name());
+
+ if (dm.measurementsEnabled() != null) {
+ result.set(MEASUREMENTS_ENABLED, new DmMeasurementOptionCodec()
+ .encode(dm.measurementsEnabled(), context));
+ }
+
+ if (dm.messagePeriod() != null) {
+ result.put(MESSAGE_PERIOD_MS, dm.messagePeriod().toMillis());
+ }
+ if (dm.frameSize() != null) {
+ result.put(FRAME_SIZE, dm.frameSize());
+ }
+
+
+ return result;
+ }
+}
diff --git a/apps/cfm/src/main/java/org/onosproject/soam/web/DmEntryCodec.java b/apps/cfm/src/main/java/org/onosproject/soam/web/DmEntryCodec.java
new file mode 100644
index 0000000..ce851d6
--- /dev/null
+++ b/apps/cfm/src/main/java/org/onosproject/soam/web/DmEntryCodec.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.soam.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.soam.delay.DelayMeasurementEntry;
+
+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 DelayMeasurementEntry object.
+ */
+public class DmEntryCodec extends JsonCodec<DelayMeasurementEntry> {
+
+ private static final String DM_ID = "dmId";
+ private static final String SESSION_STATUS = "sessionStatus";
+ private static final String FRAME_DELAY_TWO_WAY = "frameDelayTwoWay";
+ private static final String FRAME_DELAY_FORWARD = "frameDelayForward";
+ private static final String FRAME_DELAY_BACKWARD = "frameDelayBackward";
+ private static final String INTER_FRAME_DELAY_VARIATION_TWO_WAY = "interFrameDelayVariationTwoWay";
+ private static final String INTER_FRAME_DELAY_VARIATION_FORWARD = "interFrameDelayVariationForward";
+ private static final String INTER_FRAME_DELAY_VARIATION_BACKWARD = "interFrameDelayVariationBackward";
+ private static final String CURRENT = "current";
+ private static final String HISTORIC = "historic";
+
+ @Override
+ public ObjectNode encode(DelayMeasurementEntry dm, CodecContext context) {
+ checkNotNull(dm, "DM cannot be null");
+ ObjectNode result = context.mapper().createObjectNode()
+ .put(DM_ID, dm.dmId().toString());
+
+ if (dm.sessionStatus() != null) {
+ result.put(SESSION_STATUS, dm.sessionStatus().name());
+ }
+ if (dm.frameDelayTwoWay() != null) {
+ result.put(FRAME_DELAY_TWO_WAY, dm.frameDelayTwoWay().toString());
+ }
+ if (dm.frameDelayForward() != null) {
+ result.put(FRAME_DELAY_FORWARD, dm.frameDelayForward().toString());
+ }
+ if (dm.frameDelayBackward() != null) {
+ result.put(FRAME_DELAY_BACKWARD, dm.frameDelayBackward().toString());
+ }
+ if (dm.interFrameDelayVariationTwoWay() != null) {
+ result.put(INTER_FRAME_DELAY_VARIATION_TWO_WAY,
+ dm.interFrameDelayVariationTwoWay().toString());
+ }
+ if (dm.interFrameDelayVariationForward() != null) {
+ result.put(INTER_FRAME_DELAY_VARIATION_FORWARD,
+ dm.interFrameDelayVariationForward().toString());
+ }
+ if (dm.interFrameDelayVariationBackward() != null) {
+ result.put(INTER_FRAME_DELAY_VARIATION_BACKWARD,
+ dm.interFrameDelayVariationBackward().toString());
+ }
+
+ ObjectNode dmAttrs = new DmCreateCodec().encode(dm, context);
+ Iterator<Entry<String, JsonNode>> elements = dmAttrs.fields();
+ while (elements.hasNext()) {
+ Entry<String, JsonNode> element = elements.next();
+ result.set(element.getKey(), element.getValue());
+ }
+
+ if (dm.currentResult() != null) {
+ result.set(CURRENT, new DelayMeasurementStatCurrentCodec()
+ .encode(dm.currentResult(), context));
+ }
+
+ if (dm.historicalResults() != null) {
+ result.set(HISTORIC, new DelayMeasurementStatHistoryCodec()
+ .encode(dm.historicalResults(), context));
+ }
+
+ return result;
+ }
+
+ @Override
+ public ArrayNode encode(Iterable<DelayMeasurementEntry> dmEntities, CodecContext context) {
+ ArrayNode an = context.mapper().createArrayNode();
+ dmEntities.forEach(dm -> an.add(encode(dm, context)));
+ return an;
+ }
+}
diff --git a/apps/cfm/src/main/java/org/onosproject/soam/web/DmMeasurementOptionCodec.java b/apps/cfm/src/main/java/org/onosproject/soam/web/DmMeasurementOptionCodec.java
new file mode 100644
index 0000000..8c8a5a0
--- /dev/null
+++ b/apps/cfm/src/main/java/org/onosproject/soam/web/DmMeasurementOptionCodec.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.soam.web;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.onosproject.codec.CodecContext;
+import org.onosproject.codec.JsonCodec;
+import org.onosproject.incubator.net.l2monitoring.soam.delay.DelayMeasurementCreate.MeasurementOption;
+
+import com.fasterxml.jackson.databind.node.ArrayNode;
+
+/**
+ * Encode and decode to/from JSON to MeasurementOption object.
+ */
+public class DmMeasurementOptionCodec extends JsonCodec<MeasurementOption> {
+
+ @Override
+ public ArrayNode encode(Iterable<MeasurementOption> entities,
+ CodecContext context) {
+ ArrayNode an = context.mapper().createArrayNode();
+ entities.forEach(node -> an.add(node.name()));
+
+ return an;
+ }
+
+ @Override
+ public List<MeasurementOption> decode(ArrayNode json,
+ CodecContext context) {
+ if (json == null) {
+ return null;
+ }
+ List<MeasurementOption> moList = new ArrayList<>();
+ json.forEach(node -> moList.add(MeasurementOption.valueOf(node.asText())));
+ return moList;
+ }
+
+}
diff --git a/apps/cfm/src/main/java/org/onosproject/soam/web/LmCounterOptionCodec.java b/apps/cfm/src/main/java/org/onosproject/soam/web/LmCounterOptionCodec.java
new file mode 100644
index 0000000..9e3e40e
--- /dev/null
+++ b/apps/cfm/src/main/java/org/onosproject/soam/web/LmCounterOptionCodec.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.soam.web;
+
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import org.onosproject.codec.CodecContext;
+import org.onosproject.codec.JsonCodec;
+import org.onosproject.incubator.net.l2monitoring.soam.loss.LossMeasurementCreate.CounterOption;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Encode and decode to/from JSON to CounterOption object.
+ */
+public class LmCounterOptionCodec extends JsonCodec<CounterOption> {
+
+ @Override
+ public ArrayNode encode(Iterable<CounterOption> entities,
+ CodecContext context) {
+ ArrayNode an = context.mapper().createArrayNode();
+ entities.forEach(node -> an.add(node.name()));
+
+ return an;
+ }
+
+ @Override
+ public List<CounterOption> decode(ArrayNode json,
+ CodecContext context) {
+ if (json == null) {
+ return null;
+ }
+ List<CounterOption> moList = new ArrayList<>();
+ json.forEach(node -> moList.add(CounterOption.valueOf(node.asText())));
+ return moList;
+ }
+
+}
diff --git a/apps/cfm/src/main/java/org/onosproject/soam/web/LmCreateCodec.java b/apps/cfm/src/main/java/org/onosproject/soam/web/LmCreateCodec.java
new file mode 100644
index 0000000..e88fb2f
--- /dev/null
+++ b/apps/cfm/src/main/java/org/onosproject/soam/web/LmCreateCodec.java
@@ -0,0 +1,196 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.soam.web;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import org.onosproject.codec.CodecContext;
+import org.onosproject.codec.JsonCodec;
+import org.onosproject.incubator.net.l2monitoring.cfm.Mep.Priority;
+import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MepId;
+import org.onosproject.incubator.net.l2monitoring.soam.MilliPct;
+import org.onosproject.incubator.net.l2monitoring.soam.SoamConfigException;
+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.Version;
+import org.onosproject.incubator.net.l2monitoring.soam.loss.DefaultLmCreate;
+import org.onosproject.incubator.net.l2monitoring.soam.loss.LossMeasurementCreate;
+import org.onosproject.incubator.net.l2monitoring.soam.loss.LossMeasurementThreshold;
+
+import java.time.Duration;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.onlab.util.Tools.nullIsIllegal;
+import static org.onosproject.incubator.net.l2monitoring.soam.loss.LossMeasurementCreate.CounterOption;
+import static org.onosproject.incubator.net.l2monitoring.soam.loss.LossMeasurementCreate.LmType;
+import static org.onosproject.incubator.net.l2monitoring.soam.loss.LossMeasurementCreate.LmCreateBuilder;
+
+/**
+ * Encode and decode to/from JSON to LossMeasurementCreate object.
+ */
+public class LmCreateCodec extends JsonCodec<LossMeasurementCreate> {
+
+ public static final String LM = "lm";
+ public static final String VERSION = "version";
+ public static final String LM_CFG_TYPE = "lmCfgType";
+ public static final String LMLMM = "LMLMM";
+ public static final String REMOTE_MEP_ID = "remoteMepId";
+ public static final String PRIORITY = "priority";
+ public static final String COUNTERS_ENABLED = "countersEnabled";
+ public static final String THRESHOLDS = "thresholds";
+ public static final String AVAILABILITY_MEASUREMENT_INTERVAL_MINS =
+ "availabilityMeasurementIntervalMins";
+ public static final String AVAILABILITY_NUMBER_CONSECUTIVE_FLR_MEASUREMENTS =
+ "availabilityNumberConsecutiveFlrMeasurements";
+ public static final String AVAILABILITY_FLR_THRESHOLD_PCT =
+ "availabilityFlrThresholdPct";
+ public static final String AVAILABILITY_NUMBER_CONSECUTIVE_INTERVALS =
+ "availabilityNumberConsecutiveIntervals";
+ public static final String AVAILABILITY_NUMBER_CONSECUTIVE_HIGH_FLR =
+ "availabilityNumberConsecutiveHighFlr";
+ public static final String FRAME_SIZE = "frameSize";
+ public static final String MESSAGE_PERIOD_MS = "messagePeriodMs";
+ public static final String MEASUREMENT_INTERVAL_MINS =
+ "measurementIntervalMins";
+ public static final String ALIGN_MEASUREMENT_INTERVALS =
+ "alignMeasurementIntervals";
+ public static final String ALIGN_MEASUREMENT_OFFSET_MINS =
+ "alignMeasurementOffsetMins";
+ public static final String START_TIME = "startTime";
+ public static final String STOP_TIME = "stopTime";
+
+ @Override
+ public LossMeasurementCreate decode(ObjectNode json,
+ CodecContext context) {
+
+ if (json == null || !json.isObject()) {
+ return null;
+ }
+
+ JsonNode lmNode = json.get(LM);
+ Version version = Version.Y17312011;
+ if (lmNode.get(VERSION) != null) {
+ version = Version.valueOf(lmNode.get(VERSION).asText());
+ }
+ LmType lmCfgType = LmType.LMLMM;
+ if (lmNode.get(LM_CFG_TYPE) != null) {
+ lmCfgType = LmType.valueOf(lmNode.get(LM_CFG_TYPE).asText(LMLMM));
+ }
+ MepId remoteMepId = MepId.valueOf(
+ nullIsIllegal(lmNode.get(REMOTE_MEP_ID), REMOTE_MEP_ID + " is required")
+ .shortValue());
+ Priority prio = Priority.valueOf(nullIsIllegal(lmNode.get(PRIORITY),
+ PRIORITY + " is required in the format 'PRIOn'").asText());
+
+ try {
+ LmCreateBuilder builder = DefaultLmCreate
+ .builder(version, remoteMepId, prio, lmCfgType);
+
+ if (lmNode.get(COUNTERS_ENABLED) != null) {
+ context.codec(CounterOption.class)
+ .decode((ArrayNode) (lmNode.get(COUNTERS_ENABLED)), context)
+ .forEach(builder::addToCountersEnabled);
+ }
+
+ if (lmNode.get(THRESHOLDS) != null) {
+ context.codec(LossMeasurementThreshold.class)
+ .decode((ArrayNode) (lmNode.get(THRESHOLDS)), context)
+ .forEach(builder::addToLossMeasurementThreshold);
+ }
+
+ if (lmNode.get(AVAILABILITY_MEASUREMENT_INTERVAL_MINS) != null) {
+ builder = builder.availabilityMeasurementInterval(
+ Duration.ofMinutes(lmNode.get(AVAILABILITY_MEASUREMENT_INTERVAL_MINS).asInt()));
+ }
+ if (lmNode.get(AVAILABILITY_NUMBER_CONSECUTIVE_FLR_MEASUREMENTS) != null) {
+ builder = builder.availabilityNumberConsecutiveFlrMeasurements(
+ lmNode.get(AVAILABILITY_NUMBER_CONSECUTIVE_FLR_MEASUREMENTS).asInt());
+ }
+ if (lmNode.get(AVAILABILITY_FLR_THRESHOLD_PCT) != null) {
+ builder = builder.availabilityFlrThreshold(
+ MilliPct.ofPercent((float) lmNode.get(AVAILABILITY_FLR_THRESHOLD_PCT).asDouble()));
+ }
+ if (lmNode.get(AVAILABILITY_NUMBER_CONSECUTIVE_INTERVALS) != null) {
+ builder = builder.availabilityNumberConsecutiveIntervals(
+ (short) lmNode.get(AVAILABILITY_NUMBER_CONSECUTIVE_INTERVALS).asInt());
+ }
+ if (lmNode.get(AVAILABILITY_NUMBER_CONSECUTIVE_HIGH_FLR) != null) {
+ builder = builder.availabilityNumberConsecutiveHighFlr(
+ (short) lmNode.get(AVAILABILITY_NUMBER_CONSECUTIVE_HIGH_FLR).asInt());
+ }
+ if (lmNode.get(FRAME_SIZE) != null) {
+ builder = (LmCreateBuilder) builder.frameSize(
+ (short) lmNode.get(FRAME_SIZE).asInt());
+ }
+ if (lmNode.get(MESSAGE_PERIOD_MS) != null) {
+ builder = (LmCreateBuilder) builder.messagePeriod(Duration.ofMillis(
+ lmNode.get(MESSAGE_PERIOD_MS).asInt()));
+ }
+ if (lmNode.get(MEASUREMENT_INTERVAL_MINS) != null) {
+ builder = (LmCreateBuilder) builder.measurementInterval(
+ Duration.ofMinutes(
+ lmNode.get(MEASUREMENT_INTERVAL_MINS).asInt()));
+ }
+ if (lmNode.get(ALIGN_MEASUREMENT_INTERVALS) != null) {
+ builder = (LmCreateBuilder) builder.alignMeasurementIntervals(
+ lmNode.get(ALIGN_MEASUREMENT_INTERVALS).asBoolean());
+ }
+ if (lmNode.get(ALIGN_MEASUREMENT_OFFSET_MINS) != null) {
+ builder = (LmCreateBuilder) builder.alignMeasurementOffset(Duration.ofMinutes(
+ lmNode.get(ALIGN_MEASUREMENT_OFFSET_MINS).asInt()));
+ }
+ if (lmNode.get(START_TIME) != null) {
+ builder = (LmCreateBuilder) builder.startTime(context.codec(StartTime.class)
+ .decode((ObjectNode) lmNode.get(START_TIME), context));
+ }
+ if (lmNode.get(STOP_TIME) != null) {
+ builder = (LmCreateBuilder) builder.stopTime(context.codec(StopTime.class)
+ .decode((ObjectNode) lmNode.get(STOP_TIME), context));
+ }
+
+
+ return builder.build();
+ } catch (SoamConfigException e) {
+ throw new IllegalArgumentException(e);
+ }
+ }
+
+ @Override
+ public ObjectNode encode(LossMeasurementCreate lm, CodecContext context) {
+ checkNotNull(lm, "LM cannot be null");
+ ObjectNode result = context.mapper().createObjectNode()
+ .put(LM_CFG_TYPE, lm.lmCfgType().name())
+ .put(VERSION, lm.version().name())
+ .put(REMOTE_MEP_ID, lm.remoteMepId().id())
+ .put(PRIORITY, lm.priority().name());
+
+ if (lm.countersEnabled() != null) {
+ result.set(COUNTERS_ENABLED, new LmCounterOptionCodec()
+ .encode(lm.countersEnabled(), context));
+ }
+
+ if (lm.messagePeriod() != null) {
+ result.put(MESSAGE_PERIOD_MS, lm.messagePeriod().toMillis());
+ }
+ if (lm.frameSize() != null) {
+ result.put(FRAME_SIZE, lm.frameSize());
+ }
+
+
+ return result;
+ }
+}
diff --git a/apps/cfm/src/main/java/org/onosproject/soam/web/LmEntryCodec.java b/apps/cfm/src/main/java/org/onosproject/soam/web/LmEntryCodec.java
new file mode 100644
index 0000000..251bcd9
--- /dev/null
+++ b/apps/cfm/src/main/java/org/onosproject/soam/web/LmEntryCodec.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.soam.web;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import org.onosproject.codec.CodecContext;
+import org.onosproject.codec.JsonCodec;
+import org.onosproject.incubator.net.l2monitoring.soam.loss.LossMeasurementEntry;
+
+import java.util.Iterator;
+import java.util.Map.Entry;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * Encode and decode to/from JSON to LossMeasurementEntry object.
+ */
+public class LmEntryCodec extends JsonCodec<LossMeasurementEntry> {
+
+ @Override
+ public ObjectNode encode(LossMeasurementEntry lm, CodecContext context) {
+ checkNotNull(lm, "LM cannot be null");
+ ObjectNode result = context.mapper().createObjectNode()
+ .put("lmId", lm.lmId().toString());
+
+ if (lm.measuredForwardFlr() != null) {
+ result.put("measuredForwardFlr", lm.measuredForwardFlr().percentValue());
+ }
+ if (lm.measuredBackwardFlr() != null) {
+ result.put("measuredBackwardFlr", lm.measuredBackwardFlr().percentValue());
+ }
+ if (lm.measuredAvailabilityForwardStatus() != null) {
+ result.put("measuredAvailabilityForwardStatus",
+ lm.measuredAvailabilityForwardStatus().name());
+ }
+ if (lm.measuredAvailabilityBackwardStatus() != null) {
+ result.put("measuredAvailabilityBackwardStatus",
+ lm.measuredAvailabilityBackwardStatus().name());
+ }
+ if (lm.measuredForwardLastTransitionTime() != null) {
+ result.put("measuredForwardLastTransitionTime",
+ lm.measuredForwardLastTransitionTime().toString());
+ }
+ if (lm.measuredBackwardLastTransitionTime() != null) {
+ result.put("measuredBackwardLastTransitionTime",
+ lm.measuredBackwardLastTransitionTime().toString());
+ }
+
+ ObjectNode lmAttrs = new LmCreateCodec().encode(lm, context);
+ Iterator<Entry<String, JsonNode>> elements = lmAttrs.fields();
+ while (elements.hasNext()) {
+ Entry<String, JsonNode> element = elements.next();
+ result.set(element.getKey(), element.getValue());
+ }
+
+ if (lm.measurementCurrent() != null) {
+ result.set("measurementCurrent", new LossMeasurementStatCurrentCodec()
+ .encode(lm.measurementCurrent(), context));
+ }
+
+ if (lm.measurementHistories() != null) {
+ result.set("measurementHistories", new LossMeasurementStatHistoryCodec()
+ .encode(lm.measurementHistories(), context));
+ }
+
+ if (lm.availabilityCurrent() != null) {
+ result.set("availabilityCurrent", new LossAvailabilityStatCurrentCodec()
+ .encode(lm.availabilityCurrent(), context));
+ }
+
+ if (lm.availabilityHistories() != null) {
+ result.set("availabilityHistories", new LossAvailabilityStatHistoryCodec()
+ .encode(lm.availabilityHistories(), context));
+ }
+
+ return result;
+ }
+
+ @Override
+ public ArrayNode encode(Iterable<LossMeasurementEntry> lmEntities, CodecContext context) {
+ ArrayNode an = context.mapper().createArrayNode();
+ lmEntities.forEach(dm -> an.add(encode(dm, context)));
+ return an;
+ }
+}
diff --git a/apps/cfm/src/main/java/org/onosproject/soam/web/LmThresholdOptionCodec.java b/apps/cfm/src/main/java/org/onosproject/soam/web/LmThresholdOptionCodec.java
new file mode 100644
index 0000000..98c0b62
--- /dev/null
+++ b/apps/cfm/src/main/java/org/onosproject/soam/web/LmThresholdOptionCodec.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.soam.web;
+
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import org.onosproject.codec.CodecContext;
+import org.onosproject.codec.JsonCodec;
+import org.onosproject.incubator.net.l2monitoring.soam.loss.LossMeasurementThreshold.ThresholdOption;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Encode and decode to/from JSON to ThresholdOption object.
+ */
+public class LmThresholdOptionCodec extends JsonCodec<ThresholdOption> {
+
+ @Override
+ public ArrayNode encode(Iterable<ThresholdOption> entities,
+ CodecContext context) {
+ ArrayNode an = context.mapper().createArrayNode();
+ entities.forEach(node -> an.add(node.name()));
+
+ return an;
+ }
+
+ @Override
+ public List<ThresholdOption> decode(ArrayNode json,
+ CodecContext context) {
+ if (json == null) {
+ return null;
+ }
+ List<ThresholdOption> moList = new ArrayList<>();
+ json.forEach(node -> moList.add(ThresholdOption.valueOf(node.asText())));
+ return moList;
+ }
+
+}
diff --git a/apps/cfm/src/main/java/org/onosproject/soam/web/LossAvailabilityStatCodec.java b/apps/cfm/src/main/java/org/onosproject/soam/web/LossAvailabilityStatCodec.java
new file mode 100644
index 0000000..3124976
--- /dev/null
+++ b/apps/cfm/src/main/java/org/onosproject/soam/web/LossAvailabilityStatCodec.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.soam.web;
+
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import org.onosproject.codec.CodecContext;
+import org.onosproject.codec.JsonCodec;
+import org.onosproject.incubator.net.l2monitoring.soam.loss.LossAvailabilityStat;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * Encode and decode to/from JSON to LossAvailabilityStat object.
+ */
+public class LossAvailabilityStatCodec extends JsonCodec<LossAvailabilityStat> {
+
+ @Override
+ public ObjectNode encode(LossAvailabilityStat laStat, CodecContext context) {
+ checkNotNull(laStat, "LA stat cannot be null");
+ ObjectNode result = context.mapper().createObjectNode()
+ .put("elapsedTime", laStat.elapsedTime().toString())
+ .put("suspectStatus", String.valueOf(laStat.suspectStatus()));
+
+ if (laStat.forwardHighLoss() != null) {
+ result = result.put("forwardHighLoss",
+ laStat.forwardHighLoss().toString());
+ }
+ if (laStat.backwardHighLoss() != null) {
+ result = result.put("backwardHighLoss",
+ laStat.backwardHighLoss().toString());
+ }
+ if (laStat.forwardConsecutiveHighLoss() != null) {
+ result = result.put("forwardConsecutiveHighLoss",
+ laStat.forwardConsecutiveHighLoss().toString());
+ }
+ if (laStat.backwardConsecutiveHighLoss() != null) {
+ result = result.put("backwardConsecutiveHighLoss",
+ laStat.backwardConsecutiveHighLoss().toString());
+ }
+ if (laStat.forwardAvailable() != null) {
+ result = result.put("forwardAvailable",
+ laStat.forwardAvailable().toString());
+ }
+ if (laStat.backwardAvailable() != null) {
+ result = result.put("backwardAvailable",
+ laStat.backwardAvailable().toString());
+ }
+ if (laStat.forwardUnavailable() != null) {
+ result = result.put("forwardUnavailable",
+ laStat.forwardUnavailable().toString());
+ }
+ if (laStat.backwardUnavailable() != null) {
+ result = result.put("backwardUnavailable",
+ laStat.backwardUnavailable().toString());
+ }
+ if (laStat.backwardMinFrameLossRatio() != null) {
+ result = result.put("backwardMinFrameLossRatio",
+ laStat.backwardMinFrameLossRatio().toString());
+ }
+ if (laStat.backwardMaxFrameLossRatio() != null) {
+ result = result.put("backwardMaxFrameLossRatio",
+ laStat.backwardMaxFrameLossRatio().toString());
+ }
+ if (laStat.backwardAverageFrameLossRatio() != null) {
+ result = result.put("backwardAverageFrameLossRatio",
+ laStat.backwardAverageFrameLossRatio().toString());
+ }
+ if (laStat.forwardMinFrameLossRatio() != null) {
+ result = result.put("forwardMinFrameLossRatio",
+ laStat.forwardMinFrameLossRatio().toString());
+ }
+ if (laStat.forwardMaxFrameLossRatio() != null) {
+ result = result.put("forwardMaxFrameLossRatio",
+ laStat.forwardMaxFrameLossRatio().toString());
+ }
+ if (laStat.forwardAverageFrameLossRatio() != null) {
+ result = result.put("forwardAverageFrameLossRatio",
+ laStat.forwardAverageFrameLossRatio().toString());
+ }
+
+ return result;
+ }
+}
diff --git a/apps/cfm/src/main/java/org/onosproject/soam/web/LossAvailabilityStatCurrentCodec.java b/apps/cfm/src/main/java/org/onosproject/soam/web/LossAvailabilityStatCurrentCodec.java
new file mode 100644
index 0000000..a6dc5de
--- /dev/null
+++ b/apps/cfm/src/main/java/org/onosproject/soam/web/LossAvailabilityStatCurrentCodec.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.soam.web;
+
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import org.onosproject.codec.CodecContext;
+import org.onosproject.codec.JsonCodec;
+import org.onosproject.incubator.net.l2monitoring.soam.loss.LossAvailabilityStatCurrent;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * Encode and decode to/from JSON to LossAvailabilityStatCurrent object.
+ */
+public class LossAvailabilityStatCurrentCodec extends JsonCodec<LossAvailabilityStatCurrent> {
+
+ @Override
+ public ObjectNode encode(LossAvailabilityStatCurrent laCurrent, CodecContext context) {
+ checkNotNull(laCurrent, "LA current cannot be null");
+ ObjectNode result = context.mapper().createObjectNode()
+ .put("startTime", laCurrent.startTime().toString())
+ .put("elapsedTime", laCurrent.elapsedTime().toString());
+
+ ObjectNode resultAbstract = new LossAvailabilityStatCodec().encode(laCurrent, context);
+ result.setAll(resultAbstract);
+
+ return result;
+ }
+}
diff --git a/apps/cfm/src/main/java/org/onosproject/soam/web/LossAvailabilityStatHistoryCodec.java b/apps/cfm/src/main/java/org/onosproject/soam/web/LossAvailabilityStatHistoryCodec.java
new file mode 100644
index 0000000..54e5160
--- /dev/null
+++ b/apps/cfm/src/main/java/org/onosproject/soam/web/LossAvailabilityStatHistoryCodec.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.soam.web;
+
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import org.onosproject.codec.CodecContext;
+import org.onosproject.codec.JsonCodec;
+import org.onosproject.incubator.net.l2monitoring.soam.loss.LossAvailabilityStatHistory;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * Encode and decode to/from JSON to LossAvailabilityStatHistory object.
+ */
+public class LossAvailabilityStatHistoryCodec extends JsonCodec<LossAvailabilityStatHistory> {
+
+ @Override
+ public ObjectNode encode(LossAvailabilityStatHistory laHistory, CodecContext context) {
+ checkNotNull(laHistory, "LA history cannot be null");
+ ObjectNode result = context.mapper().createObjectNode()
+ .put("historyId", String.valueOf(laHistory.historyStatsId()))
+ .put("endTime", laHistory.endTime().toString());
+ ObjectNode resultAbstract = new LossAvailabilityStatCodec().encode(laHistory, context);
+ result.setAll(resultAbstract);
+ return result;
+ }
+
+ @Override
+ public ArrayNode encode(Iterable<LossAvailabilityStatHistory> historyEntities, CodecContext context) {
+ ArrayNode an = context.mapper().createArrayNode();
+ historyEntities.forEach(history -> an.add(encode(history, context)));
+ return an;
+ }
+}
diff --git a/apps/cfm/src/main/java/org/onosproject/soam/web/LossMeasurementStatCodec.java b/apps/cfm/src/main/java/org/onosproject/soam/web/LossMeasurementStatCodec.java
new file mode 100644
index 0000000..eac21e6
--- /dev/null
+++ b/apps/cfm/src/main/java/org/onosproject/soam/web/LossMeasurementStatCodec.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.soam.web;
+
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import org.onosproject.codec.CodecContext;
+import org.onosproject.codec.JsonCodec;
+import org.onosproject.incubator.net.l2monitoring.soam.loss.LossMeasurementStat;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * Encode and decode to/from JSON to LossMeasurementStat object.
+ */
+public class LossMeasurementStatCodec extends JsonCodec<LossMeasurementStat> {
+
+ @Override
+ public ObjectNode encode(LossMeasurementStat lmStat, CodecContext context) {
+ checkNotNull(lmStat, "LM stat cannot be null");
+ ObjectNode result = context.mapper().createObjectNode()
+ .put("elapsedTime", lmStat.elapsedTime().toString())
+ .put("suspectStatus", String.valueOf(lmStat.suspectStatus()));
+
+ if (lmStat.forwardTransmittedFrames() != null) {
+ result = result.put("forwardTransmittedFrames",
+ lmStat.forwardTransmittedFrames().toString());
+ }
+ if (lmStat.forwardReceivedFrames() != null) {
+ result = result.put("forwardReceivedFrames",
+ lmStat.forwardReceivedFrames().toString());
+ }
+ if (lmStat.forwardMinFrameLossRatio() != null) {
+ result = result.put("forwardMinFrameLossRatio",
+ lmStat.forwardMinFrameLossRatio().toString());
+ }
+ if (lmStat.forwardMaxFrameLossRatio() != null) {
+ result = result.put("forwardMaxFrameLossRatio",
+ lmStat.forwardMaxFrameLossRatio().toString());
+ }
+ if (lmStat.forwardAverageFrameLossRatio() != null) {
+ result = result.put("forwardAverageFrameLossRatio",
+ lmStat.forwardAverageFrameLossRatio().toString());
+ }
+ if (lmStat.backwardTransmittedFrames() != null) {
+ result = result.put("backwardTransmittedFrames",
+ lmStat.backwardTransmittedFrames().toString());
+ }
+ if (lmStat.backwardReceivedFrames() != null) {
+ result = result.put("backwardReceivedFrames",
+ lmStat.backwardReceivedFrames().toString());
+ }
+ if (lmStat.backwardMinFrameLossRatio() != null) {
+ result = result.put("backwardMinFrameLossRatio",
+ lmStat.backwardMinFrameLossRatio().toString());
+ }
+ if (lmStat.backwardMaxFrameLossRatio() != null) {
+ result = result.put("backwardMaxFrameLossRatio",
+ lmStat.backwardMaxFrameLossRatio().toString());
+ }
+ if (lmStat.backwardAverageFrameLossRatio() != null) {
+ result = result.put("backwardAverageFrameLossRatio",
+ lmStat.backwardAverageFrameLossRatio().toString());
+ }
+ if (lmStat.soamPdusSent() != null) {
+ result = result.put("soamPdusSent",
+ lmStat.soamPdusSent().toString());
+ }
+ if (lmStat.soamPdusReceived() != null) {
+ result.put("soamPdusReceived",
+ lmStat.soamPdusReceived().toString());
+ }
+
+ return result;
+ }
+}
diff --git a/apps/cfm/src/main/java/org/onosproject/soam/web/LossMeasurementStatCurrentCodec.java b/apps/cfm/src/main/java/org/onosproject/soam/web/LossMeasurementStatCurrentCodec.java
new file mode 100644
index 0000000..d525493
--- /dev/null
+++ b/apps/cfm/src/main/java/org/onosproject/soam/web/LossMeasurementStatCurrentCodec.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.soam.web;
+
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import org.onosproject.codec.CodecContext;
+import org.onosproject.codec.JsonCodec;
+import org.onosproject.incubator.net.l2monitoring.soam.loss.LossMeasurementStatCurrent;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * Encode and decode to/from JSON to LossMeasurementStatCurrent object.
+ */
+public class LossMeasurementStatCurrentCodec extends JsonCodec<LossMeasurementStatCurrent> {
+
+ @Override
+ public ObjectNode encode(LossMeasurementStatCurrent lmCurrent, CodecContext context) {
+ checkNotNull(lmCurrent, "LM current cannot be null");
+ ObjectNode result = context.mapper().createObjectNode()
+ .put("startTime", lmCurrent.startTime().toString())
+ .put("elapsedTime", lmCurrent.elapsedTime().toString());
+
+ ObjectNode resultAbstract = new LossMeasurementStatCodec().encode(lmCurrent, context);
+ result.setAll(resultAbstract);
+
+ return result;
+ }
+}
diff --git a/apps/cfm/src/main/java/org/onosproject/soam/web/LossMeasurementStatHistoryCodec.java b/apps/cfm/src/main/java/org/onosproject/soam/web/LossMeasurementStatHistoryCodec.java
new file mode 100644
index 0000000..e4ec282
--- /dev/null
+++ b/apps/cfm/src/main/java/org/onosproject/soam/web/LossMeasurementStatHistoryCodec.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.soam.web;
+
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import org.onosproject.codec.CodecContext;
+import org.onosproject.codec.JsonCodec;
+import org.onosproject.incubator.net.l2monitoring.soam.loss.LossMeasurementStatHistory;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * Encode and decode to/from JSON to LossMeasurementStatHistory object.
+ */
+public class LossMeasurementStatHistoryCodec extends JsonCodec<LossMeasurementStatHistory> {
+
+ @Override
+ public ObjectNode encode(LossMeasurementStatHistory lmHistory, CodecContext context) {
+ checkNotNull(lmHistory, "LM history cannot be null");
+ ObjectNode result = context.mapper().createObjectNode()
+ .put("historyId", String.valueOf(lmHistory.historyStatsId()))
+ .put("endTime", lmHistory.endTime().toString());
+ ObjectNode resultAbstract = new LossMeasurementStatCodec().encode(lmHistory, context);
+ result.setAll(resultAbstract);
+ return result;
+ }
+
+ @Override
+ public ArrayNode encode(Iterable<LossMeasurementStatHistory> historyEntities, CodecContext context) {
+ ArrayNode an = context.mapper().createArrayNode();
+ historyEntities.forEach(history -> an.add(encode(history, context)));
+ return an;
+ }
+}
diff --git a/apps/cfm/src/main/java/org/onosproject/soam/web/LossMeasurementThresholdCodec.java b/apps/cfm/src/main/java/org/onosproject/soam/web/LossMeasurementThresholdCodec.java
new file mode 100644
index 0000000..3a17f94
--- /dev/null
+++ b/apps/cfm/src/main/java/org/onosproject/soam/web/LossMeasurementThresholdCodec.java
@@ -0,0 +1,192 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.soam.web;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import org.onosproject.codec.CodecContext;
+import org.onosproject.codec.JsonCodec;
+import org.onosproject.incubator.net.l2monitoring.soam.MilliPct;
+import org.onosproject.incubator.net.l2monitoring.soam.SoamId;
+import org.onosproject.incubator.net.l2monitoring.soam.loss.LossMeasurementThreshold;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.onlab.util.Tools.nullIsIllegal;
+import static org.onosproject.incubator.net.l2monitoring.soam.loss.DefaultLmThreshold.*;
+
+/**
+ * Encode and decode to/from JSON to LossMeasurementThreshold object.
+ */
+public class LossMeasurementThresholdCodec extends JsonCodec<LossMeasurementThreshold> {
+ static final String MEASUREDFLRFORWARD = "measuredFlrForward";
+ static final String MAXFLRFORWARD = "maxFlrForward";
+ static final String AVERAGEFLRFORWARD = "averageFlrForward";
+ static final String MEASUREDFLRBACKWARD = "measuredFlrBackward";
+ static final String MAXFLRBACKWARD = "maxFlrBackward";
+ static final String AVERAGEFLRBACKWARD = "averageFlrBackward";
+ static final String FORWARDHIGHLOSS = "forwardHighLoss";
+ static final String FORWARDCONSECUTIVEHIGHLOSS = "forwardConsecutiveHighLoss";
+ static final String BACKWARDHIGHLOSS = "backwardHighLoss";
+ static final String BACKWARDCONSECUTIVEHIGHLOSS = "backwardConsecutiveHighLoss";
+ static final String FORWARDUNAVAILABLECOUNT = "forwardUnavailableCount";
+ static final String FORWARDAVAILABLERATIO = "forwardAvailableRatio";
+ static final String BACKWARDUNAVAILABLECOUNT = "backwardUnavailableCount";
+ static final String BACKWARDAVAILABLERATIO = "backwardAvailableRatio";
+ static final String THRESHOLDOPTIONS = "thresholdOptions";
+
+ @Override
+ public ObjectNode encode(LossMeasurementThreshold lmt, CodecContext context) {
+ checkNotNull(lmt, "LM thresholds cannot be null");
+ ObjectNode result = context.mapper().createObjectNode()
+ .put("id", lmt.thresholdId().value());
+
+ if (lmt.thresholds() != null) {
+ result.set(THRESHOLDOPTIONS, new LmThresholdOptionCodec()
+ .encode(lmt.thresholds(), context));
+ }
+
+ if (lmt.measuredFlrForward() != null) {
+ result.put(MEASUREDFLRFORWARD, lmt.measuredFlrForward().percentValue());
+ }
+ if (lmt.maxFlrForward() != null) {
+ result.put(MAXFLRFORWARD, lmt.maxFlrForward().percentValue());
+ }
+ if (lmt.averageFlrForward() != null) {
+ result.put(AVERAGEFLRFORWARD, lmt.averageFlrForward().percentValue());
+ }
+ if (lmt.measuredFlrBackward() != null) {
+ result.put(MEASUREDFLRBACKWARD, lmt.measuredFlrBackward().percentValue());
+ }
+ if (lmt.maxFlrBackward() != null) {
+ result.put(MAXFLRBACKWARD, lmt.maxFlrBackward().percentValue());
+ }
+ if (lmt.averageFlrBackward() != null) {
+ result.put(AVERAGEFLRBACKWARD, lmt.averageFlrBackward().percentValue());
+ }
+ if (lmt.forwardHighLoss() != null) {
+ result.put(FORWARDHIGHLOSS, lmt.forwardHighLoss().longValue());
+ }
+ if (lmt.forwardConsecutiveHighLoss() != null) {
+ result.put(FORWARDCONSECUTIVEHIGHLOSS, lmt.measuredFlrForward().longValue());
+ }
+ if (lmt.backwardHighLoss() != null) {
+ result.put(BACKWARDHIGHLOSS, lmt.backwardHighLoss().longValue());
+ }
+ if (lmt.backwardConsecutiveHighLoss() != null) {
+ result.put(BACKWARDCONSECUTIVEHIGHLOSS, lmt.backwardConsecutiveHighLoss().longValue());
+ }
+ if (lmt.forwardUnavailableCount() != null) {
+ result.put(FORWARDUNAVAILABLECOUNT, lmt.forwardUnavailableCount().longValue());
+ }
+ if (lmt.forwardAvailableRatio() != null) {
+ result.put(FORWARDAVAILABLERATIO, lmt.forwardAvailableRatio().percentValue());
+ }
+ if (lmt.backwardUnavailableCount() != null) {
+ result.put(BACKWARDUNAVAILABLECOUNT, lmt.backwardUnavailableCount().longValue());
+ }
+ if (lmt.backwardAvailableRatio() != null) {
+ result.put(BACKWARDAVAILABLERATIO, lmt.backwardAvailableRatio().percentValue());
+ }
+
+ return result;
+ }
+
+ @Override
+ public List<LossMeasurementThreshold> decode(ArrayNode json, CodecContext context) {
+ if (json == null) {
+ return null;
+ }
+ List<LossMeasurementThreshold> thrList = new ArrayList<>();
+ json.forEach(node -> thrList.add(decode((ObjectNode) node, context)));
+ return thrList;
+ }
+
+ @Override
+ public LossMeasurementThreshold decode(ObjectNode json, CodecContext context) {
+ if (json == null || !json.isObject()) {
+ return null;
+ }
+
+ JsonNode thrNode = json.get("threshold");
+
+ SoamId thresholdId = SoamId.valueOf(nullIsIllegal(thrNode.get("id"),
+ "thresholdId must not be null").asInt());
+ LossMeasurementThreshold.LmThresholdBuilder builder = builder(thresholdId);
+
+ if (thrNode.get("thresholds") != null) {
+ context.codec(ThresholdOption.class)
+ .decode((ArrayNode) (thrNode.get("thresholds")), context)
+ .forEach(builder::addToThreshold);
+ }
+
+ if (thrNode.get(MEASUREDFLRFORWARD) != null) {
+ builder.measuredFlrForward(MilliPct.ofPercent(
+ (float) thrNode.get(MEASUREDFLRFORWARD).asDouble()));
+ }
+ if (thrNode.get(MAXFLRFORWARD) != null) {
+ builder.maxFlrForward(MilliPct.ofPercent(
+ (float) thrNode.get(MAXFLRFORWARD).asDouble()));
+ }
+ if (thrNode.get(AVERAGEFLRFORWARD) != null) {
+ builder.averageFlrForward(MilliPct.ofPercent(
+ (float) thrNode.get(AVERAGEFLRFORWARD).asDouble()));
+ }
+ if (thrNode.get(MEASUREDFLRBACKWARD) != null) {
+ builder.measuredFlrBackward(MilliPct.ofPercent(
+ (float) thrNode.get(MEASUREDFLRBACKWARD).asDouble()));
+ }
+ if (thrNode.get(MAXFLRBACKWARD) != null) {
+ builder.maxFlrBackward(MilliPct.ofPercent(
+ (float) thrNode.get(MAXFLRBACKWARD).asDouble()));
+ }
+ if (thrNode.get(AVERAGEFLRBACKWARD) != null) {
+ builder.averageFlrBackward(MilliPct.ofPercent(
+ (float) thrNode.get(AVERAGEFLRBACKWARD).asDouble()));
+ }
+ if (thrNode.get(FORWARDHIGHLOSS) != null) {
+ builder.forwardHighLoss(thrNode.get(FORWARDHIGHLOSS).asLong());
+ }
+ if (thrNode.get(FORWARDCONSECUTIVEHIGHLOSS) != null) {
+ builder.forwardConsecutiveHighLoss(thrNode.get(FORWARDCONSECUTIVEHIGHLOSS).asLong());
+ }
+ if (thrNode.get(BACKWARDHIGHLOSS) != null) {
+ builder.backwardHighLoss(thrNode.get(BACKWARDHIGHLOSS).asLong());
+ }
+ if (thrNode.get(BACKWARDCONSECUTIVEHIGHLOSS) != null) {
+ builder.backwardConsecutiveHighLoss(thrNode.get(BACKWARDCONSECUTIVEHIGHLOSS).asLong());
+ }
+ if (thrNode.get(FORWARDUNAVAILABLECOUNT) != null) {
+ builder.forwardUnavailableCount(thrNode.get(FORWARDUNAVAILABLECOUNT).asLong());
+ }
+ if (thrNode.get(FORWARDAVAILABLERATIO) != null) {
+ builder.forwardAvailableRatio(MilliPct.ofPercent(
+ (float) thrNode.get(FORWARDAVAILABLERATIO).asDouble()));
+ }
+ if (thrNode.get(BACKWARDUNAVAILABLECOUNT) != null) {
+ builder.backwardUnavailableCount(thrNode.get(BACKWARDUNAVAILABLECOUNT).asLong());
+ }
+ if (thrNode.get(BACKWARDAVAILABLERATIO) != null) {
+ builder.backwardAvailableRatio(MilliPct.ofPercent(
+ (float) thrNode.get(BACKWARDAVAILABLERATIO).asDouble()));
+ }
+
+ return builder.build();
+ }
+}
diff --git a/apps/cfm/src/main/java/org/onosproject/soam/web/StartTimeCodec.java b/apps/cfm/src/main/java/org/onosproject/soam/web/StartTimeCodec.java
new file mode 100644
index 0000000..f84bffe
--- /dev/null
+++ b/apps/cfm/src/main/java/org/onosproject/soam/web/StartTimeCodec.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.soam.web;
+
+import java.time.Duration;
+import java.time.OffsetDateTime;
+
+import org.onosproject.codec.CodecContext;
+import org.onosproject.codec.JsonCodec;
+import org.onosproject.incubator.net.l2monitoring.soam.StartTime;
+
+import com.fasterxml.jackson.databind.node.ObjectNode;
+
+/**
+ * Encode and decode to/from JSON to StartTime object.
+ */
+public class StartTimeCodec extends JsonCodec<StartTime> {
+
+ @Override
+ public StartTime decode(ObjectNode json, CodecContext context) {
+ if (json == null || !json.isObject()) {
+ return null;
+ }
+
+ if (json.get("immediate") != null) {
+ return StartTime.immediate();
+ } else if (json.get("absolute") != null) {
+ if (json.get("absolute").get("start-time") != null) {
+ return StartTime.absolute(OffsetDateTime
+ .parse(json.get("absolute").get("start-time").asText())
+ .toInstant());
+ }
+ throw new IllegalArgumentException("StartTime absolute must contain "
+ + "a start-time in date-time format with offset");
+ } else if (json.get("relative") != null) {
+ if (json.get("relative").get("start-time") != null) {
+ return StartTime.relative(Duration.parse(json.get("relative").get("start-time").asText()));
+ }
+ throw new IllegalArgumentException("StartTime relative must contain a start-time duration");
+ } else {
+ throw new IllegalArgumentException("StartTime must be either immediate, absolute or relative");
+ }
+ }
+}
diff --git a/apps/cfm/src/main/java/org/onosproject/soam/web/StopTimeCodec.java b/apps/cfm/src/main/java/org/onosproject/soam/web/StopTimeCodec.java
new file mode 100644
index 0000000..30a1538
--- /dev/null
+++ b/apps/cfm/src/main/java/org/onosproject/soam/web/StopTimeCodec.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.soam.web;
+
+import java.time.Duration;
+import java.time.OffsetDateTime;
+
+import org.onosproject.codec.CodecContext;
+import org.onosproject.codec.JsonCodec;
+import org.onosproject.incubator.net.l2monitoring.soam.StopTime;
+
+import com.fasterxml.jackson.databind.node.ObjectNode;
+
+/**
+ * Encode and decode to/from JSON to StopTime object.
+ */
+public class StopTimeCodec extends JsonCodec<StopTime> {
+
+ @Override
+ public StopTime decode(ObjectNode json, CodecContext context) {
+ if (json == null || !json.isObject()) {
+ return null;
+ }
+
+ if (json.get("none") != null) {
+ return StopTime.none();
+ } else if (json.get("absolute") != null) {
+ if (json.get("absolute").get("stop-time") != null) {
+ return StopTime.absolute(OffsetDateTime
+ .parse(json.get("absolute").get("stop-time").asText())
+ .toInstant());
+ }
+ throw new IllegalArgumentException("StopTime absolute must contain "
+ + "a stop-time in date-time format with offset");
+ } else if (json.get("relative") != null) {
+ if (json.get("relative").get("stop-time") != null) {
+ return StopTime.relative(Duration.parse(json.get("relative").get("stop-time").asText()));
+ }
+ throw new IllegalArgumentException("StopTime relative must contain a stop-time duration");
+ } else {
+ throw new IllegalArgumentException("StopTime must be either none, absolute or relative");
+ }
+ }
+}
diff --git a/apps/cfm/src/main/java/org/onosproject/soam/web/package-info.java b/apps/cfm/src/main/java/org/onosproject/soam/web/package-info.java
new file mode 100644
index 0000000..473be24
--- /dev/null
+++ b/apps/cfm/src/main/java/org/onosproject/soam/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 SOAM objects to and from JSON.
+ */
+package org.onosproject.soam.web;
\ No newline at end of file
diff --git a/apps/cfm/src/main/resources/OSGI-INF/blueprint/shell-config.xml b/apps/cfm/src/main/resources/OSGI-INF/blueprint/shell-config.xml
new file mode 100644
index 0000000..5f53eba
--- /dev/null
+++ b/apps/cfm/src/main/resources/OSGI-INF/blueprint/shell-config.xml
@@ -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.
+ -->
+<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0">
+
+ <command-bundle xmlns="http://karaf.apache.org/xmlns/shell/v1.1.0">
+ <command>
+ <action class="org.onosproject.cfm.cli.CfmMdAddCommand"/>
+ <completers>
+ <ref component-id="mdNameTypeCompleter"/>
+ <ref component-id="placeholderCompleter"/>
+ <ref component-id="mdLevelCompleter"/>
+ <ref component-id="placeholderCompleter"/>
+ </completers>
+ </command>
+ <command>
+ <action class="org.onosproject.cfm.cli.CfmMdDeleteCommand"/>
+ <completers>
+ <ref component-id="mdNameCompleter"/>
+ </completers>
+ </command>
+ <command>
+ <action class="org.onosproject.cfm.cli.CfmMdListMdCommand"/>
+ <completers>
+ <ref component-id="mdNameCompleter"/>
+ </completers>
+ </command>
+ <command>
+ <action class="org.onosproject.cfm.cli.CfmMaAddCommand"/>
+ <completers>
+ <ref component-id="mdNameCompleter"/>
+ <ref component-id="maNameTypeCompleter"/>
+ <ref component-id="placeholderCompleter"/>
+ <ref component-id="maCcmIntervalCompleter"/>
+ <ref component-id="placeholderCompleter"/>
+ <ref component-id="placeholderCompleter"/>
+ <ref component-id="compTagTypeCompleter"/>
+ <ref component-id="compMhfCompleter"/>
+ <ref component-id="placeholderCompleter"/>
+ </completers>
+ </command>
+ <command>
+ <action class="org.onosproject.cfm.cli.CfmMaDeleteCommand"/>
+ <completers>
+ <ref component-id="maNameCompleter"/>
+ </completers>
+ </command>
+ </command-bundle>
+
+ <bean id="placeholderCompleter" class="org.onosproject.cli.PlaceholderCompleter"/>
+ <bean id="mdNameTypeCompleter" class="org.onosproject.cfm.cli.completer.CfmMdNameTypeCompleter"/>
+ <bean id="maNameTypeCompleter" class="org.onosproject.cfm.cli.completer.CfmMaNameTypeCompleter"/>
+ <bean id="mdNameCompleter" class="org.onosproject.cfm.cli.completer.CfmMdNameCompleter"/>
+ <bean id="maNameCompleter" class="org.onosproject.cfm.cli.completer.CfmMaNameCompleter"/>
+ <bean id="mdLevelCompleter" class="org.onosproject.cfm.cli.completer.CfmMdLevelCompleter"/>
+ <bean id="maCcmIntervalCompleter" class="org.onosproject.cfm.cli.completer.CfmMaCcmIntervalCompleter"/>
+ <bean id="compTagTypeCompleter" class="org.onosproject.cfm.cli.completer.CfmCompTagTypeCompleter"/>
+ <bean id="compMhfCompleter" class="org.onosproject.cfm.cli.completer.CfmCompMhfCompleter"/>
+
+</blueprint>
+
+
diff --git a/apps/cfm/src/main/webapp/WEB-INF/web.xml b/apps/cfm/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 0000000..71c89e9
--- /dev/null
+++ b/apps/cfm/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright 2017-present Open Networking Laboratory
+ ~
+ ~ 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.
+ -->
+<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns="http://java.sun.com/xml/ns/javaee"
+ xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
+ xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
+ id="ONOS" version="2.5">
+ <display-name>Layer 2 Cfm REST API v1.0</display-name>
+
+ <security-constraint>
+ <web-resource-collection>
+ <web-resource-name>Secured</web-resource-name>
+ <url-pattern>/*</url-pattern>
+ </web-resource-collection>
+ <auth-constraint>
+ <role-name>admin</role-name>
+ </auth-constraint>
+ </security-constraint>
+
+ <security-role>
+ <role-name>admin</role-name>
+ </security-role>
+
+ <login-config>
+ <auth-method>BASIC</auth-method>
+ <realm-name>karaf</realm-name>
+ </login-config>
+
+ <servlet>
+ <servlet-name>JAX-RS Service</servlet-name>
+ <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
+ <init-param>
+ <param-name>javax.ws.rs.Application</param-name>
+ <param-value>org.onosproject.cfm.impl.CfmWebApplication</param-value>
+ </init-param>
+ <load-on-startup>1</load-on-startup>
+ </servlet>
+
+ <servlet-mapping>
+ <servlet-name>JAX-RS Service</servlet-name>
+ <url-pattern>/*</url-pattern>
+ </servlet-mapping>
+</web-app>
diff --git a/apps/cfm/src/test/java/org/onosproject/cfm/CfmCodecContext.java b/apps/cfm/src/test/java/org/onosproject/cfm/CfmCodecContext.java
new file mode 100644
index 0000000..0f1de5e
--- /dev/null
+++ b/apps/cfm/src/test/java/org/onosproject/cfm/CfmCodecContext.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.cfm;
+
+import org.onosproject.codec.CodecContext;
+import org.onosproject.codec.CodecService;
+import org.onosproject.codec.JsonCodec;
+import org.onosproject.codec.impl.CodecManager;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+/**
+ * Mock codec context for use in codec unit tests.
+ */
+public class CfmCodecContext implements CodecContext {
+
+ private final ObjectMapper mapper = new ObjectMapper();
+ private final CodecManager codecManager = new CodecManager();
+ private final CfmWebComponent manager = new CfmWebComponent();
+
+ /**
+ * Constructs a new mock codec context.
+ */
+ public CfmCodecContext() {
+ codecManager.activate();
+ manager.codecService = codecManager;
+ manager.activate();
+ }
+
+ @Override
+ public ObjectMapper mapper() {
+ return mapper;
+ }
+
+ @Override
+ public <T> T getService(Class<T> serviceClass) {
+ return null;
+ }
+
+ @Override
+ public <T> JsonCodec<T> codec(Class<T> entityClass) {
+ return codecManager.getCodec(entityClass);
+ }
+
+ /**
+ * Get the codec manager.
+ *
+ * @return instance of codec manager
+ */
+ public CodecService codecManager() {
+ return codecManager;
+ }
+}
diff --git a/apps/cfm/src/test/java/org/onosproject/cfm/impl/CfmResourceTest.java b/apps/cfm/src/test/java/org/onosproject/cfm/impl/CfmResourceTest.java
new file mode 100644
index 0000000..bcbaa9a
--- /dev/null
+++ b/apps/cfm/src/test/java/org/onosproject/cfm/impl/CfmResourceTest.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.cfm.impl;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.onosproject.rest.resources.ResourceTest;
+
+/**
+ * Base class for CFM REST API tests. Performs common configuration operations.
+ */
+public class CfmResourceTest extends ResourceTest {
+
+ /**
+ * Creates a new web-resource test.
+ */
+ public CfmResourceTest() {
+ super(ResourceConfig.forApplicationClass(CfmWebApplication.class));
+ }
+}
diff --git a/apps/cfm/src/test/java/org/onosproject/cfm/impl/MaWebResourceTest.java b/apps/cfm/src/test/java/org/onosproject/cfm/impl/MaWebResourceTest.java
new file mode 100644
index 0000000..6378ea9
--- /dev/null
+++ b/apps/cfm/src/test/java/org/onosproject/cfm/impl/MaWebResourceTest.java
@@ -0,0 +1,199 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.cfm.impl;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import org.junit.Before;
+import org.junit.Test;
+import org.onlab.osgi.ServiceDirectory;
+import org.onlab.osgi.TestServiceDirectory;
+import org.onlab.packet.VlanId;
+import org.onlab.rest.BaseResource;
+import org.onosproject.cfm.CfmCodecContext;
+import org.onosproject.codec.CodecService;
+import org.onosproject.incubator.net.l2monitoring.cfm.Component;
+import org.onosproject.incubator.net.l2monitoring.cfm.DefaultComponent;
+import org.onosproject.incubator.net.l2monitoring.cfm.DefaultMaintenanceAssociation;
+import org.onosproject.incubator.net.l2monitoring.cfm.DefaultMaintenanceDomain;
+import org.onosproject.incubator.net.l2monitoring.cfm.MaintenanceAssociation;
+import org.onosproject.incubator.net.l2monitoring.cfm.MaintenanceDomain;
+import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MaIdCharStr;
+import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MaIdShort;
+import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MdId;
+import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MdIdCharStr;
+import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MepId;
+import org.onosproject.incubator.net.l2monitoring.cfm.service.CfmConfigException;
+import org.onosproject.incubator.net.l2monitoring.cfm.service.CfmMdService;
+
+import javax.ws.rs.InternalServerErrorException;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.Response;
+
+import java.io.BufferedReader;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.util.Optional;
+
+import static junit.framework.TestCase.fail;
+import static org.easymock.EasyMock.createMock;
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.replay;
+import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+
+public class MaWebResourceTest extends CfmResourceTest {
+ private final CfmMdService mdService = createMock(CfmMdService.class);
+
+ private static final MdId MDNAME1 = MdIdCharStr.asMdId("md-1");
+ private static final MaIdShort MANAME1 = MaIdCharStr.asMaId("ma-1-1");
+
+ private MaintenanceAssociation ma1;
+
+ @Before
+ public void setUpTest() throws CfmConfigException {
+ CfmCodecContext context = new CfmCodecContext();
+ ServiceDirectory testDirectory = new TestServiceDirectory()
+ .add(CfmMdService.class, mdService)
+ .add(CodecService.class, context.codecManager());
+ BaseResource.setServiceDirectory(testDirectory);
+
+ ma1 = DefaultMaintenanceAssociation
+ .builder(MANAME1, MDNAME1.getNameLength())
+ .addToRemoteMepIdList(MepId.valueOf((short) 101))
+ .addToRemoteMepIdList(MepId.valueOf((short) 102))
+ .ccmInterval(MaintenanceAssociation.CcmInterval.INTERVAL_3MS)
+ .maNumericId((short) 1)
+ .addToComponentList(
+ DefaultComponent.builder(1)
+ .tagType(Component.TagType.VLAN_STAG)
+ .mhfCreationType(Component.MhfCreationType.NONE)
+ .idPermission(Component.IdPermissionType.MANAGE)
+ .addToVidList(VlanId.vlanId((short) 1010))
+ .build())
+ .build();
+ }
+
+ @Test
+ public void testGetMa() {
+
+ expect(mdService.getMaintenanceAssociation(MDNAME1, MANAME1))
+ .andReturn(Optional.ofNullable(ma1)).anyTimes();
+ replay(mdService);
+
+ final WebTarget wt = target();
+ final String response = wt.path("md/" + MDNAME1.mdName()
+ + "/ma/" + MANAME1.maName()).request().get(String.class);
+
+ assertThat(response, is("{\"ma\":" +
+ "{\"maName\":\"ma-1-1\"," +
+ "\"maNameType\":\"CHARACTERSTRING\"," +
+ "\"maNumericId\":1," +
+ "\"ccm-interval\":\"INTERVAL_3MS\"," +
+ "\"component-list\":[{\"component\":" +
+ "{\"component-id\":1," +
+ "\"vid-list\":[{\"vid\":\"1010\"}]," +
+ "\"mhf-creation-type\":\"NONE\"," +
+ "\"id-permission\":\"MANAGE\"," +
+ "\"tag-type\":\"VLAN_STAG\"}}]," +
+ "\"rmep-list\":" +
+ "[{\"rmep\":101}," +
+ "{\"rmep\":102}]}}"));
+ }
+
+ @Test
+ public void testGetMaEmpty() throws IOException {
+ MaIdShort maId2 = MaIdCharStr.asMaId("ma-2");
+ expect(mdService
+ .getMaintenanceAssociation(MDNAME1, maId2))
+ .andReturn(Optional.empty()).anyTimes();
+ replay(mdService);
+
+ final WebTarget wt = target();
+ try {
+ final String response = wt.path("md/" + MDNAME1.mdName()
+ + "/ma/" + maId2.maName()).request().get(String.class);
+ fail("Expected InternalServerErrorException, as MA is unknown");
+ } catch (InternalServerErrorException e) {
+ ByteArrayInputStream is = (ByteArrayInputStream) e.getResponse().getEntity();
+ BufferedReader br = new BufferedReader(new InputStreamReader(is));
+ String line = null;
+ StringBuffer sb = new StringBuffer();
+ while ((line = br.readLine()) != null) {
+ sb.append(line);
+ }
+
+ assertThat(sb.toString(), is("{ \"failure\":" +
+ "\"java.lang.IllegalArgumentException: MA ma-2 not Found\" }"));
+ }
+ }
+
+ @Test
+ public void testDeleteMa() throws CfmConfigException {
+
+ expect(mdService.deleteMaintenanceAssociation(MDNAME1, MANAME1))
+ .andReturn(true).anyTimes();
+ replay(mdService);
+
+ final WebTarget wt = target();
+ final Response response = wt.path("md/" + MDNAME1.mdName()
+ + "/ma/" + MANAME1.maName()).request().delete();
+
+ assertEquals(200, response.getStatus());
+ }
+
+ @Test
+ public void testDeleteMaEmpty() throws CfmConfigException {
+ MaIdShort maId2 = MaIdCharStr.asMaId("ma-2");
+
+ expect(mdService.deleteMaintenanceAssociation(MDNAME1, maId2))
+ .andReturn(false).anyTimes();
+ replay(mdService);
+
+ final WebTarget wt = target();
+ final Response response = wt.path("md/" + MDNAME1.mdName()
+ + "/ma/" + maId2.maName()).request().delete();
+
+ assertEquals(304, response.getStatus());
+ }
+
+ @Test
+ public void testCreateMa() throws CfmConfigException {
+ MaintenanceDomain md1 = DefaultMaintenanceDomain
+ .builder(MDNAME1).mdLevel(MaintenanceDomain.MdLevel.LEVEL2).build();
+
+ expect(mdService.getMaintenanceDomain(MDNAME1))
+ .andReturn(Optional.ofNullable(md1)).anyTimes();
+ expect(mdService.createMaintenanceAssociation(MDNAME1, ma1))
+ .andReturn(false).anyTimes();
+ replay(mdService);
+
+ ObjectMapper mapper = new ObjectMapper();
+ CfmCodecContext context = new CfmCodecContext();
+ ObjectNode node = mapper.createObjectNode();
+ node.set("ma", context.codec(MaintenanceAssociation.class)
+ .encode(ma1, context));
+
+ final WebTarget wt = target();
+ final Response response = wt.path("md/" + MDNAME1.mdName()
+ + "/ma").request().post(Entity.json(node.toString()));
+
+ assertEquals(201, response.getStatus());
+ }
+}
diff --git a/apps/cfm/src/test/java/org/onosproject/cfm/impl/MdWebResourceTest.java b/apps/cfm/src/test/java/org/onosproject/cfm/impl/MdWebResourceTest.java
new file mode 100644
index 0000000..f4c9b5a
--- /dev/null
+++ b/apps/cfm/src/test/java/org/onosproject/cfm/impl/MdWebResourceTest.java
@@ -0,0 +1,193 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.cfm.impl;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import org.junit.Before;
+import org.junit.Test;
+import org.onlab.osgi.ServiceDirectory;
+import org.onlab.osgi.TestServiceDirectory;
+import org.onlab.rest.BaseResource;
+import org.onosproject.cfm.CfmCodecContext;
+import org.onosproject.codec.CodecService;
+import org.onosproject.incubator.net.l2monitoring.cfm.DefaultMaintenanceDomain;
+import org.onosproject.incubator.net.l2monitoring.cfm.MaintenanceDomain;
+import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MdId;
+import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MdIdCharStr;
+import org.onosproject.incubator.net.l2monitoring.cfm.service.CfmConfigException;
+import org.onosproject.incubator.net.l2monitoring.cfm.service.CfmMdService;
+
+import javax.ws.rs.InternalServerErrorException;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.Response;
+import java.io.BufferedReader;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+
+import static junit.framework.TestCase.fail;
+import static org.easymock.EasyMock.createMock;
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.replay;
+import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+
+public class MdWebResourceTest extends CfmResourceTest {
+ private final CfmMdService mdService = createMock(CfmMdService.class);
+
+ private static final MdId MDNAME1 = MdIdCharStr.asMdId("md-1");
+ private static final MdId MDNAME2 = MdIdCharStr.asMdId("md-2");
+
+ private List<MaintenanceDomain> mdList;
+
+ @Before
+ public void setUpTest() throws CfmConfigException {
+ CfmCodecContext context = new CfmCodecContext();
+ ServiceDirectory testDirectory = new TestServiceDirectory()
+ .add(CfmMdService.class, mdService)
+ .add(CodecService.class, context.codecManager());
+ BaseResource.setServiceDirectory(testDirectory);
+
+ mdList = new ArrayList<>();
+
+ mdList.add(DefaultMaintenanceDomain.builder(MDNAME1)
+ .mdLevel(MaintenanceDomain.MdLevel.LEVEL1).build());
+ mdList.add(DefaultMaintenanceDomain.builder(MDNAME2)
+ .mdLevel(MaintenanceDomain.MdLevel.LEVEL2).build());
+ }
+
+ @Test
+ public void testGetMds() {
+ expect(mdService.getAllMaintenanceDomain()).andReturn(mdList).anyTimes();
+ replay(mdService);
+
+ final WebTarget wt = target();
+ final String response = wt.path("md").request().get(String.class);
+
+ assertThat(response, is("{\"mds\":[[" +
+ "{\"mdName\":\"md-1\",\"mdNameType\":\"CHARACTERSTRING\"," +
+ "\"mdLevel\":\"LEVEL1\",\"maList\":[]}," +
+ "{\"mdName\":\"md-2\",\"mdNameType\":\"CHARACTERSTRING\"," +
+ "\"mdLevel\":\"LEVEL2\",\"maList\":[]}]]}"));
+ }
+
+ @Test
+ public void testGetMdsEmpty() {
+ expect(mdService.getAllMaintenanceDomain())
+ .andReturn(new ArrayList<>()).anyTimes();
+ replay(mdService);
+
+ final WebTarget wt = target();
+ final String response = wt.path("md").request().get(String.class);
+
+ assertThat(response, is("{\"mds\":[[]]}"));
+ }
+
+ @Test
+ public void testGetMd() {
+ expect(mdService.getMaintenanceDomain(MDNAME1))
+ .andReturn(Optional.ofNullable(mdList.get(0))).anyTimes();
+ replay(mdService);
+
+ final WebTarget wt = target();
+ final String response = wt.path("md/" + MDNAME1).request().get(String.class);
+
+ assertThat(response, is("{\"md\":" +
+ "{\"mdName\":\"md-1\",\"mdNameType\":\"CHARACTERSTRING\"," +
+ "\"mdLevel\":\"LEVEL1\",\"maList\":[]}}"));
+ }
+
+ @Test
+ public void testGetMdEmpty() throws IOException {
+ final MdId mdName3 = MdIdCharStr.asMdId("md-3");
+ expect(mdService.getMaintenanceDomain(mdName3))
+ .andReturn(Optional.empty()).anyTimes();
+ replay(mdService);
+
+ final WebTarget wt = target();
+ try {
+ final String response = wt.path("md/" + mdName3).request().get(String.class);
+ fail("Expected InternalServerErrorException, as MD is unknown");
+ } catch (InternalServerErrorException e) {
+ ByteArrayInputStream is = (ByteArrayInputStream) e.getResponse().getEntity();
+ BufferedReader br = new BufferedReader(new InputStreamReader(is));
+ String line = null;
+ StringBuffer sb = new StringBuffer();
+ while ((line = br.readLine()) != null) {
+ sb.append(line);
+ }
+
+ assertThat(sb.toString(), is("{ \"failure\":" +
+ "\"java.lang.IllegalArgumentException: MD md-3 not Found\" }"));
+ }
+ }
+
+ @Test
+ public void testDeleteMd() throws CfmConfigException {
+ expect(mdService.deleteMaintenanceDomain(MDNAME1))
+ .andReturn(true).anyTimes();
+ replay(mdService);
+
+ final WebTarget wt = target();
+ final Response response = wt.path("md/" + MDNAME1).request().delete();
+
+ assertEquals(200, response.getStatus());
+ }
+
+ @Test
+ public void testDeleteMdNotPresent() throws CfmConfigException {
+ expect(mdService.deleteMaintenanceDomain(MDNAME1))
+ .andReturn(false).anyTimes();
+ replay(mdService);
+
+ final WebTarget wt = target();
+ final Response response = wt.path("md/" + MDNAME1).request().delete();
+
+ assertEquals(304, response.getStatus());
+ }
+
+ @Test
+ public void testCreateMd() throws CfmConfigException {
+ MaintenanceDomain md3 = DefaultMaintenanceDomain
+ .builder(MdIdCharStr.asMdId("md-3"))
+ .mdLevel(MaintenanceDomain.MdLevel.LEVEL3)
+ .mdNumericId((short) 3)
+ .build();
+
+ expect(mdService.createMaintenanceDomain(mdList.get(1)))
+ .andReturn(false).anyTimes();
+ replay(mdService);
+
+ ObjectMapper mapper = new ObjectMapper();
+ CfmCodecContext context = new CfmCodecContext();
+ ObjectNode node = mapper.createObjectNode();
+ node.set("md", context.codec(MaintenanceDomain.class)
+ .encode(mdList.get(1), context));
+
+
+ final WebTarget wt = target();
+ final Response response = wt.path("md")
+ .request().post(Entity.json(node.toString()));
+
+ assertEquals(201, response.getStatus());
+ }
+}
diff --git a/apps/cfm/src/test/java/org/onosproject/cfm/impl/MepWebResourceTest.java b/apps/cfm/src/test/java/org/onosproject/cfm/impl/MepWebResourceTest.java
new file mode 100644
index 0000000..4dd83db
--- /dev/null
+++ b/apps/cfm/src/test/java/org/onosproject/cfm/impl/MepWebResourceTest.java
@@ -0,0 +1,378 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.cfm.impl;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import org.junit.Before;
+import org.junit.Test;
+import org.onlab.osgi.ServiceDirectory;
+import org.onlab.osgi.TestServiceDirectory;
+import org.onlab.packet.VlanId;
+import org.onlab.rest.BaseResource;
+import org.onosproject.cfm.CfmCodecContext;
+import org.onosproject.codec.CodecService;
+import org.onosproject.incubator.net.l2monitoring.cfm.DefaultMaintenanceAssociation;
+import org.onosproject.incubator.net.l2monitoring.cfm.DefaultMaintenanceDomain;
+import org.onosproject.incubator.net.l2monitoring.cfm.DefaultMep;
+import org.onosproject.incubator.net.l2monitoring.cfm.DefaultMepEntry;
+import org.onosproject.incubator.net.l2monitoring.cfm.DefaultMepLbCreate;
+import org.onosproject.incubator.net.l2monitoring.cfm.DefaultMepLtCreate;
+import org.onosproject.incubator.net.l2monitoring.cfm.MaintenanceAssociation;
+import org.onosproject.incubator.net.l2monitoring.cfm.MaintenanceDomain;
+import org.onosproject.incubator.net.l2monitoring.cfm.Mep;
+import org.onosproject.incubator.net.l2monitoring.cfm.MepEntry;
+import org.onosproject.incubator.net.l2monitoring.cfm.MepLbCreate;
+import org.onosproject.incubator.net.l2monitoring.cfm.MepLtCreate;
+import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MaIdCharStr;
+import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MaIdShort;
+import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MdId;
+import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MdIdCharStr;
+import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MepId;
+import org.onosproject.incubator.net.l2monitoring.cfm.service.CfmConfigException;
+import org.onosproject.incubator.net.l2monitoring.cfm.service.CfmMdService;
+import org.onosproject.incubator.net.l2monitoring.cfm.service.CfmMepService;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.PortNumber;
+
+import javax.ws.rs.InternalServerErrorException;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.Response;
+
+import java.io.BufferedReader;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.time.Duration;
+import java.util.ArrayList;
+import java.util.BitSet;
+import java.util.Collection;
+import java.util.Optional;
+
+import static junit.framework.TestCase.fail;
+import static org.easymock.EasyMock.*;
+import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+
+public class MepWebResourceTest extends CfmResourceTest {
+ private final CfmMepService mepService = createMock(CfmMepService.class);
+ private final CfmMdService mdService = createMock(CfmMdService.class);
+
+ private static final MdId MDNAME1 = MdIdCharStr.asMdId("md-1");
+ private static final MaIdShort MANAME1 = MaIdCharStr.asMaId("ma-1-1");
+ private static final MepId MEPID1 = MepId.valueOf((short) 1);
+
+ private MepEntry mepEntry1 = null;
+
+ @Before
+ public void setUpTest() throws CfmConfigException {
+ CfmCodecContext context = new CfmCodecContext();
+ ServiceDirectory testDirectory = new TestServiceDirectory()
+ .add(CfmMepService.class, mepService)
+ .add(CfmMdService.class, mdService)
+ .add(CodecService.class, context.codecManager());
+ BaseResource.setServiceDirectory(testDirectory);
+
+ mepEntry1 = DefaultMepEntry.builder(
+ MEPID1,
+ DeviceId.deviceId("netconf:1.2.3.4:830"),
+ PortNumber.portNumber(1),
+ Mep.MepDirection.UP_MEP, MDNAME1, MANAME1)
+ .buildEntry();
+
+ }
+
+ @Test
+ public void testGetAllMepsForMaEmpty() throws CfmConfigException {
+
+ expect(mepService.getAllMeps(MDNAME1, MANAME1)).andReturn(null).anyTimes();
+ replay(mepService);
+
+ final WebTarget wt = target();
+ final String response = wt.path("md/" + MDNAME1.mdName() + "/ma/" +
+ MANAME1.maName() + "/mep").request().get(String.class);
+ assertThat(response, is("{\"meps\":[[]]}"));
+ }
+
+ @Test
+ public void testGetAllMepsForMa1Mep() throws CfmConfigException {
+ Collection<MepEntry> meps = new ArrayList<>();
+ meps.add(mepEntry1);
+
+ expect(mepService.getAllMeps(MDNAME1, MANAME1)).andReturn(meps).anyTimes();
+ replay(mepService);
+
+ final WebTarget wt = target();
+ final String response = wt.path("md/" + MDNAME1.mdName() + "/ma/" +
+ MANAME1.maName() + "/mep").request().get(String.class);
+
+ assertThat(response, is("{\"meps\":" +
+ "[[{" +
+ "\"mepId\":" + MEPID1.value() + "," +
+ "\"deviceId\":\"netconf:1.2.3.4:830\"," +
+ "\"port\":1," +
+ "\"direction\":\"UP_MEP\"," +
+ "\"mdName\":\"" + MDNAME1.mdName() + "\"," +
+ "\"maName\":\"" + MANAME1.maName() + "\"," +
+ "\"administrative-state\":false," +
+ "\"cci-enabled\":false," +
+ "\"remoteMeps\":[]}]]}"));
+ }
+
+ @Test
+ public void testGetMepValid() throws CfmConfigException {
+
+ expect(mepService.getMep(MDNAME1, MANAME1, MepId.valueOf((short) 1)))
+ .andReturn(mepEntry1).anyTimes();
+ replay(mepService);
+
+ final WebTarget wt = target();
+ final String response = wt.path("md/" + MDNAME1.mdName() + "/ma/" +
+ MANAME1.maName() + "/mep/" + MEPID1.value()).request().get(String.class);
+
+ assertThat(response, is("{\"mep\":" +
+ "{" +
+ "\"mepId\":" + MEPID1.value() + "," +
+ "\"deviceId\":\"netconf:1.2.3.4:830\"," +
+ "\"port\":1," +
+ "\"direction\":\"UP_MEP\"," +
+ "\"mdName\":\"" + MDNAME1.mdName() + "\"," +
+ "\"maName\":\"" + MANAME1.maName() + "\"," +
+ "\"administrative-state\":false," +
+ "\"cci-enabled\":false," +
+ "\"remoteMeps\":[]}}"));
+ }
+
+ @Test
+ public void testGetMepNotFound() throws CfmConfigException, IOException {
+
+ expect(mepService.getMep(MDNAME1, MANAME1, MepId.valueOf((short) 2)))
+ .andReturn(null).anyTimes();
+ replay(mepService);
+
+ final WebTarget wt = target();
+
+ try {
+ final String response = wt.path("md/" + MDNAME1.mdName() + "/ma/" +
+ MANAME1.maName() + "/mep/" + 2).request().get(String.class);
+ fail("Expected exception to be thrown");
+ } catch (InternalServerErrorException e) {
+ ByteArrayInputStream is = (ByteArrayInputStream) e.getResponse().getEntity();
+ BufferedReader br = new BufferedReader(new InputStreamReader(is));
+ String line = null;
+ StringBuffer sb = new StringBuffer();
+ while ((line = br.readLine()) != null) {
+ sb.append(line);
+ }
+ assertEquals("{ \"failure\":\"MEP md-1/ma-1-1/2 not found\" }", sb.toString());
+ }
+ }
+
+ @Test
+ public void testDeleteMepValid() throws CfmConfigException {
+
+ expect(mepService.deleteMep(MDNAME1, MANAME1, MepId.valueOf((short) 1)))
+ .andReturn(true).anyTimes();
+ replay(mepService);
+
+ final WebTarget wt = target();
+ final Response response = wt.path("md/" + MDNAME1.mdName() + "/ma/" +
+ MANAME1.maName() + "/mep/" + MEPID1.value()).request().delete();
+
+ assertEquals("Expecting 200", 200, response.getStatus());
+ }
+
+ @Test
+ public void testDeleteMepNotFound() throws CfmConfigException {
+
+ expect(mepService.deleteMep(MDNAME1, MANAME1, MepId.valueOf((short) 2)))
+ .andReturn(false).anyTimes();
+ replay(mepService);
+
+ final WebTarget wt = target();
+ final Response response = wt.path("md/" + MDNAME1.mdName() + "/ma/" +
+ MANAME1.maName() + "/mep/2").request().delete();
+
+ assertEquals("Expecting 304", 304, response.getStatus());
+ }
+
+ @Test
+ public void testCreateMep() throws CfmConfigException, IOException {
+ MepId mepId2 = MepId.valueOf((short) 2);
+ Mep mep2 = DefaultMep.builder(
+ mepId2,
+ DeviceId.deviceId("netconf:2.2.3.4:830"),
+ PortNumber.portNumber(2),
+ Mep.MepDirection.UP_MEP, MDNAME1, MANAME1).build();
+
+ MaintenanceAssociation ma1 = DefaultMaintenanceAssociation
+ .builder(MANAME1, MDNAME1.getNameLength()).build();
+
+ expect(mdService.getMaintenanceAssociation(MDNAME1, MANAME1))
+ .andReturn(Optional.ofNullable(ma1)).anyTimes();
+ replay(mdService);
+ expect(mepService.createMep(MDNAME1, MANAME1, mep2))
+ .andReturn(true).anyTimes();
+ replay(mepService);
+
+ ObjectMapper mapper = new ObjectMapper();
+ CfmCodecContext context = new CfmCodecContext();
+ ObjectNode node = mapper.createObjectNode();
+ node.set("mep", context.codec(Mep.class).encode(mep2, context));
+
+ final WebTarget wt = target();
+ final Response response = wt.path("md/" + MDNAME1.mdName() + "/ma/" +
+ MANAME1.maName() + "/mep")
+ .request()
+ .post(Entity.json(node.toString()));
+
+ assertEquals("Expecting 201", 201, response.getStatus());
+ }
+
+ @Test
+ public void testCreateMepAlreadyExists() throws CfmConfigException, IOException {
+ MepId mepId3 = MepId.valueOf((short) 3);
+ Mep mep3 = DefaultMep.builder(
+ mepId3,
+ DeviceId.deviceId("netconf:3.2.3.4:830"),
+ PortNumber.portNumber(3),
+ Mep.MepDirection.UP_MEP, MDNAME1, MANAME1)
+ .cciEnabled(true)
+ .ccmLtmPriority(Mep.Priority.PRIO3)
+ .administrativeState(false)
+ .primaryVid(VlanId.vlanId((short) 3))
+ .defectAbsentTime(Duration.ofMinutes(2))
+ .defectPresentTime(Duration.ofMinutes(3))
+ .fngAddress(Mep.FngAddress.notSpecified())
+ .lowestFaultPriorityDefect(Mep.LowestFaultDefect.ALL_DEFECTS)
+ .build();
+
+ MaintenanceAssociation ma1 = DefaultMaintenanceAssociation
+ .builder(MANAME1, MDNAME1.getNameLength()).build();
+
+ expect(mdService.getMaintenanceAssociation(MDNAME1, MANAME1))
+ .andReturn(Optional.ofNullable(ma1)).anyTimes();
+ replay(mdService);
+ expect(mepService.createMep(MDNAME1, MANAME1, mep3))
+ .andReturn(false).anyTimes();
+ replay(mepService);
+
+ ObjectMapper mapper = new ObjectMapper();
+ CfmCodecContext context = new CfmCodecContext();
+ ObjectNode node = mapper.createObjectNode();
+ node.set("mep", context.codec(Mep.class).encode(mep3, context));
+
+ final WebTarget wt = target();
+ final Response response = wt.path("md/" + MDNAME1.mdName() + "/ma/" +
+ MANAME1.maName() + "/mep")
+ .request()
+ .post(Entity.json(node.toString()));
+
+ assertEquals("Expecting 304", 304, response.getStatus());
+ }
+
+ @Test
+ public void testTransmitLoopback() throws CfmConfigException {
+ MepLbCreate mepLbCreate1 = DefaultMepLbCreate
+ .builder(MEPID1)
+ .numberMessages(20)
+ .dataTlvHex("AA:BB:CC:DD")
+ .vlanDropEligible(true)
+ .build();
+
+ MaintenanceAssociation ma1 = DefaultMaintenanceAssociation
+ .builder(MANAME1, MDNAME1.getNameLength()).build();
+ MaintenanceDomain md1 = DefaultMaintenanceDomain.builder(MDNAME1)
+ .addToMaList(ma1).build();
+ expect(mdService.getMaintenanceDomain(MDNAME1))
+ .andReturn(Optional.ofNullable(md1)).anyTimes();
+ expect(mdService.getMaintenanceAssociation(MDNAME1, MANAME1))
+ .andReturn(Optional.ofNullable(ma1)).anyTimes();
+ replay(mdService);
+
+ ObjectMapper mapper = new ObjectMapper();
+ CfmCodecContext context = new CfmCodecContext();
+ ObjectNode node = mapper.createObjectNode();
+ node.set("loopback", context.codec(MepLbCreate.class).encode(mepLbCreate1, context));
+
+ final WebTarget wt = target();
+ final Response response = wt.path("md/" + MDNAME1.mdName() + "/ma/" +
+ MANAME1.maName() + "/mep/" + MEPID1.value() + "/transmit-loopback")
+ .request()
+ .put(Entity.json(node.toString()));
+
+ assertEquals("Expecting 202", 202, response.getStatus());
+ }
+
+ @Test
+ public void testAbortLoopback() throws CfmConfigException {
+
+ MepLbCreate mepLbCreate1 = DefaultMepLbCreate.builder(MEPID1).build();
+
+ MaintenanceAssociation ma1 = DefaultMaintenanceAssociation
+ .builder(MANAME1, MDNAME1.getNameLength()).build();
+ MaintenanceDomain md1 = DefaultMaintenanceDomain.builder(MDNAME1)
+ .addToMaList(ma1).build();
+ expect(mdService.getMaintenanceDomain(MDNAME1))
+ .andReturn(Optional.ofNullable(md1)).anyTimes();
+ expect(mdService.getMaintenanceAssociation(MDNAME1, MANAME1))
+ .andReturn(Optional.ofNullable(ma1)).anyTimes();
+ replay(mdService);
+
+ final WebTarget wt = target();
+ final Response response = wt.path("md/" + MDNAME1.mdName() + "/ma/" +
+ MANAME1.maName() + "/mep/" + MEPID1.value() + "/abort-loopback")
+ .request()
+ .put(Entity.json(""));
+
+ assertEquals("Expecting 202", 202, response.getStatus());
+
+ }
+
+ @Test
+ public void testTransmitLinktrace() throws CfmConfigException {
+ MepLtCreate mepLtCreate1 = DefaultMepLtCreate
+ .builder(MEPID1)
+ .defaultTtl((short) 20)
+ .transmitLtmFlags(BitSet.valueOf(new byte[]{1}))
+ .build();
+
+ MaintenanceAssociation ma1 = DefaultMaintenanceAssociation
+ .builder(MANAME1, MDNAME1.getNameLength()).build();
+ MaintenanceDomain md1 = DefaultMaintenanceDomain.builder(MDNAME1)
+ .addToMaList(ma1).build();
+ expect(mdService.getMaintenanceDomain(MDNAME1))
+ .andReturn(Optional.ofNullable(md1)).anyTimes();
+ expect(mdService.getMaintenanceAssociation(MDNAME1, MANAME1))
+ .andReturn(Optional.ofNullable(ma1)).anyTimes();
+ replay(mdService);
+
+ ObjectMapper mapper = new ObjectMapper();
+ CfmCodecContext context = new CfmCodecContext();
+ ObjectNode node = mapper.createObjectNode();
+ node.set("linktrace", context.codec(MepLtCreate.class).encode(mepLtCreate1, context));
+
+ final WebTarget wt = target();
+ final Response response = wt.path("md/" + MDNAME1.mdName() + "/ma/" +
+ MANAME1.maName() + "/mep/" + MEPID1.value() + "/transmit-linktrace")
+ .request()
+ .put(Entity.json(node.toString()));
+
+ assertEquals("Expecting 202", 202, response.getStatus());
+ }
+}
diff --git a/apps/cfm/src/test/java/org/onosproject/cfm/web/MaintenanceAssociationCodecTest.java b/apps/cfm/src/test/java/org/onosproject/cfm/web/MaintenanceAssociationCodecTest.java
new file mode 100644
index 0000000..9712c7d
--- /dev/null
+++ b/apps/cfm/src/test/java/org/onosproject/cfm/web/MaintenanceAssociationCodecTest.java
@@ -0,0 +1,248 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.cfm.web;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import org.junit.Before;
+import org.junit.Test;
+import org.onosproject.cfm.CfmCodecContext;
+import org.onosproject.incubator.net.l2monitoring.cfm.DefaultMaintenanceAssociation;
+import org.onosproject.incubator.net.l2monitoring.cfm.MaintenanceAssociation;
+import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MaId2Octet;
+import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MaIdCharStr;
+import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MaIdIccY1731;
+import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MaIdPrimaryVid;
+import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MaIdRfc2685VpnId;
+import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MaIdShort;
+import org.onosproject.incubator.net.l2monitoring.cfm.service.CfmConfigException;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Test that the MaintenanceAssociationCodec can successfully parse Json in to a MaintenanceAssociation.
+ */
+public class MaintenanceAssociationCodecTest {
+ private static final MaIdShort MAID1_CHAR = MaIdCharStr.asMaId("ma-1");
+ private static final MaIdShort MAID2_VID = MaIdPrimaryVid.asMaId(1234);
+ private static final MaIdShort MAID3_OCTET = MaId2Octet.asMaId(12467);
+ private static final MaIdShort MAID4_RFC = MaIdRfc2685VpnId.asMaIdHex("aa:bb:cc:dd:ee:ff:99");
+ private static final MaIdShort MAID5_Y1731 = MaIdIccY1731.asMaId("abc", "defghij");
+
+
+ private ObjectMapper mapper;
+ private CfmCodecContext context;
+
+ @Before
+ public void setUp() throws Exception, CfmConfigException {
+ mapper = new ObjectMapper();
+ context = new CfmCodecContext();
+ }
+
+ @Test
+ public void testDecodeMa1() throws IOException {
+ String mdString = "{\"ma\": { \"maName\": \"ma-1\"," +
+ "\"maNameType\": \"CHARACTERSTRING\"," +
+ "\"component-list\": [], " +
+ "\"rmep-list\": [], " +
+ "\"maNumericId\": 1}}";
+
+ InputStream input = new ByteArrayInputStream(
+ mdString.getBytes(StandardCharsets.UTF_8));
+ JsonNode cfg = mapper.readTree(input);
+ MaintenanceAssociation maDecode1 = ((MaintenanceAssociationCodec) context
+ .codec(MaintenanceAssociation.class))
+ .decode((ObjectNode) cfg, context, 10);
+ assertEquals(MAID1_CHAR, maDecode1.maId());
+ assertEquals(1, maDecode1.maNumericId());
+ }
+
+ @Test
+ public void testDecodeMa1NoTypeGiven() throws IOException {
+ String mdString = "{\"ma\": { \"maName\": \"ma-1\"," +
+ "\"component-list\": [], " +
+ "\"rmep-list\": [], " +
+ "\"maNumericId\": 1}}";
+
+ InputStream input = new ByteArrayInputStream(
+ mdString.getBytes(StandardCharsets.UTF_8));
+ JsonNode cfg = mapper.readTree(input);
+ MaintenanceAssociation maDecode1 = ((MaintenanceAssociationCodec) context
+ .codec(MaintenanceAssociation.class))
+ .decode((ObjectNode) cfg, context, 10);
+ assertEquals(MAID1_CHAR, maDecode1.maId());
+ assertEquals(1, maDecode1.maNumericId());
+ }
+
+ @Test
+ public void testDecodeMa2() throws IOException {
+ String mdString = "{\"ma\": { \"maName\": 1234," +
+ "\"maNameType\": \"PRIMARYVID\"," +
+ "\"component-list\": [], " +
+ "\"rmep-list\": [], " +
+ "\"maNumericId\": 2}}";
+
+ InputStream input = new ByteArrayInputStream(
+ mdString.getBytes(StandardCharsets.UTF_8));
+ JsonNode cfg = mapper.readTree(input);
+ MaintenanceAssociation maDecode2 = ((MaintenanceAssociationCodec) context
+ .codec(MaintenanceAssociation.class))
+ .decode((ObjectNode) cfg, context, 10);
+ assertEquals(MAID2_VID, maDecode2.maId());
+ }
+
+ @Test
+ public void testDecodeMa3() throws IOException {
+ String mdString = "{\"ma\": { \"maName\": 12467," +
+ "\"maNameType\": \"TWOOCTET\"," +
+ "\"component-list\": [], " +
+ "\"rmep-list\": [], " +
+ "\"maNumericId\": 3}}";
+
+ InputStream input = new ByteArrayInputStream(
+ mdString.getBytes(StandardCharsets.UTF_8));
+ JsonNode cfg = mapper.readTree(input);
+ MaintenanceAssociation maDecode3 = ((MaintenanceAssociationCodec) context
+ .codec(MaintenanceAssociation.class))
+ .decode((ObjectNode) cfg, context, 10);
+ assertEquals(MAID3_OCTET, maDecode3.maId());
+ }
+
+ @Test
+ public void testDecodeMa4() throws IOException {
+ String mdString = "{\"ma\": { \"maName\": \"aa:bb:cc:dd:ee:ff:99\"," +
+ "\"maNameType\": \"RFC2685VPNID\"," +
+ "\"component-list\": [], " +
+ "\"rmep-list\": [], " +
+ "\"maNumericId\": 4}}";
+
+ InputStream input = new ByteArrayInputStream(
+ mdString.getBytes(StandardCharsets.UTF_8));
+ JsonNode cfg = mapper.readTree(input);
+ MaintenanceAssociation maDecode4 = ((MaintenanceAssociationCodec) context
+ .codec(MaintenanceAssociation.class))
+ .decode((ObjectNode) cfg, context, 10);
+ assertEquals(MAID4_RFC, maDecode4.maId());
+ }
+
+ @Test
+ public void testDecodeMa5() throws IOException {
+ String mdString = "{\"ma\": { \"maName\": \"abc:defghij\"," +
+ "\"maNameType\": \"ICCY1731\"," +
+ "\"component-list\": [], " +
+ "\"rmep-list\": [], " +
+ "\"maNumericId\": 5}}";
+
+ InputStream input = new ByteArrayInputStream(
+ mdString.getBytes(StandardCharsets.UTF_8));
+ JsonNode cfg = mapper.readTree(input);
+ MaintenanceAssociation maDecode5 = ((MaintenanceAssociationCodec) context
+ .codec(MaintenanceAssociation.class))
+ .decode((ObjectNode) cfg, context, 10);
+ assertEquals(MAID5_Y1731, maDecode5.maId());
+ }
+
+ @Test
+ public void testEncodeMa1() throws CfmConfigException {
+ MaintenanceAssociation ma1 = DefaultMaintenanceAssociation.builder(MAID1_CHAR, 10)
+ .maNumericId((short) 1)
+ .build();
+
+ ObjectNode node = mapper.createObjectNode();
+ node.set("ma", context.codec(MaintenanceAssociation.class).encode(ma1, context));
+
+ assertEquals("{\"ma\":{" +
+ "\"maName\":\"ma-1\"," +
+ "\"maNameType\":\"CHARACTERSTRING\"," +
+ "\"maNumericId\":1," +
+ "\"component-list\":[]," +
+ "\"rmep-list\":[]}}", node.toString());
+ }
+
+ @Test
+ public void testEncodeMa2() throws CfmConfigException {
+ MaintenanceAssociation ma1 = DefaultMaintenanceAssociation.builder(MAID2_VID, 10)
+ .maNumericId((short) 2)
+ .build();
+
+ ObjectNode node = mapper.createObjectNode();
+ node.set("ma", context.codec(MaintenanceAssociation.class).encode(ma1, context));
+
+ assertEquals("{\"ma\":{" +
+ "\"maName\":\"1234\"," +
+ "\"maNameType\":\"PRIMARYVID\"," +
+ "\"maNumericId\":2," +
+ "\"component-list\":[]," +
+ "\"rmep-list\":[]}}", node.toString());
+ }
+
+ @Test
+ public void testEncodeMa3() throws CfmConfigException {
+ MaintenanceAssociation ma1 = DefaultMaintenanceAssociation.builder(MAID3_OCTET, 10)
+ .maNumericId((short) 3)
+ .build();
+
+ ObjectNode node = mapper.createObjectNode();
+ node.set("ma", context.codec(MaintenanceAssociation.class).encode(ma1, context));
+
+ assertEquals("{\"ma\":{" +
+ "\"maName\":\"12467\"," +
+ "\"maNameType\":\"TWOOCTET\"," +
+ "\"maNumericId\":3," +
+ "\"component-list\":[]," +
+ "\"rmep-list\":[]}}", node.toString());
+ }
+
+ @Test
+ public void testEncodeMa4() throws CfmConfigException {
+ MaintenanceAssociation ma1 = DefaultMaintenanceAssociation.builder(MAID4_RFC, 10)
+ .maNumericId((short) 4)
+ .build();
+
+ ObjectNode node = mapper.createObjectNode();
+ node.set("ma", context.codec(MaintenanceAssociation.class).encode(ma1, context));
+
+ assertEquals("{\"ma\":{" +
+ "\"maName\":\"aa:bb:cc:dd:ee:ff:99\"," +
+ "\"maNameType\":\"RFC2685VPNID\"," +
+ "\"maNumericId\":4," +
+ "\"component-list\":[]," +
+ "\"rmep-list\":[]}}", node.toString());
+ }
+
+ @Test
+ public void testEncodeMa5() throws CfmConfigException {
+ MaintenanceAssociation ma1 = DefaultMaintenanceAssociation.builder(MAID5_Y1731, 10)
+ .maNumericId((short) 5)
+ .build();
+
+ ObjectNode node = mapper.createObjectNode();
+ node.set("ma", context.codec(MaintenanceAssociation.class).encode(ma1, context));
+
+ assertEquals("{\"ma\":{" +
+ "\"maName\":\"abc:defghij\"," +
+ "\"maNameType\":\"ICCY1731\"," +
+ "\"maNumericId\":5," +
+ "\"component-list\":[]," +
+ "\"rmep-list\":[]}}", node.toString());
+ }
+}
diff --git a/apps/cfm/src/test/java/org/onosproject/cfm/web/MaintenanceDomainCodecTest.java b/apps/cfm/src/test/java/org/onosproject/cfm/web/MaintenanceDomainCodecTest.java
new file mode 100644
index 0000000..b1fff52
--- /dev/null
+++ b/apps/cfm/src/test/java/org/onosproject/cfm/web/MaintenanceDomainCodecTest.java
@@ -0,0 +1,197 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.cfm.web;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import com.google.common.net.InternetDomainName;
+import org.junit.Before;
+import org.junit.Test;
+import org.onlab.packet.MacAddress;
+import org.onosproject.cfm.CfmCodecContext;
+import org.onosproject.incubator.net.l2monitoring.cfm.DefaultMaintenanceDomain;
+import org.onosproject.incubator.net.l2monitoring.cfm.MaintenanceDomain;
+import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MdId;
+import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MdIdCharStr;
+import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MdIdDomainName;
+import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MdIdMacUint;
+import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MdIdNone;
+import org.onosproject.incubator.net.l2monitoring.cfm.service.CfmConfigException;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Test that the MaintenanceDomainCodec can successfully parse Json in to a MaintenanceDomain.
+ */
+public class MaintenanceDomainCodecTest {
+ private static final MdId MDID1_CHAR = MdIdCharStr.asMdId("test-1");
+ private static final MdId MDID2_DOMAIN = MdIdDomainName.asMdId(
+ InternetDomainName.from("test.opennetworking.org"));
+ private static final MdId MDID3_MACUINT =
+ MdIdMacUint.asMdId(MacAddress.valueOf("aa:bb:cc:dd:ee:ff"), 181);
+ private static final MdId MDID4_NONE = MdIdNone.asMdId();
+
+ private ObjectMapper mapper;
+ private CfmCodecContext context;
+
+ @Before
+ public void setUp() throws Exception, CfmConfigException {
+ mapper = new ObjectMapper();
+ context = new CfmCodecContext();
+ }
+
+ @Test
+ public void testDecodeMd1() throws IOException {
+ String mdString = "{\"md\": { \"mdName\": \"test-1\"," +
+ "\"mdNameType\": \"CHARACTERSTRING\"," +
+ "\"mdLevel\": \"LEVEL1\", \"mdNumericId\": 1}}";
+
+ InputStream input = new ByteArrayInputStream(
+ mdString.getBytes(StandardCharsets.UTF_8));
+ JsonNode cfg = mapper.readTree(input);
+ MaintenanceDomain mdDecode1 = context
+ .codec(MaintenanceDomain.class).decode((ObjectNode) cfg, context);
+ assertEquals(MDID1_CHAR, mdDecode1.mdId());
+ assertEquals(MaintenanceDomain.MdLevel.LEVEL1, mdDecode1.mdLevel());
+ assertEquals(1, mdDecode1.mdNumericId());
+ }
+
+ @Test
+ public void testDecodeMd1NoTypeGiven() throws IOException {
+ String mdString = "{\"md\": { \"mdName\": \"test-1\"," +
+ "\"mdLevel\": \"LEVEL1\", \"mdNumericId\": 1}}";
+
+ InputStream input = new ByteArrayInputStream(
+ mdString.getBytes(StandardCharsets.UTF_8));
+ JsonNode cfg = mapper.readTree(input);
+ MaintenanceDomain mdDecode1 = context
+ .codec(MaintenanceDomain.class).decode((ObjectNode) cfg, context);
+ assertEquals(MDID1_CHAR, mdDecode1.mdId());
+ assertEquals(MaintenanceDomain.MdLevel.LEVEL1, mdDecode1.mdLevel());
+ assertEquals(1, mdDecode1.mdNumericId());
+ }
+
+
+ @Test
+ public void testDecodeMd2() throws IOException {
+ String mdString = "{\"md\": { \"mdName\": \"test.opennetworking.org\"," +
+ "\"mdNameType\": \"DOMAINNAME\"}}";
+
+ InputStream input = new ByteArrayInputStream(
+ mdString.getBytes(StandardCharsets.UTF_8));
+ JsonNode cfg = mapper.readTree(input);
+ MaintenanceDomain mdDecode1 = context
+ .codec(MaintenanceDomain.class).decode((ObjectNode) cfg, context);
+ assertEquals(MDID2_DOMAIN, mdDecode1.mdId());
+ assertEquals(MaintenanceDomain.MdLevel.LEVEL0, mdDecode1.mdLevel());
+ assertEquals(0, mdDecode1.mdNumericId());
+ }
+
+ @Test
+ public void testDecodeMd3() throws IOException {
+ String mdString = "{\"md\": { \"mdName\": \"aa:bb:cc:dd:ee:ff:181\"," +
+ "\"mdNameType\": \"MACANDUINT\"}}";
+
+ InputStream input = new ByteArrayInputStream(
+ mdString.getBytes(StandardCharsets.UTF_8));
+ JsonNode cfg = mapper.readTree(input);
+ MaintenanceDomain mdDecode1 = context
+ .codec(MaintenanceDomain.class).decode((ObjectNode) cfg, context);
+ assertEquals(MDID3_MACUINT, mdDecode1.mdId());
+ }
+
+ @Test
+ public void testDecodeMd4() throws IOException {
+ String mdString = "{\"md\": { \"mdName\": \"\"," +
+ "\"mdNameType\": \"NONE\"}}";
+
+ InputStream input = new ByteArrayInputStream(
+ mdString.getBytes(StandardCharsets.UTF_8));
+ JsonNode cfg = mapper.readTree(input);
+ MaintenanceDomain mdDecode1 = context
+ .codec(MaintenanceDomain.class).decode((ObjectNode) cfg, context);
+ assertEquals(MDID4_NONE, mdDecode1.mdId());
+ }
+
+ @Test
+ public void testEncodeMd1() throws CfmConfigException {
+ MaintenanceDomain md1 = DefaultMaintenanceDomain.builder(MDID1_CHAR)
+ .mdLevel(MaintenanceDomain.MdLevel.LEVEL1)
+ .mdNumericId((short) 1)
+ .build();
+
+ ObjectNode node = mapper.createObjectNode();
+ node.set("md", context.codec(MaintenanceDomain.class).encode(md1, context));
+
+ assertEquals("{\"md\":{" +
+ "\"mdName\":\"test-1\"," +
+ "\"mdNameType\":\"CHARACTERSTRING\"," +
+ "\"mdLevel\":\"LEVEL1\"," +
+ "\"mdNumericId\":1," +
+ "\"maList\":[]}}", node.toString());
+ }
+
+ @Test
+ public void testEncodeMd2() throws CfmConfigException {
+ MaintenanceDomain md2 = DefaultMaintenanceDomain.builder(MDID2_DOMAIN)
+ .mdLevel(MaintenanceDomain.MdLevel.LEVEL2).build();
+
+ ObjectNode node = mapper.createObjectNode();
+ node.set("md", context.codec(MaintenanceDomain.class).encode(md2, context));
+
+ assertEquals("{\"md\":{" +
+ "\"mdName\":\"test.opennetworking.org\"," +
+ "\"mdNameType\":\"DOMAINNAME\"," +
+ "\"mdLevel\":\"LEVEL2\"," +
+ "\"maList\":[]}}", node.toString());
+ }
+
+ @Test
+ public void testEncodeMd3() throws CfmConfigException {
+ MaintenanceDomain md3 = DefaultMaintenanceDomain.builder(MDID3_MACUINT)
+ .mdLevel(MaintenanceDomain.MdLevel.LEVEL3).build();
+
+ ObjectNode node = mapper.createObjectNode();
+ node.set("md", context.codec(MaintenanceDomain.class).encode(md3, context));
+
+ assertEquals("{\"md\":{" +
+ "\"mdName\":\"AA:BB:CC:DD:EE:FF:181\"," +
+ "\"mdNameType\":\"MACANDUINT\"," +
+ "\"mdLevel\":\"LEVEL3\"," +
+ "\"maList\":[]}}", node.toString());
+ }
+
+ @Test
+ public void testEncodeMd4() throws CfmConfigException {
+ MaintenanceDomain md4 = DefaultMaintenanceDomain.builder(MDID4_NONE)
+ .mdLevel(MaintenanceDomain.MdLevel.LEVEL4).build();
+
+ ObjectNode node = mapper.createObjectNode();
+ node.set("md", context.codec(MaintenanceDomain.class).encode(md4, context));
+
+ assertEquals("{\"md\":{" +
+ "\"mdName\":\"\"," +
+ "\"mdNameType\":\"NONE\"," +
+ "\"mdLevel\":\"LEVEL4\"," +
+ "\"maList\":[]}}", node.toString());
+ }
+}
diff --git a/apps/cfm/src/test/java/org/onosproject/cfm/web/MepEntryCodecTest.java b/apps/cfm/src/test/java/org/onosproject/cfm/web/MepEntryCodecTest.java
new file mode 100644
index 0000000..ea07b8e
--- /dev/null
+++ b/apps/cfm/src/test/java/org/onosproject/cfm/web/MepEntryCodecTest.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.cfm.web;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.onlab.packet.MacAddress;
+import org.onosproject.cfm.CfmCodecContext;
+import org.onosproject.incubator.net.l2monitoring.cfm.DefaultMepEntry;
+import org.onosproject.incubator.net.l2monitoring.cfm.Mep.MepDirection;
+import org.onosproject.incubator.net.l2monitoring.cfm.Mep.Priority;
+import org.onosproject.incubator.net.l2monitoring.cfm.MepEntry;
+import org.onosproject.incubator.net.l2monitoring.cfm.MepEntry.MepEntryBuilder;
+import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MaIdCharStr;
+import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MdIdCharStr;
+import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MepId;
+import org.onosproject.incubator.net.l2monitoring.cfm.service.CfmConfigException;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.PortNumber;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+
+/**
+ * Test that the MepEntryCodec can successfully parse Json in to a Mep.
+ */
+public class MepEntryCodecTest {
+ ObjectMapper mapper;
+ CfmCodecContext context;
+ MepEntry mepEntry1;
+
+ @Before
+ public void setUp() throws Exception, CfmConfigException {
+ mapper = new ObjectMapper();
+ context = new CfmCodecContext();
+ MepEntryBuilder builder = DefaultMepEntry.builder(
+ MepId.valueOf((short) 22),
+ DeviceId.deviceId("netconf:1234:830"),
+ PortNumber.portNumber(2),
+ MepDirection.UP_MEP,
+ MdIdCharStr.asMdId("md-1"),
+ MaIdCharStr.asMaId("ma-1-1"))
+ .macAddress(MacAddress.valueOf("aa:bb:cc:dd:ee:ff"));
+ builder = (MepEntryBuilder) builder
+ .administrativeState(true)
+ .cciEnabled(true)
+ .ccmLtmPriority(Priority.PRIO1);
+ mepEntry1 = builder.buildEntry();
+
+ }
+
+ @Test
+ public void testEncodeMepEntryCodecContext() {
+ ObjectNode node = mapper.createObjectNode();
+ node.set("mep", context.codec(MepEntry.class).encode(mepEntry1, context));
+
+ assertEquals(22, node.get("mep").get("mepId").asInt());
+ assertEquals("aa:bb:cc:dd:ee:ff".toUpperCase(),
+ node.get("mep").get("macAddress").asText());
+ assertTrue(node.get("mep").get("administrative-state").asBoolean());
+ assertTrue(node.get("mep").get("cci-enabled").asBoolean());
+ assertEquals(Priority.PRIO1.ordinal(),
+ node.get("mep").get("ccm-ltm-priority").asInt());
+ }
+
+ @Test
+ public void testEncodeIterableOfMepEntryCodecContext() throws CfmConfigException {
+ MepEntry mepEntry2 = DefaultMepEntry.builder(
+ MepId.valueOf((short) 33),
+ DeviceId.deviceId("netconf:4321:830"),
+ PortNumber.portNumber(1),
+ MepDirection.DOWN_MEP,
+ MdIdCharStr.asMdId("md-2"),
+ MaIdCharStr.asMaId("ma-2-2"))
+ .buildEntry();
+
+ ArrayList<MepEntry> meps = new ArrayList<>();
+ meps.add(mepEntry1);
+ meps.add(mepEntry2);
+
+ ObjectNode node = mapper.createObjectNode();
+ node.set("mep", context.codec(MepEntry.class)
+ .encode(meps, context));
+
+ Iterator<JsonNode> an = node.get("mep").elements();
+ while (an.hasNext()) {
+ JsonNode jn = an.next();
+ assertEquals("md-", jn.get("mdName").asText().substring(0, 3));
+ }
+ }
+
+}
diff --git a/apps/cfm/src/test/java/org/onosproject/cfm/web/MepLbCreateCodecTest.java b/apps/cfm/src/test/java/org/onosproject/cfm/web/MepLbCreateCodecTest.java
new file mode 100644
index 0000000..e2c853a
--- /dev/null
+++ b/apps/cfm/src/test/java/org/onosproject/cfm/web/MepLbCreateCodecTest.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.cfm.web;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.onosproject.cfm.CfmCodecContext;
+import org.onosproject.incubator.net.l2monitoring.cfm.MepLbCreate;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+
+public class MepLbCreateCodecTest {
+ ObjectMapper mapper;
+ CfmCodecContext context;
+
+ @Before
+ public void setUp() throws Exception {
+ mapper = new ObjectMapper();
+ context = new CfmCodecContext();
+ }
+
+ @Test
+ public void testDecodeMepLbCreateMepId() throws JsonProcessingException, IOException {
+ String loopbackString = "{\"loopback\": { \"remoteMepId\": 20," +
+ "\"numberMessages\": 10, \"vlanDropEligible\": true," +
+ "\"vlanPriority\": 6, \"dataTlvHex\": \"0A:BB:CC\" }}";
+
+ InputStream input = new ByteArrayInputStream(
+ loopbackString.getBytes(StandardCharsets.UTF_8));
+ JsonNode cfg = mapper.readTree(input);
+ MepLbCreate mepLbCreate = context
+ .codec(MepLbCreate.class).decode((ObjectNode) cfg, context);
+
+ assertNull(mepLbCreate.remoteMepAddress());
+ assertEquals(20, mepLbCreate.remoteMepId().id().shortValue());
+ assertEquals(10, mepLbCreate.numberMessages().intValue());
+ assertEquals(6, mepLbCreate.vlanPriority().ordinal());
+ assertEquals(true, mepLbCreate.vlanDropEligible());
+ assertEquals("0A:BB:CC".toLowerCase(), mepLbCreate.dataTlvHex());
+ }
+
+ @Test
+ public void testDecodeMepLbCreateMepMac() throws JsonProcessingException, IOException {
+ String loopbackString = "{\"loopback\": { " +
+ "\"remoteMepMac\": \"AA:BB:CC:DD:EE:FF\" }}";
+ InputStream input = new ByteArrayInputStream(
+ loopbackString.getBytes(StandardCharsets.UTF_8));
+ JsonNode cfg = mapper.readTree(input);
+ MepLbCreate mepLbCreate = context
+ .codec(MepLbCreate.class).decode((ObjectNode) cfg, context);
+
+ assertNull(mepLbCreate.remoteMepId());
+ assertEquals("AA:BB:CC:DD:EE:FF", mepLbCreate.remoteMepAddress().toString());
+ assertNull(mepLbCreate.dataTlvHex());
+ }
+}
diff --git a/apps/cfm/src/test/java/org/onosproject/cfm/web/MepLbEntryCodecTest.java b/apps/cfm/src/test/java/org/onosproject/cfm/web/MepLbEntryCodecTest.java
new file mode 100644
index 0000000..844e4a2
--- /dev/null
+++ b/apps/cfm/src/test/java/org/onosproject/cfm/web/MepLbEntryCodecTest.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.cfm.web;
+
+import static org.junit.Assert.assertEquals;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.onosproject.cfm.CfmCodecContext;
+import org.onosproject.incubator.net.l2monitoring.cfm.DefaultMepLbEntry;
+import org.onosproject.incubator.net.l2monitoring.cfm.MepLbEntry;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+public class MepLbEntryCodecTest {
+ ObjectMapper mapper;
+ CfmCodecContext context;
+
+ @Before
+ public void setUp() throws Exception {
+ mapper = new ObjectMapper();
+ context = new CfmCodecContext();
+ }
+
+ @Test
+ public void testEncodeMepLbEntryCodecContext() {
+ MepLbEntry mepLbEntry1 = DefaultMepLbEntry.builder()
+ .countLbrMacMisMatch(987654321L)
+ .countLbrReceived(987654322L)
+ .countLbrTransmitted(987654323L)
+ .countLbrValidInOrder(987654324L)
+ .countLbrValidOutOfOrder(987654325L)
+ .nextLbmIdentifier(987654326L)
+ .build();
+
+ assertEquals(987654321L, mepLbEntry1.countLbrMacMisMatch());
+ assertEquals(987654322L, mepLbEntry1.countLbrReceived());
+ assertEquals(987654323L, mepLbEntry1.countLbrTransmitted());
+ assertEquals(987654324L, mepLbEntry1.countLbrValidInOrder());
+ assertEquals(987654325L, mepLbEntry1.countLbrValidOutOfOrder());
+ assertEquals(987654326L, mepLbEntry1.nextLbmIdentifier());
+ }
+
+}
diff --git a/apps/cfm/src/test/java/org/onosproject/cfm/web/MepLtCreateCodecTest.java b/apps/cfm/src/test/java/org/onosproject/cfm/web/MepLtCreateCodecTest.java
new file mode 100644
index 0000000..3e0ec46
--- /dev/null
+++ b/apps/cfm/src/test/java/org/onosproject/cfm/web/MepLtCreateCodecTest.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.cfm.web;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import org.junit.Before;
+import org.junit.Test;
+import org.onosproject.cfm.CfmCodecContext;
+import org.onosproject.incubator.net.l2monitoring.cfm.DefaultMepLtCreate;
+import org.onosproject.incubator.net.l2monitoring.cfm.MepLtCreate;
+import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MepId;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+import java.util.BitSet;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+
+public class MepLtCreateCodecTest {
+ ObjectMapper mapper;
+ CfmCodecContext context;
+
+ @Before
+ public void setUp() throws Exception {
+ mapper = new ObjectMapper();
+ context = new CfmCodecContext();
+ }
+
+ @Test
+ public void testDecodeMepLtCreateMepId() throws JsonProcessingException, IOException {
+ String linktraceString = "{\"linktrace\": { " +
+ "\"remoteMepId\": 20," +
+ "\"defaultTtl\": 21," +
+ "\"transmitLtmFlags\": \"use-fdb-only\"}}";
+
+ InputStream input = new ByteArrayInputStream(
+ linktraceString.getBytes(StandardCharsets.UTF_8));
+ JsonNode cfg = mapper.readTree(input);
+ MepLtCreate mepLtCreate = context
+ .codec(MepLtCreate.class).decode((ObjectNode) cfg, context);
+
+ assertNull(mepLtCreate.remoteMepAddress());
+ assertEquals(20, mepLtCreate.remoteMepId().id().shortValue());
+ assertEquals(21, mepLtCreate.defaultTtl().intValue());
+ assertEquals(BitSet.valueOf(new byte[]{1}), mepLtCreate.transmitLtmFlags());
+ }
+
+ @Test
+ public void testDecodeMepLtCreateInvalidTransmitLtmFlags()
+ throws JsonProcessingException, IOException {
+ String linktraceString = "{\"linktrace\": { " +
+ "\"remoteMepId\": 20," +
+ "\"transmitLtmFlags\": \"1\"}}";
+
+ InputStream input = new ByteArrayInputStream(
+ linktraceString.getBytes(StandardCharsets.UTF_8));
+ JsonNode cfg = mapper.readTree(input);
+ try {
+ context.codec(MepLtCreate.class).decode((ObjectNode) cfg, context);
+ } catch (IllegalArgumentException e) {
+ assertEquals("Expecting value 'use-fdb-only' or '' " +
+ "for transmitLtmFlags", e.getMessage());
+ }
+ }
+
+ @Test
+ public void testEncodeMepLtCreate() {
+ MepId mepId1 = MepId.valueOf((short) 1);
+ MepLtCreate mepLtCreate1 = DefaultMepLtCreate
+ .builder(mepId1)
+ .defaultTtl((short) 20)
+ .transmitLtmFlags(BitSet.valueOf(new byte[]{1}))
+ .build();
+
+ ObjectMapper mapper = new ObjectMapper();
+ CfmCodecContext context = new CfmCodecContext();
+ ObjectNode node = mapper.createObjectNode();
+ node.set("linktrace", context.codec(MepLtCreate.class).encode(mepLtCreate1, context));
+
+ assertEquals("{\"linktrace\":{" +
+ "\"remoteMepId\":1," +
+ "\"defaultTtl\":20," +
+ "\"transmitLtmFlags\":\"use-fdb-only\"}}", node.toString());
+ }
+}
diff --git a/apps/cfm/src/test/java/org/onosproject/cfm/web/RemoteMepEntryCodecTest.java b/apps/cfm/src/test/java/org/onosproject/cfm/web/RemoteMepEntryCodecTest.java
new file mode 100644
index 0000000..7ad77ac
--- /dev/null
+++ b/apps/cfm/src/test/java/org/onosproject/cfm/web/RemoteMepEntryCodecTest.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.cfm.web;
+
+import static org.junit.Assert.assertEquals;
+
+import java.time.Duration;
+import java.util.ArrayList;
+import java.util.Iterator;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.onlab.packet.MacAddress;
+import org.onosproject.cfm.CfmCodecContext;
+import org.onosproject.incubator.net.l2monitoring.cfm.DefaultRemoteMepEntry;
+import org.onosproject.incubator.net.l2monitoring.cfm.RemoteMepEntry;
+import org.onosproject.incubator.net.l2monitoring.cfm.RemoteMepEntry.InterfaceStatusTlvType;
+import org.onosproject.incubator.net.l2monitoring.cfm.RemoteMepEntry.PortStatusTlvType;
+import org.onosproject.incubator.net.l2monitoring.cfm.RemoteMepEntry.RemoteMepState;
+import org.onosproject.incubator.net.l2monitoring.cfm.SenderIdTlv.SenderIdTlvType;
+import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MepId;
+import org.onosproject.incubator.net.l2monitoring.cfm.service.CfmConfigException;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+
+public class RemoteMepEntryCodecTest {
+ ObjectMapper mapper;
+ CfmCodecContext context;
+ RemoteMepEntry remoteMep1;
+
+ @Before
+ public void setUp() throws Exception, CfmConfigException {
+ mapper = new ObjectMapper();
+ context = new CfmCodecContext();
+ remoteMep1 = DefaultRemoteMepEntry
+ .builder(MepId.valueOf((short) 10), RemoteMepState.RMEP_OK)
+ .failedOrOkTime(Duration.ofMillis(546546546L))
+ .interfaceStatusTlvType(InterfaceStatusTlvType.IS_LOWERLAYERDOWN)
+ .macAddress(MacAddress.IPV4_MULTICAST)
+ .portStatusTlvType(PortStatusTlvType.PS_NO_STATUS_TLV)
+ .rdi(true)
+ .senderIdTlvType(SenderIdTlvType.SI_NETWORK_ADDRESS)
+ .build();
+ }
+
+ @Test
+ public void testEncodeRemoteMepEntryCodecContext() {
+ ObjectNode node = mapper.createObjectNode();
+ node.set("remoteMep", context.codec(RemoteMepEntry.class)
+ .encode(remoteMep1, context));
+
+ assertEquals(10, node.get("remoteMep").get("remoteMepId").asInt());
+ }
+
+ @Test
+ public void testEncodeIterableOfRemoteMepEntryCodecContext()
+ throws CfmConfigException {
+ RemoteMepEntry remoteMep2 = DefaultRemoteMepEntry
+ .builder(MepId.valueOf((short) 20), RemoteMepState.RMEP_IDLE)
+ .build();
+
+ ArrayList<RemoteMepEntry> remoteMeps = new ArrayList<>();
+ remoteMeps.add(remoteMep1);
+ remoteMeps.add(remoteMep2);
+
+ ObjectNode node = mapper.createObjectNode();
+ node.set("remoteMep", context.codec(RemoteMepEntry.class)
+ .encode(remoteMeps, context));
+
+ Iterator<JsonNode> an = node.get("remoteMep").elements();
+ while (an.hasNext()) {
+ JsonNode jn = an.next();
+ assertEquals("RMEP_", jn.get("remoteMepState").asText().substring(0, 5));
+ }
+ }
+}
diff --git a/apps/cfm/src/test/java/org/onosproject/soam/impl/DmWebResourceTest.java b/apps/cfm/src/test/java/org/onosproject/soam/impl/DmWebResourceTest.java
new file mode 100644
index 0000000..702bc09
--- /dev/null
+++ b/apps/cfm/src/test/java/org/onosproject/soam/impl/DmWebResourceTest.java
@@ -0,0 +1,299 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.soam.impl;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import org.junit.Before;
+import org.junit.Test;
+import org.onlab.osgi.ServiceDirectory;
+import org.onlab.osgi.TestServiceDirectory;
+import org.onlab.rest.BaseResource;
+import org.onosproject.cfm.CfmCodecContext;
+import org.onosproject.cfm.impl.CfmResourceTest;
+import org.onosproject.codec.CodecService;
+import org.onosproject.incubator.net.l2monitoring.cfm.DefaultMepEntry;
+import org.onosproject.incubator.net.l2monitoring.cfm.Mep;
+import org.onosproject.incubator.net.l2monitoring.cfm.MepEntry;
+import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MaIdCharStr;
+import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MaIdShort;
+import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MdId;
+import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MdIdCharStr;
+import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MepId;
+import org.onosproject.incubator.net.l2monitoring.cfm.service.CfmConfigException;
+import org.onosproject.incubator.net.l2monitoring.cfm.service.CfmMepService;
+import org.onosproject.incubator.net.l2monitoring.soam.SoamConfigException;
+import org.onosproject.incubator.net.l2monitoring.soam.SoamId;
+import org.onosproject.incubator.net.l2monitoring.soam.SoamService;
+import org.onosproject.incubator.net.l2monitoring.soam.delay.DefaultDelayMeasurementEntry;
+import org.onosproject.incubator.net.l2monitoring.soam.delay.DefaultDelayMeasurementStatCurrent;
+import org.onosproject.incubator.net.l2monitoring.soam.delay.DelayMeasurementCreate;
+import org.onosproject.incubator.net.l2monitoring.soam.delay.DelayMeasurementEntry;
+import org.onosproject.incubator.net.l2monitoring.soam.delay.DelayMeasurementStatCurrent;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.PortNumber;
+
+import javax.ws.rs.InternalServerErrorException;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.Response;
+
+import java.io.BufferedReader;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.time.Duration;
+import java.time.Instant;
+import java.util.ArrayList;
+import java.util.List;
+
+import static junit.framework.TestCase.fail;
+import static org.easymock.EasyMock.*;
+import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+
+public class DmWebResourceTest extends CfmResourceTest {
+ private final CfmMepService mepService = createMock(CfmMepService.class);
+ private final SoamService soamService = createMock(SoamService.class);
+
+ private static final MdId MDNAME1 = MdIdCharStr.asMdId("md-1");
+ private static final MaIdShort MANAME1 = MaIdCharStr.asMaId("ma-1-1");
+ private static final MepId MEPID1 = MepId.valueOf((short) 1);
+ private static final SoamId DM1 = SoamId.valueOf(1);
+ private static final SoamId DM2 = SoamId.valueOf(2);
+
+ private DelayMeasurementEntry dm1;
+ private DelayMeasurementEntry dm2;
+
+ private final Instant now = Instant.now();
+
+ @Before
+ public void setUpTest() throws CfmConfigException, SoamConfigException {
+ CfmCodecContext context = new CfmCodecContext();
+ ServiceDirectory testDirectory = new TestServiceDirectory()
+ .add(CfmMepService.class, mepService)
+ .add(SoamService.class, soamService)
+ .add(CodecService.class, context.codecManager());
+ BaseResource.setServiceDirectory(testDirectory);
+
+ DelayMeasurementStatCurrent.DmStatCurrentBuilder dmCurrBuilder1 =
+ (DelayMeasurementStatCurrent.DmStatCurrentBuilder)
+ DefaultDelayMeasurementStatCurrent
+ .builder(Duration.ofMinutes(1), false)
+ .startTime(now)
+ .frameDelayBackwardAvg(Duration.ofMillis(10))
+ .frameDelayForwardAvg(Duration.ofMillis(11))
+ .frameDelayRangeBackwardAvg(Duration.ofMillis(12));
+
+ dm1 = DefaultDelayMeasurementEntry.builder(DM1,
+ DelayMeasurementCreate.DmType.DMDMM,
+ DelayMeasurementCreate.Version.Y17312008,
+ MepId.valueOf((short) 2),
+ Mep.Priority.PRIO1)
+ .sessionStatus(DelayMeasurementEntry.SessionStatus.ACTIVE)
+ .frameDelayTwoWay(Duration.ofMillis(40))
+ .frameDelayBackward(Duration.ofMillis(30))
+ .frameDelayForward(Duration.ofMillis(10))
+ .interFrameDelayVariationTwoWay(Duration.ofMillis(8))
+ .interFrameDelayVariationBackward(Duration.ofMillis(3))
+ .interFrameDelayVariationForward(Duration.ofMillis(5))
+ .currentResult((DelayMeasurementStatCurrent) dmCurrBuilder1.build())
+ .build();
+
+ dm2 = DefaultDelayMeasurementEntry.builder(DM2,
+ DelayMeasurementCreate.DmType.DMDMM,
+ DelayMeasurementCreate.Version.Y17312011,
+ MepId.valueOf((short) 2),
+ Mep.Priority.PRIO2)
+ .build();
+ }
+
+ @Test
+ public void testGetAllDmsForMep() throws CfmConfigException, SoamConfigException {
+
+ List<DelayMeasurementEntry> dmList = new ArrayList<>();
+ dmList.add(dm1);
+ dmList.add(dm2);
+
+ expect(soamService.getAllDms(MDNAME1, MANAME1, MEPID1)).andReturn(dmList).anyTimes();
+ replay(soamService);
+
+ final WebTarget wt = target();
+ final String response = wt.path("md/" + MDNAME1.mdName() + "/ma/" +
+ MANAME1.maName() + "/mep/" + MEPID1.value() + "/dm")
+ .request().get(String.class);
+
+ assertThat(response, is("{\"dms\":[[" +
+ "{" +
+ "\"dmId\":\"1\"," +
+ "\"sessionStatus\":\"ACTIVE\"," +
+ "\"frameDelayTwoWay\":\"PT0.04S\"," +
+ "\"frameDelayForward\":\"PT0.01S\"," +
+ "\"frameDelayBackward\":\"PT0.03S\"," +
+ "\"interFrameDelayVariationTwoWay\":\"PT0.008S\"," +
+ "\"interFrameDelayVariationForward\":\"PT0.005S\"," +
+ "\"interFrameDelayVariationBackward\":\"PT0.003S\"," +
+ "\"dmCfgType\":\"DMDMM\"," +
+ "\"version\":\"Y17312008\"," +
+ "\"remoteMepId\":2," +
+ "\"priority\":\"PRIO1\"," +
+ "\"measurementsEnabled\":[]," +
+ "\"current\":{" +
+ "\"startTime\":\"" + now + "\"," +
+ "\"elapsedTime\":\"PT1M\"," +
+ "\"suspectStatus\":\"false\"," +
+ "\"frameDelayForwardAvg\":\"PT0.011S\"," +
+ "\"frameDelayBackwardAvg\":\"PT0.01S\"," +
+ "\"frameDelayRangeBackwardAvg\":\"PT0.012S\"" +
+ "}," +
+ "\"historic\":[]" +
+ "},{" +
+ "\"dmId\":\"2\"," +
+ "\"dmCfgType\":\"DMDMM\"," +
+ "\"version\":\"Y17312011\"," +
+ "\"remoteMepId\":2," +
+ "\"priority\":\"PRIO2\"," +
+ "\"measurementsEnabled\":[]," +
+ "\"historic\":[]}]]" +
+ "}"));
+ }
+
+ @Test
+ public void testGetAllDmsForMepEmpty() throws CfmConfigException, SoamConfigException {
+
+ List<DelayMeasurementEntry> dmListEmpty = new ArrayList<>();
+
+ expect(soamService.getAllDms(MDNAME1, MANAME1, MEPID1)).andReturn(dmListEmpty).anyTimes();
+ replay(soamService);
+
+ final WebTarget wt = target();
+ final String response = wt.path("md/" + MDNAME1.mdName() + "/ma/" +
+ MANAME1.maName() + "/mep/" + MEPID1.value() + "/dm")
+ .request().get(String.class);
+
+ assertThat(response, is("{\"dms\":[[]]}"));
+ }
+
+ @Test
+ public void testGetDm() throws CfmConfigException, SoamConfigException {
+
+ expect(soamService.getDm(MDNAME1, MANAME1, MEPID1, DM1)).andReturn(dm1).anyTimes();
+ replay(soamService);
+
+ final WebTarget wt = target();
+ final String response = wt.path("md/" + MDNAME1.mdName() + "/ma/" +
+ MANAME1.maName() + "/mep/" + MEPID1.value() + "/dm/" + DM1.value())
+ .request().get(String.class);
+
+ assertThat(response, is("{\"dm\":" +
+ "{" +
+ "\"dmId\":\"1\"," +
+ "\"sessionStatus\":\"ACTIVE\"," +
+ "\"frameDelayTwoWay\":\"PT0.04S\"," +
+ "\"frameDelayForward\":\"PT0.01S\"," +
+ "\"frameDelayBackward\":\"PT0.03S\"," +
+ "\"interFrameDelayVariationTwoWay\":\"PT0.008S\"," +
+ "\"interFrameDelayVariationForward\":\"PT0.005S\"," +
+ "\"interFrameDelayVariationBackward\":\"PT0.003S\"," +
+ "\"dmCfgType\":\"DMDMM\"," +
+ "\"version\":\"Y17312008\"," +
+ "\"remoteMepId\":2," +
+ "\"priority\":\"PRIO1\"," +
+ "\"measurementsEnabled\":[]," +
+ "\"current\":{" +
+ "\"startTime\":\"" + now + "\"," +
+ "\"elapsedTime\":\"PT1M\"," +
+ "\"suspectStatus\":\"false\"," +
+ "\"frameDelayForwardAvg\":\"PT0.011S\"," +
+ "\"frameDelayBackwardAvg\":\"PT0.01S\"," +
+ "\"frameDelayRangeBackwardAvg\":\"PT0.012S\"" +
+ "}," +
+ "\"historic\":[]" +
+ "}}"));
+ }
+
+ @Test
+ public void testGetDmInvalid() throws CfmConfigException, SoamConfigException, IOException {
+
+ SoamId dm3 = SoamId.valueOf(3);
+
+ expect(soamService.getDm(MDNAME1, MANAME1, MEPID1, dm3)).andReturn(null).anyTimes();
+ replay(soamService);
+
+ final WebTarget wt = target();
+ try {
+ final String response = wt.path("md/" + MDNAME1.mdName() + "/ma/" +
+ MANAME1.maName() + "/mep/" + MEPID1.value() + "/dm/" + dm3.value())
+ .request().get(String.class);
+ fail("Expecting excpetion");
+ } catch (InternalServerErrorException e) {
+ ByteArrayInputStream is = (ByteArrayInputStream) e.getResponse().getEntity();
+ BufferedReader br = new BufferedReader(new InputStreamReader(is));
+ String line = null;
+ StringBuffer sb = new StringBuffer();
+ while ((line = br.readLine()) != null) {
+ sb.append(line);
+ }
+ assertEquals("{ \"failure\":\"DM md-1/ma-1-1/1/3 not found\" }", sb.toString());
+ }
+ }
+
+ @Test
+ public void testAbortDm() {
+
+ final WebTarget wt = target();
+ final Response response = wt.path("md/" + MDNAME1.mdName() + "/ma/" +
+ MANAME1.maName() + "/mep/" + MEPID1.value() + "/dm/" + DM1.value())
+ .request().delete();
+
+ assertEquals(200, response.getStatus());
+ }
+
+ @Test
+ public void testCreateDm() throws CfmConfigException, SoamConfigException {
+ MepEntry mep1 = DefaultMepEntry.builder(MEPID1, DeviceId.deviceId("netconf:1.2.3.4:830"),
+ PortNumber.portNumber(1), Mep.MepDirection.UP_MEP, MDNAME1, MANAME1).buildEntry();
+
+ expect(mepService.getMep(MDNAME1, MANAME1, MEPID1)).andReturn(mep1).anyTimes();
+ replay(mepService);
+
+ ObjectMapper mapper = new ObjectMapper();
+ CfmCodecContext context = new CfmCodecContext();
+ ObjectNode node = mapper.createObjectNode();
+ node.set("dm", context.codec(DelayMeasurementCreate.class).encode(dm1, context));
+
+ final WebTarget wt = target();
+ final Response response = wt.path("md/" + MDNAME1.mdName() + "/ma/" +
+ MANAME1.maName() + "/mep/" + MEPID1.value() + "/dm")
+ .request().post(Entity.json(node.toString()));
+
+ assertEquals(201, response.getStatus());
+ }
+
+ @Test
+ public void testClearDmHistory() {
+
+ final WebTarget wt = target();
+ final Response response = wt.path("md/" + MDNAME1.mdName() + "/ma/" +
+ MANAME1.maName() + "/mep/" + MEPID1.value() + "/dm/" + DM1.value() +
+ "/clear-history")
+ .request().put(Entity.json(""));
+
+ assertEquals(200, response.getStatus());
+ }
+
+}
diff --git a/apps/cfm/src/test/java/org/onosproject/soam/impl/LmWebResourceTest.java b/apps/cfm/src/test/java/org/onosproject/soam/impl/LmWebResourceTest.java
new file mode 100644
index 0000000..9dd70e6
--- /dev/null
+++ b/apps/cfm/src/test/java/org/onosproject/soam/impl/LmWebResourceTest.java
@@ -0,0 +1,262 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.soam.impl;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import org.junit.Before;
+import org.junit.Test;
+import org.onlab.osgi.ServiceDirectory;
+import org.onlab.osgi.TestServiceDirectory;
+import org.onlab.rest.BaseResource;
+import org.onosproject.cfm.CfmCodecContext;
+import org.onosproject.cfm.impl.CfmResourceTest;
+import org.onosproject.codec.CodecService;
+import org.onosproject.incubator.net.l2monitoring.cfm.DefaultMepEntry;
+import org.onosproject.incubator.net.l2monitoring.cfm.Mep;
+import org.onosproject.incubator.net.l2monitoring.cfm.MepEntry;
+import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MaIdCharStr;
+import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MaIdShort;
+import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MdId;
+import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MdIdCharStr;
+import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MepId;
+import org.onosproject.incubator.net.l2monitoring.cfm.service.CfmConfigException;
+import org.onosproject.incubator.net.l2monitoring.cfm.service.CfmMepService;
+import org.onosproject.incubator.net.l2monitoring.soam.MilliPct;
+import org.onosproject.incubator.net.l2monitoring.soam.SoamConfigException;
+import org.onosproject.incubator.net.l2monitoring.soam.SoamId;
+import org.onosproject.incubator.net.l2monitoring.soam.SoamService;
+import org.onosproject.incubator.net.l2monitoring.soam.delay.DelayMeasurementCreate;
+import org.onosproject.incubator.net.l2monitoring.soam.loss.DefaultLmEntry;
+import org.onosproject.incubator.net.l2monitoring.soam.loss.LossMeasurementCreate;
+import org.onosproject.incubator.net.l2monitoring.soam.loss.LossMeasurementEntry;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.PortNumber;
+
+import javax.ws.rs.InternalServerErrorException;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.Response;
+import java.io.BufferedReader;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.time.Instant;
+import java.util.ArrayList;
+import java.util.List;
+
+import static org.easymock.EasyMock.createMock;
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.replay;
+import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+
+public class LmWebResourceTest extends CfmResourceTest {
+ private final CfmMepService mepService = createMock(CfmMepService.class);
+ private final SoamService soamService = createMock(SoamService.class);
+
+ private static final MdId MDNAME1 = MdIdCharStr.asMdId("md-1");
+ private static final MaIdShort MANAME1 = MaIdCharStr.asMaId("ma-1-1");
+ private static final MepId MEPID1 = MepId.valueOf((short) 1);
+ private static final SoamId LMID1 = SoamId.valueOf(1);
+ private static final SoamId LMID2 = SoamId.valueOf(2);
+
+ private LossMeasurementEntry lm1;
+ private LossMeasurementEntry lm2;
+
+ private final Instant now = Instant.now();
+
+ @Before
+ public void setUpTest() throws CfmConfigException, SoamConfigException {
+ CfmCodecContext context = new CfmCodecContext();
+ ServiceDirectory testDirectory = new TestServiceDirectory()
+ .add(CfmMepService.class, mepService)
+ .add(SoamService.class, soamService)
+ .add(CodecService.class, context.codecManager());
+ BaseResource.setServiceDirectory(testDirectory);
+
+ lm1 = DefaultLmEntry.builder(
+ DelayMeasurementCreate.Version.Y17312008,
+ MepId.valueOf((short) 10),
+ Mep.Priority.PRIO1,
+ LossMeasurementCreate.LmType.LMLMM,
+ LMID1)
+ .build();
+ lm2 = DefaultLmEntry.builder(
+ DelayMeasurementCreate.Version.Y17312011,
+ MepId.valueOf((short) 10),
+ Mep.Priority.PRIO2,
+ LossMeasurementCreate.LmType.LMLMM,
+ LMID2)
+ .measuredAvailabilityBackwardStatus(LossMeasurementEntry.AvailabilityType.AVAILABLE)
+ .measuredAvailabilityForwardStatus(LossMeasurementEntry.AvailabilityType.UNKNOWN)
+ .measuredBackwardFlr(MilliPct.ofPercent(49.9f))
+ .measuredForwardFlr(MilliPct.ofRatio(0.51f))
+ .measuredBackwardLastTransitionTime(now)
+ .measuredForwardLastTransitionTime(now)
+ .build();
+
+ }
+
+ @Test
+ public void testGetAllLmsForMep() throws CfmConfigException, SoamConfigException {
+ List<LossMeasurementEntry> lmList = new ArrayList<>();
+ lmList.add(lm1);
+ lmList.add(lm2);
+
+ expect(soamService.getAllLms(MDNAME1, MANAME1, MEPID1)).andReturn(lmList).anyTimes();
+ replay(soamService);
+
+ final WebTarget wt = target();
+ final String response = wt.path("md/" + MDNAME1.mdName() + "/ma/" +
+ MANAME1.maName() + "/mep/" + MEPID1.value() + "/lm")
+ .request().get(String.class);
+
+ assertThat(response, is("{\"lms\":[[" +
+ "{" +
+ "\"lmId\":\"1\"," +
+ "\"lmCfgType\":\"LMLMM\"," +
+ "\"version\":\"Y17312008\"," +
+ "\"remoteMepId\":10," +
+ "\"priority\":\"PRIO1\"," +
+ "\"countersEnabled\":[]," +
+ "\"measurementHistories\":[]," +
+ "\"availabilityHistories\":[]" +
+ "},{" +
+ "\"lmId\":\"2\"," +
+ "\"measuredForwardFlr\":51.0," +
+ "\"measuredBackwardFlr\":49.9," +
+ "\"measuredAvailabilityForwardStatus\":\"UNKNOWN\"," +
+ "\"measuredAvailabilityBackwardStatus\":\"AVAILABLE\"," +
+ "\"measuredForwardLastTransitionTime\":\"" + now + "\"," +
+ "\"measuredBackwardLastTransitionTime\":\"" + now + "\"," +
+ "\"lmCfgType\":\"LMLMM\"," +
+ "\"version\":\"Y17312011\"," +
+ "\"remoteMepId\":10," +
+ "\"priority\":\"PRIO2\"," +
+ "\"countersEnabled\":[]," +
+ "\"measurementHistories\":[]," +
+ "\"availabilityHistories\":[]" +
+ "}]]}"));
+ }
+
+ @Test
+ public void testGetAllLmsForMepEmpty() throws CfmConfigException, SoamConfigException {
+ List<LossMeasurementEntry> lmList = new ArrayList<>();
+
+ expect(soamService.getAllLms(MDNAME1, MANAME1, MEPID1)).andReturn(lmList).anyTimes();
+ replay(soamService);
+
+ final WebTarget wt = target();
+ final String response = wt.path("md/" + MDNAME1.mdName() + "/ma/" +
+ MANAME1.maName() + "/mep/" + MEPID1.value() + "/lm")
+ .request().get(String.class);
+
+ assertThat(response, is("{\"lms\":[[]]}"));
+ }
+
+ @Test
+ public void testGetLm() throws CfmConfigException, SoamConfigException {
+
+ expect(soamService.getLm(MDNAME1, MANAME1, MEPID1, LMID1)).andReturn(lm1).anyTimes();
+ replay(soamService);
+
+ final WebTarget wt = target();
+ final String response = wt.path("md/" + MDNAME1.mdName() + "/ma/" +
+ MANAME1.maName() + "/mep/" + MEPID1.value() + "/lm/" + LMID1.value())
+ .request().get(String.class);
+
+ assertThat(response, is("{\"lm\":" +
+ "{" +
+ "\"lmId\":\"1\"," +
+ "\"lmCfgType\":\"LMLMM\"," +
+ "\"version\":\"Y17312008\"," +
+ "\"remoteMepId\":10," +
+ "\"priority\":\"PRIO1\"," +
+ "\"countersEnabled\":[]," +
+ "\"measurementHistories\":[]," +
+ "\"availabilityHistories\":[]" +
+ "}}"));
+ }
+
+ @Test
+ public void testGetLmEmpty() throws CfmConfigException, SoamConfigException, IOException {
+ SoamId lmId3 = SoamId.valueOf(3);
+ expect(soamService.getLm(MDNAME1, MANAME1, MEPID1, lmId3))
+ .andReturn(null).anyTimes();
+ replay(soamService);
+
+ final WebTarget wt = target();
+ try {
+ final String response = wt.path("md/" + MDNAME1.mdName() + "/ma/" +
+ MANAME1.maName() + "/mep/" + MEPID1.value() + "/lm/" + lmId3.value())
+ .request().get(String.class);
+ } catch (InternalServerErrorException e) {
+ ByteArrayInputStream is = (ByteArrayInputStream) e.getResponse().getEntity();
+ BufferedReader br = new BufferedReader(new InputStreamReader(is));
+ String line = null;
+ StringBuffer sb = new StringBuffer();
+ while ((line = br.readLine()) != null) {
+ sb.append(line);
+ }
+ assertEquals("{ \"failure\":\"LM md-1/ma-1-1/1/3 not found\" }",
+ sb.toString());
+ }
+ }
+
+ @Test
+ public void testAbortLm() {
+
+ final WebTarget wt = target();
+ final Response response = wt.path("md/" + MDNAME1.mdName() + "/ma/" +
+ MANAME1.maName() + "/mep/" + MEPID1.value() + "/lm/" + LMID1.value())
+ .request().delete();
+ assertEquals(200, response.getStatus());
+ }
+
+ @Test
+ public void testCreateLm() throws CfmConfigException, SoamConfigException {
+ MepEntry mep1 = DefaultMepEntry.builder(MEPID1, DeviceId.deviceId("netconf:1.2.3.4:830"),
+ PortNumber.portNumber(1), Mep.MepDirection.UP_MEP, MDNAME1, MANAME1).buildEntry();
+
+ expect(mepService.getMep(MDNAME1, MANAME1, MEPID1)).andReturn(mep1).anyTimes();
+ replay(mepService);
+
+ ObjectMapper mapper = new ObjectMapper();
+ CfmCodecContext context = new CfmCodecContext();
+ ObjectNode node = mapper.createObjectNode();
+ node.set("lm", context.codec(LossMeasurementCreate.class).encode(lm1, context));
+
+ final WebTarget wt = target();
+ final Response response = wt.path("md/" + MDNAME1.mdName() + "/ma/" +
+ MANAME1.maName() + "/mep/" + MEPID1.value() + "/lm")
+ .request().post(Entity.json(node.toString()));
+ assertEquals(201, response.getStatus());
+ }
+
+ @Test
+ public void testClearLmHistory() {
+
+ final WebTarget wt = target();
+ final Response response = wt.path("md/" + MDNAME1.mdName() + "/ma/" +
+ MANAME1.maName() + "/mep/" + MEPID1.value() + "/lm/" + LMID1.value() +
+ "/clear-history")
+ .request().put(Entity.json(""));
+
+ assertEquals(200, response.getStatus());
+ }
+}
diff --git a/apps/cfm/src/test/java/org/onosproject/soam/web/DmCreateCodecTest.java b/apps/cfm/src/test/java/org/onosproject/soam/web/DmCreateCodecTest.java
new file mode 100644
index 0000000..2f31310
--- /dev/null
+++ b/apps/cfm/src/test/java/org/onosproject/soam/web/DmCreateCodecTest.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.soam.web;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+import java.time.Duration;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.onosproject.cfm.CfmCodecContext;
+import org.onosproject.incubator.net.l2monitoring.cfm.Mep.Priority;
+import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MepId;
+import org.onosproject.incubator.net.l2monitoring.soam.SoamConfigException;
+import org.onosproject.incubator.net.l2monitoring.soam.delay.DefaultDelayMeasurementCreate;
+import org.onosproject.incubator.net.l2monitoring.soam.delay.DelayMeasurementCreate;
+import org.onosproject.incubator.net.l2monitoring.soam.delay.DelayMeasurementCreate.DmCreateBuilder;
+import org.onosproject.incubator.net.l2monitoring.soam.delay.DelayMeasurementCreate.DmType;
+import org.onosproject.incubator.net.l2monitoring.soam.delay.DelayMeasurementCreate.MeasurementOption;
+import org.onosproject.incubator.net.l2monitoring.soam.delay.DelayMeasurementCreate.Version;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+
+public class DmCreateCodecTest {
+ ObjectMapper mapper;
+ CfmCodecContext context;
+
+ @Before
+ public void setUp() throws Exception {
+ mapper = new ObjectMapper();
+ context = new CfmCodecContext();
+ }
+
+ @Test
+ public void testDecodeObjectNodeCodecContext1()
+ throws JsonProcessingException, IOException {
+ String moStr = "{\"dm\": {}}";
+
+ InputStream input = new ByteArrayInputStream(
+ moStr.getBytes(StandardCharsets.UTF_8));
+ JsonNode cfg = mapper.readTree(input);
+
+ try {
+ context.codec(DelayMeasurementCreate.class)
+ .decode((ObjectNode) cfg, context);
+ fail("Expecting an exception");
+ } catch (IllegalArgumentException e) {
+ assertEquals("remoteMepId is required", e.getMessage());
+ }
+ }
+
+ @Test
+ public void testDecodeObjectNodeCodecContext2()
+ throws JsonProcessingException, IOException {
+ String moStr = "{\"dm\": {" +
+ "\"version\":\"Y17312008\"," +
+ "\"dmType\":\"DMDMM\"," +
+ "\"remoteMepId\":12," +
+ "\"priority\":\"PRIO6\"," +
+ "\"measurementsEnabled\" :" +
+ "[\"FRAME_DELAY_RANGE_BACKWARD_AVERAGE\", " +
+ "\"INTER_FRAME_DELAY_VARIATION_FORWARD_AVERAGE\"]" +
+ "}}";
+
+ InputStream input = new ByteArrayInputStream(
+ moStr.getBytes(StandardCharsets.UTF_8));
+ JsonNode cfg = mapper.readTree(input);
+
+ DelayMeasurementCreate dmCreate = context
+ .codec(DelayMeasurementCreate.class)
+ .decode((ObjectNode) cfg, context);
+
+ assertEquals(Version.Y17312008, dmCreate.version());
+ assertEquals(DmType.DMDMM, dmCreate.dmCfgType());
+ assertEquals(12, dmCreate.remoteMepId().id().shortValue());
+ }
+
+ @Test
+ public void testEncodeDelayMeasurementCreateCodecContext()
+ throws SoamConfigException {
+ DmCreateBuilder builder = DefaultDelayMeasurementCreate
+ .builder(DmType.DM1DMRX, Version.Y17312011,
+ MepId.valueOf((short) 16), Priority.PRIO5);
+ builder.addToMeasurementsEnabled(
+ MeasurementOption.FRAME_DELAY_BACKWARD_MAX);
+ builder.addToMeasurementsEnabled(
+ MeasurementOption.FRAME_DELAY_TWO_WAY_MAX);
+ builder.addToMeasurementsEnabled(
+ MeasurementOption.INTER_FRAME_DELAY_VARIATION_BACKWARD_BINS);
+ builder = (DmCreateBuilder) builder.messagePeriod(Duration.ofMillis(100));
+ builder = (DmCreateBuilder) builder.frameSize((short) 1200);
+
+ ObjectNode node = mapper.createObjectNode();
+ node.set("dm", context.codec(DelayMeasurementCreate.class)
+ .encode(builder.build(), context));
+
+ assertEquals(DmType.DM1DMRX.name(), node.get("dm").get("dmCfgType").asText());
+ assertEquals(Version.Y17312011.name(), node.get("dm").get("version").asText());
+ assertEquals(16, node.get("dm").get("remoteMepId").asInt());
+ assertEquals(Priority.PRIO5.name(), node.get("dm").get("priority").asText());
+ assertEquals(100, node.get("dm").get("messagePeriodMs").asInt());
+ assertEquals(1200, node.get("dm").get("frameSize").asInt());
+
+ assertEquals(3, ((ArrayNode) node.get("dm").get("measurementsEnabled")).size());
+ }
+
+}
diff --git a/apps/cfm/src/test/java/org/onosproject/soam/web/DmEntryCodecTest.java b/apps/cfm/src/test/java/org/onosproject/soam/web/DmEntryCodecTest.java
new file mode 100644
index 0000000..028fad8
--- /dev/null
+++ b/apps/cfm/src/test/java/org/onosproject/soam/web/DmEntryCodecTest.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.soam.web;
+
+import static org.junit.Assert.assertEquals;
+
+import java.io.IOException;
+import java.time.Duration;
+import java.util.ArrayList;
+import java.util.Collection;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.onosproject.cfm.CfmCodecContext;
+import org.onosproject.incubator.net.l2monitoring.cfm.Mep.Priority;
+import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MepId;
+import org.onosproject.incubator.net.l2monitoring.soam.SoamConfigException;
+import org.onosproject.incubator.net.l2monitoring.soam.SoamId;
+import org.onosproject.incubator.net.l2monitoring.soam.delay.DefaultDelayMeasurementEntry;
+import org.onosproject.incubator.net.l2monitoring.soam.delay.DelayMeasurementCreate.DmType;
+import org.onosproject.incubator.net.l2monitoring.soam.delay.DelayMeasurementCreate.MeasurementOption;
+import org.onosproject.incubator.net.l2monitoring.soam.delay.DelayMeasurementCreate.Version;
+import org.onosproject.incubator.net.l2monitoring.soam.delay.DelayMeasurementEntry;
+import org.onosproject.incubator.net.l2monitoring.soam.delay.DelayMeasurementEntry.DmEntryBuilder;
+import org.onosproject.incubator.net.l2monitoring.soam.delay.DelayMeasurementEntry.SessionStatus;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+
+public class DmEntryCodecTest {
+ ObjectMapper mapper;
+ CfmCodecContext context;
+ DelayMeasurementEntry dmEntry1;
+
+ @Before
+ public void setUp() throws Exception, SoamConfigException {
+ mapper = new ObjectMapper();
+ context = new CfmCodecContext();
+ DmEntryBuilder builder = DefaultDelayMeasurementEntry
+ .builder(SoamId.valueOf(12), DmType.DM1DMTX,
+ Version.Y17312008, MepId.valueOf((short) 10), Priority.PRIO4);
+ builder = builder.sessionStatus(SessionStatus.NOT_ACTIVE);
+ builder = builder.frameDelayTwoWay(Duration.ofNanos(101 * 1000));
+ builder = builder.frameDelayForward(Duration.ofNanos(102 * 1000));
+ builder = builder.frameDelayBackward(Duration.ofNanos(103 * 1000));
+ builder = builder.interFrameDelayVariationTwoWay(Duration.ofNanos(104 * 1000));
+ builder = builder.interFrameDelayVariationForward(Duration.ofNanos(105 * 1000));
+ builder = builder.interFrameDelayVariationBackward(Duration.ofNanos(106 * 1000));
+ builder.addToMeasurementsEnabled(MeasurementOption.FRAME_DELAY_BACKWARD_MAX);
+ builder.addToMeasurementsEnabled(MeasurementOption.FRAME_DELAY_TWO_WAY_MAX);
+ builder.addToMeasurementsEnabled(MeasurementOption.INTER_FRAME_DELAY_VARIATION_BACKWARD_BINS);
+
+ dmEntry1 = builder.build();
+ }
+
+ @Test
+ public void testEncodeDelayMeasurementEntryCodecContext()
+ throws JsonProcessingException, IOException {
+ ObjectNode node = mapper.createObjectNode();
+ node.set("dm", context.codec(DelayMeasurementEntry.class)
+ .encode(dmEntry1, context));
+
+ assertEquals(12, node.get("dm").get("dmId").asInt());
+ assertEquals(DmType.DM1DMTX.name(), node.get("dm").get("dmCfgType").asText());
+ assertEquals(Version.Y17312008.name(), node.get("dm").get("version").asText());
+ assertEquals(10, node.get("dm").get("remoteMepId").asInt());
+ assertEquals(3, ((ArrayNode) node.get("dm").get("measurementsEnabled")).size());
+
+ assertEquals(SessionStatus.NOT_ACTIVE.name(),
+ node.get("dm").get("sessionStatus").asText());
+ assertEquals("PT0.000101S",
+ node.get("dm").get("frameDelayTwoWay").asText());
+ assertEquals("PT0.000102S",
+ node.get("dm").get("frameDelayForward").asText());
+ assertEquals("PT0.000103S",
+ node.get("dm").get("frameDelayBackward").asText());
+ assertEquals("PT0.000104S",
+ node.get("dm").get("interFrameDelayVariationTwoWay").asText());
+ assertEquals("PT0.000105S",
+ node.get("dm").get("interFrameDelayVariationForward").asText());
+ assertEquals("PT0.000106S",
+ node.get("dm").get("interFrameDelayVariationBackward").asText());
+
+ }
+
+ @Test
+ public void testEncodeIterableOfDelayMeasurementEntryCodecContext()
+ throws SoamConfigException {
+ DmEntryBuilder builder2 = DefaultDelayMeasurementEntry
+ .builder(SoamId.valueOf(14), DmType.DM1DMRX,
+ Version.Y17312011, MepId.valueOf((short) 16), Priority.PRIO5);
+ builder2.addToMeasurementsEnabled(MeasurementOption.FRAME_DELAY_BACKWARD_MIN);
+ builder2.addToMeasurementsEnabled(MeasurementOption.FRAME_DELAY_TWO_WAY_MIN);
+ builder2.addToMeasurementsEnabled(MeasurementOption.INTER_FRAME_DELAY_VARIATION_BACKWARD_MIN);
+
+ Collection<DelayMeasurementEntry> dmEntries = new ArrayList<>();
+ dmEntries.add(dmEntry1);
+ dmEntries.add(builder2.build());
+ ObjectNode node = mapper.createObjectNode();
+ node.set("dm", context.codec(DelayMeasurementEntry.class)
+ .encode(dmEntries, context));
+
+ assertEquals(2, ((ArrayNode) node.get("dm")).size());
+ }
+
+}
diff --git a/apps/cfm/src/test/java/org/onosproject/soam/web/DmMeasurementOptionCodecTest.java b/apps/cfm/src/test/java/org/onosproject/soam/web/DmMeasurementOptionCodecTest.java
new file mode 100644
index 0000000..4986d5a
--- /dev/null
+++ b/apps/cfm/src/test/java/org/onosproject/soam/web/DmMeasurementOptionCodecTest.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.soam.web;
+
+import static org.junit.Assert.assertEquals;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.onosproject.cfm.CfmCodecContext;
+import org.onosproject.incubator.net.l2monitoring.soam.delay.DelayMeasurementCreate.MeasurementOption;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+
+public class DmMeasurementOptionCodecTest {
+ ObjectMapper mapper;
+ CfmCodecContext context;
+
+ @Before
+ public void setUp() throws Exception {
+ mapper = new ObjectMapper();
+ context = new CfmCodecContext();
+ }
+
+ @Test
+ public void testEncodeIterableOfMeasurementOptionCodecContext() {
+ List<MeasurementOption> moList = new ArrayList<>();
+ moList.add(MeasurementOption.FRAME_DELAY_BACKWARD_MAX);
+ moList.add(MeasurementOption.FRAME_DELAY_FORWARD_BINS);
+
+ ArrayNode an =
+ context.codec(MeasurementOption.class).encode(moList, context);
+
+ assertEquals(MeasurementOption.FRAME_DELAY_BACKWARD_MAX.toString(),
+ an.get(0).asText());
+ assertEquals(MeasurementOption.FRAME_DELAY_FORWARD_BINS.toString(),
+ an.get(1).asText());
+ }
+
+ @Test
+ public void testDecodeArrayNodeCodecContext()
+ throws JsonProcessingException, IOException {
+ String moStr = "{\"measurementsEnabled\": " +
+ "[\"FRAME_DELAY_RANGE_BACKWARD_AVERAGE\", " +
+ "\"INTER_FRAME_DELAY_VARIATION_FORWARD_AVERAGE\"]}";
+ InputStream input = new ByteArrayInputStream(
+ moStr.getBytes(StandardCharsets.UTF_8));
+ JsonNode cfg = mapper.readTree(input);
+ Iterable<MeasurementOption> moIter = context
+ .codec(MeasurementOption.class)
+ .decode((ArrayNode) cfg.get("measurementsEnabled"), context);
+
+ Iterator<MeasurementOption> source = moIter.iterator();
+ List<MeasurementOption> moList = new ArrayList<>();
+ source.forEachRemaining(moList::add);
+
+ assertEquals(MeasurementOption.FRAME_DELAY_RANGE_BACKWARD_AVERAGE.toString(),
+ moList.get(0).name());
+ assertEquals(MeasurementOption.INTER_FRAME_DELAY_VARIATION_FORWARD_AVERAGE.toString(),
+ moList.get(1).name());
+ }
+
+}
diff --git a/apps/cfm/src/test/resources/examples/CreateDM_Mep10.json b/apps/cfm/src/test/resources/examples/CreateDM_Mep10.json
new file mode 100644
index 0000000..4b8a962
--- /dev/null
+++ b/apps/cfm/src/test/resources/examples/CreateDM_Mep10.json
@@ -0,0 +1,18 @@
+{
+ "dm": {
+ "remoteMepId":20,
+ "dmCfgType": "DMDMM",
+ "version": "Y17312008",
+ "priority": "PRIO1",
+ "messagePeriodMs": 100,
+ "startTime": {
+ "immediate":true
+ },
+ "stopTime": {
+ "none":true
+ },
+ "frameSize": 1500,
+ "measurementIntervalMins": 1,
+ "measurementsEnabled": ["FRAME_DELAY_TWO_WAY_BINS","FRAME_DELAY_TWO_WAY_AVERAGE"]
+ }
+}
\ No newline at end of file
diff --git a/apps/cfm/src/test/resources/examples/CreateMa1InDomainA.json b/apps/cfm/src/test/resources/examples/CreateMa1InDomainA.json
new file mode 100644
index 0000000..0790aee
--- /dev/null
+++ b/apps/cfm/src/test/resources/examples/CreateMa1InDomainA.json
@@ -0,0 +1,23 @@
+{
+ "ma": {
+ "maName": "ma-vlan-1",
+ "maNameType": "CHARACTERSTRING",
+ "maNumericId": 1,
+ "ccm-interval": "INTERVAL_1S",
+ "component-list": [
+ { "component": {
+ "component-id":"1",
+ "tag-type": "VLAN_STAG",
+ "vid-list": [
+ {"vid":1}
+ ]
+ }
+ }
+ ],
+ "rmep-list": [
+ { "rmep":10 },
+ { "rmep":20 },
+ { "rmep":30 }
+ ]
+ }
+}
\ No newline at end of file
diff --git a/apps/cfm/src/test/resources/examples/CreateMa2InDomainA.json b/apps/cfm/src/test/resources/examples/CreateMa2InDomainA.json
new file mode 100644
index 0000000..67afdba
--- /dev/null
+++ b/apps/cfm/src/test/resources/examples/CreateMa2InDomainA.json
@@ -0,0 +1,22 @@
+{
+ "ma": {
+ "maName": "ma-vlan-2",
+ "maNameType": "CHARACTERSTRING",
+ "maNumericId": 2,
+ "ccm-interval": "INTERVAL_1S",
+ "component-list": [
+ { "component": {
+ "component-id":"1",
+ "tag-type": "VLAN_STAG",
+ "vid-list": [
+ {"vid":2}
+ ]
+ }
+ }
+ ],
+ "rmep-list": [
+ { "rmep":10 },
+ { "rmep":20 }
+ ]
+ }
+}
\ No newline at end of file
diff --git a/apps/cfm/src/test/resources/examples/CreateMdDomainA.json b/apps/cfm/src/test/resources/examples/CreateMdDomainA.json
new file mode 100644
index 0000000..2dbb761
--- /dev/null
+++ b/apps/cfm/src/test/resources/examples/CreateMdDomainA.json
@@ -0,0 +1,7 @@
+{"md": {
+ "mdName": "DomainA",
+ "mdNameType": "CHARACTERSTRING",
+ "mdLevel": "LEVEL3",
+ "mdNumeridId": 1
+}
+}
\ No newline at end of file
diff --git a/apps/cfm/src/test/resources/examples/CreateMep10.json b/apps/cfm/src/test/resources/examples/CreateMep10.json
new file mode 100644
index 0000000..3b393c2
--- /dev/null
+++ b/apps/cfm/src/test/resources/examples/CreateMep10.json
@@ -0,0 +1,14 @@
+{
+ "mep": {
+ "mepId": 10,
+ "deviceId": "netconf:192.168.56.10:830",
+ "port": 0,
+ "direction": "DOWN_MEP",
+ "mdName": "DomainA",
+ "maName": "ma-vlan-1",
+ "primary-vid": 1,
+ "administrative-state": true,
+ "ccm-ltm-priority": 4,
+ "cci-enabled" :true
+ }
+}
\ No newline at end of file
diff --git a/apps/cfm/src/test/resources/examples/README.md b/apps/cfm/src/test/resources/examples/README.md
new file mode 100644
index 0000000..3557cff
--- /dev/null
+++ b/apps/cfm/src/test/resources/examples/README.md
@@ -0,0 +1,12 @@
+Run:
+
+* curl --user onos:rocks -d @CreateMdDomainA.json http://localhost:8181/onos/cfm/md --header "Content-Type:application/json"
+* curl --user onos:rocks -d @CreateMa1InDomainA.json http://localhost:8181/onos/cfm/md/DomainA/ma --header "Content-Type:application/json"
+* curl --user onos:rocks -d @CreateMa2InDomainA.json http://localhost:8181/onos/cfm/md/DomainA/ma --header "Content-Type:application/json"
+* curl --user onos:rocks http://localhost:8181/onos/cfm/md
+
+Create a Device that supports CFM Programmable, say at netconf:192.168.56.10:830
+* curl --user onos:rocks -d @CreateMep10.json http://localhost:8181/onos/cfm/md/DomainA/ma/ma-vlan-1/mep --header "Content-Type:application/json"
+
+Now call the RPC to create a Delay Measurement on that device
+* curl --user onos:rocks -X PUT -d @CreateDM_Mep10.json http://localhost:8181/onos/cfm/md/DomainA/ma/ma-vlan-1/mep/10/dm --header "Content-Type:application/json"
diff --git a/apps/pom.xml b/apps/pom.xml
index 26deeee..a5dba52 100644
--- a/apps/pom.xml
+++ b/apps/pom.xml
@@ -97,6 +97,7 @@
<module>evpn-route-service</module>
<module>l3vpn</module>
<module>openstacknetworkingui</module>
+ <module>cfm</module>
</modules>
<properties>