blob: 766715fd53904b8ceacd965f61714838b8af4849 [file] [log] [blame]
cheng fan48e832c2015-05-29 01:54:47 +08001/*
2 * Copyright 2015 Open Networking Laboratory
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 */
16package org.onosproject.provider.pcep.tunnel.impl;
17
18import static com.google.common.base.Preconditions.checkNotNull;
19import static org.onosproject.net.DeviceId.deviceId;
20import static org.onosproject.net.PortNumber.portNumber;
21import static org.slf4j.LoggerFactory.getLogger;
22
23import java.util.ArrayList;
24import java.util.HashMap;
25import java.util.List;
26import java.util.Optional;
27
28import org.apache.felix.scr.annotations.Activate;
29import org.apache.felix.scr.annotations.Component;
30import org.apache.felix.scr.annotations.Deactivate;
31import org.apache.felix.scr.annotations.Reference;
32import org.apache.felix.scr.annotations.ReferenceCardinality;
33import org.apache.felix.scr.annotations.Service;
34import org.onosproject.core.DefaultGroupId;
35import org.onosproject.incubator.net.tunnel.DefaultOpticalTunnelEndPoint;
36import org.onosproject.incubator.net.tunnel.DefaultTunnelDescription;
37import org.onosproject.incubator.net.tunnel.OpticalLogicId;
38import org.onosproject.incubator.net.tunnel.OpticalTunnelEndPoint;
39import org.onosproject.incubator.net.tunnel.Tunnel;
40import org.onosproject.incubator.net.tunnel.TunnelDescription;
41import org.onosproject.incubator.net.tunnel.TunnelEndPoint;
42import org.onosproject.incubator.net.tunnel.TunnelId;
43import org.onosproject.incubator.net.tunnel.TunnelName;
44import org.onosproject.incubator.net.tunnel.TunnelProvider;
45import org.onosproject.incubator.net.tunnel.TunnelProviderRegistry;
46import org.onosproject.incubator.net.tunnel.TunnelProviderService;
47import org.onosproject.net.ConnectPoint;
48import org.onosproject.net.DefaultAnnotations;
49import org.onosproject.net.DefaultLink;
50import org.onosproject.net.DefaultPath;
51import org.onosproject.net.DeviceId;
52import org.onosproject.net.ElementId;
53import org.onosproject.net.Link;
54import org.onosproject.net.Path;
55import org.onosproject.net.PortNumber;
56import org.onosproject.net.provider.AbstractProvider;
57import org.onosproject.net.provider.ProviderId;
58import org.onosproject.pcep.api.PcepController;
59import org.onosproject.pcep.api.PcepDpid;
60import org.onosproject.pcep.api.PcepHopNodeDescription;
61import org.onosproject.pcep.api.PcepOperator.OperationType;
62import org.onosproject.pcep.api.PcepTunnel;
63import org.onosproject.pcep.api.PcepTunnel.PATHTYPE;
64import org.onosproject.pcep.api.PcepTunnelListener;
65import org.slf4j.Logger;
66
67import static org.onosproject.pcep.api.PcepDpid.*;
68
69/**
70 * Provider which uses an PCEP controller to detect, update, create network
71 * tunnels.
72 */
73@Component(immediate = true)
74@Service
75public class PcepTunnelProvider extends AbstractProvider
76 implements TunnelProvider {
77
78 private static final Logger log = getLogger(PcepTunnelProvider.class);
79 private static final long MAX_BANDWIDTH = 99999744;
80 private static final long MIN_BANDWIDTH = 64;
cheng fan7716ec92015-05-31 01:53:19 +080081 private static final String BANDWIDTH_UINT = "kbps";
cheng fan48e832c2015-05-29 01:54:47 +080082 static final String PROVIDER_ID = "org.onosproject.provider.tunnel.default";
83
84 private static final String TUNNLE_NOT_NULL = "Create failed,The given port may be wrong or has been occupied.";
85
86 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
87 protected TunnelProviderRegistry tunnelProviderRegistry;
88
89 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
90 protected PcepController controller;
91
92 TunnelProviderService service;
93
94 HashMap<String, TunnelId> tunnelMap = new HashMap<String, TunnelId>();
95
96 private InnerTunnerProvider listener = new InnerTunnerProvider();
97
98 /**
99 * Creates a Tunnel provider.
100 */
101 public PcepTunnelProvider() {
102 super(new ProviderId("default", PROVIDER_ID));
103 }
104
105 @Activate
106 public void activate() {
107 service = tunnelProviderRegistry.register(this);
108 controller.addTunnelListener(listener);
109 log.info("Started");
110 }
111
112 @Deactivate
113 public void deactivate() {
114 tunnelProviderRegistry.unregister(this);
115 controller.removeTunnelListener(listener);
116 log.info("Stopped");
117 }
118
119 @Override
120 public void setupTunnel(Tunnel tunnel, Path path) {
121 // TODO Auto-generated method stub
122
123 }
124
125 @Override
126 public void setupTunnel(ElementId srcElement, Tunnel tunnel, Path path) {
127 // TODO Auto-generated method stub
128
129 }
130
131 @Override
132 public void releaseTunnel(Tunnel tunnel) {
133 // TODO Auto-generated method stub
134
135 }
136
137 @Override
138 public void releaseTunnel(ElementId srcElement, Tunnel tunnel) {
139 // TODO Auto-generated method stub
140
141 }
142
143 @Override
144 public void updateTunnel(Tunnel tunnel, Path path) {
145 // TODO Auto-generated method stub
146
147 }
148
149 @Override
150 public void updateTunnel(ElementId srcElement, Tunnel tunnel, Path path) {
151 // TODO Auto-generated method stub
152
153 }
154
155 @Override
156 public TunnelId tunnelAdded(TunnelDescription tunnel) {
157
cheng fan7716ec92015-05-31 01:53:19 +0800158 long bandwidth = Long
159 .parseLong(tunnel.annotations().value("bandwidth"));
cheng fan48e832c2015-05-29 01:54:47 +0800160
161 if (bandwidth < MIN_BANDWIDTH || bandwidth > MAX_BANDWIDTH) {
cheng fan7716ec92015-05-31 01:53:19 +0800162 error("Update failed, invalid bandwidth.");
cheng fan48e832c2015-05-29 01:54:47 +0800163 return null;
164 }
165
166 // endpoints
167 OpticalTunnelEndPoint src = (org.onosproject.incubator.net.tunnel.OpticalTunnelEndPoint) tunnel
168 .src();
169 OpticalTunnelEndPoint dst = (OpticalTunnelEndPoint) tunnel.dst();
170 // devices
171 DeviceId srcId = (DeviceId) src.elementId().get();
172 DeviceId dstId = (DeviceId) dst.elementId().get();
173
174 // ports
175 long srcPort = src.portNumber().get().toLong();
176 long dstPort = dst.portNumber().get().toLong();
177
178 // type
179 if (tunnel.type() != Tunnel.Type.VLAN) {
cheng fan7716ec92015-05-31 01:53:19 +0800180 error("Illegal tunnel type. Only support VLAN tunnel creation.");
cheng fan48e832c2015-05-29 01:54:47 +0800181 return null;
182 }
183
184 PcepTunnel pcepTunnel = controller.applyTunnel(srcId, dstId, srcPort,
185 dstPort, bandwidth,
186 tunnel.tunnelName()
187 .value());
188
189 checkNotNull(pcepTunnel, TUNNLE_NOT_NULL);
190 TunnelDescription tunnelAdded = buildOpticalTunnel(pcepTunnel, null);
191 TunnelId tunnelId = service.tunnelAdded(tunnelAdded);
192
193 tunnelMap.put(String.valueOf(pcepTunnel.id()), tunnelId);
194 return tunnelId;
195 }
196
197 @Override
198 public void tunnelRemoved(TunnelDescription tunnel) {
199 Tunnel tunnelOld = tunnelQueryById(tunnel.id());
200 checkNotNull(tunnelOld, "The tunnel id is not exsited.");
201 if (tunnelOld.type() != Tunnel.Type.VLAN) {
cheng fan7716ec92015-05-31 01:53:19 +0800202 error("Llegal tunnel type. Only support VLAN tunnel deletion.");
cheng fan48e832c2015-05-29 01:54:47 +0800203 return;
204 }
205 String pcepTunnelId = getPCEPTunnelKey(tunnel.id());
206 checkNotNull(pcepTunnelId, "The tunnel id is not exsited.");
cheng fan7716ec92015-05-31 01:53:19 +0800207 if (!controller.deleteTunnel(pcepTunnelId)) {
208 error("Delete tunnel failed, Maybe some devices have been disconnected.");
209 return;
cheng fan48e832c2015-05-29 01:54:47 +0800210 }
211 tunnelMap.remove(pcepTunnelId);
212 service.tunnelRemoved(tunnel);
cheng fan48e832c2015-05-29 01:54:47 +0800213 }
214
215 @Override
216 public void tunnelUpdated(TunnelDescription tunnel) {
217
218 Tunnel tunnelOld = tunnelQueryById(tunnel.id());
219 if (tunnelOld.type() != Tunnel.Type.VLAN) {
cheng fan7716ec92015-05-31 01:53:19 +0800220 error("Llegal tunnel type. Only support VLAN tunnel update.");
cheng fan48e832c2015-05-29 01:54:47 +0800221 return;
222 }
cheng fan7716ec92015-05-31 01:53:19 +0800223 long bandwidth = Long
224 .parseLong(tunnel.annotations().value("bandwidth"));
cheng fan48e832c2015-05-29 01:54:47 +0800225 if (bandwidth < MIN_BANDWIDTH || bandwidth > MAX_BANDWIDTH) {
cheng fan7716ec92015-05-31 01:53:19 +0800226 error("Update failed, invalid bandwidth.");
cheng fan48e832c2015-05-29 01:54:47 +0800227 return;
228 }
229 String pcepTunnelId = getPCEPTunnelKey(tunnel.id());
230
231 checkNotNull(pcepTunnelId, "Invalid tunnel id");
232 if (!controller.updateTunnelBandwidth(pcepTunnelId, bandwidth)) {
233
cheng fan7716ec92015-05-31 01:53:19 +0800234 error("Update failed,maybe invalid bandwidth.");
cheng fan48e832c2015-05-29 01:54:47 +0800235 return;
236
237 }
238 service.tunnelUpdated(tunnel);
239 }
240
cheng fan7716ec92015-05-31 01:53:19 +0800241 private void error(String info) {
242 System.err.println(info);
243 }
244
cheng fan48e832c2015-05-29 01:54:47 +0800245 // Short-hand for creating a connection point.
246 private ConnectPoint connectPoint(PcepDpid id, long port) {
247 return new ConnectPoint(deviceId(uri(id)), portNumber(port));
248 }
249
250 // Short-hand for creating a link.
251 private Link link(PcepDpid src, long sp, PcepDpid dst, long dp) {
252 return new DefaultLink(id(), connectPoint(src, sp), connectPoint(dst,
253 dp),
254 Link.Type.TUNNEL);
255 }
256
257 // Creates a path that leads through the given devices.
258 private Path createPath(List<PcepHopNodeDescription> hopList,
259 PATHTYPE pathtype) {
260 if (hopList == null || hopList.size() == 0) {
261 return null;
262 }
263 List<Link> links = new ArrayList<>();
264 for (int i = 1; i < hopList.size() - 1; i = i + 2) {
265 links.add(link(hopList.get(i).getDeviceId(), hopList.get(i)
266 .getPortNum(), hopList.get(i + 1).getDeviceId(), hopList
267 .get(i + 1).getPortNum()));
268 }
269
270 int hopNum = hopList.size() - 2;
271 DefaultAnnotations extendAnnotations = DefaultAnnotations.builder()
272 .set("pathNum", String.valueOf(hopNum))
273 .set("pathType", String.valueOf(pathtype)).build();
274 return new DefaultPath(id(), links, hopNum, extendAnnotations);
275 }
276
277 // convert the path description to a string.
278 public String pathToString(List<Link> links) {
279 StringBuilder builder = new StringBuilder();
280 builder.append("{");
281 for (Link link : links) {
282 builder.append("(Device:" + link.src().deviceId() + " Port:"
283 + link.src().port().toLong());
284 builder.append(" Device:" + link.dst().deviceId() + " Port:"
285 + link.dst().port().toLong());
286 builder.append(")");
287 }
288 builder.append("}");
289 return builder.toString();
290 }
291
292 // build a TunnelDescription.
293 private TunnelDescription buildOpticalTunnel(PcepTunnel pcepTunnel,
294 TunnelId tunnelId) {
295 TunnelEndPoint srcPoint = null;
296 TunnelEndPoint dstPoint = null;
297 Tunnel.Type tunnelType = null;
298 TunnelName name = TunnelName.tunnelName(pcepTunnel.name());
299
300 // add path after codes of tunnel's path merged
301 Path path = createPath(pcepTunnel.getHopList(),
302 pcepTunnel.getPathType());
303
304 OpticalTunnelEndPoint.Type endPointType = null;
305 switch (pcepTunnel.type()) {
306 case OCH:
307 tunnelType = Tunnel.Type.OCH;
308 endPointType = OpticalTunnelEndPoint.Type.LAMBDA;
309 break;
310
311 case OTN:
312 tunnelType = Tunnel.Type.ODUK;
313 endPointType = OpticalTunnelEndPoint.Type.TIMESLOT;
314 break;
315
316 case UNI:
317 tunnelType = Tunnel.Type.VLAN;
318 endPointType = null;
319 break;
320
321 default:
322 break;
323 }
324 DeviceId srcDid = deviceId(uri(pcepTunnel.srcDeviceID()));
325 DeviceId dstDid = deviceId(uri(pcepTunnel.dstDeviceId()));
326 PortNumber srcPort = PortNumber.portNumber(pcepTunnel.srcPort());
327 PortNumber dstPort = PortNumber.portNumber(pcepTunnel.dstPort());
328
329 srcPoint = new DefaultOpticalTunnelEndPoint(id(), Optional.of(srcDid),
330 Optional.of(srcPort), null,
331 endPointType,
332 OpticalLogicId.logicId(0),
333 true);
334 dstPoint = new DefaultOpticalTunnelEndPoint(id(), Optional.of(dstDid),
335 Optional.of(dstPort), null,
336 endPointType,
337 OpticalLogicId.logicId(0),
338 true);
339
340 // basic annotations
cheng fan7716ec92015-05-31 01:53:19 +0800341 DefaultAnnotations annotations = DefaultAnnotations
342 .builder()
cheng fan48e832c2015-05-29 01:54:47 +0800343 .set("SLA", String.valueOf(pcepTunnel.getSla()))
cheng fan7716ec92015-05-31 01:53:19 +0800344 .set("bandwidth",
345 String.valueOf(pcepTunnel.bandWidth()) + BANDWIDTH_UINT)
cheng fan48e832c2015-05-29 01:54:47 +0800346 .set("index", String.valueOf(pcepTunnel.id())).build();
347
cheng fan48e832c2015-05-29 01:54:47 +0800348 // a VLAN tunnel always carry OCH tunnel, this annotation is the index
349 // of a OCH tunnel.
350 if (pcepTunnel.underLayTunnelId() != 0) {
351 DefaultAnnotations extendAnnotations = DefaultAnnotations
352 .builder()
353 .set("underLayTunnelIndex",
354 String.valueOf(pcepTunnel.underLayTunnelId())).build();
355 annotations = DefaultAnnotations.merge(annotations,
356 extendAnnotations);
357
358 }
359 TunnelDescription tunnel = new DefaultTunnelDescription(
360 tunnelId,
361 srcPoint,
362 dstPoint,
363 tunnelType,
364 new DefaultGroupId(
365 0),
366 id(), name,
367 path,
368 annotations);
369 return tunnel;
370
371 }
372
373 /**
374 * Get the tunnelID according to the tunnel key.
375 *
376 * @param tunnelKey tunnel key
377 * @return corresponding tunnel id of the a tunnel key.
378 */
379 private TunnelId getTunnelId(String tunnelKey) {
380
381 for (String key : tunnelMap.keySet()) {
382 if (key.equals(tunnelKey)) {
383 return tunnelMap.get(key);
384 }
385 }
386 return null;
387 }
388
389 /**
390 * Get the tunnel key according to the tunnelID.
391 *
392 * @param tunnelId tunnel id
393 * @return corresponding a tunnel key of the tunnel id.
394 */
395 private String getPCEPTunnelKey(TunnelId tunnelId) {
396 for (String key : tunnelMap.keySet()) {
397 if (tunnelMap.get(key).id() == tunnelId.id()) {
398 return key;
399 }
400 }
401 return null;
402
403 }
404
405 private class InnerTunnerProvider implements PcepTunnelListener {
406
407 @Override
408 public void handlePCEPTunnel(PcepTunnel pcepTunnel) {
409
410 TunnelDescription tunnel = null;
411 // instance and id identify a tunnel together
412 String tunnelKey = String.valueOf(pcepTunnel.getInstance())
413 + String.valueOf(pcepTunnel.id());
414
415 if (tunnelKey == null || "".equals(tunnelKey)) {
416 log.error("Invalid PCEP tunnel");
417 return;
418 }
419
420 TunnelId tunnelId = getTunnelId(tunnelKey);
421
422 tunnel = buildOpticalTunnel(pcepTunnel, tunnelId);
423
424 OperationType operType = pcepTunnel.getOperationType();
425 switch (operType) {
426 case ADD:
427 tunnelId = service.tunnelAdded(tunnel);
428 tunnelMap.put(tunnelKey, tunnelId);
429 break;
430
431 case UPDATE:
432 service.tunnelUpdated(tunnel);
433 break;
434
435 case DELETE:
436 service.tunnelRemoved(tunnel);
437 tunnelMap.remove(tunnelKey);
438 break;
439
440 default:
441 log.error("Invalid tunnel operation");
442
443 }
444
445 }
446 }
447
448 @Override
449 public Tunnel tunnelQueryById(TunnelId tunnelId) {
450 return service.tunnelQueryById(tunnelId);
451 }
452
453}