[ONOS-7826] Adding a TAPI 2.1 Flow Rule programmable behaviour
Change-Id: I0b0070c4ca2a8c175b1fc6a4107566bb503e5b04
diff --git a/drivers/odtn-driver/src/main/java/org/onosproject/drivers/odtn/impl/DeviceConnectionCache.java b/drivers/odtn-driver/src/main/java/org/onosproject/drivers/odtn/impl/DeviceConnectionCache.java
index ba0ea76..827ebd4 100644
--- a/drivers/odtn-driver/src/main/java/org/onosproject/drivers/odtn/impl/DeviceConnectionCache.java
+++ b/drivers/odtn-driver/src/main/java/org/onosproject/drivers/odtn/impl/DeviceConnectionCache.java
@@ -101,16 +101,15 @@
* @param flowId flow id
* @return the flow rule
*/
- public FlowRule get(DeviceId did, FlowId flowId) {
+ public DeviceConnection get(DeviceId did, FlowId flowId) {
if (!flowCache.containsKey(did)) {
return null;
}
Set<DeviceConnection> set = flowCache.get(did);
- DeviceConnection connection = set.stream()
+ return set.stream()
.filter(c -> c.getFlowRule().id() == flowId)
.findFirst()
.orElse(null);
- return connection != null ? connection.getFlowRule() : null;
}
/**
diff --git a/drivers/odtn-driver/src/main/java/org/onosproject/drivers/odtn/tapi/TapiDeviceDescriptionDiscovery.java b/drivers/odtn-driver/src/main/java/org/onosproject/drivers/odtn/tapi/TapiDeviceDescriptionDiscovery.java
index 4bc7e69..c106202 100644
--- a/drivers/odtn-driver/src/main/java/org/onosproject/drivers/odtn/tapi/TapiDeviceDescriptionDiscovery.java
+++ b/drivers/odtn-driver/src/main/java/org/onosproject/drivers/odtn/tapi/TapiDeviceDescriptionDiscovery.java
@@ -46,7 +46,24 @@
import java.util.List;
import static com.google.common.base.Preconditions.checkNotNull;
-import static org.onosproject.drivers.odtn.tapi.TapiDeviceHelper.*;
+import static org.onosproject.drivers.odtn.tapi.TapiDeviceHelper.LAYER_PROTOCOL_NAME;
+import static org.onosproject.drivers.odtn.tapi.TapiDeviceHelper.MEDIA_CHANNEL_SERVICE_INTERFACE_POINT_SPEC;
+import static org.onosproject.drivers.odtn.tapi.TapiDeviceHelper.AVAILABLE_SPECTRUM;
+import static org.onosproject.drivers.odtn.tapi.TapiDeviceHelper.ADJUSTMENT_GRANULARITY;
+import static org.onosproject.drivers.odtn.tapi.TapiDeviceHelper.BASE_FREQUENCY;
+import static org.onosproject.drivers.odtn.tapi.TapiDeviceHelper.FREQUENCY_CONSTRAINT;
+import static org.onosproject.drivers.odtn.tapi.TapiDeviceHelper.GRID_TYPE;
+import static org.onosproject.drivers.odtn.tapi.TapiDeviceHelper.LOWER_FREQUENCY;
+import static org.onosproject.drivers.odtn.tapi.TapiDeviceHelper.MC_POOL;
+import static org.onosproject.drivers.odtn.tapi.TapiDeviceHelper.PHOTONIC_LAYER_QUALIFIER_NMC;
+import static org.onosproject.drivers.odtn.tapi.TapiDeviceHelper.PHOTONIC_MEDIA;
+import static org.onosproject.drivers.odtn.tapi.TapiDeviceHelper.SERVICE_INTERFACE_POINT;
+import static org.onosproject.drivers.odtn.tapi.TapiDeviceHelper.SUPPORTED_LAYER_PROTOCOL_QUALIFIER;
+import static org.onosproject.drivers.odtn.tapi.TapiDeviceHelper.UPPER_FREQUENCY;
+import static org.onosproject.drivers.odtn.tapi.TapiDeviceHelper.UUID;
+import static org.onosproject.drivers.odtn.tapi.TapiDeviceHelper.toMbpsFromHz;
+import static org.onosproject.drivers.odtn.tapi.TapiDeviceHelper.getChannelSpacing;
+import static org.onosproject.drivers.odtn.tapi.TapiDeviceHelper.getSlotGranularity;
import static org.onosproject.net.optical.device.OchPortHelper.ochPortDescription;
import static org.slf4j.LoggerFactory.getLogger;
@@ -142,7 +159,7 @@
OchSignal ochSignal = getOchSignal(mcPool);
synchronized (N_PORT_LOCK) {
//annotations(portNumber-uuid)
- annotations.set(nPort.toString(), uuid);
+ annotations.set(UUID, uuid);
//add och port
ports.add(ochPortDescription(nPort, true, OduSignalType.ODU4,
diff --git a/drivers/odtn-driver/src/main/java/org/onosproject/drivers/odtn/tapi/TapiDeviceHelper.java b/drivers/odtn-driver/src/main/java/org/onosproject/drivers/odtn/tapi/TapiDeviceHelper.java
index 11bf38c..d4e9c06 100644
--- a/drivers/odtn-driver/src/main/java/org/onosproject/drivers/odtn/tapi/TapiDeviceHelper.java
+++ b/drivers/odtn-driver/src/main/java/org/onosproject/drivers/odtn/tapi/TapiDeviceHelper.java
@@ -40,6 +40,16 @@
public static final String LOWER_FREQUENCY = "lower-frequency";
public static final String AVAILABLE_SPECTRUM = "available-spectrum";
public static final long BASE_FREQUENCY = 193100000; //Working in Mhz
+ public static final String TAPI_CONNECTIVITY_CONNECTIVITY_SERVICE = "tapi-connectivity:connectivity-service";
+ public static final String END_POINT = "end-point";
+ public static final String SERVICE_LAYER = "service-layer";
+ public static final String SERVICE_TYPE = "service-type";
+ public static final String POINT_TO_POINT_CONNECTIVITY = "POINT_TO_POINT_CONNECTIVITY";
+ public static final String LOCAL_ID = "local-id";
+ public static final String LAYER_PROTOCOL_QUALIFIER = "layer-protocol-qualifier";
+ public static final String TAPI_PHOTONIC_MEDIA_PHOTONIC_LAYER_QUALIFIER_NMC =
+ "tapi-photonic-media:PHOTONIC_LAYER_QUALIFIER_NMC";
+ public static final String SERVICE_INTERFACE_POINT_UUID = "service-interface-point-uuid";
private TapiDeviceHelper(){}
diff --git a/drivers/odtn-driver/src/main/java/org/onosproject/drivers/odtn/tapi/TapiDeviceLambdaQuery.java b/drivers/odtn-driver/src/main/java/org/onosproject/drivers/odtn/tapi/TapiDeviceLambdaQuery.java
index 0127f43..fa46b87 100644
--- a/drivers/odtn-driver/src/main/java/org/onosproject/drivers/odtn/tapi/TapiDeviceLambdaQuery.java
+++ b/drivers/odtn-driver/src/main/java/org/onosproject/drivers/odtn/tapi/TapiDeviceLambdaQuery.java
@@ -48,6 +48,7 @@
import static org.onosproject.drivers.odtn.tapi.TapiDeviceHelper.LOWER_FREQUENCY;
import static org.onosproject.drivers.odtn.tapi.TapiDeviceHelper.MC_POOL;
import static org.onosproject.drivers.odtn.tapi.TapiDeviceHelper.UPPER_FREQUENCY;
+import static org.onosproject.drivers.odtn.tapi.TapiDeviceHelper.UUID;
import static org.onosproject.drivers.odtn.tapi.TapiDeviceHelper.toMbpsFromHz;
import static org.onosproject.drivers.odtn.tapi.TapiDeviceHelper.getChannelSpacing;
import static org.onosproject.drivers.odtn.tapi.TapiDeviceHelper.getSlotGranularity;
@@ -92,7 +93,7 @@
log.error("Port {} does not exist", port);
return ImmutableSet.of();
}
- String uuid = p.annotations().value(port.toString());
+ String uuid = p.annotations().value(UUID);
try {
InputStream inputStream = controller.get(deviceId, SIP_REQUEST_DATA_API + uuid,
diff --git a/drivers/odtn-driver/src/main/java/org/onosproject/drivers/odtn/tapi/TapiFlowRuleProgrammable.java b/drivers/odtn-driver/src/main/java/org/onosproject/drivers/odtn/tapi/TapiFlowRuleProgrammable.java
new file mode 100644
index 0000000..c0bea12
--- /dev/null
+++ b/drivers/odtn-driver/src/main/java/org/onosproject/drivers/odtn/tapi/TapiFlowRuleProgrammable.java
@@ -0,0 +1,297 @@
+/*
+ * Copyright 2018-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * This work was partially supported by EC H2020 project METRO-HAUL (761727).
+ */
+package org.onosproject.drivers.odtn.tapi;
+
+import com.fasterxml.jackson.core.JsonEncoding;
+import com.fasterxml.jackson.core.JsonFactory;
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.ObjectReader;
+import com.google.common.collect.ImmutableList;
+import org.apache.http.HttpStatus;
+import org.onosproject.drivers.odtn.impl.DeviceConnection;
+import org.onosproject.drivers.odtn.impl.DeviceConnectionCache;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.device.DeviceService;
+import org.onosproject.net.driver.AbstractHandlerBehaviour;
+import org.onosproject.net.flow.DefaultFlowEntry;
+import org.onosproject.net.flow.FlowEntry;
+import org.onosproject.net.flow.FlowRule;
+import org.onosproject.net.flow.FlowRuleProgrammable;
+import org.onosproject.net.flow.criteria.Criterion;
+import org.onosproject.net.flow.criteria.PortCriterion;
+import org.onosproject.net.flow.instructions.Instruction;
+import org.onosproject.net.flow.instructions.Instructions;
+import org.onosproject.protocol.rest.RestSBController;
+import org.slf4j.Logger;
+
+import javax.ws.rs.core.MediaType;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.UUID;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.onosproject.drivers.odtn.tapi.TapiDeviceHelper.END_POINT;
+import static org.onosproject.drivers.odtn.tapi.TapiDeviceHelper.LAYER_PROTOCOL_NAME;
+import static org.onosproject.drivers.odtn.tapi.TapiDeviceHelper.LAYER_PROTOCOL_QUALIFIER;
+import static org.onosproject.drivers.odtn.tapi.TapiDeviceHelper.LOCAL_ID;
+import static org.onosproject.drivers.odtn.tapi.TapiDeviceHelper.PHOTONIC_MEDIA;
+import static org.onosproject.drivers.odtn.tapi.TapiDeviceHelper.POINT_TO_POINT_CONNECTIVITY;
+import static org.onosproject.drivers.odtn.tapi.TapiDeviceHelper.SERVICE_INTERFACE_POINT;
+import static org.onosproject.drivers.odtn.tapi.TapiDeviceHelper.SERVICE_INTERFACE_POINT_UUID;
+import static org.onosproject.drivers.odtn.tapi.TapiDeviceHelper.SERVICE_LAYER;
+import static org.onosproject.drivers.odtn.tapi.TapiDeviceHelper.SERVICE_TYPE;
+import static org.onosproject.drivers.odtn.tapi.TapiDeviceHelper.TAPI_CONNECTIVITY_CONNECTIVITY_SERVICE;
+import static org.onosproject.drivers.odtn.tapi.TapiDeviceHelper.TAPI_PHOTONIC_MEDIA_PHOTONIC_LAYER_QUALIFIER_NMC;
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Driver Implementation of the DeviceDescrption discovery for ONF Transport-API (TAPI) v2.1 based
+ * open line systems (OLS).
+ */
+
+public class TapiFlowRuleProgrammable extends AbstractHandlerBehaviour
+ implements FlowRuleProgrammable {
+
+ private static final Logger log = getLogger(TapiFlowRuleProgrammable.class);
+ private static final String CONN_REQ_POST_API = "/restconf/data/tapi-common:context/" +
+ "tapi-connectivity:connectivity-context/";
+ private static final String CONN_REQ_REMOVE_DATA_API = "/restconf/data/tapi-common:context/" +
+ "tapi-connectivity:connectivity-context/connectivity-service=";
+ private static final String CONN_REQ_GET_API = "/restconf/data/tapi-common:context/" +
+ "tapi-connectivity:connectivity-context/";
+
+
+ @Override
+ public Collection<FlowEntry> getFlowEntries() {
+ DeviceId deviceId = did();
+ RestSBController controller = checkNotNull(handler().get(RestSBController.class));
+ ObjectMapper om = new ObjectMapper();
+ final ObjectReader reader = om.reader();
+ // all waveserver responses contain data node, which contains the requested data
+ InputStream response = controller.get(deviceId, CONN_REQ_GET_API, MediaType.APPLICATION_JSON_TYPE);
+ JsonNode jsonNode = null;
+ try {
+ jsonNode = reader.readTree(response);
+ if (jsonNode == null) {
+ log.error("JsonNode is null for response {}", response);
+ return ImmutableList.of();
+ }
+ Set<String> uuids = parseTapiGetConnectivityRequest(jsonNode);
+ DeviceConnectionCache cache = getConnectionCache();
+ if (cache.get(deviceId) == null) {
+ return ImmutableList.of();
+ }
+ List<FlowEntry> entries = new ArrayList<>();
+ uuids.forEach(uuid -> {
+ FlowRule rule = cache.get(deviceId, uuid);
+ if (rule != null) {
+ entries.add(new DefaultFlowEntry(rule, FlowEntry.FlowEntryState.ADDED, 0, 0, 0));
+ } else {
+ log.info("Non existing rule for uuid {}", uuid);
+ }
+ });
+ return entries;
+ } catch (IOException e) {
+ return ImmutableList.of();
+ }
+ }
+
+ @Override
+ public Collection<FlowRule> applyFlowRules(Collection<FlowRule> rules) {
+ DeviceId deviceId = handler().data().deviceId();
+ RestSBController controller = checkNotNull(handler().get(RestSBController.class));
+ ImmutableList.Builder<FlowRule> added = ImmutableList.builder();
+ rules.forEach(flowRule -> {
+ String uuid = createUuid();
+ ByteArrayOutputStream applyConnectivityRequest = createConnectivityRequest(uuid, flowRule);
+ if (applyConnectivityRequest.size() != 0) {
+ int result = controller.post(deviceId, CONN_REQ_POST_API,
+ new ByteArrayInputStream(applyConnectivityRequest.toByteArray()),
+ MediaType.APPLICATION_JSON_TYPE);
+ // TODO retrieve the UUID from the location and store with that identifier
+ // at the moment is implied that the sent one is the same used by the TAPI server.
+ if (result == HttpStatus.SC_CREATED) {
+ getConnectionCache().add(deviceId, uuid, flowRule);
+ added.add(flowRule);
+ }
+ }
+ });
+ return added.build();
+ }
+
+ @Override
+ public Collection<FlowRule> removeFlowRules(Collection<FlowRule> rules) {
+ DeviceId deviceId = handler().data().deviceId();
+ RestSBController controller = checkNotNull(handler().get(RestSBController.class));
+ ImmutableList.Builder<FlowRule> removed = ImmutableList.builder();
+ rules.forEach(flowRule -> {
+ DeviceConnection conn = getConnectionCache().get(deviceId, flowRule.id());
+ if (conn == null || conn.getId() == null) {
+ log.warn("Can't find associate device connection for flow {} and device {}",
+ flowRule.id(), deviceId);
+ return;
+ }
+ int result = controller.delete(deviceId, CONN_REQ_REMOVE_DATA_API + conn.getId(),
+ null, MediaType.APPLICATION_JSON_TYPE);
+ if (result == HttpStatus.SC_NO_CONTENT) {
+ getConnectionCache().remove(deviceId, flowRule);
+ removed.add(flowRule);
+ }
+ });
+ return removed.build();
+ }
+
+ /**
+ * Get the deviceId for which the methods apply.
+ *
+ * @return The deviceId as contained in the handler data
+ */
+ private DeviceId did() {
+ return handler().data().deviceId();
+ }
+
+ private DeviceConnectionCache getConnectionCache() {
+ return DeviceConnectionCache.init();
+ }
+
+ protected Set<String> parseTapiGetConnectivityRequest(JsonNode tapiConnectivityReply) {
+ /*
+ {
+ "tapi-connectivity:connectivity-service":[
+ {
+ "uuid":"ffb006d4-349e-4d2f-817e-0906c88458d0",
+ <other fields>
+ }
+ ]
+ }
+ */
+ Set<String> uuids = new HashSet<>();
+ if (tapiConnectivityReply.has(TAPI_CONNECTIVITY_CONNECTIVITY_SERVICE)) {
+ tapiConnectivityReply.get(TAPI_CONNECTIVITY_CONNECTIVITY_SERVICE).elements()
+ .forEachRemaining(node -> uuids.add(node.get(TapiDeviceHelper.UUID).asText()));
+ } else {
+ log.warn("Cant retrieve connectivity UUID from {}", tapiConnectivityReply);
+ }
+ //This is only one uuid or empty in case of failures
+ return uuids;
+ }
+
+ ByteArrayOutputStream createConnectivityRequest(String uuid, FlowRule rule) {
+ /*
+ {
+ "tapi-connectivity:connectivity-service":[
+ {
+ "uuid":"ffb006d4-349e-4d2f-817e-0906c88458d0",
+ "service-layer":"PHOTONIC_MEDIA",
+ "service-type":"POINT_TO_POINT_CONNECTIVITY",
+ "end-point":[
+ {
+ "local-id":"1",
+ "layer-protocol-name":"PHOTONIC_MEDIA",
+ "layer-protocol-qualifier":"tapi-photonic-media:PHOTONIC_LAYER_QUALIFIER_NMC",
+ "service-interface-point":{
+ "service-interface-point-uuid":"0923962e-b83f-4702-9b16-a1a0db0dc1f9"
+ }
+ },
+ {
+ "local-id":"2",
+ "layer-protocol-name":"PHOTONIC_MEDIA",
+ "layer-protocol-qualifier":"tapi-photonic-media:PHOTONIC_LAYER_QUALIFIER_NMC",
+ "service-interface-point":{
+ "service-interface-point-uuid":"76be95de-5769-4e5d-b65e-62cb6c39cf6b "
+ }
+ }
+ ]
+ }
+ ]
+ }
+ */
+ DeviceService deviceService = handler().get(DeviceService.class);
+ PortCriterion inputPortCriterion = (PortCriterion) checkNotNull(rule.selector()
+ .getCriterion(Criterion.Type.IN_PORT));
+ String inputPortUuid = deviceService.getPort(rule.deviceId(),
+ inputPortCriterion.port()).annotations().value(TapiDeviceHelper.UUID);
+
+ Instructions.OutputInstruction outInstruction = (Instructions.OutputInstruction) checkNotNull(rule.treatment()
+ .allInstructions().stream().filter(instr -> instr.type().equals(Instruction.Type.OUTPUT))
+ .findFirst().orElse(null));
+ String outputPortUuid = deviceService.getPort(rule.deviceId(),
+ outInstruction.port()).annotations().value(TapiDeviceHelper.UUID);
+ ByteArrayOutputStream stream = new ByteArrayOutputStream();
+ try {
+ JsonGenerator generator = getJsonGenerator(stream);
+ generator.writeStartObject();
+ generator.writeArrayFieldStart(TAPI_CONNECTIVITY_CONNECTIVITY_SERVICE);
+ generator.writeStartObject();
+ generator.writeStringField(TapiDeviceHelper.UUID, uuid);
+ generator.writeStringField(SERVICE_LAYER, PHOTONIC_MEDIA);
+ generator.writeStringField(SERVICE_TYPE, POINT_TO_POINT_CONNECTIVITY);
+ generator.writeArrayFieldStart(END_POINT);
+ addEndPoint(generator, inputPortUuid);
+ addEndPoint(generator, outputPortUuid);
+ generator.writeEndArray();
+ generator.writeEndObject();
+ generator.writeEndArray();
+ generator.writeEndObject();
+ generator.close();
+ return stream;
+ } catch (IOException e) {
+ log.error("Cant' create json", e);
+ }
+ return stream;
+ }
+
+ private JsonGenerator getJsonGenerator(ByteArrayOutputStream stream) throws IOException {
+ JsonFactory factory = new JsonFactory();
+ return factory.createGenerator(stream, JsonEncoding.UTF8);
+ }
+ /*
+ {
+ "local-id":"1",
+ "layer-protocol-name":"PHOTONIC_MEDIA",
+ "layer-protocol-qualifier":"tapi-photonic-media:PHOTONIC_LAYER_QUALIFIER_NMC",
+ "service-interface-point":{
+ "service-interface-point-uuid":"0923962e-b83f-4702-9b16-a1a0db0dc1f9"
+ }
+ }
+ */
+ private void addEndPoint(JsonGenerator generator, String sipUuid) throws IOException {
+ generator.writeStartObject();
+ generator.writeStringField(LOCAL_ID, sipUuid);
+ generator.writeStringField(LAYER_PROTOCOL_NAME, PHOTONIC_MEDIA);
+ generator.writeStringField(LAYER_PROTOCOL_QUALIFIER,
+ TAPI_PHOTONIC_MEDIA_PHOTONIC_LAYER_QUALIFIER_NMC);
+ generator.writeObjectFieldStart(SERVICE_INTERFACE_POINT);
+ generator.writeStringField(SERVICE_INTERFACE_POINT_UUID, sipUuid);
+ generator.writeEndObject();
+ generator.writeEndObject();
+ }
+
+ private String createUuid() {
+ return UUID.randomUUID().toString();
+ }
+}