blob: ce09453858cf08843cd6dd4a3d6e85bfa1549700 [file] [log] [blame]
Andrea Campanella2bdf2042019-01-28 13:47:11 +01001/*
2 * Copyright 2018-present Open Networking Foundation
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 *
16 * This work was partially supported by EC H2020 project METRO-HAUL (761727).
17 */
18package org.onosproject.drivers.odtn.tapi;
19
20import com.fasterxml.jackson.core.JsonEncoding;
21import com.fasterxml.jackson.core.JsonFactory;
22import com.fasterxml.jackson.core.JsonGenerator;
23import com.fasterxml.jackson.databind.JsonNode;
Andrea Campanella2bdf2042019-01-28 13:47:11 +010024import com.google.common.collect.ImmutableList;
25import org.apache.http.HttpStatus;
26import org.onosproject.drivers.odtn.impl.DeviceConnection;
27import org.onosproject.drivers.odtn.impl.DeviceConnectionCache;
28import org.onosproject.net.DeviceId;
29import org.onosproject.net.device.DeviceService;
30import org.onosproject.net.driver.AbstractHandlerBehaviour;
31import org.onosproject.net.flow.DefaultFlowEntry;
32import org.onosproject.net.flow.FlowEntry;
33import org.onosproject.net.flow.FlowRule;
34import org.onosproject.net.flow.FlowRuleProgrammable;
35import org.onosproject.net.flow.criteria.Criterion;
36import org.onosproject.net.flow.criteria.PortCriterion;
37import org.onosproject.net.flow.instructions.Instruction;
38import org.onosproject.net.flow.instructions.Instructions;
39import org.onosproject.protocol.rest.RestSBController;
40import org.slf4j.Logger;
41
42import javax.ws.rs.core.MediaType;
43import java.io.ByteArrayInputStream;
44import java.io.ByteArrayOutputStream;
45import java.io.IOException;
Andrea Campanella2bdf2042019-01-28 13:47:11 +010046import java.util.ArrayList;
47import java.util.Collection;
48import java.util.HashSet;
49import java.util.List;
50import java.util.Set;
51import java.util.UUID;
Andrea Campanellab9e491b2019-02-18 17:45:01 +010052import java.util.concurrent.CompletableFuture;
Andrea Campanella2bdf2042019-01-28 13:47:11 +010053
54import static com.google.common.base.Preconditions.checkNotNull;
55import static org.onosproject.drivers.odtn.tapi.TapiDeviceHelper.END_POINT;
56import static org.onosproject.drivers.odtn.tapi.TapiDeviceHelper.LAYER_PROTOCOL_NAME;
57import static org.onosproject.drivers.odtn.tapi.TapiDeviceHelper.LAYER_PROTOCOL_QUALIFIER;
58import static org.onosproject.drivers.odtn.tapi.TapiDeviceHelper.LOCAL_ID;
59import static org.onosproject.drivers.odtn.tapi.TapiDeviceHelper.PHOTONIC_MEDIA;
60import static org.onosproject.drivers.odtn.tapi.TapiDeviceHelper.POINT_TO_POINT_CONNECTIVITY;
61import static org.onosproject.drivers.odtn.tapi.TapiDeviceHelper.SERVICE_INTERFACE_POINT;
62import static org.onosproject.drivers.odtn.tapi.TapiDeviceHelper.SERVICE_INTERFACE_POINT_UUID;
63import static org.onosproject.drivers.odtn.tapi.TapiDeviceHelper.SERVICE_LAYER;
64import static org.onosproject.drivers.odtn.tapi.TapiDeviceHelper.SERVICE_TYPE;
65import static org.onosproject.drivers.odtn.tapi.TapiDeviceHelper.TAPI_CONNECTIVITY_CONNECTIVITY_SERVICE;
66import static org.onosproject.drivers.odtn.tapi.TapiDeviceHelper.TAPI_PHOTONIC_MEDIA_PHOTONIC_LAYER_QUALIFIER_NMC;
67import static org.slf4j.LoggerFactory.getLogger;
68
69/**
70 * Driver Implementation of the DeviceDescrption discovery for ONF Transport-API (TAPI) v2.1 based
71 * open line systems (OLS).
72 */
73
74public class TapiFlowRuleProgrammable extends AbstractHandlerBehaviour
75 implements FlowRuleProgrammable {
76
77 private static final Logger log = getLogger(TapiFlowRuleProgrammable.class);
78 private static final String CONN_REQ_POST_API = "/restconf/data/tapi-common:context/" +
79 "tapi-connectivity:connectivity-context/";
80 private static final String CONN_REQ_REMOVE_DATA_API = "/restconf/data/tapi-common:context/" +
81 "tapi-connectivity:connectivity-context/connectivity-service=";
82 private static final String CONN_REQ_GET_API = "/restconf/data/tapi-common:context/" +
Andrea Campanellac4953eb2019-03-18 15:38:46 -070083 "tapi-connectivity:connectivity-context/";
Andrea Campanella2bdf2042019-01-28 13:47:11 +010084
85
86 @Override
87 public Collection<FlowEntry> getFlowEntries() {
88 DeviceId deviceId = did();
Andrea Campanellab9e491b2019-02-18 17:45:01 +010089 //TODO this is a blocking call on ADVA OLS, right now using cache.
90// RestSBController controller = checkNotNull(handler().get(RestSBController.class));
91// ObjectMapper om = new ObjectMapper();
92// final ObjectReader reader = om.reader();
93// InputStream response = controller.get(deviceId, CONN_REQ_GET_API, MediaType.APPLICATION_JSON_TYPE);
94// JsonNode jsonNode = null;
95// try {
96// jsonNode = reader.readTree(response);
97// if (jsonNode == null) {
Andrea Campanellac4953eb2019-03-18 15:38:46 -070098// log.error("JsonNode is null for response {}", response);
Andrea Campanellab9e491b2019-02-18 17:45:01 +010099// return ImmutableList.of();
100// }
101// Set<String> uuids = parseTapiGetConnectivityRequest(jsonNode);
102// DeviceConnectionCache cache = getConnectionCache();
103// if (cache.get(deviceId) == null) {
104// return ImmutableList.of();
105// }
106// List<FlowEntry> entries = new ArrayList<>();
107// uuids.forEach(uuid -> {
108// FlowRule rule = cache.get(deviceId, uuid);
109// if (rule != null) {
110// entries.add(new DefaultFlowEntry(rule, FlowEntry.FlowEntryState.ADDED, 0, 0, 0));
111// } else {
112// log.info("Non existing rule for uuid {}", uuid);
113// }
114// });
115// return entries;
116// } catch (IOException e) {
117// return ImmutableList.of();
118// }
119 List<FlowEntry> entries = new ArrayList<>();
120 Set<FlowRule> rules = getConnectionCache().get(deviceId);
121 if (rules != null) {
122 rules.forEach(rule -> {
123 entries.add(new DefaultFlowEntry(rule, FlowEntry.FlowEntryState.ADDED, 0, 0, 0));
Andrea Campanella2bdf2042019-01-28 13:47:11 +0100124 });
Andrea Campanella2bdf2042019-01-28 13:47:11 +0100125 }
Andrea Campanellab9e491b2019-02-18 17:45:01 +0100126 return entries;
Andrea Campanella2bdf2042019-01-28 13:47:11 +0100127 }
128
129 @Override
130 public Collection<FlowRule> applyFlowRules(Collection<FlowRule> rules) {
131 DeviceId deviceId = handler().data().deviceId();
132 RestSBController controller = checkNotNull(handler().get(RestSBController.class));
133 ImmutableList.Builder<FlowRule> added = ImmutableList.builder();
134 rules.forEach(flowRule -> {
135 String uuid = createUuid();
136 ByteArrayOutputStream applyConnectivityRequest = createConnectivityRequest(uuid, flowRule);
137 if (applyConnectivityRequest.size() != 0) {
Andrea Campanellab9e491b2019-02-18 17:45:01 +0100138 CompletableFuture<Integer> flowInstallation =
139 CompletableFuture.supplyAsync(() -> controller.post(deviceId, CONN_REQ_POST_API,
Andrea Campanella2bdf2042019-01-28 13:47:11 +0100140 new ByteArrayInputStream(applyConnectivityRequest.toByteArray()),
Andrea Campanellab9e491b2019-02-18 17:45:01 +0100141 MediaType.APPLICATION_JSON_TYPE));
142 flowInstallation.thenApply(result -> {
143 if (result == HttpStatus.SC_CREATED) {
144 getConnectionCache().add(deviceId, uuid, flowRule);
145 added.add(flowRule);
146 } else {
147 log.error("Can't add flow {}, result {}", flowRule, result);
148 }
149 return result;
150 });
Andrea Campanella2bdf2042019-01-28 13:47:11 +0100151 // TODO retrieve the UUID from the location and store with that identifier
152 // at the moment is implied that the sent one is the same used by the TAPI server.
Andrea Campanella2bdf2042019-01-28 13:47:11 +0100153 }
154 });
Andrea Campanellab9e491b2019-02-18 17:45:01 +0100155 //TODO workaround for blocking call on ADVA OLS should return added
156 return rules;
Andrea Campanella2bdf2042019-01-28 13:47:11 +0100157 }
158
159 @Override
160 public Collection<FlowRule> removeFlowRules(Collection<FlowRule> rules) {
161 DeviceId deviceId = handler().data().deviceId();
162 RestSBController controller = checkNotNull(handler().get(RestSBController.class));
163 ImmutableList.Builder<FlowRule> removed = ImmutableList.builder();
164 rules.forEach(flowRule -> {
165 DeviceConnection conn = getConnectionCache().get(deviceId, flowRule.id());
166 if (conn == null || conn.getId() == null) {
167 log.warn("Can't find associate device connection for flow {} and device {}",
168 flowRule.id(), deviceId);
169 return;
170 }
Andrea Campanellab9e491b2019-02-18 17:45:01 +0100171 CompletableFuture<Integer> flowInstallation =
172 CompletableFuture.supplyAsync(() -> controller.delete(deviceId,
173 CONN_REQ_REMOVE_DATA_API + conn.getId(),
174 null, MediaType.APPLICATION_JSON_TYPE));
175 flowInstallation.thenApply(result -> {
176 if (result == HttpStatus.SC_NO_CONTENT) {
177 getConnectionCache().remove(deviceId, flowRule);
178 removed.add(flowRule);
179 } else {
180 log.error("Can't remove flow {}, result {}", flowRule, result);
181 }
182 return result;
183 });
Andrea Campanella2bdf2042019-01-28 13:47:11 +0100184 });
Andrea Campanellab9e491b2019-02-18 17:45:01 +0100185 //TODO workaround for blocking call on ADVA OLS shoudl return removed
186 return rules;
Andrea Campanella2bdf2042019-01-28 13:47:11 +0100187 }
188
189 /**
190 * Get the deviceId for which the methods apply.
191 *
192 * @return The deviceId as contained in the handler data
193 */
194 private DeviceId did() {
195 return handler().data().deviceId();
196 }
197
198 private DeviceConnectionCache getConnectionCache() {
199 return DeviceConnectionCache.init();
200 }
201
202 protected Set<String> parseTapiGetConnectivityRequest(JsonNode tapiConnectivityReply) {
203 /*
204 {
205 "tapi-connectivity:connectivity-service":[
206 {
207 "uuid":"ffb006d4-349e-4d2f-817e-0906c88458d0",
208 <other fields>
209 }
210 ]
211 }
212 */
213 Set<String> uuids = new HashSet<>();
214 if (tapiConnectivityReply.has(TAPI_CONNECTIVITY_CONNECTIVITY_SERVICE)) {
215 tapiConnectivityReply.get(TAPI_CONNECTIVITY_CONNECTIVITY_SERVICE).elements()
216 .forEachRemaining(node -> uuids.add(node.get(TapiDeviceHelper.UUID).asText()));
Andrea Campanellac4953eb2019-03-18 15:38:46 -0700217 } else {
Andrea Campanellab9e491b2019-02-18 17:45:01 +0100218 log.warn("Can't retrieve connectivity UUID from {}", tapiConnectivityReply);
Andrea Campanella2bdf2042019-01-28 13:47:11 +0100219 }
220 //This is only one uuid or empty in case of failures
221 return uuids;
222 }
223
224 ByteArrayOutputStream createConnectivityRequest(String uuid, FlowRule rule) {
225 /*
226 {
227 "tapi-connectivity:connectivity-service":[
228 {
229 "uuid":"ffb006d4-349e-4d2f-817e-0906c88458d0",
230 "service-layer":"PHOTONIC_MEDIA",
231 "service-type":"POINT_TO_POINT_CONNECTIVITY",
232 "end-point":[
233 {
234 "local-id":"1",
235 "layer-protocol-name":"PHOTONIC_MEDIA",
236 "layer-protocol-qualifier":"tapi-photonic-media:PHOTONIC_LAYER_QUALIFIER_NMC",
237 "service-interface-point":{
238 "service-interface-point-uuid":"0923962e-b83f-4702-9b16-a1a0db0dc1f9"
239 }
240 },
241 {
242 "local-id":"2",
243 "layer-protocol-name":"PHOTONIC_MEDIA",
244 "layer-protocol-qualifier":"tapi-photonic-media:PHOTONIC_LAYER_QUALIFIER_NMC",
245 "service-interface-point":{
246 "service-interface-point-uuid":"76be95de-5769-4e5d-b65e-62cb6c39cf6b "
247 }
248 }
249 ]
250 }
251 ]
252 }
253 */
254 DeviceService deviceService = handler().get(DeviceService.class);
255 PortCriterion inputPortCriterion = (PortCriterion) checkNotNull(rule.selector()
256 .getCriterion(Criterion.Type.IN_PORT));
257 String inputPortUuid = deviceService.getPort(rule.deviceId(),
258 inputPortCriterion.port()).annotations().value(TapiDeviceHelper.UUID);
259
260 Instructions.OutputInstruction outInstruction = (Instructions.OutputInstruction) checkNotNull(rule.treatment()
261 .allInstructions().stream().filter(instr -> instr.type().equals(Instruction.Type.OUTPUT))
262 .findFirst().orElse(null));
263 String outputPortUuid = deviceService.getPort(rule.deviceId(),
264 outInstruction.port()).annotations().value(TapiDeviceHelper.UUID);
265 ByteArrayOutputStream stream = new ByteArrayOutputStream();
266 try {
267 JsonGenerator generator = getJsonGenerator(stream);
268 generator.writeStartObject();
269 generator.writeArrayFieldStart(TAPI_CONNECTIVITY_CONNECTIVITY_SERVICE);
270 generator.writeStartObject();
271 generator.writeStringField(TapiDeviceHelper.UUID, uuid);
272 generator.writeStringField(SERVICE_LAYER, PHOTONIC_MEDIA);
273 generator.writeStringField(SERVICE_TYPE, POINT_TO_POINT_CONNECTIVITY);
274 generator.writeArrayFieldStart(END_POINT);
275 addEndPoint(generator, inputPortUuid);
276 addEndPoint(generator, outputPortUuid);
277 generator.writeEndArray();
278 generator.writeEndObject();
279 generator.writeEndArray();
280 generator.writeEndObject();
281 generator.close();
282 return stream;
283 } catch (IOException e) {
284 log.error("Cant' create json", e);
285 }
286 return stream;
287 }
288
289 private JsonGenerator getJsonGenerator(ByteArrayOutputStream stream) throws IOException {
290 JsonFactory factory = new JsonFactory();
291 return factory.createGenerator(stream, JsonEncoding.UTF8);
292 }
293 /*
294 {
295 "local-id":"1",
296 "layer-protocol-name":"PHOTONIC_MEDIA",
297 "layer-protocol-qualifier":"tapi-photonic-media:PHOTONIC_LAYER_QUALIFIER_NMC",
298 "service-interface-point":{
299 "service-interface-point-uuid":"0923962e-b83f-4702-9b16-a1a0db0dc1f9"
300 }
301 }
302 */
303 private void addEndPoint(JsonGenerator generator, String sipUuid) throws IOException {
304 generator.writeStartObject();
305 generator.writeStringField(LOCAL_ID, sipUuid);
306 generator.writeStringField(LAYER_PROTOCOL_NAME, PHOTONIC_MEDIA);
307 generator.writeStringField(LAYER_PROTOCOL_QUALIFIER,
308 TAPI_PHOTONIC_MEDIA_PHOTONIC_LAYER_QUALIFIER_NMC);
309 generator.writeObjectFieldStart(SERVICE_INTERFACE_POINT);
310 generator.writeStringField(SERVICE_INTERFACE_POINT_UUID, sipUuid);
311 generator.writeEndObject();
312 generator.writeEndObject();
313 }
314
315 private String createUuid() {
316 return UUID.randomUUID().toString();
317 }
318}