blob: 3a081ceaa8a9ffc9f805f8d0188a2947caff6044 [file] [log] [blame]
chengfan9d60b6e2016-12-01 11:06:39 +08001/*
2 * Copyright 2016-present 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 */
16
17package org.onosproject.provider.te.tunnel;
18
19import com.fasterxml.jackson.databind.JsonNode;
20import com.fasterxml.jackson.databind.node.ObjectNode;
21import org.apache.felix.scr.annotations.Activate;
22import org.apache.felix.scr.annotations.Component;
23import org.apache.felix.scr.annotations.Deactivate;
24import org.apache.felix.scr.annotations.Reference;
25import org.apache.felix.scr.annotations.ReferenceCardinality;
26import 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;
36import org.onosproject.protocol.restconf.RestConfNotificationEventListener;
37import org.onosproject.protocol.restconf.RestConfSBController;
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;
92 private static final String MEDIA_TYPE_JSON = "json";
93
94 private static final String SHOULD_IN_ONE = "Tunnel should be setup in one topo";
95 private static final String PROVIDER_ID = "org.onosproject.provider.ietf";
96 private static final String RESTCONF_ROOT = "/onos/restconf";
chengfan6c240ab2016-12-30 17:02:01 +080097 private static final String TE_TUNNEL_KEY = "TeTunnelKey";
chengfan9d60b6e2016-12-01 11:06:39 +080098
99 private final RestConfNotificationEventListener listener =
100 new InternalTunnelNotificationListener();
101
102 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
103 protected RestConfSBController controller;
104
105 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
106 protected YmsService ymsService;
107
108 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
109 protected TeTunnelService tunnelService;
110
111 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
112 protected TeTunnelProviderService providerService;
113
114 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
115 protected TeTopologyService topologyService;
116
117 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
118 protected TunnelProviderRegistry tunnelProviderRegistry;
119
120 private YangCodecHandler codecHandler;
121
122 @Activate
123 public void activate() {
124 tunnelProviderRegistry.register(this);
125 codecHandler = ymsService.getYangCodecHandler();
126 codecHandler.addDeviceSchema(IetfTe.class);
chengfanfb8e9652016-12-02 10:48:24 +0800127 codecHandler.addDeviceSchema(IetfTeTypes.class);
128 codecHandler.registerOverriddenCodec(new DefaultJsonCodec(ymsService),
129 YangProtocolEncodingFormat.JSON);
chengfan9d60b6e2016-12-01 11:06:39 +0800130 collectInitialTunnels();
131 subscribe();
132 log.info("Started");
133 }
134
135 @Deactivate
136 public void deactivate() {
137 tunnelProviderRegistry.unregister(this);
138 unsubscribe();
139 log.info("Stopped");
140
141 }
142
143 public TeTunnelRestconfProvider() {
144 super(new ProviderId(SCHEMA, PROVIDER_ID));
145 }
146
147 private void collectInitialTunnels() {
148 for (DeviceId deviceId : controller.getDevices().keySet()) {
149 ObjectNode jsonNodes = executeGetRequest(deviceId);
150 if (jsonNodes == null) {
151 continue;
152 }
153 ObjectNode tunnelsNode = (ObjectNode) jsonNodes.get(TUNNELS);
154 if (tunnelsNode == null) {
155 continue;
156 }
157 Tunnels teTunnels = getYangTunnelsObject(tunnelsNode);
158 if (teTunnels == null) {
159 continue;
160 }
161 updateTeTunnels(teTunnels);
162 }
163 }
164
165 private void subscribe() {
166 for (DeviceId deviceId : controller.getDevices().keySet()) {
167 try {
168 controller.enableNotifications(deviceId, TUNNELS_URL,
169 MEDIA_TYPE_JSON,
170 listener);
171 } catch (Exception e) {
172 log.error("Failed to subscribe for {} : {}", deviceId,
173 e.getMessage());
174 }
175 }
176 }
177
178 private void unsubscribe() {
179 controller.getDevices()
180 .keySet()
181 .forEach(deviceId -> controller
182 .removeNotificationListener(deviceId));
183 }
184
185 @Override
186 public void setupTunnel(Tunnel tunnel, Path path) {
187 TeTunnel teTunnel = tunnelService.getTeTunnel(tunnel.tunnelId());
188 long tid = teTunnel.srcNode().topologyId();
189 checkState(tid == teTunnel.dstNode().topologyId(), SHOULD_IN_ONE);
190 setupTunnel(getOwnDevice(tid), tunnel, path);
191 }
192
193 @Override
194 public void setupTunnel(ElementId srcElement, Tunnel tunnel, Path path) {
chengfan6c240ab2016-12-30 17:02:01 +0800195 if (!tunnel.annotations().keys().contains(TE_TUNNEL_KEY)) {
196 log.warn("No tunnel key info in tunnel {}", tunnel);
197 return;
198 }
chengfan9d60b6e2016-12-01 11:06:39 +0800199
chengfan6c240ab2016-12-30 17:02:01 +0800200 String teTunnelKey = tunnel.annotations().value(TE_TUNNEL_KEY);
201
202 Optional<TeTunnel> optTunnel = tunnelService.getTeTunnels()
203 .stream()
204 .filter(t -> t.teTunnelKey().toString().equals(teTunnelKey))
205 .findFirst();
206
207 if (!optTunnel.isPresent()) {
208 log.warn("No te tunnel map to tunnel {}", tunnel);
209 return;
210 }
211
212 IetfTe ietfTe = buildIetfTe(optTunnel.get());
chengfan9d60b6e2016-12-01 11:06:39 +0800213
214 YangCompositeEncoding encoding = codecHandler.
215 encodeCompositeOperation(RESTCONF_ROOT, null, ietfTe,
216 JSON, EDIT_CONFIG_REQUEST);
217 String identifier = encoding.getResourceIdentifier();
218 String resourceInformation = encoding.getResourceInformation();
219
220 if (srcElement == null) {
221 log.error("Can't find remote device for tunnel : {}", tunnel);
222 return;
223 }
chengfan9d60b6e2016-12-01 11:06:39 +0800224 controller.post((DeviceId) srcElement, identifier,
225 new ByteArrayInputStream(resourceInformation.getBytes()),
chengfan6c240ab2016-12-30 17:02:01 +0800226 MEDIA_TYPE_JSON, ObjectNode.class);
chengfan9d60b6e2016-12-01 11:06:39 +0800227 }
228
229 @Override
230 public void releaseTunnel(Tunnel tunnel) {
231 //TODO implement release tunnel method
232 }
233
234 @Override
235 public void releaseTunnel(ElementId srcElement, Tunnel tunnel) {
236 //TODO implement release tunnel with src method
237 }
238
239 @Override
240 public void updateTunnel(Tunnel tunnel, Path path) {
241 //TODO implement update tunnel method
242
243 }
244
245 @Override
246 public void updateTunnel(ElementId srcElement, Tunnel tunnel, Path path) {
247 //TODO implement update tunnel with src method
248 }
249
250 @Override
251 public TunnelId tunnelAdded(TunnelDescription tunnel) {
252 //TODO implement tunnel add method when te tunnel app merged to core
253 return null;
254 }
255
256 @Override
257 public void tunnelRemoved(TunnelDescription tunnel) {
258 //TODO implement tunnel remove method when te tunnel app merged to core
259
260 }
261
262 @Override
263 public void tunnelUpdated(TunnelDescription tunnel) {
264 //TODO implement tunnel update method when te tunnel app merged to core
265 }
266
267 @Override
268 public Tunnel tunnelQueryById(TunnelId tunnelId) {
269 return null;
270 }
271
272 private ObjectNode executeGetRequest(DeviceId deviceId) {
273 //the request url is ietf-te:te/tunnels
274 //the response node will begin with tunnels
275 //be careful here to when get the tunnels data
276 InputStream resultStream =
277 controller.get(deviceId, TUNNELS_URL, MEDIA_TYPE_JSON);
278 return toJson(resultStream);
279 }
280
281 private Tunnels getYangTunnelsObject(ObjectNode tunnelsNode) {
282 checkNotNull(tunnelsNode, "Input object node should not be null");
283
284 YangCompositeEncoding yce =
285 new YangCompositeEncodingImpl(URI,
286 TUNNELS_URL,
287 jsonToString(tunnelsNode));
288
289 Object yo = codecHandler.decode(yce, JSON, QUERY_REPLY);
290
291 if (yo == null) {
292 log.error("YMS decoder returns null");
293 return null;
294 }
295 IetfTe ietfTe = null;
296 Tunnels tunnels = null;
297 if (yo instanceof List) {
298 List<Object> list = (List<Object>) yo;
299 ietfTe = (IetfTe) list.get(DEFAULT_INDEX);
300 }
301 if (ietfTe != null && ietfTe.te() != null) {
302 tunnels = ietfTe.te().tunnels();
303 }
304 return tunnels;
305 }
306
307 private void updateTeTunnels(Tunnels tunnels) {
308 TeTopologyKey key = getTopologyKey();
309
310 tunnels.tunnel().forEach(tunnel -> {
311 DefaultTeTunnel teTunnel = yang2TeTunnel(tunnel, key);
312 providerService.updateTeTunnel(teTunnel);
313 });
314 }
315
316 private TeTopologyKey getTopologyKey() {
317 TeTopologyKey key = null;
318 Optional<TeTopology> teTopology = topologyService.teTopologies()
319 .teTopologies()
320 .values()
321 .stream()
322 .filter(topology -> topology.flags().get(BIT_MERGED))
323 .findFirst();
324 if (teTopology.isPresent()) {
325 TeTopology topology = teTopology.get();
326 key = topology.teTopologyId();
327 }
328 return key;
329 }
330
331 private DeviceId getOwnDevice(long topologyId) {
332 DeviceId deviceId = null;
333 Optional<TeTopology> topoOpt = topologyService.teTopologies()
334 .teTopologies()
335 .values()
336 .stream()
337 .filter(tp -> tp.teTopologyId().topologyId() == topologyId)
338 .findFirst();
339
340 if (topoOpt.isPresent()) {
341 deviceId = topoOpt.get().ownerId();
342 }
343 return deviceId;
344 }
345
346 private class InternalTunnelNotificationListener implements
347 RestConfNotificationEventListener {
348
349 @Override
350 public void handleNotificationEvent(DeviceId deviceId, Object eventJsonString) {
351 ObjectNode response = toJson((String) eventJsonString);
352 if (response == null) {
353 return;
354 }
355 JsonNode teNode = response.get(TE);
356 if (teNode == null) {
357 log.error("Illegal te json object from {}", deviceId);
358 return;
359 }
360 JsonNode tunnelsNode = teNode.get(TUNNELS);
361 if (tunnelsNode == null) {
362 log.error("Illegal tunnel json object from {}", deviceId);
363 return;
364 }
365
366 Tunnels tunnels = getYangTunnelsObject((ObjectNode) tunnelsNode);
367 if (tunnels == null) {
368 return;
369 }
370 updateTeTunnels(tunnels);
371 }
372 }
373}