blob: cbd4d1be30bf40d8a6d2857a6912e52f1750ab84 [file] [log] [blame]
Thomas Vachuska781d18b2014-10-27 10:31:25 -07001/*
Thomas Vachuska4f1a60c2014-10-28 13:39:07 -07002 * Copyright 2014 Open Networking Laboratory
Thomas Vachuska781d18b2014-10-27 10:31:25 -07003 *
Thomas Vachuska4f1a60c2014-10-28 13:39:07 -07004 * 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
Thomas Vachuska781d18b2014-10-27 10:31:25 -07007 *
Thomas Vachuska4f1a60c2014-10-28 13:39:07 -07008 * 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.
Thomas Vachuska781d18b2014-10-27 10:31:25 -070015 */
Brian O'Connorabafb502014-12-02 22:26:20 -080016package org.onosproject.calendar;
Thomas Vachuska140d5852014-10-16 12:17:45 -070017
Hongtao Yin36f79aa2015-02-14 03:51:39 -080018import org.onlab.packet.Ethernet;
19import org.onlab.rest.BaseResource;
20import org.onlab.util.Tools;
21import org.onosproject.core.ApplicationId;
22import org.onosproject.core.CoreService;
Brian O'Connorabafb502014-12-02 22:26:20 -080023import org.onosproject.net.ConnectPoint;
24import org.onosproject.net.DeviceId;
Hongtao Yin36f79aa2015-02-14 03:51:39 -080025import org.onosproject.net.HostId;
26import org.onosproject.net.flow.DefaultTrafficSelector;
27import org.onosproject.net.flow.TrafficSelector;
28import org.onosproject.net.flow.TrafficTreatment;
29import org.onosproject.net.intent.ConnectivityIntent;
30import org.onosproject.net.intent.Constraint;
31import org.onosproject.net.intent.HostToHostIntent;
Brian O'Connorabafb502014-12-02 22:26:20 -080032import org.onosproject.net.intent.Intent;
33import org.onosproject.net.intent.IntentEvent;
Brian O'Connorabafb502014-12-02 22:26:20 -080034import org.onosproject.net.intent.IntentListener;
35import org.onosproject.net.intent.IntentService;
36import org.onosproject.net.intent.IntentState;
Hongtao Yin36f79aa2015-02-14 03:51:39 -080037import org.onosproject.net.intent.Key;
38import org.onosproject.net.intent.TwoWayP2PIntent;
39import org.onosproject.net.intent.constraint.BandwidthConstraint;
40import org.onosproject.net.intent.constraint.LatencyConstraint;
41import org.onosproject.net.resource.Bandwidth;
42import org.slf4j.Logger;
Thomas Vachuska56d1a702014-11-11 19:14:57 -080043
Hongtao Yin621c57a2014-10-30 14:28:03 -070044import javax.ws.rs.DELETE;
Hongtao Yin36f79aa2015-02-14 03:51:39 -080045import javax.ws.rs.POST;
46import javax.ws.rs.PUT;
Hongtao Yin621c57a2014-10-30 14:28:03 -070047import javax.ws.rs.PathParam;
48import javax.ws.rs.core.Response;
Hongtao Yin36f79aa2015-02-14 03:51:39 -080049import java.net.URI;
50import java.time.Duration;
51import java.time.temporal.ChronoUnit;
52import java.util.LinkedList;
53import java.util.List;
54import java.util.concurrent.CountDownLatch;
55import java.util.concurrent.TimeUnit;
Thomas Vachuska56d1a702014-11-11 19:14:57 -080056
Brian O'Connorabafb502014-12-02 22:26:20 -080057import static org.onosproject.net.PortNumber.portNumber;
58import static org.onosproject.net.flow.DefaultTrafficTreatment.builder;
Hongtao Yin36f79aa2015-02-14 03:51:39 -080059import static org.onosproject.net.intent.IntentState.*;
Hongtao Yin621c57a2014-10-30 14:28:03 -070060import static org.slf4j.LoggerFactory.getLogger;
Thomas Vachuska56d1a702014-11-11 19:14:57 -080061
Thomas Vachuska140d5852014-10-16 12:17:45 -070062/**
63 * Web resource for triggering calendared intents.
64 */
Hongtao Yin621c57a2014-10-30 14:28:03 -070065@javax.ws.rs.Path("intent")
Thomas Vachuska140d5852014-10-16 12:17:45 -070066public class BandwidthCalendarResource extends BaseResource {
67
Hongtao Yin621c57a2014-10-30 14:28:03 -070068 private static final Logger log = getLogger(BandwidthCalendarResource.class);
Hongtao Yin36f79aa2015-02-14 03:51:39 -080069 private static final long TIMEOUT = 10; // seconds
Hongtao Yin621c57a2014-10-30 14:28:03 -070070
Hongtao Yin36f79aa2015-02-14 03:51:39 -080071 private static final String INVALID_PARAMETER = "INVALID_PARAMETER\n";
72 private static final String OPERATION_INSTALLED = "INSTALLED\n";
73 private static final String OPERATION_FAILED = "FAILED\n";
74 private static final String OPERATION_WITHDRAWN = "WITHDRAWN\n";
75
76 /**
77 * Setup a bi-directional path with constraints between switch to switch.
78 * Switch is identified by DPID.
79 *
80 * @param src the path source (DPID or hostID)
81 * @param dst the path destination (DPID or hostID)
82 * @param srcPort the source port (-1 if src/dest is a host)
83 * @param dstPort the destination port (-1 if src/dest is a host)
84 * @param bandwidth the bandwidth (mbps) requirement for the path
85 * @param latency the latency (micro sec) requirement for the path
86 * @return intent key if successful,
87 * server error message or "FAILED" if failed to create or submit intent
88 */
89 @javax.ws.rs.Path("/{src}/{dst}/{srcPort}/{dstPort}/{bandwidth}/{latency}")
Thomas Vachuska140d5852014-10-16 12:17:45 -070090 @POST
Hongtao Yin36f79aa2015-02-14 03:51:39 -080091 // TODO could allow applications to provide optional key
92 // ... if you do, you will need to change from LongKeys to StringKeys
93 public Response setupPath(@PathParam("src") String src,
94 @PathParam("dst") String dst,
95 @PathParam("srcPort") String srcPort,
96 @PathParam("dstPort") String dstPort,
97 @PathParam("bandwidth") String bandwidth,
98 @PathParam("latency") String latency) {
Hongtao Yin621c57a2014-10-30 14:28:03 -070099
Hongtao Yin36f79aa2015-02-14 03:51:39 -0800100 log.info("Path Constraints: Src = {} SrcPort = {} Dest = {} DestPort = {} " +
101 "BW = {} latency = {}",
102 src, srcPort, dst, dstPort, bandwidth, latency);
103
104 if (src == null || dst == null || srcPort == null || dstPort == null) {
105 return Response.ok(INVALID_PARAMETER).build();
106 }
107
108 Long bandwidthL = 0L;
109 Long latencyL = 0L;
110 try {
111 bandwidthL = Long.parseLong(bandwidth, 10);
112 latencyL = Long.parseLong(latency, 10);
113 } catch (Exception e) {
114 return Response.ok(INVALID_PARAMETER).build();
115 }
116
117 Intent intent = createIntent(null, src, dst, srcPort, dstPort, bandwidthL, latencyL);
118 try {
119 if (submitIntent(intent)) {
120 return Response.ok(intent.key() + "\n").build();
121 } else {
122 return Response.ok(OPERATION_FAILED).build();
123 }
124 } catch (Exception e) {
125 return Response.status(Response.Status.INTERNAL_SERVER_ERROR).build();
126 }
127 }
128
129 /**
130 * Modify a bi-directional path's bandwidth.
131 *
132 * @param intentKey the path intent key
133 * @param src the path source (DPID or hostID)
134 * @param dst the path destination (DPID or hostID)
135 * @param srcPort the source port (-1 if src/dest is a host)
136 * @param dstPort the destination port (-1 if src/dest is a host)
137 * @param bandwidth the bandwidth (mbps) requirement for the path
Ray Milkey144eec52015-02-20 11:22:32 -0800138 * @return Intent state, "INSTALLED", if successful,
Hongtao Yin36f79aa2015-02-14 03:51:39 -0800139 * server error message or "FAILED" if failed to modify any direction intent
140 */
141 @javax.ws.rs.Path("/{intentKey}/{src}/{dst}/{srcPort}/{dstPort}/{bandwidth}")
142 @PUT
143 public Response modifyBandwidth(@PathParam("intentKey") String intentKey,
144 @PathParam("src") String src,
145 @PathParam("dst") String dst,
146 @PathParam("srcPort") String srcPort,
147 @PathParam("dstPort") String dstPort,
148 @PathParam("bandwidth") String bandwidth) {
149
150 log.info("Modify bw for intentKey = {}; src = {}; dst = {};" +
151 "srcPort = {}; dstPort = {}; with new bandwidth = {}",
152 intentKey, src, dst, srcPort, dstPort, bandwidth);
153
154 if (src == null || dst == null || srcPort == null || dstPort == null) {
155 return Response.ok(INVALID_PARAMETER).build();
156 }
157
158 Long bandwidthL = 0L;
159 try {
160 bandwidthL = Long.parseLong(bandwidth, 10);
161 } catch (Exception e) {
162 return Response.ok(INVALID_PARAMETER).build();
163 }
Hongtao Yin621c57a2014-10-30 14:28:03 -0700164
Thomas Vachuska140d5852014-10-16 12:17:45 -0700165 IntentService service = get(IntentService.class);
Hongtao Yin36f79aa2015-02-14 03:51:39 -0800166 Intent originalIntent
167 = service.getIntent(Key.of(Tools.fromHex(intentKey.replace("0x", "")), appId()));
Thomas Vachuska140d5852014-10-16 12:17:45 -0700168
Hongtao Yin36f79aa2015-02-14 03:51:39 -0800169 if (originalIntent == null) {
170 return Response.status(Response.Status.NOT_FOUND).build();
171 }
172
173 // get the latency constraint from the original intent
174 Long latencyL = 0L;
175 if (originalIntent instanceof ConnectivityIntent) {
176 ConnectivityIntent connectivityIntent = (ConnectivityIntent) originalIntent;
177 for (Constraint constraint : connectivityIntent.constraints()) {
178 if (constraint instanceof LatencyConstraint) {
179 latencyL = ((LatencyConstraint) constraint).latency().get(ChronoUnit.MICROS);
180 }
181 }
182 }
183
184 Intent newIntent = createIntent(originalIntent.key(), src, dst,
185 srcPort, dstPort, bandwidthL, latencyL);
186 try {
187 if (submitIntent(newIntent)) {
188 return Response.ok(OPERATION_INSTALLED).build();
189 } else {
190 return Response.ok(OPERATION_FAILED).build();
191 }
192 } catch (Exception e) {
193 return Response.status(Response.Status.INTERNAL_SERVER_ERROR).build();
194 }
195 }
196
197
198 /**
199 * Create an Intent for a bidirectional path with constraints.
200 *
201 * @param key optional intent key
202 * @param src the path source (DPID or hostID)
203 * @param dst the path destination (DPID or hostID)
204 * @param srcPort the source port (-1 if src/dest is a host)
205 * @param dstPort the destination port (-1 if src/dest is a host)
206 * @param bandwidth the bandwidth (mbps) requirement for the path
207 * @param latency the latency (micro sec) requirement for the path
208 * @return the appropriate intent
209 */
210 private Intent createIntent(Key key,
211 String src,
212 String dst,
213 String srcPort,
214 String dstPort,
215 Long bandwidth,
216 Long latency) {
Thomas Vachuska140d5852014-10-16 12:17:45 -0700217
Ray Milkeycaa450b2014-10-29 15:54:24 -0700218 TrafficSelector selector = buildTrafficSelector();
219 TrafficTreatment treatment = builder().build();
220
Hongtao Yin36f79aa2015-02-14 03:51:39 -0800221 final Constraint constraintBandwidth =
222 new BandwidthConstraint(Bandwidth.mbps(bandwidth));
223 final Constraint constraintLatency =
224 new LatencyConstraint(Duration.of(latency, ChronoUnit.MICROS));
225 final List<Constraint> constraints = new LinkedList<>();
226
227 constraints.add(constraintBandwidth);
228 constraints.add(constraintLatency);
229
230 if (srcPort.equals("-1")) {
231 HostId srcPoint = HostId.hostId(src);
232 HostId dstPoint = HostId.hostId(dst);
233 return new HostToHostIntent(appId(), key, srcPoint, dstPoint,
Ray Milkey50a9b722015-03-12 10:38:55 -0700234 selector, treatment, constraints,
235 Intent.DEFAULT_INTENT_PRIORITY);
Hongtao Yin36f79aa2015-02-14 03:51:39 -0800236 } else {
237 ConnectPoint srcPoint = new ConnectPoint(deviceId(src), portNumber(srcPort));
238 ConnectPoint dstPoint = new ConnectPoint(deviceId(dst), portNumber(dstPort));
239 return new TwoWayP2PIntent(appId(), key, srcPoint, dstPoint,
Ray Milkey50a9b722015-03-12 10:38:55 -0700240 selector, treatment, constraints,
241 Intent.DEFAULT_INTENT_PRIORITY);
Hongtao Yin36f79aa2015-02-14 03:51:39 -0800242 }
243 }
244
245
246 /**
247 * Synchronously submits an intent to the Intent Service.
248 *
249 * @param intent intent to submit
250 * @return true if operation succeed, false otherwise
251 */
252 private boolean submitIntent(Intent intent)
253 throws InterruptedException {
254 IntentService service = get(IntentService.class);
Ray Milkeycaa450b2014-10-29 15:54:24 -0700255
Thomas Vachuska56d1a702014-11-11 19:14:57 -0800256 CountDownLatch latch = new CountDownLatch(1);
Hongtao Yin36f79aa2015-02-14 03:51:39 -0800257 InternalIntentListener listener = new InternalIntentListener(intent, service, latch);
Thomas Vachuska56d1a702014-11-11 19:14:57 -0800258 service.addListener(listener);
Hongtao Yin36f79aa2015-02-14 03:51:39 -0800259 service.submit(intent);
260 log.info("Submitted Calendar App intent and waiting: {}", intent);
261 if (latch.await(TIMEOUT, TimeUnit.SECONDS) &&
262 listener.getState() == INSTALLED) {
263 return true;
Thomas Vachuska56d1a702014-11-11 19:14:57 -0800264 }
Hongtao Yin36f79aa2015-02-14 03:51:39 -0800265 return false;
Hongtao Yin621c57a2014-10-30 14:28:03 -0700266 }
267
Hongtao Yin36f79aa2015-02-14 03:51:39 -0800268 /**
269 * Remove a bi-directional path with created intent key.
270 *
271 * @param intentKey the string key for the intent to remove
272 * @return Intent state, "WITHDRAWN", if successful,
273 * server error message or FAILED" if any direction intent remove failed
274 */
275 @javax.ws.rs.Path("/{intentKey}")
Hongtao Yin621c57a2014-10-30 14:28:03 -0700276 @DELETE
Hongtao Yin36f79aa2015-02-14 03:51:39 -0800277 public Response removePath(@PathParam("intentKey") String intentKey) {
278
279 log.info("Receiving tear down request for {}", intentKey);
280
281 if (intentKey == null) {
282 return Response.ok(INVALID_PARAMETER).build();
Thomas Vachuska56d1a702014-11-11 19:14:57 -0800283 }
Hongtao Yin36f79aa2015-02-14 03:51:39 -0800284
285 IntentService service = get(IntentService.class);
286 Intent intent = service.getIntent(Key.of(Tools.fromHex(intentKey.replace("0x", "")), appId()));
287
288 if (intent == null) {
289 return Response.status(Response.Status.NOT_FOUND).build();
290 }
291
292 try {
293 if (withdrawIntent(intent)) {
294 return Response.ok(OPERATION_WITHDRAWN).build();
295 } else {
296 return Response.ok(OPERATION_FAILED).build();
297 }
298 } catch (Exception e) {
299 return Response.status(Response.Status.INTERNAL_SERVER_ERROR).build();
300 }
Hongtao Yin621c57a2014-10-30 14:28:03 -0700301 }
302
Hongtao Yin36f79aa2015-02-14 03:51:39 -0800303 /**
304 * Synchronously withdraws an intent to the Intent Service.
305 *
306 * @param intent intent to submit
307 * @return true if operation succeed, false otherwise
308 */
309 private boolean withdrawIntent(Intent intent)
310 throws InterruptedException {
311 IntentService service = get(IntentService.class);
Hongtao Yin621c57a2014-10-30 14:28:03 -0700312
Hongtao Yin36f79aa2015-02-14 03:51:39 -0800313 CountDownLatch latch = new CountDownLatch(1);
314 InternalIntentListener listener = new InternalIntentListener(intent, service, latch);
315 service.addListener(listener);
316 service.withdraw(intent);
317 log.info("Withdrawing intent and waiting: {}", intent);
318 if (latch.await(TIMEOUT, TimeUnit.SECONDS) &&
319 listener.getState() == WITHDRAWN) {
320 return true;
321 }
322 return false;
Thomas Vachuska140d5852014-10-16 12:17:45 -0700323 }
324
Hongtao Yin36f79aa2015-02-14 03:51:39 -0800325
326 private static TrafficSelector buildTrafficSelector() {
Ray Milkeycaa450b2014-10-29 15:54:24 -0700327 TrafficSelector.Builder selectorBuilder = DefaultTrafficSelector.builder();
328 Short ethType = Ethernet.TYPE_IPV4;
329
330 selectorBuilder.matchEthType(ethType);
331
332 return selectorBuilder.build();
333 }
334
Hongtao Yin36f79aa2015-02-14 03:51:39 -0800335 private static DeviceId deviceId(String dpid) {
Thomas Vachuska140d5852014-10-16 12:17:45 -0700336 return DeviceId.deviceId(URI.create("of:" + dpid));
337 }
338
Ray Milkeycaa450b2014-10-29 15:54:24 -0700339 protected ApplicationId appId() {
Brian O'Connorabafb502014-12-02 22:26:20 -0800340 return get(CoreService.class).registerApplication("org.onosproject.calendar");
Ray Milkeycaa450b2014-10-29 15:54:24 -0700341 }
Thomas Vachuska56d1a702014-11-11 19:14:57 -0800342
343 // Auxiliary listener to wait until the given intent reaches the installed or failed states.
344 private final class InternalIntentListener implements IntentListener {
345 private final Intent intent;
346 private final IntentService service;
347 private final CountDownLatch latch;
348 private IntentState state;
349
350 private InternalIntentListener(Intent intent, IntentService service,
351 CountDownLatch latch) {
352 this.intent = intent;
353 this.service = service;
354 this.latch = latch;
355 }
356
357 @Override
358 public void event(IntentEvent event) {
359 if (event.subject().equals(intent)) {
Ray Milkeyf9af43c2015-02-09 16:45:48 -0800360 state = service.getIntentState(intent.key());
Thomas Vachuska56d1a702014-11-11 19:14:57 -0800361 if (state == INSTALLED || state == FAILED || state == WITHDRAWN) {
362 latch.countDown();
Hongtao Yin36f79aa2015-02-14 03:51:39 -0800363 service.removeListener(this);
Thomas Vachuska56d1a702014-11-11 19:14:57 -0800364 }
Thomas Vachuska56d1a702014-11-11 19:14:57 -0800365 }
366 }
367
368 public IntentState getState() {
369 return state;
370 }
371 }
Thomas Vachuska140d5852014-10-16 12:17:45 -0700372}