blob: 3c765aa79ad79bc17aad4fe0cb3cd86238430f3a [file] [log] [blame]
chengfan9d60b6e2016-12-01 11:06:39 +08001/*
Brian O'Connora09fe5b2017-08-03 21:12:30 -07002 * Copyright 2016-present Open Networking Foundation
chengfan9d60b6e2016-12-01 11:06:39 +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 */
16
17package org.onosproject.provider.te.tunnel;
18
19import com.fasterxml.jackson.databind.JsonNode;
20import com.fasterxml.jackson.databind.node.ObjectNode;
Ray Milkeyd84f89b2018-08-17 14:54:17 -070021import org.osgi.service.component.annotations.Activate;
22import org.osgi.service.component.annotations.Component;
23import org.osgi.service.component.annotations.Deactivate;
24import org.osgi.service.component.annotations.Reference;
25import org.osgi.service.component.annotations.ReferenceCardinality;
chengfan9d60b6e2016-12-01 11:06:39 +080026import org.onosproject.incubator.net.tunnel.Tunnel;
27import org.onosproject.incubator.net.tunnel.TunnelDescription;
28import org.onosproject.incubator.net.tunnel.TunnelId;
29import org.onosproject.incubator.net.tunnel.TunnelProvider;
30import org.onosproject.incubator.net.tunnel.TunnelProviderRegistry;
31import org.onosproject.net.DeviceId;
32import org.onosproject.net.ElementId;
33import org.onosproject.net.Path;
34import org.onosproject.net.provider.AbstractProvider;
35import org.onosproject.net.provider.ProviderId;
chengfan9d60b6e2016-12-01 11:06:39 +080036import org.onosproject.protocol.restconf.RestConfSBController;
Henry Yu05dcc212017-01-05 16:05:26 -050037import org.onosproject.protocol.restconf.RestconfNotificationEventListener;
chengfanfb8e9652016-12-02 10:48:24 +080038import org.onosproject.provider.te.utils.DefaultJsonCodec;
chengfan9d60b6e2016-12-01 11:06:39 +080039import org.onosproject.provider.te.utils.YangCompositeEncodingImpl;
40import org.onosproject.tetopology.management.api.TeTopology;
41import org.onosproject.tetopology.management.api.TeTopologyKey;
42import org.onosproject.tetopology.management.api.TeTopologyService;
43import org.onosproject.tetunnel.api.TeTunnelProviderService;
44import org.onosproject.tetunnel.api.TeTunnelService;
45import org.onosproject.tetunnel.api.tunnel.DefaultTeTunnel;
46import org.onosproject.tetunnel.api.tunnel.TeTunnel;
47import org.onosproject.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.te.rev20160705.IetfTe;
48import org.onosproject.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.te.rev20160705.ietfte.tunnelsgrouping.Tunnels;
chengfanfb8e9652016-12-02 10:48:24 +080049import org.onosproject.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.te.types.rev20160705.IetfTeTypes;
chengfan9d60b6e2016-12-01 11:06:39 +080050import org.onosproject.yms.ych.YangCodecHandler;
51import org.onosproject.yms.ych.YangCompositeEncoding;
chengfanfb8e9652016-12-02 10:48:24 +080052import org.onosproject.yms.ych.YangProtocolEncodingFormat;
chengfan9d60b6e2016-12-01 11:06:39 +080053import org.onosproject.yms.ymsm.YmsService;
54import org.slf4j.Logger;
55import org.slf4j.LoggerFactory;
56
chengfan9d60b6e2016-12-01 11:06:39 +080057import java.io.ByteArrayInputStream;
58import java.io.InputStream;
59import java.util.List;
60import java.util.Optional;
61
62import static com.google.common.base.Preconditions.checkNotNull;
63import static com.google.common.base.Preconditions.checkState;
64import static org.onosproject.provider.te.utils.CodecTools.jsonToString;
65import static org.onosproject.provider.te.utils.CodecTools.toJson;
66import static org.onosproject.tetopology.management.api.TeTopology.BIT_MERGED;
67import static org.onosproject.teyang.utils.tunnel.TunnelConverter.buildIetfTe;
68import static org.onosproject.teyang.utils.tunnel.TunnelConverter.yang2TeTunnel;
69import static org.onosproject.yms.ych.YangProtocolEncodingFormat.JSON;
70import static org.onosproject.yms.ych.YangResourceIdentifierType.URI;
71import static org.onosproject.yms.ydt.YmsOperationType.EDIT_CONFIG_REQUEST;
72import static org.onosproject.yms.ydt.YmsOperationType.QUERY_REPLY;
73
74
75/**
76 * Provider which uses RESTCONF to do cross-domain tunnel creation/deletion/
77 * update/deletion and so on operations on the domain networks.
78 */
79
80@Component(immediate = true)
81public class TeTunnelRestconfProvider extends AbstractProvider
82 implements TunnelProvider {
83
84 private final Logger log = LoggerFactory.getLogger(getClass());
85
86 private static final String SCHEMA = "ietf";
87 private static final String IETF = "ietf";
88 private static final String TE = "te";
89 private static final int DEFAULT_INDEX = 1;
90 private static final String TUNNELS = "tunnels";
91 private static final String TUNNELS_URL = IETF + ":" + TE + "/" + TUNNELS;
Henry Yu05dcc212017-01-05 16:05:26 -050092 private static final String IETF_NOTIFICATION_URI = "netconf";
chengfan9d60b6e2016-12-01 11:06:39 +080093 private static final String MEDIA_TYPE_JSON = "json";
94
95 private static final String SHOULD_IN_ONE = "Tunnel should be setup in one topo";
96 private static final String PROVIDER_ID = "org.onosproject.provider.ietf";
97 private static final String RESTCONF_ROOT = "/onos/restconf";
chengfan6c240ab2016-12-30 17:02:01 +080098 private static final String TE_TUNNEL_KEY = "TeTunnelKey";
chengfan9d60b6e2016-12-01 11:06:39 +080099
Henry Yu05dcc212017-01-05 16:05:26 -0500100 //private final RestconfNotificationEventListener listener =
101 // new InternalTunnelNotificationListener();
chengfan9d60b6e2016-12-01 11:06:39 +0800102
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700103 @Reference(cardinality = ReferenceCardinality.MANDATORY)
chengfan9d60b6e2016-12-01 11:06:39 +0800104 protected RestConfSBController controller;
105
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700106 @Reference(cardinality = ReferenceCardinality.MANDATORY)
chengfan9d60b6e2016-12-01 11:06:39 +0800107 protected YmsService ymsService;
108
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700109 @Reference(cardinality = ReferenceCardinality.MANDATORY)
chengfan9d60b6e2016-12-01 11:06:39 +0800110 protected TeTunnelService tunnelService;
111
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700112 @Reference(cardinality = ReferenceCardinality.MANDATORY)
chengfan9d60b6e2016-12-01 11:06:39 +0800113 protected TeTunnelProviderService providerService;
114
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700115 @Reference(cardinality = ReferenceCardinality.MANDATORY)
chengfan9d60b6e2016-12-01 11:06:39 +0800116 protected TeTopologyService topologyService;
117
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700118 @Reference(cardinality = ReferenceCardinality.MANDATORY)
chengfan9d60b6e2016-12-01 11:06:39 +0800119 protected TunnelProviderRegistry tunnelProviderRegistry;
120
121 private YangCodecHandler codecHandler;
122
123 @Activate
124 public void activate() {
125 tunnelProviderRegistry.register(this);
126 codecHandler = ymsService.getYangCodecHandler();
127 codecHandler.addDeviceSchema(IetfTe.class);
chengfanfb8e9652016-12-02 10:48:24 +0800128 codecHandler.addDeviceSchema(IetfTeTypes.class);
129 codecHandler.registerOverriddenCodec(new DefaultJsonCodec(ymsService),
130 YangProtocolEncodingFormat.JSON);
chengfan9d60b6e2016-12-01 11:06:39 +0800131 collectInitialTunnels();
132 subscribe();
133 log.info("Started");
134 }
135
136 @Deactivate
137 public void deactivate() {
138 tunnelProviderRegistry.unregister(this);
139 unsubscribe();
140 log.info("Stopped");
141
142 }
143
144 public TeTunnelRestconfProvider() {
145 super(new ProviderId(SCHEMA, PROVIDER_ID));
146 }
147
148 private void collectInitialTunnels() {
149 for (DeviceId deviceId : controller.getDevices().keySet()) {
150 ObjectNode jsonNodes = executeGetRequest(deviceId);
151 if (jsonNodes == null) {
152 continue;
153 }
154 ObjectNode tunnelsNode = (ObjectNode) jsonNodes.get(TUNNELS);
155 if (tunnelsNode == null) {
156 continue;
157 }
158 Tunnels teTunnels = getYangTunnelsObject(tunnelsNode);
159 if (teTunnels == null) {
160 continue;
161 }
162 updateTeTunnels(teTunnels);
163 }
164 }
165
166 private void subscribe() {
167 for (DeviceId deviceId : controller.getDevices().keySet()) {
168 try {
Henry Yu05dcc212017-01-05 16:05:26 -0500169 if (!controller.isNotificationEnabled(deviceId)) {
170 controller.enableNotifications(deviceId, IETF_NOTIFICATION_URI,
171 "application/json",
172 new InternalTunnelNotificationListener());
173 } else {
174 controller.addNotificationListener(deviceId,
175 new InternalTunnelNotificationListener());
176 }
chengfan9d60b6e2016-12-01 11:06:39 +0800177 } catch (Exception e) {
178 log.error("Failed to subscribe for {} : {}", deviceId,
179 e.getMessage());
180 }
181 }
182 }
183
184 private void unsubscribe() {
185 controller.getDevices()
186 .keySet()
187 .forEach(deviceId -> controller
Henry Yu05dcc212017-01-05 16:05:26 -0500188 .removeNotificationListener(deviceId,
189 new InternalTunnelNotificationListener()));
chengfan9d60b6e2016-12-01 11:06:39 +0800190 }
191
192 @Override
193 public void setupTunnel(Tunnel tunnel, Path path) {
194 TeTunnel teTunnel = tunnelService.getTeTunnel(tunnel.tunnelId());
195 long tid = teTunnel.srcNode().topologyId();
196 checkState(tid == teTunnel.dstNode().topologyId(), SHOULD_IN_ONE);
197 setupTunnel(getOwnDevice(tid), tunnel, path);
198 }
199
200 @Override
201 public void setupTunnel(ElementId srcElement, Tunnel tunnel, Path path) {
chengfan6c240ab2016-12-30 17:02:01 +0800202 if (!tunnel.annotations().keys().contains(TE_TUNNEL_KEY)) {
203 log.warn("No tunnel key info in tunnel {}", tunnel);
204 return;
205 }
chengfan9d60b6e2016-12-01 11:06:39 +0800206
chengfan6c240ab2016-12-30 17:02:01 +0800207 String teTunnelKey = tunnel.annotations().value(TE_TUNNEL_KEY);
208
209 Optional<TeTunnel> optTunnel = tunnelService.getTeTunnels()
210 .stream()
211 .filter(t -> t.teTunnelKey().toString().equals(teTunnelKey))
212 .findFirst();
213
214 if (!optTunnel.isPresent()) {
215 log.warn("No te tunnel map to tunnel {}", tunnel);
216 return;
217 }
218
chaodeyu2a0e59d2017-02-11 21:29:34 +0800219 IetfTe ietfTe = buildIetfTe(optTunnel.get(), true);
chengfan9d60b6e2016-12-01 11:06:39 +0800220
221 YangCompositeEncoding encoding = codecHandler.
222 encodeCompositeOperation(RESTCONF_ROOT, null, ietfTe,
223 JSON, EDIT_CONFIG_REQUEST);
224 String identifier = encoding.getResourceIdentifier();
225 String resourceInformation = encoding.getResourceInformation();
226
227 if (srcElement == null) {
228 log.error("Can't find remote device for tunnel : {}", tunnel);
229 return;
230 }
chengfan9d60b6e2016-12-01 11:06:39 +0800231 controller.post((DeviceId) srcElement, identifier,
232 new ByteArrayInputStream(resourceInformation.getBytes()),
chengfan6c240ab2016-12-30 17:02:01 +0800233 MEDIA_TYPE_JSON, ObjectNode.class);
chengfan9d60b6e2016-12-01 11:06:39 +0800234 }
235
236 @Override
237 public void releaseTunnel(Tunnel tunnel) {
238 //TODO implement release tunnel method
239 }
240
241 @Override
242 public void releaseTunnel(ElementId srcElement, Tunnel tunnel) {
243 //TODO implement release tunnel with src method
244 }
245
246 @Override
247 public void updateTunnel(Tunnel tunnel, Path path) {
248 //TODO implement update tunnel method
249
250 }
251
252 @Override
253 public void updateTunnel(ElementId srcElement, Tunnel tunnel, Path path) {
254 //TODO implement update tunnel with src method
255 }
256
257 @Override
258 public TunnelId tunnelAdded(TunnelDescription tunnel) {
259 //TODO implement tunnel add method when te tunnel app merged to core
260 return null;
261 }
262
263 @Override
264 public void tunnelRemoved(TunnelDescription tunnel) {
265 //TODO implement tunnel remove method when te tunnel app merged to core
266
267 }
268
269 @Override
270 public void tunnelUpdated(TunnelDescription tunnel) {
271 //TODO implement tunnel update method when te tunnel app merged to core
272 }
273
274 @Override
275 public Tunnel tunnelQueryById(TunnelId tunnelId) {
276 return null;
277 }
278
279 private ObjectNode executeGetRequest(DeviceId deviceId) {
280 //the request url is ietf-te:te/tunnels
281 //the response node will begin with tunnels
282 //be careful here to when get the tunnels data
283 InputStream resultStream =
284 controller.get(deviceId, TUNNELS_URL, MEDIA_TYPE_JSON);
285 return toJson(resultStream);
286 }
287
288 private Tunnels getYangTunnelsObject(ObjectNode tunnelsNode) {
289 checkNotNull(tunnelsNode, "Input object node should not be null");
290
291 YangCompositeEncoding yce =
292 new YangCompositeEncodingImpl(URI,
293 TUNNELS_URL,
294 jsonToString(tunnelsNode));
295
296 Object yo = codecHandler.decode(yce, JSON, QUERY_REPLY);
297
298 if (yo == null) {
299 log.error("YMS decoder returns null");
300 return null;
301 }
302 IetfTe ietfTe = null;
303 Tunnels tunnels = null;
304 if (yo instanceof List) {
305 List<Object> list = (List<Object>) yo;
306 ietfTe = (IetfTe) list.get(DEFAULT_INDEX);
307 }
308 if (ietfTe != null && ietfTe.te() != null) {
309 tunnels = ietfTe.te().tunnels();
310 }
311 return tunnels;
312 }
313
314 private void updateTeTunnels(Tunnels tunnels) {
315 TeTopologyKey key = getTopologyKey();
316
317 tunnels.tunnel().forEach(tunnel -> {
318 DefaultTeTunnel teTunnel = yang2TeTunnel(tunnel, key);
319 providerService.updateTeTunnel(teTunnel);
320 });
321 }
322
323 private TeTopologyKey getTopologyKey() {
324 TeTopologyKey key = null;
325 Optional<TeTopology> teTopology = topologyService.teTopologies()
326 .teTopologies()
327 .values()
328 .stream()
329 .filter(topology -> topology.flags().get(BIT_MERGED))
330 .findFirst();
331 if (teTopology.isPresent()) {
332 TeTopology topology = teTopology.get();
333 key = topology.teTopologyId();
334 }
335 return key;
336 }
337
338 private DeviceId getOwnDevice(long topologyId) {
339 DeviceId deviceId = null;
340 Optional<TeTopology> topoOpt = topologyService.teTopologies()
341 .teTopologies()
342 .values()
343 .stream()
344 .filter(tp -> tp.teTopologyId().topologyId() == topologyId)
345 .findFirst();
346
347 if (topoOpt.isPresent()) {
348 deviceId = topoOpt.get().ownerId();
349 }
350 return deviceId;
351 }
352
Henry Yu05dcc212017-01-05 16:05:26 -0500353
chengfan9d60b6e2016-12-01 11:06:39 +0800354 private class InternalTunnelNotificationListener implements
Henry Yu05dcc212017-01-05 16:05:26 -0500355 RestconfNotificationEventListener {
chengfan9d60b6e2016-12-01 11:06:39 +0800356
357 @Override
358 public void handleNotificationEvent(DeviceId deviceId, Object eventJsonString) {
359 ObjectNode response = toJson((String) eventJsonString);
360 if (response == null) {
361 return;
362 }
363 JsonNode teNode = response.get(TE);
364 if (teNode == null) {
365 log.error("Illegal te json object from {}", deviceId);
366 return;
367 }
368 JsonNode tunnelsNode = teNode.get(TUNNELS);
369 if (tunnelsNode == null) {
370 log.error("Illegal tunnel json object from {}", deviceId);
371 return;
372 }
373
374 Tunnels tunnels = getYangTunnelsObject((ObjectNode) tunnelsNode);
375 if (tunnels == null) {
376 return;
377 }
378 updateTeTunnels(tunnels);
379 }
380 }
381}