blob: 2143e89c0f78756931b9baa05990f07aba853053 [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
138 * @return @return Intent state, "INSTALLED", if successful,
139 * 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,
234 selector, treatment, constraints);
235 } else {
236 ConnectPoint srcPoint = new ConnectPoint(deviceId(src), portNumber(srcPort));
237 ConnectPoint dstPoint = new ConnectPoint(deviceId(dst), portNumber(dstPort));
238 return new TwoWayP2PIntent(appId(), key, srcPoint, dstPoint,
239 selector, treatment, constraints);
240 }
241 }
242
243
244 /**
245 * Synchronously submits an intent to the Intent Service.
246 *
247 * @param intent intent to submit
248 * @return true if operation succeed, false otherwise
249 */
250 private boolean submitIntent(Intent intent)
251 throws InterruptedException {
252 IntentService service = get(IntentService.class);
Ray Milkeycaa450b2014-10-29 15:54:24 -0700253
Thomas Vachuska56d1a702014-11-11 19:14:57 -0800254 CountDownLatch latch = new CountDownLatch(1);
Hongtao Yin36f79aa2015-02-14 03:51:39 -0800255 InternalIntentListener listener = new InternalIntentListener(intent, service, latch);
Thomas Vachuska56d1a702014-11-11 19:14:57 -0800256 service.addListener(listener);
Hongtao Yin36f79aa2015-02-14 03:51:39 -0800257 service.submit(intent);
258 log.info("Submitted Calendar App intent and waiting: {}", intent);
259 if (latch.await(TIMEOUT, TimeUnit.SECONDS) &&
260 listener.getState() == INSTALLED) {
261 return true;
Thomas Vachuska56d1a702014-11-11 19:14:57 -0800262 }
Hongtao Yin36f79aa2015-02-14 03:51:39 -0800263 return false;
Hongtao Yin621c57a2014-10-30 14:28:03 -0700264 }
265
Hongtao Yin36f79aa2015-02-14 03:51:39 -0800266 /**
267 * Remove a bi-directional path with created intent key.
268 *
269 * @param intentKey the string key for the intent to remove
270 * @return Intent state, "WITHDRAWN", if successful,
271 * server error message or FAILED" if any direction intent remove failed
272 */
273 @javax.ws.rs.Path("/{intentKey}")
Hongtao Yin621c57a2014-10-30 14:28:03 -0700274 @DELETE
Hongtao Yin36f79aa2015-02-14 03:51:39 -0800275 public Response removePath(@PathParam("intentKey") String intentKey) {
276
277 log.info("Receiving tear down request for {}", intentKey);
278
279 if (intentKey == null) {
280 return Response.ok(INVALID_PARAMETER).build();
Thomas Vachuska56d1a702014-11-11 19:14:57 -0800281 }
Hongtao Yin36f79aa2015-02-14 03:51:39 -0800282
283 IntentService service = get(IntentService.class);
284 Intent intent = service.getIntent(Key.of(Tools.fromHex(intentKey.replace("0x", "")), appId()));
285
286 if (intent == null) {
287 return Response.status(Response.Status.NOT_FOUND).build();
288 }
289
290 try {
291 if (withdrawIntent(intent)) {
292 return Response.ok(OPERATION_WITHDRAWN).build();
293 } else {
294 return Response.ok(OPERATION_FAILED).build();
295 }
296 } catch (Exception e) {
297 return Response.status(Response.Status.INTERNAL_SERVER_ERROR).build();
298 }
Hongtao Yin621c57a2014-10-30 14:28:03 -0700299 }
300
Hongtao Yin36f79aa2015-02-14 03:51:39 -0800301 /**
302 * Synchronously withdraws an intent to the Intent Service.
303 *
304 * @param intent intent to submit
305 * @return true if operation succeed, false otherwise
306 */
307 private boolean withdrawIntent(Intent intent)
308 throws InterruptedException {
309 IntentService service = get(IntentService.class);
Hongtao Yin621c57a2014-10-30 14:28:03 -0700310
Hongtao Yin36f79aa2015-02-14 03:51:39 -0800311 CountDownLatch latch = new CountDownLatch(1);
312 InternalIntentListener listener = new InternalIntentListener(intent, service, latch);
313 service.addListener(listener);
314 service.withdraw(intent);
315 log.info("Withdrawing intent and waiting: {}", intent);
316 if (latch.await(TIMEOUT, TimeUnit.SECONDS) &&
317 listener.getState() == WITHDRAWN) {
318 return true;
319 }
320 return false;
Thomas Vachuska140d5852014-10-16 12:17:45 -0700321 }
322
Hongtao Yin36f79aa2015-02-14 03:51:39 -0800323
324 private static TrafficSelector buildTrafficSelector() {
Ray Milkeycaa450b2014-10-29 15:54:24 -0700325 TrafficSelector.Builder selectorBuilder = DefaultTrafficSelector.builder();
326 Short ethType = Ethernet.TYPE_IPV4;
327
328 selectorBuilder.matchEthType(ethType);
329
330 return selectorBuilder.build();
331 }
332
Hongtao Yin36f79aa2015-02-14 03:51:39 -0800333 private static DeviceId deviceId(String dpid) {
Thomas Vachuska140d5852014-10-16 12:17:45 -0700334 return DeviceId.deviceId(URI.create("of:" + dpid));
335 }
336
Ray Milkeycaa450b2014-10-29 15:54:24 -0700337 protected ApplicationId appId() {
Brian O'Connorabafb502014-12-02 22:26:20 -0800338 return get(CoreService.class).registerApplication("org.onosproject.calendar");
Ray Milkeycaa450b2014-10-29 15:54:24 -0700339 }
Thomas Vachuska56d1a702014-11-11 19:14:57 -0800340
341 // Auxiliary listener to wait until the given intent reaches the installed or failed states.
342 private final class InternalIntentListener implements IntentListener {
343 private final Intent intent;
344 private final IntentService service;
345 private final CountDownLatch latch;
346 private IntentState state;
347
348 private InternalIntentListener(Intent intent, IntentService service,
349 CountDownLatch latch) {
350 this.intent = intent;
351 this.service = service;
352 this.latch = latch;
353 }
354
355 @Override
356 public void event(IntentEvent event) {
357 if (event.subject().equals(intent)) {
Ray Milkeyf9af43c2015-02-09 16:45:48 -0800358 state = service.getIntentState(intent.key());
Thomas Vachuska56d1a702014-11-11 19:14:57 -0800359 if (state == INSTALLED || state == FAILED || state == WITHDRAWN) {
360 latch.countDown();
Hongtao Yin36f79aa2015-02-14 03:51:39 -0800361 service.removeListener(this);
Thomas Vachuska56d1a702014-11-11 19:14:57 -0800362 }
Thomas Vachuska56d1a702014-11-11 19:14:57 -0800363 }
364 }
365
366 public IntentState getState() {
367 return state;
368 }
369 }
Thomas Vachuska140d5852014-10-16 12:17:45 -0700370}