blob: 4643d7bce4b4adff38cc68f4c88c52b639338c66 [file] [log] [blame]
Laszlo Papp5bdd0e42017-10-27 10:18:21 +01001/*
2 * Copyright 2017-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
17package org.onosproject.net.optical.rest;
18
19import static org.onlab.util.Tools.nullIsIllegal;
20import static org.onlab.util.Tools.nullIsNotFound;
21
22import com.fasterxml.jackson.databind.JsonNode;
alessio3039bdb2018-11-29 14:12:32 +010023import com.fasterxml.jackson.databind.node.ArrayNode;
Laszlo Papp5bdd0e42017-10-27 10:18:21 +010024import com.fasterxml.jackson.databind.node.ObjectNode;
alessio3039bdb2018-11-29 14:12:32 +010025import org.onlab.graph.ScalarWeight;
Laszlo Papp5bdd0e42017-10-27 10:18:21 +010026import org.onosproject.core.ApplicationId;
27import org.onosproject.core.CoreService;
alessio3039bdb2018-11-29 14:12:32 +010028import org.onosproject.net.Device;
29import org.onosproject.net.AnnotationKeys;
30import org.onosproject.net.Link;
31import org.onosproject.net.DefaultPath;
Laszlo Papp5bdd0e42017-10-27 10:18:21 +010032import org.onosproject.net.ConnectPoint;
alessio3039bdb2018-11-29 14:12:32 +010033import org.onosproject.net.OchSignal;
34import org.onosproject.net.DeviceId;
Laszlo Papp5bdd0e42017-10-27 10:18:21 +010035import org.onosproject.net.device.DeviceService;
alessio3039bdb2018-11-29 14:12:32 +010036import org.onosproject.net.flow.criteria.Criterion;
37import org.onosproject.net.flow.criteria.OchSignalCriterion;
Laszlo Papp5bdd0e42017-10-27 10:18:21 +010038import org.onosproject.net.intent.Intent;
39import org.onosproject.net.intent.IntentService;
alessio3039bdb2018-11-29 14:12:32 +010040import org.onosproject.net.intent.FlowRuleIntent;
41import org.onosproject.net.intent.IntentState;
Laszlo Papp5bdd0e42017-10-27 10:18:21 +010042import org.onosproject.net.intent.Key;
alessio3039bdb2018-11-29 14:12:32 +010043import org.onosproject.net.intent.OpticalConnectivityIntent;
44import org.onosproject.net.link.LinkService;
Laszlo Papp5bdd0e42017-10-27 10:18:21 +010045import org.onosproject.net.optical.json.OchSignalCodec;
alessio3039bdb2018-11-29 14:12:32 +010046import org.onosproject.net.provider.ProviderId;
Laszlo Papp5bdd0e42017-10-27 10:18:21 +010047import org.onosproject.rest.AbstractWebResource;
48import org.slf4j.Logger;
49
Laszlo Papp5bdd0e42017-10-27 10:18:21 +010050import javax.ws.rs.POST;
alessio3039bdb2018-11-29 14:12:32 +010051import javax.ws.rs.GET;
52import javax.ws.rs.DELETE;
Laszlo Papp5bdd0e42017-10-27 10:18:21 +010053import javax.ws.rs.Path;
54import javax.ws.rs.Produces;
alessio3039bdb2018-11-29 14:12:32 +010055import javax.ws.rs.PathParam;
56import javax.ws.rs.Consumes;
Laszlo Papp5bdd0e42017-10-27 10:18:21 +010057import javax.ws.rs.core.Context;
58import javax.ws.rs.core.MediaType;
59import javax.ws.rs.core.Response;
60import javax.ws.rs.core.UriBuilder;
61import javax.ws.rs.core.UriInfo;
62import java.io.IOException;
63import java.io.InputStream;
alessio3039bdb2018-11-29 14:12:32 +010064import java.util.ArrayList;
65import java.util.Iterator;
66import java.util.List;
67import java.util.stream.Collectors;
Laszlo Papp5bdd0e42017-10-27 10:18:21 +010068
alessio3039bdb2018-11-29 14:12:32 +010069import static org.onosproject.net.optical.util.OpticalIntentUtility.createExplicitOpticalIntent;
Laszlo Papp5bdd0e42017-10-27 10:18:21 +010070import static org.slf4j.LoggerFactory.getLogger;
71
Ray Milkey86ee5e82018-04-02 15:33:07 -070072import static org.onlab.util.Tools.readTreeFromStream;
Laszlo Papp5bdd0e42017-10-27 10:18:21 +010073
Ray Milkey86ee5e82018-04-02 15:33:07 -070074
Laszlo Papp5bdd0e42017-10-27 10:18:21 +010075/**
76 * Query, submit and withdraw optical network intents.
77 */
78@Path("intents")
79public class OpticalIntentsWebResource extends AbstractWebResource {
80
81 private static final Logger log = getLogger(OpticalIntentsWebResource.class);
82
83 private static final String JSON_INVALID = "Invalid json input";
Laszlo Papp5bdd0e42017-10-27 10:18:21 +010084 private static final String APP_ID = "appId";
Laszlo Papp5bdd0e42017-10-27 10:18:21 +010085 private static final String INGRESS_POINT = "ingressPoint";
86 private static final String EGRESS_POINT = "egressPoint";
Laszlo Papp5bdd0e42017-10-27 10:18:21 +010087 private static final String BIDIRECTIONAL = "bidirectional";
Laszlo Papp5bdd0e42017-10-27 10:18:21 +010088 private static final String SIGNAL = "signal";
alessio3039bdb2018-11-29 14:12:32 +010089 private static final String SUGGESTEDPATH = "suggestedPath";
90 private static final String MISSING_MEMBER_MESSAGE = " member is required";
91 private static final String E_APP_ID_NOT_FOUND = "Application ID is not found";
92 private static final ProviderId PROVIDER_ID = new ProviderId("netconf", "optical-rest");
Laszlo Papp5bdd0e42017-10-27 10:18:21 +010093
94 @Context
95 private UriInfo uriInfo;
96
97 /**
98 * Submits a new optical intent.
99 * Creates and submits optical intents from the JSON request.
100 *
101 * @param stream input JSON
102 * @return status of the request - CREATED if the JSON is correct,
103 * BAD_REQUEST if the JSON is invalid
104 * @onos.rsModel CreateIntent
105 */
106 @POST
107 @Consumes(MediaType.APPLICATION_JSON)
108 @Produces(MediaType.APPLICATION_JSON)
109 public Response createIntent(InputStream stream) {
110 try {
111 IntentService service = get(IntentService.class);
Ray Milkey86ee5e82018-04-02 15:33:07 -0700112 ObjectNode root = readTreeFromStream(mapper(), stream);
Laszlo Papp5bdd0e42017-10-27 10:18:21 +0100113 Intent intent = decode(root);
114 service.submit(intent);
115 UriBuilder locationBuilder = uriInfo.getBaseUriBuilder()
116 .path("intents")
117 .path(intent.appId().name())
118 .path(Long.toString(intent.id().fingerprint()));
119 return Response
120 .created(locationBuilder.build())
121 .build();
122 } catch (IOException ioe) {
123 throw new IllegalArgumentException(ioe);
124 }
125 }
126
alessio3039bdb2018-11-29 14:12:32 +0100127 /**
128 * Get the optical intents on the network.
129 *
130 * @return 200 OK
131 */
132 @GET
133 @Produces(MediaType.APPLICATION_JSON)
134 public Response getIntents() {
135
136 DeviceService deviceService = get(DeviceService.class);
137
138 IntentService intentService = get(IntentService.class);
139 Iterator intentItr = intentService.getIntents().iterator();
140
141 ArrayNode arrayFlows = mapper().createArrayNode();
142
143 while (intentItr.hasNext()) {
144
145 Intent intent = (Intent) intentItr.next();
146 if (intent instanceof OpticalConnectivityIntent) {
147
148 OpticalConnectivityIntent opticalConnectivityIntent = (OpticalConnectivityIntent) intent;
149
150 Device srcDevice = deviceService.getDevice(opticalConnectivityIntent.getSrc().deviceId());
151 Device dstDevice = deviceService.getDevice(opticalConnectivityIntent.getDst().deviceId());
152
153 String srcDeviceName = srcDevice.annotations().value(AnnotationKeys.NAME);
154 String dstDeviceName = dstDevice.annotations().value(AnnotationKeys.NAME);
155
156 ObjectNode objectNode = mapper().createObjectNode();
157
158 objectNode.put("intent id", opticalConnectivityIntent.id().toString());
159 objectNode.put("app id", opticalConnectivityIntent.appId().name());
160 objectNode.put("state", intentService.getIntentState(opticalConnectivityIntent.key()).toString());
161 objectNode.put("src", opticalConnectivityIntent.getSrc().toString());
162 objectNode.put("dst", opticalConnectivityIntent.getDst().toString());
163 objectNode.put("srcName", srcDeviceName);
164 objectNode.put("dstName", dstDeviceName);
165
166 //Only for INSTALLED intents
167 if (intentService.getIntentState(intent.key()) == IntentState.INSTALLED) {
168
169 //Retrieve associated FlowRuleIntent
170 FlowRuleIntent installableIntent =
171 (FlowRuleIntent) intentService.getInstallableIntents(opticalConnectivityIntent.key())
172 .stream()
173 .filter(FlowRuleIntent.class::isInstance)
174 .findFirst()
175 .orElse(null);
176
177 //Retrieve used ochSignal from the Selector of one of the installed FlowRule
178 //TODO store utilized ochSignal in the intent resources
179 if (installableIntent != null) {
180 OchSignal signal = installableIntent.flowRules().stream()
181 .map(r -> ((OchSignalCriterion)
182 r.selector().getCriterion(Criterion.Type.OCH_SIGID)).lambda())
183 .findFirst()
184 .orElse(null);
185
186 objectNode.put("ochSignal", signal.toString());
187 objectNode.put("centralFreq", signal.centralFrequency().asTHz() + " THz");
188 }
189
190 //Retrieve path and print it to REST
191 if (installableIntent != null) {
192 String path = installableIntent.resources().stream()
193 .filter(Link.class::isInstance)
194 .map(Link.class::cast)
195 .map(r -> deviceService.getDevice(r.src().deviceId()))
196 .map(r -> r.annotations().value(AnnotationKeys.NAME))
197 .collect(Collectors.joining(" -> "));
198
199 List<Link> pathLinks = installableIntent.resources().stream()
200 .filter(Link.class::isInstance)
201 .map(Link.class::cast)
202 .collect(Collectors.toList());
203
204 DefaultPath defaultPath = new DefaultPath(PROVIDER_ID, pathLinks, new ScalarWeight(1));
205
206 objectNode.put("path", defaultPath.toString());
207 objectNode.put("pathName", path + " -> " + dstDeviceName);
208 }
209 }
210
211 arrayFlows.add(objectNode);
212 }
213 }
214
215 ObjectNode root = this.mapper().createObjectNode().putPOJO("Intents", arrayFlows);
216 return ok(root).build();
217 }
218
219 /**
220 * Delete the specified optical intent.
221 *
222 * @param appId application identifier
223 * @param keyString intent key
224 * @return 204 NO CONTENT
225 */
226 @DELETE
227 @Consumes(MediaType.APPLICATION_JSON)
228 @Path("{appId}/{key}")
229 public Response deleteIntent(@PathParam("appId") String appId,
230 @PathParam("key") String keyString) {
231
232 final ApplicationId app = get(CoreService.class).getAppId(appId);
233 nullIsNotFound(app, "Application Id not found");
234
235 IntentService intentService = get(IntentService.class);
236 Intent intent = intentService.getIntent(Key.of(keyString, app));
237 if (intent == null) {
238 intent = intentService.getIntent(Key.of(Long.decode(keyString), app));
239 }
240 nullIsNotFound(intent, "Intent Id is not found");
241
242 if (intent instanceof OpticalConnectivityIntent) {
243 intentService.withdraw(intent);
244 } else {
245 throw new IllegalArgumentException("Specified intent is not of type OpticalConnectivityIntent");
246 }
247
248 return Response.noContent().build();
249 }
250
Laszlo Papp5bdd0e42017-10-27 10:18:21 +0100251 private Intent decode(ObjectNode json) {
252 JsonNode ingressJson = json.get(INGRESS_POINT);
253 if (!ingressJson.isObject()) {
254 throw new IllegalArgumentException(JSON_INVALID);
255 }
256
257 ConnectPoint ingress = codec(ConnectPoint.class).decode((ObjectNode) ingressJson, this);
258
259 JsonNode egressJson = json.get(EGRESS_POINT);
260 if (!egressJson.isObject()) {
261 throw new IllegalArgumentException(JSON_INVALID);
262 }
263
264 ConnectPoint egress = codec(ConnectPoint.class).decode((ObjectNode) egressJson, this);
265
266 JsonNode bidirectionalJson = json.get(BIDIRECTIONAL);
267 boolean bidirectional = bidirectionalJson != null ? bidirectionalJson.asBoolean() : false;
268
269 JsonNode signalJson = json.get(SIGNAL);
270 OchSignal signal = null;
271 if (signalJson != null) {
272 if (!signalJson.isObject()) {
273 throw new IllegalArgumentException(JSON_INVALID);
274 } else {
275 signal = OchSignalCodec.decode((ObjectNode) signalJson);
276 }
277 }
278
279 String appIdString = nullIsIllegal(json.get(APP_ID), APP_ID + MISSING_MEMBER_MESSAGE).asText();
280 CoreService service = getService(CoreService.class);
281 ApplicationId appId = nullIsNotFound(service.getAppId(appIdString), E_APP_ID_NOT_FOUND);
alessio3039bdb2018-11-29 14:12:32 +0100282
Laszlo Papp5bdd0e42017-10-27 10:18:21 +0100283 Key key = null;
284 DeviceService deviceService = get(DeviceService.class);
285
alessio3039bdb2018-11-29 14:12:32 +0100286 JsonNode suggestedPathJson = json.get(SUGGESTEDPATH);
287 DefaultPath suggestedPath = null;
288 LinkService linkService = get(LinkService.class);
289
290 if (suggestedPathJson != null) {
291 if (!suggestedPathJson.isObject()) {
292 throw new IllegalArgumentException(JSON_INVALID);
293 } else {
294 ArrayNode linksJson = nullIsIllegal((ArrayNode) suggestedPathJson.get("links"),
295 "Suggested path specified without links");
296
297 List<Link> listLinks = new ArrayList<>();
298
299 for (JsonNode node : linksJson) {
300
301 String srcString = node.get("src").asText();
302 String dstString = node.get("dst").asText();
303
304 ConnectPoint srcConnectPoint = ConnectPoint.fromString(srcString);
305 ConnectPoint dstConnectPoint = ConnectPoint.fromString(dstString);
306
307 Link link = linkService.getLink(srcConnectPoint, dstConnectPoint);
308 if (link == null) {
alessiof6722312019-02-12 16:43:36 +0100309 log.warn("Not existing link in the suggested path src {} dst {}",
310 srcConnectPoint, dstConnectPoint);
alessio3039bdb2018-11-29 14:12:32 +0100311 throw new IllegalArgumentException("Not existing link in the suggested path");
312 }
313
314 listLinks.add(link);
315 }
316
317 if ((!listLinks.get(0).src().deviceId().equals(ingress.deviceId())) ||
318 (!listLinks.get(0).src().port().equals(ingress.port())) ||
319 (!listLinks.get(listLinks.size() - 1).dst().deviceId().equals(egress.deviceId())) ||
320 (!listLinks.get(listLinks.size() - 1).dst().port().equals(egress.port()))) {
321 throw new IllegalArgumentException(
322 "Suggested path not compatible with ingress or egress connect points");
323 }
324
325 if (!isPathContiguous(listLinks)) {
326 throw new IllegalArgumentException(
327 "Links specified in the suggested path are not contiguous");
328 }
329
330 suggestedPath = new DefaultPath(PROVIDER_ID, listLinks, new ScalarWeight(1));
331
332 log.debug("OpticalIntent along suggestedPath {}", suggestedPath);
333 }
334 }
335
336 return createExplicitOpticalIntent(
337 ingress, egress, deviceService, key, appId, bidirectional, signal, suggestedPath);
338 }
339
340 private boolean isPathContiguous(List<Link> path) {
341 DeviceId previousDst;
342 DeviceId currentSrc;
343
344 for (int i = 1; i < path.size(); i++) {
345 previousDst = path.get(i - 1).dst().deviceId();
346 currentSrc = path.get(i).src().deviceId();
347
348 if (!previousDst.equals(currentSrc)) {
349 log.debug("OpticalIntent links are not contiguous previous {} current {}", previousDst, currentSrc);
350 return false;
351 }
352 }
353 return true;
Laszlo Papp5bdd0e42017-10-27 10:18:21 +0100354 }
355}