blob: 77961ed7e68079d85f2e1f4231a6875347e5ae67 [file] [log] [blame]
tony-liuf7d2a262016-10-17 16:32:24 +08001/*
Brian O'Connora09fe5b2017-08-03 21:12:30 -07002 * Copyright 2016-present Open Networking Foundation
tony-liuf7d2a262016-10-17 16:32:24 +08003 *
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.actn.mdsc.tetunnelctl;
17
18import com.google.common.collect.Lists;
19import org.apache.felix.scr.annotations.Activate;
20import org.apache.felix.scr.annotations.Component;
21import org.apache.felix.scr.annotations.Deactivate;
22import org.apache.felix.scr.annotations.Reference;
23import org.apache.felix.scr.annotations.ReferenceCardinality;
24import org.onosproject.actn.mdsc.pce.TeTunnelPceService;
25import org.onosproject.incubator.net.tunnel.Tunnel;
26import org.onosproject.incubator.net.tunnel.TunnelAdminService;
27import org.onosproject.incubator.net.tunnel.TunnelEvent;
28import org.onosproject.incubator.net.tunnel.TunnelId;
29import org.onosproject.incubator.net.tunnel.TunnelListener;
30import org.onosproject.incubator.net.tunnel.TunnelService;
31import org.onosproject.tetopology.management.api.TeTopology;
32import org.onosproject.tetopology.management.api.TeTopologyKey;
33import org.onosproject.tetopology.management.api.TeTopologyService;
34import org.onosproject.tetopology.management.api.node.TeNodeKey;
35import org.onosproject.tetopology.management.api.node.TtpKey;
36import org.onosproject.tetunnel.api.TeTunnelAdminService;
37import org.onosproject.tetunnel.api.TeTunnelService;
38import org.onosproject.tetunnel.api.tunnel.DefaultTeTunnel;
39import org.onosproject.tetunnel.api.tunnel.TeTunnel;
40import org.onosproject.tetunnel.api.tunnel.TeTunnelKey;
41import org.onosproject.tetunnel.api.tunnel.path.TePath;
42import org.onosproject.tetunnel.api.tunnel.path.TeRouteSubobject;
43import org.onosproject.tetunnel.api.tunnel.path.TeRouteUnnumberedLink;
44import org.slf4j.Logger;
45import org.slf4j.LoggerFactory;
46
47import java.util.Collection;
48import java.util.List;
49import java.util.Objects;
50
51/**
52 * TE Tunnel controller/processor which manages TE tunnel processing.
53 * <p>
54 * For example, when creating a cross-domain tunnel from a MDSC, the
55 * processor will call a relevant PCE to get an end-to-end cross-domain path,
56 * then spits the path into segment tunnels(domain tunnels), and then informs
57 * PNCs to setup domain tunnels respectively.
58 */
59@Component(immediate = true)
60public class TeTunnelCtl {
61
62 private static final Logger log = LoggerFactory.getLogger(TeTunnelCtl.class);
63
64 private final TunnelListener tunnelListener = new InternalTunnelListener();
65
66 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
67 protected TunnelService tunnelService;
68
69 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
70 protected TunnelAdminService tunnelAdminService;
71
72 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
73 protected TeTunnelService teTunnelService;
74
75 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
76 protected TeTunnelAdminService teTunnelAdminService;
77
78 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
79 protected TeTopologyService teTopologyService;
80
81 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
82 protected TeTunnelPceService teTunnelPceService;
83
84 @Activate
85 protected void activate() {
86 tunnelService.addListener(tunnelListener);
87
88 log.info("Started");
89 }
90
91 @Deactivate
92 protected void deactivate() {
93 tunnelService.removeListener(tunnelListener);
94
95 log.info("Stopped");
96 }
97
98 private void addTeTunnel(TeTunnel teTunnel) {
99 if (teTunnel == null) {
100 return;
101 }
102
103 Tunnel tunnel = tunnelService.queryTunnel(
104 teTunnelService.getTunnelId(teTunnel.teTunnelKey()));
105 if (tunnel == null) {
106 log.error("tunnel does not exist, {}", teTunnel.teTunnelKey());
107 return;
108 }
109 if (tunnel.state() != Tunnel.State.INIT) {
110 log.error("tunnel state error, {}, {}", teTunnel.teTunnelKey(),
111 tunnel.state());
112 return;
113 }
114 tunnelAdminService.updateTunnelState(tunnel, Tunnel.State.ESTABLISHING);
115
116 //TODO support multi-thread
117 if (isTeTunnelCrossDomain(teTunnel)) {
118 if (!addCrossDomainTeTunnel(teTunnel)) {
119 tunnelAdminService.updateTunnelState(tunnel, Tunnel.State.FAILED);
120 }
121 }
122 /*
123 * "else" is to do nothing.
124 * When adding a single domain tunnel, the TunnelManager will call
125 * tunnel providers, then the providers will pass the request to
126 * the domain controller. Nothing to do here.
127 */
128 }
129
130 private boolean isTeTunnelCrossDomain(TeTunnel teTunnel) {
131 TeTopology srcTopo = teTopologyService.teTopology(
132 teTopologyService.teNode(teTunnel.srcNode())
133 .underlayTeTopologyId());
134 TeTopology dstTopo = teTopologyService.teTopology(
135 teTopologyService.teNode(teTunnel.dstNode())
136 .underlayTeTopologyId());
137 return (srcTopo != null && dstTopo != null
138 && srcTopo.ownerId().equals(dstTopo.ownerId()));
139 }
140
141 private boolean addCrossDomainTeTunnel(TeTunnel teTunnel) {
142 List<TeRouteSubobject> route = null;
143 TePath primaryPath = teTunnel.primaryPaths().get(0);
144 if (primaryPath != null &&
145 primaryPath.type() == TePath.Type.EXPLICIT) {
146 route = primaryPath.explicitRoute();
147 } else {
148 Collection<List<TeRouteSubobject>> routes =
149 teTunnelPceService.computePaths(teTunnel);
150 if (routes == null || routes.isEmpty()) {
151 log.error("no available route for {}",
152 teTunnel.teTunnelKey());
153 return false;
154 }
155
156 //FIXME: try other pce when failed?
157 route = routes.iterator().next();
158 }
159
160 if (route == null) {
161 log.error("no available route for {}",
162 teTunnel.teTunnelKey());
163 return false;
164 }
165
166 return spitRoute(teTunnel, route);
167 }
168
169 //spits route to segment tunnels
170 private boolean spitRoute(TeTunnel teTunnel, List<TeRouteSubobject> route) {
171 List<TeTunnelKey> segmentTunnels = Lists.newArrayList();
172 boolean success = true;
173 TeNodeKey srcNode = teTunnel.srcNode();
174 TtpKey srcTp = teTunnel.srcTp();
175 TeNodeKey dstNode = null;
176 TtpKey dstTp = null;
177
178 for (TeRouteSubobject teRouteSubobject : route) {
179 if (!(teRouteSubobject instanceof TeRouteUnnumberedLink)) {
180 log.error("unsupported type {}", teRouteSubobject.type());
181 success = false;
182 break;
183 }
184
185 TeRouteUnnumberedLink teRouteUnnumberedLink =
186 (TeRouteUnnumberedLink) teRouteSubobject;
187 dstNode = teRouteUnnumberedLink.node();
188 dstTp = teRouteUnnumberedLink.ttp();
189 if (Objects.equals(srcNode, dstNode) &&
190 Objects.equals(srcTp, dstTp)) {
191 continue;
192 }
193 if (Objects.equals(srcNode, dstNode)) {
194 if (!addSegmentTunnel(segmentTunnels, teTunnel,
195 srcNode, srcTp, dstNode, dstTp)) {
196 success = false;
197 break;
198 }
199 }
200
201 srcNode = dstNode;
202 srcTp = dstTp;
203 }
204
205 if (success && !(Objects.equals(dstNode, teTunnel.dstNode()) &&
206 Objects.equals(dstTp, teTunnel.dstTp()))) {
207 srcNode = dstNode;
208 srcTp = dstTp;
209 dstNode = teTunnel.dstNode();
210 dstTp = teTunnel.dstTp();
211 if (!addSegmentTunnel(segmentTunnels, teTunnel,
212 srcNode, srcTp, dstNode, dstTp)) {
213 success = false;
214 }
215 }
216
217 if (!success) {
218 // roll back segment tunnels
219 for (TeTunnelKey key : segmentTunnels) {
220 teTunnelAdminService.removeTeTunnel(key);
221 }
222 } else {
223 teTunnelAdminService.setSegmentTunnel(teTunnel.teTunnelKey(),
224 segmentTunnels);
225 }
226 return success;
227 }
228
229 private boolean addSegmentTunnel(List<TeTunnelKey> segmentTunnels,
230 TeTunnel teTunnel,
231 TeNodeKey srcNode, TtpKey srcTp,
232 TeNodeKey dstNode, TtpKey dstTp) {
233 TeTunnelKey teTunnelKey = getNextTeTunnelKey(srcNode.teTopologyKey());
234 TeTunnel teTunnelSegment = DefaultTeTunnel.builder()
235 .teTunnelKey(teTunnelKey)
236 .srcNode(srcNode)
237 .dstNode(dstNode)
238 .srcTp(srcTp)
239 .dstTp(dstTp)
240 .adminState(teTunnel.adminStatus())
241 .lspProtectionType(teTunnel.lspProtectionType())
242 .type(teTunnel.type())
243 .build();
244 TunnelId tunnelId =
245 teTunnelAdminService.createTeTunnel(teTunnelSegment);
246 if (tunnelId == null) {
247 log.error("failed to create segment tunnel: {},{},{},{}",
248 srcNode, srcTp, dstNode, dstTp);
249 return false;
250 }
251 segmentTunnels.add(teTunnelKey);
252 return true;
253 }
254
255 private TeTunnelKey getNextTeTunnelKey(TeTopologyKey key) {
256 //FIXME need a better way to get a te tunnel id
Ray Milkey3717e602018-02-01 13:49:47 -0800257 long teTunnelId = teTunnelService.getTeTunnels(key).size() + 1L;
tony-liuf7d2a262016-10-17 16:32:24 +0800258 return new TeTunnelKey(key, teTunnelId);
259 }
260
261 private void updateTeTunnel(TeTunnel teTunnel, Tunnel tunnel) {
262 if (teTunnel == null) {
263 return;
264 }
265
266 if (tunnel.state() == Tunnel.State.ESTABLISHED) {
267 tunnelEstablished(teTunnel);
268 } else if (tunnel.state() == Tunnel.State.REMOVING) {
269 removingTunnel(teTunnel);
270 }
271
272 //TODO update TE tunnel content
273 }
274
275 private void tunnelEstablished(TeTunnel teTunnel) {
276 TeTunnel e2eTeTunnel = retriveE2eTunnel(teTunnel);
277 if (e2eTeTunnel != null) {
278 boolean goodToContinue = true;
279 for (TeTunnelKey key : e2eTeTunnel.segmentTunnels()) {
280 goodToContinue = checkSegmentTunnel(key);
281 if (!goodToContinue) {
282 break;
283 }
284 }
285
286 if (goodToContinue) {
287 tunnelAdminService.updateTunnelState(
288 tunnelService.queryTunnel(
289 teTunnelService.getTunnelId(
290 teTunnel.teTunnelKey())),
291 Tunnel.State.ESTABLISHED
292 );
293 }
294 }
295 }
296
297 private TeTunnel retriveE2eTunnel(TeTunnel segmentTunnel) {
298 return teTunnelService.getTeTunnel(segmentTunnel.e2eTunnelKey());
299 }
300
301 private boolean checkSegmentTunnel(TeTunnelKey key) {
302 Tunnel segmentTunnel = tunnelService.queryTunnel(
303 teTunnelService.getTunnelId(key));
304 if (segmentTunnel == null ||
305 segmentTunnel.state() != Tunnel.State.ESTABLISHED) {
306 return false;
307 }
308 return true;
309 }
310
311 private void removingTunnel(TeTunnel teTunnel) {
312 List<TeTunnelKey> segmentTunnels = teTunnel.segmentTunnels();
313 if (segmentTunnels != null && !segmentTunnels.isEmpty()) {
314 for (TeTunnelKey key : segmentTunnels) {
315 teTunnelAdminService.removeTeTunnel(key);
316 }
317 }
318 }
319
320 // Listens on tunnel events.
321 private class InternalTunnelListener implements TunnelListener {
322 @Override
323 public void event(TunnelEvent event) {
324 switch (event.type()) {
325 case TUNNEL_ADDED:
326 addTunnel(event.subject());
327 break;
328 case TUNNEL_UPDATED:
329 updateTunnel(event.subject());
330 break;
331 //TODO: TE Tunnel remove/... event process
332 default:
333 log.warn("unknown event: {}", event.type());
334 break;
335 }
336 }
337
338 private void addTunnel(Tunnel tunnel) {
339 addTeTunnel(teTunnelService.getTeTunnel(tunnel.tunnelId()));
340 }
341
342 private void updateTunnel(Tunnel tunnel) {
343 updateTeTunnel(teTunnelService.getTeTunnel(tunnel.tunnelId()),
344 tunnel);
345 }
346 }
347}