blob: c0bea12ebf1175f97a44d3cd19015c666235a10c [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;
24import com.fasterxml.jackson.databind.ObjectMapper;
25import com.fasterxml.jackson.databind.ObjectReader;
26import com.google.common.collect.ImmutableList;
27import org.apache.http.HttpStatus;
28import org.onosproject.drivers.odtn.impl.DeviceConnection;
29import org.onosproject.drivers.odtn.impl.DeviceConnectionCache;
30import org.onosproject.net.DeviceId;
31import org.onosproject.net.device.DeviceService;
32import org.onosproject.net.driver.AbstractHandlerBehaviour;
33import org.onosproject.net.flow.DefaultFlowEntry;
34import org.onosproject.net.flow.FlowEntry;
35import org.onosproject.net.flow.FlowRule;
36import org.onosproject.net.flow.FlowRuleProgrammable;
37import org.onosproject.net.flow.criteria.Criterion;
38import org.onosproject.net.flow.criteria.PortCriterion;
39import org.onosproject.net.flow.instructions.Instruction;
40import org.onosproject.net.flow.instructions.Instructions;
41import org.onosproject.protocol.rest.RestSBController;
42import org.slf4j.Logger;
43
44import javax.ws.rs.core.MediaType;
45import java.io.ByteArrayInputStream;
46import java.io.ByteArrayOutputStream;
47import java.io.IOException;
48import java.io.InputStream;
49import java.util.ArrayList;
50import java.util.Collection;
51import java.util.HashSet;
52import java.util.List;
53import java.util.Set;
54import java.util.UUID;
55
56import static com.google.common.base.Preconditions.checkNotNull;
57import static org.onosproject.drivers.odtn.tapi.TapiDeviceHelper.END_POINT;
58import static org.onosproject.drivers.odtn.tapi.TapiDeviceHelper.LAYER_PROTOCOL_NAME;
59import static org.onosproject.drivers.odtn.tapi.TapiDeviceHelper.LAYER_PROTOCOL_QUALIFIER;
60import static org.onosproject.drivers.odtn.tapi.TapiDeviceHelper.LOCAL_ID;
61import static org.onosproject.drivers.odtn.tapi.TapiDeviceHelper.PHOTONIC_MEDIA;
62import static org.onosproject.drivers.odtn.tapi.TapiDeviceHelper.POINT_TO_POINT_CONNECTIVITY;
63import static org.onosproject.drivers.odtn.tapi.TapiDeviceHelper.SERVICE_INTERFACE_POINT;
64import static org.onosproject.drivers.odtn.tapi.TapiDeviceHelper.SERVICE_INTERFACE_POINT_UUID;
65import static org.onosproject.drivers.odtn.tapi.TapiDeviceHelper.SERVICE_LAYER;
66import static org.onosproject.drivers.odtn.tapi.TapiDeviceHelper.SERVICE_TYPE;
67import static org.onosproject.drivers.odtn.tapi.TapiDeviceHelper.TAPI_CONNECTIVITY_CONNECTIVITY_SERVICE;
68import static org.onosproject.drivers.odtn.tapi.TapiDeviceHelper.TAPI_PHOTONIC_MEDIA_PHOTONIC_LAYER_QUALIFIER_NMC;
69import 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);
80 private static final String CONN_REQ_POST_API = "/restconf/data/tapi-common:context/" +
81 "tapi-connectivity:connectivity-context/";
82 private static final String CONN_REQ_REMOVE_DATA_API = "/restconf/data/tapi-common:context/" +
83 "tapi-connectivity:connectivity-context/connectivity-service=";
84 private static final String CONN_REQ_GET_API = "/restconf/data/tapi-common:context/" +
85 "tapi-connectivity:connectivity-context/";
86
87
88 @Override
89 public Collection<FlowEntry> getFlowEntries() {
90 DeviceId deviceId = did();
91 RestSBController controller = checkNotNull(handler().get(RestSBController.class));
92 ObjectMapper om = new ObjectMapper();
93 final ObjectReader reader = om.reader();
94 // all waveserver responses contain data node, which contains the requested data
95 InputStream response = controller.get(deviceId, CONN_REQ_GET_API, MediaType.APPLICATION_JSON_TYPE);
96 JsonNode jsonNode = null;
97 try {
98 jsonNode = reader.readTree(response);
99 if (jsonNode == null) {
100 log.error("JsonNode is null for response {}", response);
101 return ImmutableList.of();
102 }
103 Set<String> uuids = parseTapiGetConnectivityRequest(jsonNode);
104 DeviceConnectionCache cache = getConnectionCache();
105 if (cache.get(deviceId) == null) {
106 return ImmutableList.of();
107 }
108 List<FlowEntry> entries = new ArrayList<>();
109 uuids.forEach(uuid -> {
110 FlowRule rule = cache.get(deviceId, uuid);
111 if (rule != null) {
112 entries.add(new DefaultFlowEntry(rule, FlowEntry.FlowEntryState.ADDED, 0, 0, 0));
113 } else {
114 log.info("Non existing rule for uuid {}", uuid);
115 }
116 });
117 return entries;
118 } catch (IOException e) {
119 return ImmutableList.of();
120 }
121 }
122
123 @Override
124 public Collection<FlowRule> applyFlowRules(Collection<FlowRule> rules) {
125 DeviceId deviceId = handler().data().deviceId();
126 RestSBController controller = checkNotNull(handler().get(RestSBController.class));
127 ImmutableList.Builder<FlowRule> added = ImmutableList.builder();
128 rules.forEach(flowRule -> {
129 String uuid = createUuid();
130 ByteArrayOutputStream applyConnectivityRequest = createConnectivityRequest(uuid, flowRule);
131 if (applyConnectivityRequest.size() != 0) {
132 int result = controller.post(deviceId, CONN_REQ_POST_API,
133 new ByteArrayInputStream(applyConnectivityRequest.toByteArray()),
134 MediaType.APPLICATION_JSON_TYPE);
135 // TODO retrieve the UUID from the location and store with that identifier
136 // at the moment is implied that the sent one is the same used by the TAPI server.
137 if (result == HttpStatus.SC_CREATED) {
138 getConnectionCache().add(deviceId, uuid, flowRule);
139 added.add(flowRule);
140 }
141 }
142 });
143 return added.build();
144 }
145
146 @Override
147 public Collection<FlowRule> removeFlowRules(Collection<FlowRule> rules) {
148 DeviceId deviceId = handler().data().deviceId();
149 RestSBController controller = checkNotNull(handler().get(RestSBController.class));
150 ImmutableList.Builder<FlowRule> removed = ImmutableList.builder();
151 rules.forEach(flowRule -> {
152 DeviceConnection conn = getConnectionCache().get(deviceId, flowRule.id());
153 if (conn == null || conn.getId() == null) {
154 log.warn("Can't find associate device connection for flow {} and device {}",
155 flowRule.id(), deviceId);
156 return;
157 }
158 int result = controller.delete(deviceId, CONN_REQ_REMOVE_DATA_API + conn.getId(),
159 null, MediaType.APPLICATION_JSON_TYPE);
160 if (result == HttpStatus.SC_NO_CONTENT) {
161 getConnectionCache().remove(deviceId, flowRule);
162 removed.add(flowRule);
163 }
164 });
165 return removed.build();
166 }
167
168 /**
169 * Get the deviceId for which the methods apply.
170 *
171 * @return The deviceId as contained in the handler data
172 */
173 private DeviceId did() {
174 return handler().data().deviceId();
175 }
176
177 private DeviceConnectionCache getConnectionCache() {
178 return DeviceConnectionCache.init();
179 }
180
181 protected Set<String> parseTapiGetConnectivityRequest(JsonNode tapiConnectivityReply) {
182 /*
183 {
184 "tapi-connectivity:connectivity-service":[
185 {
186 "uuid":"ffb006d4-349e-4d2f-817e-0906c88458d0",
187 <other fields>
188 }
189 ]
190 }
191 */
192 Set<String> uuids = new HashSet<>();
193 if (tapiConnectivityReply.has(TAPI_CONNECTIVITY_CONNECTIVITY_SERVICE)) {
194 tapiConnectivityReply.get(TAPI_CONNECTIVITY_CONNECTIVITY_SERVICE).elements()
195 .forEachRemaining(node -> uuids.add(node.get(TapiDeviceHelper.UUID).asText()));
196 } else {
197 log.warn("Cant retrieve connectivity UUID from {}", tapiConnectivityReply);
198 }
199 //This is only one uuid or empty in case of failures
200 return uuids;
201 }
202
203 ByteArrayOutputStream createConnectivityRequest(String uuid, FlowRule rule) {
204 /*
205 {
206 "tapi-connectivity:connectivity-service":[
207 {
208 "uuid":"ffb006d4-349e-4d2f-817e-0906c88458d0",
209 "service-layer":"PHOTONIC_MEDIA",
210 "service-type":"POINT_TO_POINT_CONNECTIVITY",
211 "end-point":[
212 {
213 "local-id":"1",
214 "layer-protocol-name":"PHOTONIC_MEDIA",
215 "layer-protocol-qualifier":"tapi-photonic-media:PHOTONIC_LAYER_QUALIFIER_NMC",
216 "service-interface-point":{
217 "service-interface-point-uuid":"0923962e-b83f-4702-9b16-a1a0db0dc1f9"
218 }
219 },
220 {
221 "local-id":"2",
222 "layer-protocol-name":"PHOTONIC_MEDIA",
223 "layer-protocol-qualifier":"tapi-photonic-media:PHOTONIC_LAYER_QUALIFIER_NMC",
224 "service-interface-point":{
225 "service-interface-point-uuid":"76be95de-5769-4e5d-b65e-62cb6c39cf6b "
226 }
227 }
228 ]
229 }
230 ]
231 }
232 */
233 DeviceService deviceService = handler().get(DeviceService.class);
234 PortCriterion inputPortCriterion = (PortCriterion) checkNotNull(rule.selector()
235 .getCriterion(Criterion.Type.IN_PORT));
236 String inputPortUuid = deviceService.getPort(rule.deviceId(),
237 inputPortCriterion.port()).annotations().value(TapiDeviceHelper.UUID);
238
239 Instructions.OutputInstruction outInstruction = (Instructions.OutputInstruction) checkNotNull(rule.treatment()
240 .allInstructions().stream().filter(instr -> instr.type().equals(Instruction.Type.OUTPUT))
241 .findFirst().orElse(null));
242 String outputPortUuid = deviceService.getPort(rule.deviceId(),
243 outInstruction.port()).annotations().value(TapiDeviceHelper.UUID);
244 ByteArrayOutputStream stream = new ByteArrayOutputStream();
245 try {
246 JsonGenerator generator = getJsonGenerator(stream);
247 generator.writeStartObject();
248 generator.writeArrayFieldStart(TAPI_CONNECTIVITY_CONNECTIVITY_SERVICE);
249 generator.writeStartObject();
250 generator.writeStringField(TapiDeviceHelper.UUID, uuid);
251 generator.writeStringField(SERVICE_LAYER, PHOTONIC_MEDIA);
252 generator.writeStringField(SERVICE_TYPE, POINT_TO_POINT_CONNECTIVITY);
253 generator.writeArrayFieldStart(END_POINT);
254 addEndPoint(generator, inputPortUuid);
255 addEndPoint(generator, outputPortUuid);
256 generator.writeEndArray();
257 generator.writeEndObject();
258 generator.writeEndArray();
259 generator.writeEndObject();
260 generator.close();
261 return stream;
262 } catch (IOException e) {
263 log.error("Cant' create json", e);
264 }
265 return stream;
266 }
267
268 private JsonGenerator getJsonGenerator(ByteArrayOutputStream stream) throws IOException {
269 JsonFactory factory = new JsonFactory();
270 return factory.createGenerator(stream, JsonEncoding.UTF8);
271 }
272 /*
273 {
274 "local-id":"1",
275 "layer-protocol-name":"PHOTONIC_MEDIA",
276 "layer-protocol-qualifier":"tapi-photonic-media:PHOTONIC_LAYER_QUALIFIER_NMC",
277 "service-interface-point":{
278 "service-interface-point-uuid":"0923962e-b83f-4702-9b16-a1a0db0dc1f9"
279 }
280 }
281 */
282 private void addEndPoint(JsonGenerator generator, String sipUuid) throws IOException {
283 generator.writeStartObject();
284 generator.writeStringField(LOCAL_ID, sipUuid);
285 generator.writeStringField(LAYER_PROTOCOL_NAME, PHOTONIC_MEDIA);
286 generator.writeStringField(LAYER_PROTOCOL_QUALIFIER,
287 TAPI_PHOTONIC_MEDIA_PHOTONIC_LAYER_QUALIFIER_NMC);
288 generator.writeObjectFieldStart(SERVICE_INTERFACE_POINT);
289 generator.writeStringField(SERVICE_INTERFACE_POINT_UUID, sipUuid);
290 generator.writeEndObject();
291 generator.writeEndObject();
292 }
293
294 private String createUuid() {
295 return UUID.randomUUID().toString();
296 }
297}