Implement REST API of policy
Change-Id: I46f5a3cd6d66cf7a3a90a5d153b3878fdf5cbd31
diff --git a/impl/src/main/java/org/onosproject/segmentrouting/policy/api/DropPolicyCodec.java b/impl/src/main/java/org/onosproject/segmentrouting/policy/api/DropPolicyCodec.java
new file mode 100644
index 0000000..407bd08
--- /dev/null
+++ b/impl/src/main/java/org/onosproject/segmentrouting/policy/api/DropPolicyCodec.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2021-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.segmentrouting.policy.api;
+
+import com.fasterxml.jackson.databind.node.ObjectNode;
+
+import org.onosproject.codec.CodecContext;
+import org.onosproject.codec.JsonCodec;
+
+/**
+ * Codec of DropPolicy class.
+ */
+public final class DropPolicyCodec extends JsonCodec<DropPolicy> {
+
+ // JSON field names
+ public static final String POLICY_ID = "policy_id";
+ public static final String POLICY_TYPE = "policy_type";
+
+ @Override
+ public ObjectNode encode(DropPolicy policy, CodecContext context) {
+ final ObjectNode result = context.mapper().createObjectNode()
+ .put(POLICY_ID, policy.policyId().toString())
+ .put(POLICY_TYPE, policy.policyType().toString());
+
+ return result;
+ }
+
+ @Override
+ public DropPolicy decode(ObjectNode json, CodecContext context) {
+ return new DropPolicy();
+ }
+}
diff --git a/impl/src/main/java/org/onosproject/segmentrouting/policy/api/RedirectPolicyCodec.java b/impl/src/main/java/org/onosproject/segmentrouting/policy/api/RedirectPolicyCodec.java
new file mode 100644
index 0000000..efb2828
--- /dev/null
+++ b/impl/src/main/java/org/onosproject/segmentrouting/policy/api/RedirectPolicyCodec.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2021-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.segmentrouting.policy.api;
+
+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.net.DeviceId;
+
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Set;
+
+import static org.onlab.util.Tools.nullIsIllegal;
+
+/**
+ * Codec of RedirectPolicy class.
+ */
+public final class RedirectPolicyCodec extends JsonCodec<RedirectPolicy> {
+
+ // JSON field names
+ public static final String POLICY_ID = "policy_id";
+ public static final String POLICY_TYPE = "policy_type";
+ public static final String SPINES_TO_ENFORCES = "spinesToEnforce";
+ public static final String DEVICE_ID = "deviceId";
+ public static final String MISSING_MEMBER_MESSAGE =
+ " member is required in Redirect Policy";
+
+ @Override
+ public ObjectNode encode(RedirectPolicy policy, CodecContext context) {
+ final ObjectNode result = context.mapper().createObjectNode()
+ .put(POLICY_ID, policy.policyId().toString())
+ .put(POLICY_TYPE, policy.policyType().toString());
+
+ ArrayNode deviceIdArr = result.putObject(SPINES_TO_ENFORCES).putArray(DEVICE_ID);
+ for (DeviceId deviceId : policy.spinesToEnforce()) {
+ deviceIdArr.add(deviceId.toString());
+ }
+
+ return result;
+ }
+
+ @Override
+ public RedirectPolicy decode(ObjectNode json, CodecContext context) {
+ List<DeviceId> spinesToEnforce = new LinkedList<>();
+
+ ObjectNode spinesNode = nullIsIllegal(get(json, SPINES_TO_ENFORCES),
+ SPINES_TO_ENFORCES + MISSING_MEMBER_MESSAGE);
+ ArrayNode deviceIdArr = nullIsIllegal((ArrayNode) spinesNode.get(DEVICE_ID),
+ DEVICE_ID + MISSING_MEMBER_MESSAGE);
+ deviceIdArr.forEach(deviceId -> spinesToEnforce.add(DeviceId.deviceId(deviceId.textValue())));
+
+ return new RedirectPolicy(Set.copyOf(spinesToEnforce));
+ }
+}
diff --git a/impl/src/main/java/org/onosproject/segmentrouting/policy/api/TrafficMatchCodec.java b/impl/src/main/java/org/onosproject/segmentrouting/policy/api/TrafficMatchCodec.java
new file mode 100644
index 0000000..1f4f8be
--- /dev/null
+++ b/impl/src/main/java/org/onosproject/segmentrouting/policy/api/TrafficMatchCodec.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2021-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.segmentrouting.policy.api;
+
+import com.fasterxml.jackson.databind.node.ObjectNode;
+
+import org.onosproject.codec.CodecContext;
+import org.onosproject.codec.JsonCodec;
+import org.onosproject.net.flow.TrafficSelector;
+
+import static org.onlab.util.Tools.nullIsIllegal;
+
+/**
+ * Codec of TrafficMatch class.
+ */
+public final class TrafficMatchCodec extends JsonCodec<TrafficMatch> {
+
+ // JSON field names
+ public static final String TRAFFIC_MATCH_ID = "traffic_match_id";
+ public static final String TRAFFIC_SELECTOR = "selector";
+ public static final String POLICY_ID = "policy_id";
+ public static final String MISSING_MEMBER_MESSAGE =
+ " member is required in Traffic Match";
+
+ @Override
+ public ObjectNode encode(TrafficMatch trafficMatch, CodecContext context) {
+ final JsonCodec<TrafficSelector> selectorCodec =
+ context.codec(TrafficSelector.class);
+
+ final ObjectNode selector = selectorCodec.encode(trafficMatch.trafficSelector(), context);
+ final ObjectNode result = context.mapper().createObjectNode()
+ .put(TRAFFIC_MATCH_ID, trafficMatch.trafficMatchId().toString())
+ .put(POLICY_ID, trafficMatch.policyId().toString())
+ .set(TRAFFIC_SELECTOR, selector);
+
+ return result;
+ }
+
+ @Override
+ public TrafficMatch decode(ObjectNode json, CodecContext context) {
+ final JsonCodec<TrafficSelector> selectorCodec =
+ context.codec(TrafficSelector.class);
+
+ ObjectNode selectorJson = nullIsIllegal(get(json, TRAFFIC_SELECTOR),
+ TRAFFIC_SELECTOR + MISSING_MEMBER_MESSAGE);
+ TrafficSelector trafficSelector = selectorCodec.decode(selectorJson, context);
+
+ PolicyId policyId = PolicyId.of(nullIsIllegal(json.get(POLICY_ID),
+ POLICY_ID + MISSING_MEMBER_MESSAGE).asText());
+
+ return new TrafficMatch(trafficSelector, policyId);
+ }
+}
diff --git a/impl/src/main/java/org/onosproject/segmentrouting/policy/impl/PolicyManager.java b/impl/src/main/java/org/onosproject/segmentrouting/policy/impl/PolicyManager.java
index 8f7c69c..87fa61a 100644
--- a/impl/src/main/java/org/onosproject/segmentrouting/policy/impl/PolicyManager.java
+++ b/impl/src/main/java/org/onosproject/segmentrouting/policy/impl/PolicyManager.java
@@ -26,6 +26,7 @@
import org.onlab.util.PredictableExecutor;
import org.onosproject.cluster.ClusterService;
import org.onosproject.cluster.NodeId;
+import org.onosproject.codec.CodecService;
import org.onosproject.core.ApplicationId;
import org.onosproject.core.CoreService;
import org.onosproject.net.ConnectPoint;
@@ -48,6 +49,7 @@
import org.onosproject.segmentrouting.SegmentRoutingService;
import org.onosproject.segmentrouting.config.DeviceConfigNotFoundException;
import org.onosproject.segmentrouting.policy.api.DropPolicy;
+import org.onosproject.segmentrouting.policy.api.DropPolicyCodec;
import org.onosproject.segmentrouting.policy.api.Policy;
import org.onosproject.segmentrouting.policy.api.Policy.PolicyType;
import org.onosproject.segmentrouting.policy.api.PolicyData;
@@ -55,7 +57,9 @@
import org.onosproject.segmentrouting.policy.api.PolicyService;
import org.onosproject.segmentrouting.policy.api.PolicyState;
import org.onosproject.segmentrouting.policy.api.RedirectPolicy;
+import org.onosproject.segmentrouting.policy.api.RedirectPolicyCodec;
import org.onosproject.segmentrouting.policy.api.TrafficMatch;
+import org.onosproject.segmentrouting.policy.api.TrafficMatchCodec;
import org.onosproject.segmentrouting.policy.api.TrafficMatchData;
import org.onosproject.segmentrouting.policy.api.TrafficMatchId;
import org.onosproject.segmentrouting.policy.api.TrafficMatchState;
@@ -153,6 +157,9 @@
private CoreService coreService;
@Reference(cardinality = ReferenceCardinality.MANDATORY)
+ private CodecService codecService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY)
private StorageService storageService;
@Reference(cardinality = ReferenceCardinality.MANDATORY)
@@ -161,7 +168,7 @@
@Reference(cardinality = ReferenceCardinality.MANDATORY)
private WorkPartitionService workPartitionService;
- @Reference(cardinality = ReferenceCardinality.OPTIONAL)
+ @Reference(cardinality = ReferenceCardinality.MANDATORY)
private SegmentRoutingService srService;
@Reference(cardinality = ReferenceCardinality.MANDATORY)
@@ -174,6 +181,10 @@
public void activate() {
appId = coreService.registerApplication(APP_NAME);
+ codecService.registerCodec(DropPolicy.class, new DropPolicyCodec());
+ codecService.registerCodec(RedirectPolicy.class, new RedirectPolicyCodec());
+ codecService.registerCodec(TrafficMatch.class, new TrafficMatchCodec());
+
policies = storageService.<PolicyId, PolicyRequest>consistentMapBuilder()
.withName(POLICY_STORE)
.withSerializer(serializer).build();
@@ -203,6 +214,9 @@
@Deactivate
public void deactivate() {
// Teardown everything
+ codecService.unregisterCodec(DropPolicy.class);
+ codecService.unregisterCodec(RedirectPolicy.class);
+ codecService.unregisterCodec(TrafficMatch.class);
policies.removeListener(mapPolListener);
policies.destroy();
policiesMap.clear();
diff --git a/impl/src/test/java/org/onosproject/segmentrouting/policy/api/DropPolicyCodecTest.java b/impl/src/test/java/org/onosproject/segmentrouting/policy/api/DropPolicyCodecTest.java
new file mode 100644
index 0000000..5d95625
--- /dev/null
+++ b/impl/src/test/java/org/onosproject/segmentrouting/policy/api/DropPolicyCodecTest.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2021-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.segmentrouting.policy.api;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import junit.framework.TestCase;
+import org.junit.Before;
+import org.junit.Test;
+import org.onosproject.codec.CodecContext;
+import org.onosproject.codec.JsonCodec;
+import org.onosproject.codec.impl.MockCodecContext;
+
+import java.io.InputStream;
+
+public class DropPolicyCodecTest extends TestCase {
+ private DropPolicy dropPolicy;
+
+ private CodecContext context;
+ private JsonCodec<DropPolicy> codec;
+
+ @Before
+ public void setUp() throws Exception {
+ context = new MockCodecContext();
+ codec = new DropPolicyCodec();
+
+ dropPolicy = new DropPolicy();
+ }
+
+ @Test
+ public void testEncode() throws Exception {
+ ObjectMapper mapper = new ObjectMapper();
+
+ InputStream jsonStream1 = RedirectPolicyCodecTest.class.getResourceAsStream("/droppolicy.json");
+ JsonNode expected = mapper.readTree(jsonStream1);
+
+ JsonNode actual = codec.encode(dropPolicy, context);
+
+ assertEquals(expected.get(RedirectPolicyCodec.POLICY_ID), actual.get(RedirectPolicyCodec.POLICY_ID));
+ assertEquals(expected.get(RedirectPolicyCodec.POLICY_TYPE), actual.get(RedirectPolicyCodec.POLICY_TYPE));
+ }
+
+ @Test
+ public void testDecode() throws Exception {
+ ObjectMapper mapper = new ObjectMapper();
+
+ InputStream jsonStream1 = RedirectPolicyCodecTest.class.getResourceAsStream("/droppolicy.json");
+ ObjectNode json = mapper.readTree(jsonStream1).deepCopy();
+
+ DropPolicy actual = codec.decode(json, context);
+
+ assertEquals(dropPolicy, actual);
+ }
+}
\ No newline at end of file
diff --git a/impl/src/test/java/org/onosproject/segmentrouting/policy/api/RedirectPolicyCodecTest.java b/impl/src/test/java/org/onosproject/segmentrouting/policy/api/RedirectPolicyCodecTest.java
new file mode 100644
index 0000000..7541436
--- /dev/null
+++ b/impl/src/test/java/org/onosproject/segmentrouting/policy/api/RedirectPolicyCodecTest.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2021-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.segmentrouting.policy.api;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import junit.framework.TestCase;
+import org.junit.Before;
+import org.junit.Test;
+import org.onosproject.codec.CodecContext;
+import org.onosproject.codec.JsonCodec;
+import org.onosproject.codec.impl.MockCodecContext;
+import org.onosproject.net.DeviceId;
+
+import java.io.InputStream;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Set;
+
+public class RedirectPolicyCodecTest extends TestCase {
+ private RedirectPolicy redirectPolicy;
+
+ private CodecContext context;
+ private JsonCodec<RedirectPolicy> codec;
+
+ @Before
+ public void setUp() throws Exception {
+ context = new MockCodecContext();
+ codec = new RedirectPolicyCodec();
+
+ List<DeviceId> deviceIds = new LinkedList<>();
+ deviceIds.add(DeviceId.deviceId("of:0000000000000001"));
+ deviceIds.add(DeviceId.deviceId("of:0000000000000002"));
+ deviceIds.add(DeviceId.deviceId("of:0000000000000003"));
+ redirectPolicy = new RedirectPolicy(Set.copyOf(deviceIds));
+ }
+
+ @Test
+ public void testEncode() throws Exception {
+ ObjectMapper mapper = new ObjectMapper();
+
+ InputStream jsonStream1 = RedirectPolicyCodecTest.class.getResourceAsStream("/redirectpolicy.json");
+ JsonNode expected = mapper.readTree(jsonStream1);
+
+ JsonNode actual = codec.encode(redirectPolicy, context);
+
+ assertEquals(expected.get(RedirectPolicyCodec.POLICY_ID), actual.get(RedirectPolicyCodec.POLICY_ID));
+ assertEquals(expected.get(RedirectPolicyCodec.POLICY_TYPE), actual.get(RedirectPolicyCodec.POLICY_TYPE));
+ assertEquals(expected.get(RedirectPolicyCodec.DEVICE_ID), actual.get(RedirectPolicyCodec.DEVICE_ID));
+ }
+
+ @Test
+ public void testDecode() throws Exception {
+ ObjectMapper mapper = new ObjectMapper();
+
+ InputStream jsonStream1 = RedirectPolicyCodecTest.class.getResourceAsStream("/redirectpolicy.json");
+ ObjectNode json = mapper.readTree(jsonStream1).deepCopy();
+
+ RedirectPolicy actual = codec.decode(json, context);
+
+ assertEquals(redirectPolicy, actual);
+ }
+}
\ No newline at end of file
diff --git a/impl/src/test/java/org/onosproject/segmentrouting/policy/api/TrafficMatchCodecTest.java b/impl/src/test/java/org/onosproject/segmentrouting/policy/api/TrafficMatchCodecTest.java
new file mode 100644
index 0000000..c70eb7c
--- /dev/null
+++ b/impl/src/test/java/org/onosproject/segmentrouting/policy/api/TrafficMatchCodecTest.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2021-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.segmentrouting.policy.api;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import junit.framework.TestCase;
+import org.junit.Before;
+import org.junit.Test;
+import org.onlab.packet.Ip4Address;
+import org.onlab.packet.TpPort;
+import org.onosproject.codec.CodecContext;
+import org.onosproject.codec.JsonCodec;
+import org.onosproject.codec.impl.MockCodecContext;
+import org.onosproject.net.flow.DefaultTrafficSelector;
+import org.onosproject.net.flow.TrafficSelector;
+
+import java.io.InputStream;
+
+public class TrafficMatchCodecTest extends TestCase {
+ private TrafficMatch trafficMatch;
+ private TrafficSelector trafficSelector;
+ private PolicyId policyId;
+
+ private CodecContext context;
+ private JsonCodec<TrafficMatch> codec;
+
+ @Before
+ public void setUp() throws Exception {
+ context = new MockCodecContext();
+ codec = new TrafficMatchCodec();
+
+ trafficSelector = DefaultTrafficSelector.builder()
+ .matchIPProtocol((byte) 0x06)
+ .matchIPSrc(Ip4Address.valueOf("10.0.0.1").toIpPrefix())
+ .matchIPDst(Ip4Address.valueOf("10.0.0.2").toIpPrefix())
+ .matchTcpSrc(TpPort.tpPort(80))
+ .matchTcpDst(TpPort.tpPort(81))
+ .build();
+ policyId = PolicyId.of("DROP");
+
+ trafficMatch = new TrafficMatch(trafficSelector, policyId);
+ }
+
+ @Test
+ public void testEncode() throws Exception {
+ ObjectMapper mapper = new ObjectMapper();
+
+ InputStream jsonStream1 = RedirectPolicyCodecTest.class.getResourceAsStream("/trafficmatch.json");
+ JsonNode expected = mapper.readTree(jsonStream1);
+
+ JsonNode actual = codec.encode(trafficMatch, context);
+
+ assertEquals(expected.get(TrafficMatchCodec.TRAFFIC_MATCH_ID), actual.get(TrafficMatchCodec.TRAFFIC_MATCH_ID));
+ assertEquals(expected.get(TrafficMatchCodec.POLICY_ID), actual.get(TrafficMatchCodec.POLICY_ID));
+ for (int i = 0; i < trafficMatch.trafficSelector().criteria().size(); i++) {
+ assertEquals(expected.get(TrafficMatchCodec.TRAFFIC_SELECTOR).get(i),
+ actual.get(TrafficMatchCodec.TRAFFIC_SELECTOR).get(i));
+ }
+ }
+
+ @Test
+ public void testDecode() throws Exception {
+ ObjectMapper mapper = new ObjectMapper();
+
+ InputStream jsonStream1 = RedirectPolicyCodecTest.class.getResourceAsStream("/trafficmatch.json");
+ ObjectNode json = mapper.readTree(jsonStream1).deepCopy();
+
+ TrafficMatch actual = codec.decode(json, context);
+
+ assertEquals(trafficMatch, actual);
+ }
+}
\ No newline at end of file
diff --git a/impl/src/test/resources/droppolicy.json b/impl/src/test/resources/droppolicy.json
new file mode 100644
index 0000000..9a0e0ee
--- /dev/null
+++ b/impl/src/test/resources/droppolicy.json
@@ -0,0 +1,4 @@
+{
+ "policy_id": "DROP",
+ "policy_type": "DROP"
+}
\ No newline at end of file
diff --git a/impl/src/test/resources/redirectpolicy.json b/impl/src/test/resources/redirectpolicy.json
new file mode 100644
index 0000000..928788d
--- /dev/null
+++ b/impl/src/test/resources/redirectpolicy.json
@@ -0,0 +1,11 @@
+{
+ "policy_id": "REDIRECT[of:0000000000000001, of:0000000000000002, of:0000000000000003]",
+ "policy_type": "REDIRECT",
+ "spinesToEnforce": {
+ "deviceId": [
+ "of:0000000000000001",
+ "of:0000000000000002",
+ "of:0000000000000003"
+ ]
+ }
+}
\ No newline at end of file
diff --git a/impl/src/test/resources/trafficmatch.json b/impl/src/test/resources/trafficmatch.json
new file mode 100644
index 0000000..aea9585
--- /dev/null
+++ b/impl/src/test/resources/trafficmatch.json
@@ -0,0 +1,28 @@
+{
+ "traffic_match_id": "-1756753738",
+ "policy_id": "DROP",
+ "selector": {
+ "criteria": [
+ {
+ "type": "IP_PROTO",
+ "protocol": 6
+ },
+ {
+ "type": "IPV4_SRC",
+ "ip": "10.0.0.1/32"
+ },
+ {
+ "type": "IPV4_DST",
+ "ip": "10.0.0.2/32"
+ },
+ {
+ "type": "TCP_SRC",
+ "tcpPort": 80
+ },
+ {
+ "type": "TCP_DST",
+ "tcpPort": 81
+ }
+ ]
+ }
+}
\ No newline at end of file
diff --git a/web/src/main/java/org/onosproject/segmentrouting/web/PolicyCodec.java b/web/src/main/java/org/onosproject/segmentrouting/web/PolicyCodec.java
deleted file mode 100644
index 0dedc7c..0000000
--- a/web/src/main/java/org/onosproject/segmentrouting/web/PolicyCodec.java
+++ /dev/null
@@ -1,125 +0,0 @@
-/*
- * Copyright 2015-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.segmentrouting.web;
-
-import org.onosproject.codec.CodecContext;
-import org.onosproject.codec.JsonCodec;
-import com.fasterxml.jackson.databind.node.ObjectNode;
-import org.onosproject.segmentrouting.Policy;
-import org.onosproject.segmentrouting.TunnelPolicy;
-
-/**
- * Codec of Policy class.
- */
-public final class PolicyCodec extends JsonCodec<Policy> {
-
- // JSON field names
- private static final String POLICY_ID = "policy_id";
- private static final String PRIORITY = "priority";
- private static final String TYPE = "policy_type";
- private static final String TUNNEL_ID = "tunnel_id";
- private static final String DST_IP = "dst_ip";
- private static final String SRC_IP = "src_ip";
- private static final String PROTO_TYPE = "proto_type";
- private static final String SRC_PORT = "src_tp_port";
- private static final String DST_PORT = "dst_tp_port";
-
- @Override
- public ObjectNode encode(Policy policy, CodecContext context) {
- final ObjectNode result = context.mapper().createObjectNode()
- .put(POLICY_ID, policy.id());
-
- result.put(PRIORITY, policy.priority());
- result.put(TYPE, policy.type().toString());
-
- if (policy.dstIp() != null) {
- result.put(DST_IP, policy.dstIp());
- }
- if (policy.srcIp() != null) {
- result.put(SRC_IP, policy.srcIp());
- }
- if (policy.ipProto() != null) {
- result.put(PROTO_TYPE, policy.ipProto());
- }
-
- int srcPort = policy.srcPort() & 0xffff;
- if (policy.srcPort() != 0) {
- result.put(SRC_PORT, srcPort);
- }
- int dstPort = policy.dstPort() & 0xffff;
- if (policy.dstPort() != 0) {
- result.put(DST_PORT, dstPort);
- }
- if (policy.type() == Policy.Type.TUNNEL_FLOW) {
- result.put(TUNNEL_ID, ((TunnelPolicy) policy).tunnelId());
- }
-
- return result;
- }
-
- @Override
- public Policy decode(ObjectNode json, CodecContext context) {
-
- String pid = json.path(POLICY_ID).asText();
- String type = json.path(TYPE).asText();
- int priority = json.path(PRIORITY).asInt();
- String dstIp = json.path(DST_IP).asText();
- String srcIp = json.path(SRC_IP).asText();
- String tunnelId = json.path(TUNNEL_ID).asText();
- String protoType = json.path(PROTO_TYPE).asText();
- short srcPort = json.path(SRC_PORT).shortValue();
- short dstPort = json.path(DST_PORT).shortValue();
-
- if (json.path(POLICY_ID).isMissingNode() || pid == null) {
- // TODO: handle errors
- return null;
- }
-
- TunnelPolicy.Builder tpb = TunnelPolicy.builder().setPolicyId(pid);
- if (!json.path(TYPE).isMissingNode() && type != null &&
- Policy.Type.valueOf(type).equals(Policy.Type.TUNNEL_FLOW)) {
-
- if (json.path(TUNNEL_ID).isMissingNode() || tunnelId == null) {
- return null;
- }
-
- tpb.setTunnelId(tunnelId);
- tpb.setType(Policy.Type.valueOf(type));
-
- if (!json.path(PRIORITY).isMissingNode()) {
- tpb.setPriority(priority);
- }
- if (dstIp != null) {
- tpb.setDstIp(dstIp);
- }
- if (srcIp != null) {
- tpb.setSrcIp(srcIp);
- }
- if (protoType != null) {
- tpb.setIpProto(protoType);
- }
- if (dstPort != 0) {
- tpb.setDstPort(dstPort);
- }
- if (srcPort != 0) {
- tpb.setSrcPort(srcPort);
- }
- }
-
- return tpb.build();
- }
-
-}
diff --git a/web/src/main/java/org/onosproject/segmentrouting/web/PolicyWebResource.java b/web/src/main/java/org/onosproject/segmentrouting/web/PolicyWebResource.java
index 08663e4..5a24ca5 100644
--- a/web/src/main/java/org/onosproject/segmentrouting/web/PolicyWebResource.java
+++ b/web/src/main/java/org/onosproject/segmentrouting/web/PolicyWebResource.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015-present Open Networking Foundation
+ * Copyright 2021-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.
@@ -15,92 +15,264 @@
*/
package org.onosproject.segmentrouting.web;
-import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
-
+import org.onosproject.net.flow.DefaultTrafficSelector;
import org.onosproject.rest.AbstractWebResource;
-import org.onosproject.segmentrouting.Policy;
-import org.onosproject.segmentrouting.SegmentRoutingService;
+import org.onosproject.segmentrouting.policy.api.DropPolicy;
+import org.onosproject.segmentrouting.policy.api.Policy;
+import org.onosproject.segmentrouting.policy.api.Policy.PolicyType;
+import org.onosproject.segmentrouting.policy.api.PolicyData;
+import org.onosproject.segmentrouting.policy.api.PolicyId;
+import org.onosproject.segmentrouting.policy.api.PolicyService;
+import org.onosproject.segmentrouting.policy.api.RedirectPolicy;
+import org.onosproject.segmentrouting.policy.api.TrafficMatch;
+import org.onosproject.segmentrouting.policy.api.TrafficMatchData;
+import org.onosproject.segmentrouting.policy.api.TrafficMatchId;
+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.Path;
import javax.ws.rs.POST;
+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.util.List;
+import java.util.Set;
import static org.onlab.util.Tools.readTreeFromStream;
/**
- * Query, create and remove segment routing plicies.
+ * Query, create and remove Policies and Traffic Matches.
*/
-// @Path("policy")
+@Path("policy")
public class PolicyWebResource extends AbstractWebResource {
+ private static Logger log = LoggerFactory.getLogger(PolicyWebResource.class);
- private static final PolicyCodec POLICY_CODEC = new PolicyCodec();
+ private static final String EMPTY_TRAFFIC_SELECTOR =
+ "Empty traffic selector is not allowed";
+ private static final String POLICY = "policy";
+ private static final String POLICY_ID = "policy_id";
+ private static final String TRAFFIC_MATCH = "trafficMatch";
+ private static final String TRAFFIC_MATCH_ID = "traffic_match_id";
/**
- * Get all segment routing policies.
- * Returns an array of segment routing policies.
+ * Get all Policies.
*
- * @return status of OK
+ * @return 200 OK will a collection of Policies
*/
@GET
@Produces(MediaType.APPLICATION_JSON)
- public Response getPolicy() {
- SegmentRoutingService srService = get(SegmentRoutingService.class);
- List<Policy> policies = srService.getPolicies();
- ObjectNode result = new ObjectMapper().createObjectNode();
- result.set("policy", new PolicyCodec().encode(policies, this));
+ public Response getPolicies() {
+ PolicyService policyService = get(PolicyService.class);
+ ObjectNode root = mapper().createObjectNode();
+ ArrayNode policiesArr = root.putArray(POLICY);
- return ok(result.toString()).build();
+ //Create a filter set contains all PolicyType
+ Set<PolicyType> policyTypes = Set.of(PolicyType.values());
+
+ for (PolicyData policyData : policyService.policies(policyTypes)) {
+ Policy policy = policyData.policy();
+ switch (policy.policyType()) {
+ case DROP:
+ policiesArr.add(codec(DropPolicy.class).encode((DropPolicy) policy, this));
+ break;
+ case REDIRECT:
+ policiesArr.add(codec(RedirectPolicy.class).encode((RedirectPolicy) policy, this));
+ break;
+ default:
+ continue;
+ }
+ }
+
+ return Response.ok(root).build();
}
/**
- * Create a new segment routing policy.
+ * Get all Drop Policies.
*
- * @param input JSON stream for policy to create
- * @return status of the request - OK if the policy is created,
- * @throws IOException if JSON processing fails
+ * @return 200 OK will a collection of Dop Policies
+ */
+ @GET
+ @Produces(MediaType.APPLICATION_JSON)
+ @Path("drop")
+ public Response getDropPolicies() {
+ PolicyService policyService = get(PolicyService.class);
+ ObjectNode root = mapper().createObjectNode();
+ ArrayNode policiesArr = root.putArray(POLICY);
+
+ Set<PolicyType> policyTypes = Set.of(PolicyType.DROP);
+
+ for (PolicyData policyData : policyService.policies(policyTypes)) {
+ Policy policy = policyData.policy();
+ policiesArr.add(codec(DropPolicy.class).encode((DropPolicy) policy, this));
+ }
+
+ return Response.ok(root).build();
+ }
+
+ /**
+ * Create a new Drop Policy.
+ *
+ * @return 200 OK and policyId
*/
@POST
@Consumes(MediaType.APPLICATION_JSON)
- public Response createPolicy(InputStream input) throws IOException {
- ObjectMapper mapper = new ObjectMapper();
- ObjectNode policyJson = readTreeFromStream(mapper, input);
- SegmentRoutingService srService = get(SegmentRoutingService.class);
- Policy policyInfo = POLICY_CODEC.decode(policyJson, this);
+ @Produces(MediaType.APPLICATION_JSON)
+ @Path("drop")
+ public Response createDropPolicy() {
+ PolicyService policyService = get(PolicyService.class);
+ ObjectNode root = mapper().createObjectNode();
- if (policyInfo.type() == Policy.Type.TUNNEL_FLOW) {
- srService.createPolicy(policyInfo);
- return Response.ok().build();
- } else {
- return Response.serverError().build();
- }
+ DropPolicy dropPolicy = new DropPolicy();
+ policyService.addOrUpdatePolicy(dropPolicy);
+
+ root.put(POLICY_ID, dropPolicy.policyId().toString());
+
+ return Response.ok(root).build();
}
/**
- * Delete a segment routing policy.
+ * Get all Redirect Policies.
*
- * @param input JSON stream for policy to delete
- * @return 204 NO CONTENT if the policy is removed
- * @throws IOException if JSON is invalid
+ * @return 200 OK will a collection of Redirect Policies
+ */
+ @GET
+ @Produces(MediaType.APPLICATION_JSON)
+ @Path("redirect")
+ public Response getRedirectPolicies() {
+ PolicyService policyService = get(PolicyService.class);
+ ObjectNode root = mapper().createObjectNode();
+ ArrayNode policiesArr = root.putArray(POLICY);
+
+ Set<PolicyType> policyTypes = Set.of(PolicyType.REDIRECT);
+
+ for (PolicyData policyData : policyService.policies(policyTypes)) {
+ Policy policy = policyData.policy();
+ policiesArr.add(codec(RedirectPolicy.class).encode((RedirectPolicy) policy, this));
+ }
+
+ return Response.ok(root).build();
+ }
+
+ /**
+ * Create a new Redirect Policy.
+ *
+ * @param input Json for the Redirect Policy
+ * @return 200 OK and policyId
+ * @onos.rsModel RedirectPolicyCreate
+ */
+ @POST
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ @Path("redirect")
+ public Response createRedirectPolicy(InputStream input) {
+ PolicyService policyService = get(PolicyService.class);
+ ObjectNode root = mapper().createObjectNode();
+
+ try {
+ ObjectNode jsonTree = readTreeFromStream(mapper(), input);
+ RedirectPolicy redirectPolicy = codec(RedirectPolicy.class).
+ decode(jsonTree, this);
+ policyService.addOrUpdatePolicy(redirectPolicy);
+ root.put(POLICY_ID, redirectPolicy.policyId().toString());
+ } catch (IOException ex) {
+ throw new IllegalArgumentException(ex);
+ }
+
+ return Response.ok(root).build();
+ }
+
+ /**
+ * Delete a Policy by policyId.
+ *
+ * @param policyId Policy identifier
+ * @return 204 NO CONTENT
*/
@DELETE
- @Consumes(MediaType.APPLICATION_JSON)
- public Response removePolicy(InputStream input) throws IOException {
- ObjectMapper mapper = new ObjectMapper();
- ObjectNode policyJson = readTreeFromStream(mapper, input);
- SegmentRoutingService srService = get(SegmentRoutingService.class);
- Policy policyInfo = POLICY_CODEC.decode(policyJson, this);
- // TODO: Check the result
- srService.removePolicy(policyInfo);
+ @Produces(MediaType.APPLICATION_JSON)
+ @Path("{policyId}")
+ public Response deletePolicy(@PathParam("policyId") String policyId) {
+ PolicyService policyService = get(PolicyService.class);
+
+ policyService.removePolicy(PolicyId.of(policyId));
return Response.noContent().build();
}
+ /**
+ * Get all Traffic Matches.
+ *
+ * @return 200 OK will a collection of Traffic Matches
+ */
+ @GET
+ @Produces(MediaType.APPLICATION_JSON)
+ @Path("trafficmatch")
+ public Response getTrafficMatches() {
+ PolicyService policyService = get(PolicyService.class);
+ ObjectNode root = mapper().createObjectNode();
+ ArrayNode trafficMatchArr = root.putArray(TRAFFIC_MATCH);
+
+ for (TrafficMatchData trafficMatchData : policyService.trafficMatches()) {
+ TrafficMatch trafficMatch = trafficMatchData.trafficMatch();
+ trafficMatchArr.add(codec(TrafficMatch.class).encode(trafficMatch, this));
+ }
+
+ return Response.ok(root).build();
+ }
+
+ /**
+ * Create a new Traffic Match.
+ *
+ * @param input Json for the Traffic Match
+ * @return status of the request - CREATED and TrafficMatchId if the JSON is correct,
+ * BAD_REQUEST if the JSON is invalid
+ * @onos.rsModel TrafficMatchCreate
+ */
+ @POST
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ @Path("trafficmatch")
+ public Response createTrafficMatch(InputStream input) {
+ PolicyService policyService = get(PolicyService.class);
+ ObjectNode root = mapper().createObjectNode();
+
+ try {
+ ObjectNode jsonTree = readTreeFromStream(mapper(), input);
+ TrafficMatch trafficMatch = codec(TrafficMatch.class).
+ decode(jsonTree, this);
+ if (trafficMatch.trafficSelector()
+ .equals(DefaultTrafficSelector.emptySelector())) {
+ throw new IllegalArgumentException(EMPTY_TRAFFIC_SELECTOR);
+ }
+ policyService.addOrUpdateTrafficMatch(trafficMatch);
+ root.put(TRAFFIC_MATCH_ID, trafficMatch.trafficMatchId().toString());
+ } catch (IOException ex) {
+ throw new IllegalArgumentException(ex);
+ }
+
+ return Response.ok(root).build();
+ }
+
+ /**
+ * Delete a Traffic Match by trafficMatchId.
+ *
+ * @param trafficMatchId Traffic Match identifier
+ * @return 204 NO CONTENT
+ */
+ @DELETE
+ @Produces(MediaType.APPLICATION_JSON)
+ @Path("trafficmatch/{trafficMatchId}")
+ public Response deleteTrafficMatch(@PathParam("trafficMatchId") String trafficMatchId) {
+ PolicyService policyService = get(PolicyService.class);
+
+ policyService.removeTrafficMatch(TrafficMatchId.of(trafficMatchId));
+
+ return Response.noContent().build();
+ }
}
diff --git a/web/src/main/java/org/onosproject/segmentrouting/web/SegmentRoutingWebApplication.java b/web/src/main/java/org/onosproject/segmentrouting/web/SegmentRoutingWebApplication.java
index 6704030..e62bf90 100644
--- a/web/src/main/java/org/onosproject/segmentrouting/web/SegmentRoutingWebApplication.java
+++ b/web/src/main/java/org/onosproject/segmentrouting/web/SegmentRoutingWebApplication.java
@@ -29,7 +29,8 @@
return getClasses(
PseudowireWebResource.class,
McastWebResource.class,
- XconnectWebResource.class
+ XconnectWebResource.class,
+ PolicyWebResource.class
);
}
}
diff --git a/web/src/main/resources/definitions/RedirectPolicyCreate.json b/web/src/main/resources/definitions/RedirectPolicyCreate.json
new file mode 100644
index 0000000..cade149
--- /dev/null
+++ b/web/src/main/resources/definitions/RedirectPolicyCreate.json
@@ -0,0 +1,32 @@
+{
+ "type": "object",
+ "title": "redirect-policy-creation",
+ "required": [
+ "spinesToEnforce"
+ ],
+ "properties": {
+ "spinesToEnforce": {
+ "type": "object",
+ "title": "spinesToEnforce",
+ "required": [
+ "deviceId"
+ ],
+ "properties": {
+ "deviceId": {
+ "type": "array",
+ "xml": {
+ "name": "deviceId",
+ "wrapped": true
+ },
+ "items": {
+ "type": "string",
+ "title": "deviceId",
+ "example": "of:0000000000000001",
+ "description": "deviceId of spines to enforce"
+ },
+ "description": "Array of deviceId"
+ }
+ }
+ }
+ }
+}
diff --git a/web/src/main/resources/definitions/TrafficMatchCreate.json b/web/src/main/resources/definitions/TrafficMatchCreate.json
new file mode 100644
index 0000000..3ab1022
--- /dev/null
+++ b/web/src/main/resources/definitions/TrafficMatchCreate.json
@@ -0,0 +1,245 @@
+{
+ "type": "object",
+ "title": "traffic-match-creation",
+ "required": [
+ "policy_id",
+ "selector"
+ ],
+ "properties": {
+ "policy_id": {
+ "type": "string",
+ "example": "DROP",
+ "description": "ID of associated policy"
+ },
+ "selector": {
+ "type": "object",
+ "title": "selector",
+ "required": [
+ "criteria"
+ ],
+ "properties": {
+ "criteria": {
+ "type": "array",
+ "xml": {
+ "name": "criteria",
+ "wrapped": true
+ },
+ "items": {
+ "type": "object",
+ "title": "criteria",
+ "properties": {
+ "type": {
+ "type": "string",
+ "description": "Ethernet field name",
+ "example": "ETH_TYPE"
+ },
+ "ethType": {
+ "type": "int64",
+ "format": "int64",
+ "example": "0x88cc",
+ "description": "Ethernet frame type"
+ },
+ "mac": {
+ "type": "string",
+ "example": "00:00:11:00:00:01"
+ },
+ "port": {
+ "type": "int64",
+ "format": "int64",
+ "example": 1,
+ "description": "Match port"
+ },
+ "metadata": {
+ "type": "Hex16",
+ "format": "Hex16",
+ "example": "0xabcdL",
+ "description": "Metadata passed between tables"
+ },
+ "vlanId": {
+ "type": "uint16",
+ "format": "uint16",
+ "example": "0x1000"
+ },
+ "priority": {
+ "type": "int64",
+ "format": "int64",
+ "example": 1,
+ "description": "VLAN priority."
+ },
+ "ipDscp": {
+ "type": "byte",
+ "format": "byte",
+ "description": "IP DSCP (6 bits in ToS field)"
+ },
+ "ipEcn": {
+ "type": "byte",
+ "format": "byte",
+ "description": "IP ECN (2 bits in ToS field)."
+ },
+ "protocol": {
+ "type": "uint16",
+ "format": "uint16",
+ "example": 1,
+ "description": "IP protocol"
+ },
+ "ip": {
+ "type": "string",
+ "example": "10.1.1.0/24",
+ "description": "IP source address"
+ },
+ "tcpPort": {
+ "type": "integer",
+ "format": "uint16",
+ "example": 1,
+ "description": "TCP source address"
+ },
+ "udpPort": {
+ "type": "uint16",
+ "format": "uint16",
+ "example": 1,
+ "description": "UDP source address"
+ },
+ "sctpPort": {
+ "type": "uint16",
+ "format": "uint16",
+ "example": 1,
+ "description": "SCTP source address"
+ },
+ "icmpType": {
+ "type": "uint16",
+ "format": "uint16",
+ "example": 1,
+ "description": "Internet Control Message Protocol for IPV4 code (RFC0792)"
+ },
+ "icmpCode": {
+ "type": "uint16",
+ "format": "uint16",
+ "example": 1,
+ "description": "Internet Control Message Protocol for IPV4 code (RFC0792)"
+ },
+ "flowLabel": {
+ "type": "Hex16",
+ "format": "Hex16",
+ "example": "0xffffe",
+ "description": "IPv6 Flow Label (RFC 6437)"
+ },
+ "icmpv6Type": {
+ "type": "uint16",
+ "format": "uint16",
+ "example": 1,
+ "description": "Internet Control Message Protocol for IPV6 type (RFC2463)"
+ },
+ "icmpv6Code": {
+ "type": "uint16",
+ "format": "uint16",
+ "example": 1,
+ "description": "Internet Control Message Protocol for IPV6 code (RFC2463)"
+ },
+ "targetAddress": {
+ "type": "String",
+ "example": "10.1.1.0/24",
+ "description": "IPv6 Neighbor discovery target address"
+ },
+ "label": {
+ "type": "int32",
+ "format": "int32",
+ "example": 1,
+ "description": "MPLS label"
+ },
+ "exthdrFlags": {
+ "type": "int64",
+ "format": "int64",
+ "example": 1,
+ "description": "IPv6 extension header pseudo-field"
+ },
+ "lambda": {
+ "type": "int64",
+ "format": "int64",
+ "example": 1,
+ "description": "wavelength abstraction"
+ },
+ "gridType": {
+ "type": "String",
+ "example": "DWDM",
+ "description": "Type of wavelength grid"
+ },
+ "channelSpacing": {
+ "type": "int64",
+ "format": "int64",
+ "example": 100,
+ "description": "Optical channel spacing"
+ },
+ "spacingMultiplier": {
+ "type": "integer",
+ "format": "int64",
+ "example": 4,
+ "description": "Optical channel spacing multiplier"
+ },
+ "slotGranularity": {
+ "type": "int64",
+ "format": "int64",
+ "example": 8
+ },
+ "ochSignalId": {
+ "type": "integer",
+ "format": "int64",
+ "example": 1,
+ "description": "Optical channel signal ID"
+ },
+ "tunnelId": {
+ "type": "int64",
+ "format": "int64",
+ "example": 5,
+ "description": "Tunnel ID"
+ },
+ "ochSignalType": {
+ "type": "int64",
+ "format": "int64",
+ "example": 1,
+ "description": "Optical channel signal type"
+ },
+ "oduSignalId": {
+ "type": "int64",
+ "format": "int64",
+ "example": 1,
+ "description": "ODU (Optical channel Data Unit) signal ID."
+ },
+ "tributaryPortNumber": {
+ "type": "int64",
+ "format": "int64",
+ "example": 11,
+ "description": "OPU (Optical channel Payload Unit) port number."
+ },
+ "tributarySlotLen": {
+ "type": "int64",
+ "format": "int64",
+ "example": 80,
+ "description": "OPU (Optical channel Payload Unit) slot length."
+ },
+ "tributarySlotBitmap": {
+ "type": "array",
+ "title": "tributarySlotBitmap",
+ "description": "OPU (Optical channel Payload Unit) slot bitmap.",
+ "required": [
+ "byte",
+ "port"
+ ],
+ "items": {
+ "type": "byte",
+ "title": "byte",
+ "example": 1
+ }
+ },
+ "oduSignalType": {
+ "type": "int64",
+ "format": "int64",
+ "example": 4,
+ "description": "ODU (Optical channel Data Unit) signal type."
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}