blob: 36e0e6c244903f7dc6063a6f0412aa1b4e832ae1 [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;
Andrea Campanella2bdf2042019-01-28 13:47:11 +010023import com.google.common.collect.ImmutableList;
24import org.apache.http.HttpStatus;
25import org.onosproject.drivers.odtn.impl.DeviceConnection;
26import org.onosproject.drivers.odtn.impl.DeviceConnectionCache;
27import org.onosproject.net.DeviceId;
28import org.onosproject.net.device.DeviceService;
29import org.onosproject.net.driver.AbstractHandlerBehaviour;
30import org.onosproject.net.flow.DefaultFlowEntry;
31import org.onosproject.net.flow.FlowEntry;
32import org.onosproject.net.flow.FlowRule;
33import org.onosproject.net.flow.FlowRuleProgrammable;
34import org.onosproject.net.flow.criteria.Criterion;
35import org.onosproject.net.flow.criteria.PortCriterion;
36import org.onosproject.net.flow.instructions.Instruction;
37import org.onosproject.net.flow.instructions.Instructions;
38import org.onosproject.protocol.rest.RestSBController;
39import org.slf4j.Logger;
40
41import javax.ws.rs.core.MediaType;
42import java.io.ByteArrayInputStream;
43import java.io.ByteArrayOutputStream;
44import java.io.IOException;
Andrea Campanella2bdf2042019-01-28 13:47:11 +010045import java.util.ArrayList;
46import java.util.Collection;
Andrea Campanella2bdf2042019-01-28 13:47:11 +010047import java.util.List;
48import java.util.Set;
49import java.util.UUID;
Andrea Campanellab9e491b2019-02-18 17:45:01 +010050import java.util.concurrent.CompletableFuture;
Andrea Campanella2bdf2042019-01-28 13:47:11 +010051
52import static com.google.common.base.Preconditions.checkNotNull;
Andrea Campanellae4b2c682019-08-23 15:42:55 +020053import static org.onosproject.drivers.odtn.tapi.TapiDeviceHelper.CONN_REQ_POST_API;
54import static org.onosproject.drivers.odtn.tapi.TapiDeviceHelper.CONN_REQ_REMOVE_DATA_API;
Andrea Campanella2bdf2042019-01-28 13:47:11 +010055import 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;
Andrea Campanellae4b2c682019-08-23 15:42:55 +020067import static org.onosproject.drivers.odtn.tapi.TapiDeviceHelper.getConnectionCache;
68import static org.onosproject.drivers.odtn.tapi.TapiDeviceHelper.getUuids;
Andrea Campanella2bdf2042019-01-28 13:47:11 +010069import static org.slf4j.LoggerFactory.getLogger;
70
71/**
72 * Driver Implementation of the DeviceDescrption discovery for ONF Transport-API (TAPI) v2.1 based
73 * open line systems (OLS).
74 */
75
76public class TapiFlowRuleProgrammable extends AbstractHandlerBehaviour
77 implements FlowRuleProgrammable {
78
79 private static final Logger log = getLogger(TapiFlowRuleProgrammable.class);
Andrea Campanella2bdf2042019-01-28 13:47:11 +010080
81
82 @Override
83 public Collection<FlowEntry> getFlowEntries() {
84 DeviceId deviceId = did();
Andrea Campanellab9e491b2019-02-18 17:45:01 +010085 //TODO this is a blocking call on ADVA OLS, right now using cache.
Andrea Campanellae4b2c682019-08-23 15:42:55 +020086 //return getFlowsFromConnectivityServices(deviceId);
Andrea Campanellab9e491b2019-02-18 17:45:01 +010087 List<FlowEntry> entries = new ArrayList<>();
88 Set<FlowRule> rules = getConnectionCache().get(deviceId);
89 if (rules != null) {
90 rules.forEach(rule -> {
91 entries.add(new DefaultFlowEntry(rule, FlowEntry.FlowEntryState.ADDED, 0, 0, 0));
Andrea Campanella2bdf2042019-01-28 13:47:11 +010092 });
Andrea Campanella2bdf2042019-01-28 13:47:11 +010093 }
Andrea Campanellab9e491b2019-02-18 17:45:01 +010094 return entries;
Andrea Campanella2bdf2042019-01-28 13:47:11 +010095 }
96
97 @Override
98 public Collection<FlowRule> applyFlowRules(Collection<FlowRule> rules) {
99 DeviceId deviceId = handler().data().deviceId();
100 RestSBController controller = checkNotNull(handler().get(RestSBController.class));
101 ImmutableList.Builder<FlowRule> added = ImmutableList.builder();
102 rules.forEach(flowRule -> {
103 String uuid = createUuid();
104 ByteArrayOutputStream applyConnectivityRequest = createConnectivityRequest(uuid, flowRule);
105 if (applyConnectivityRequest.size() != 0) {
Andrea Campanellae4b2c682019-08-23 15:42:55 +0200106 log.debug("Connectivity request {}", applyConnectivityRequest.toString());
107 CompletableFuture<Integer> flowInstallation =
Andrea Campanellab9e491b2019-02-18 17:45:01 +0100108 CompletableFuture.supplyAsync(() -> controller.post(deviceId, CONN_REQ_POST_API,
Andrea Campanellae4b2c682019-08-23 15:42:55 +0200109 new ByteArrayInputStream(applyConnectivityRequest.toByteArray()),
110 MediaType.APPLICATION_JSON_TYPE));
111 log.debug("Added {} to {}", flowRule, deviceId);
112 getConnectionCache().add(deviceId, uuid, flowRule);
113 added.add(flowRule);
Andrea Campanellab9e491b2019-02-18 17:45:01 +0100114 flowInstallation.thenApply(result -> {
Andrea Campanellae4b2c682019-08-23 15:42:55 +0200115 if (result == HttpStatus.SC_CREATED || result == HttpStatus.SC_OK) {
116 log.info("Added {} to deviceId {}", flowRule, deviceId);
Andrea Campanellab9e491b2019-02-18 17:45:01 +0100117 } else {
Andrea Campanellae4b2c682019-08-23 15:42:55 +0200118 log.error("Can't add flow {}, result {}", flowRule, result);
119 getConnectionCache().remove(deviceId, flowRule);
Andrea Campanellab9e491b2019-02-18 17:45:01 +0100120 }
121 return result;
122 });
Andrea Campanella2bdf2042019-01-28 13:47:11 +0100123 }
124 });
Andrea Campanellab9e491b2019-02-18 17:45:01 +0100125 //TODO workaround for blocking call on ADVA OLS should return added
126 return rules;
Andrea Campanella2bdf2042019-01-28 13:47:11 +0100127 }
128
129 @Override
130 public Collection<FlowRule> removeFlowRules(Collection<FlowRule> rules) {
131 DeviceId deviceId = handler().data().deviceId();
132 RestSBController controller = checkNotNull(handler().get(RestSBController.class));
133 ImmutableList.Builder<FlowRule> removed = ImmutableList.builder();
134 rules.forEach(flowRule -> {
135 DeviceConnection conn = getConnectionCache().get(deviceId, flowRule.id());
136 if (conn == null || conn.getId() == null) {
137 log.warn("Can't find associate device connection for flow {} and device {}",
138 flowRule.id(), deviceId);
139 return;
140 }
Andrea Campanellae4b2c682019-08-23 15:42:55 +0200141 CompletableFuture<Integer> flowRemoval =
Andrea Campanellab9e491b2019-02-18 17:45:01 +0100142 CompletableFuture.supplyAsync(() -> controller.delete(deviceId,
143 CONN_REQ_REMOVE_DATA_API + conn.getId(),
144 null, MediaType.APPLICATION_JSON_TYPE));
Andrea Campanellae4b2c682019-08-23 15:42:55 +0200145 flowRemoval.thenApply(result -> {
Andrea Campanellab9e491b2019-02-18 17:45:01 +0100146 if (result == HttpStatus.SC_NO_CONTENT) {
147 getConnectionCache().remove(deviceId, flowRule);
148 removed.add(flowRule);
149 } else {
150 log.error("Can't remove flow {}, result {}", flowRule, result);
151 }
152 return result;
153 });
Andrea Campanella2bdf2042019-01-28 13:47:11 +0100154 });
Andrea Campanellab9e491b2019-02-18 17:45:01 +0100155 //TODO workaround for blocking call on ADVA OLS shoudl return removed
156 return rules;
Andrea Campanella2bdf2042019-01-28 13:47:11 +0100157 }
158
159 /**
160 * Get the deviceId for which the methods apply.
161 *
162 * @return The deviceId as contained in the handler data
163 */
164 private DeviceId did() {
165 return handler().data().deviceId();
166 }
167
Andrea Campanellae4b2c682019-08-23 15:42:55 +0200168 //Currently uused because get is a blocking call on ADVA
169 private Collection<FlowEntry> getFlowsFromConnectivityServices(DeviceId deviceId) {
170 Set<String> uuids = getUuids(deviceId, handler());
171 if (uuids.isEmpty()) {
172 return ImmutableList.of();
173 }
174 DeviceConnectionCache cache = getConnectionCache();
175 if (cache.get(deviceId) == null) {
176 return ImmutableList.of();
177 }
178 List<FlowEntry> entries = new ArrayList<>();
179 uuids.forEach(uuid -> {
180 FlowRule rule = cache.get(deviceId, uuid);
181 if (rule != null) {
182 entries.add(new DefaultFlowEntry(rule, FlowEntry.FlowEntryState.ADDED, 0, 0, 0));
183 } else {
184 log.info("Non existing rule for uuid {}", uuid);
185 }
186 });
187 return entries;
Andrea Campanella2bdf2042019-01-28 13:47:11 +0100188 }
189
Andrea Campanella2bdf2042019-01-28 13:47:11 +0100190
191 ByteArrayOutputStream createConnectivityRequest(String uuid, FlowRule rule) {
192 /*
193 {
194 "tapi-connectivity:connectivity-service":[
195 {
196 "uuid":"ffb006d4-349e-4d2f-817e-0906c88458d0",
197 "service-layer":"PHOTONIC_MEDIA",
198 "service-type":"POINT_TO_POINT_CONNECTIVITY",
199 "end-point":[
200 {
201 "local-id":"1",
202 "layer-protocol-name":"PHOTONIC_MEDIA",
203 "layer-protocol-qualifier":"tapi-photonic-media:PHOTONIC_LAYER_QUALIFIER_NMC",
204 "service-interface-point":{
205 "service-interface-point-uuid":"0923962e-b83f-4702-9b16-a1a0db0dc1f9"
206 }
207 },
208 {
209 "local-id":"2",
210 "layer-protocol-name":"PHOTONIC_MEDIA",
211 "layer-protocol-qualifier":"tapi-photonic-media:PHOTONIC_LAYER_QUALIFIER_NMC",
212 "service-interface-point":{
213 "service-interface-point-uuid":"76be95de-5769-4e5d-b65e-62cb6c39cf6b "
214 }
215 }
216 ]
217 }
218 ]
219 }
220 */
221 DeviceService deviceService = handler().get(DeviceService.class);
222 PortCriterion inputPortCriterion = (PortCriterion) checkNotNull(rule.selector()
223 .getCriterion(Criterion.Type.IN_PORT));
224 String inputPortUuid = deviceService.getPort(rule.deviceId(),
225 inputPortCriterion.port()).annotations().value(TapiDeviceHelper.UUID);
226
227 Instructions.OutputInstruction outInstruction = (Instructions.OutputInstruction) checkNotNull(rule.treatment()
228 .allInstructions().stream().filter(instr -> instr.type().equals(Instruction.Type.OUTPUT))
229 .findFirst().orElse(null));
230 String outputPortUuid = deviceService.getPort(rule.deviceId(),
231 outInstruction.port()).annotations().value(TapiDeviceHelper.UUID);
232 ByteArrayOutputStream stream = new ByteArrayOutputStream();
233 try {
234 JsonGenerator generator = getJsonGenerator(stream);
235 generator.writeStartObject();
236 generator.writeArrayFieldStart(TAPI_CONNECTIVITY_CONNECTIVITY_SERVICE);
237 generator.writeStartObject();
238 generator.writeStringField(TapiDeviceHelper.UUID, uuid);
239 generator.writeStringField(SERVICE_LAYER, PHOTONIC_MEDIA);
240 generator.writeStringField(SERVICE_TYPE, POINT_TO_POINT_CONNECTIVITY);
241 generator.writeArrayFieldStart(END_POINT);
Andrea Campanellae4b2c682019-08-23 15:42:55 +0200242 //ADVA OLS requires these to be 1,2 for every connection
243 addEndPoint(generator, inputPortUuid, 1);
244 addEndPoint(generator, outputPortUuid, 2);
Andrea Campanella2bdf2042019-01-28 13:47:11 +0100245 generator.writeEndArray();
246 generator.writeEndObject();
247 generator.writeEndArray();
248 generator.writeEndObject();
249 generator.close();
250 return stream;
251 } catch (IOException e) {
252 log.error("Cant' create json", e);
253 }
254 return stream;
255 }
256
257 private JsonGenerator getJsonGenerator(ByteArrayOutputStream stream) throws IOException {
258 JsonFactory factory = new JsonFactory();
259 return factory.createGenerator(stream, JsonEncoding.UTF8);
260 }
Andrea Campanellae4b2c682019-08-23 15:42:55 +0200261
Andrea Campanella2bdf2042019-01-28 13:47:11 +0100262 /*
263 {
264 "local-id":"1",
265 "layer-protocol-name":"PHOTONIC_MEDIA",
266 "layer-protocol-qualifier":"tapi-photonic-media:PHOTONIC_LAYER_QUALIFIER_NMC",
267 "service-interface-point":{
268 "service-interface-point-uuid":"0923962e-b83f-4702-9b16-a1a0db0dc1f9"
269 }
270 }
271 */
Andrea Campanellae4b2c682019-08-23 15:42:55 +0200272 private void addEndPoint(JsonGenerator generator, String sipUuid, int localId) throws IOException {
Andrea Campanella2bdf2042019-01-28 13:47:11 +0100273 generator.writeStartObject();
Andrea Campanellae4b2c682019-08-23 15:42:55 +0200274 //ADVA OLS requires Local-id to be integer incremental numbers
275 generator.writeStringField(LOCAL_ID, Integer.toString(localId));
Andrea Campanella2bdf2042019-01-28 13:47:11 +0100276 generator.writeStringField(LAYER_PROTOCOL_NAME, PHOTONIC_MEDIA);
277 generator.writeStringField(LAYER_PROTOCOL_QUALIFIER,
278 TAPI_PHOTONIC_MEDIA_PHOTONIC_LAYER_QUALIFIER_NMC);
279 generator.writeObjectFieldStart(SERVICE_INTERFACE_POINT);
280 generator.writeStringField(SERVICE_INTERFACE_POINT_UUID, sipUuid);
281 generator.writeEndObject();
282 generator.writeEndObject();
283 }
284
285 private String createUuid() {
286 return UUID.randomUUID().toString();
287 }
288}