blob: 17e91a4c2b75e9d0dc5f9ed4fda03eb8cd227116 [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
57import javax.ws.rs.core.MediaType;
58import java.io.ByteArrayInputStream;
59import java.io.InputStream;
60import java.util.List;
61import java.util.Optional;
62
63import static com.google.common.base.Preconditions.checkNotNull;
64import static com.google.common.base.Preconditions.checkState;
65import static org.onosproject.provider.te.utils.CodecTools.jsonToString;
66import static org.onosproject.provider.te.utils.CodecTools.toJson;
67import static org.onosproject.tetopology.management.api.TeTopology.BIT_MERGED;
68import static org.onosproject.teyang.utils.tunnel.TunnelConverter.buildIetfTe;
69import static org.onosproject.teyang.utils.tunnel.TunnelConverter.yang2TeTunnel;
70import static org.onosproject.yms.ych.YangProtocolEncodingFormat.JSON;
71import static org.onosproject.yms.ych.YangResourceIdentifierType.URI;
72import static org.onosproject.yms.ydt.YmsOperationType.EDIT_CONFIG_REQUEST;
73import static org.onosproject.yms.ydt.YmsOperationType.QUERY_REPLY;
74
75
76/**
77 * Provider which uses RESTCONF to do cross-domain tunnel creation/deletion/
78 * update/deletion and so on operations on the domain networks.
79 */
80
81@Component(immediate = true)
82public class TeTunnelRestconfProvider extends AbstractProvider
83 implements TunnelProvider {
84
85 private final Logger log = LoggerFactory.getLogger(getClass());
86
87 private static final String SCHEMA = "ietf";
88 private static final String IETF = "ietf";
89 private static final String TE = "te";
90 private static final int DEFAULT_INDEX = 1;
91 private static final String TUNNELS = "tunnels";
92 private static final String TUNNELS_URL = IETF + ":" + TE + "/" + TUNNELS;
93 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";
98
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) {
195 TeTunnel teTunnel = tunnelService.getTeTunnel(tunnel.tunnelId());
196
197 IetfTe ietfTe = buildIetfTe(teTunnel);
198
199 YangCompositeEncoding encoding = codecHandler.
200 encodeCompositeOperation(RESTCONF_ROOT, null, ietfTe,
201 JSON, EDIT_CONFIG_REQUEST);
202 String identifier = encoding.getResourceIdentifier();
203 String resourceInformation = encoding.getResourceInformation();
204
205 if (srcElement == null) {
206 log.error("Can't find remote device for tunnel : {}", tunnel);
207 return;
208 }
chengfan9d60b6e2016-12-01 11:06:39 +0800209 controller.post((DeviceId) srcElement, identifier,
210 new ByteArrayInputStream(resourceInformation.getBytes()),
211 MediaType.APPLICATION_JSON, ObjectNode.class);
212 }
213
214 @Override
215 public void releaseTunnel(Tunnel tunnel) {
216 //TODO implement release tunnel method
217 }
218
219 @Override
220 public void releaseTunnel(ElementId srcElement, Tunnel tunnel) {
221 //TODO implement release tunnel with src method
222 }
223
224 @Override
225 public void updateTunnel(Tunnel tunnel, Path path) {
226 //TODO implement update tunnel method
227
228 }
229
230 @Override
231 public void updateTunnel(ElementId srcElement, Tunnel tunnel, Path path) {
232 //TODO implement update tunnel with src method
233 }
234
235 @Override
236 public TunnelId tunnelAdded(TunnelDescription tunnel) {
237 //TODO implement tunnel add method when te tunnel app merged to core
238 return null;
239 }
240
241 @Override
242 public void tunnelRemoved(TunnelDescription tunnel) {
243 //TODO implement tunnel remove method when te tunnel app merged to core
244
245 }
246
247 @Override
248 public void tunnelUpdated(TunnelDescription tunnel) {
249 //TODO implement tunnel update method when te tunnel app merged to core
250 }
251
252 @Override
253 public Tunnel tunnelQueryById(TunnelId tunnelId) {
254 return null;
255 }
256
257 private ObjectNode executeGetRequest(DeviceId deviceId) {
258 //the request url is ietf-te:te/tunnels
259 //the response node will begin with tunnels
260 //be careful here to when get the tunnels data
261 InputStream resultStream =
262 controller.get(deviceId, TUNNELS_URL, MEDIA_TYPE_JSON);
263 return toJson(resultStream);
264 }
265
266 private Tunnels getYangTunnelsObject(ObjectNode tunnelsNode) {
267 checkNotNull(tunnelsNode, "Input object node should not be null");
268
269 YangCompositeEncoding yce =
270 new YangCompositeEncodingImpl(URI,
271 TUNNELS_URL,
272 jsonToString(tunnelsNode));
273
274 Object yo = codecHandler.decode(yce, JSON, QUERY_REPLY);
275
276 if (yo == null) {
277 log.error("YMS decoder returns null");
278 return null;
279 }
280 IetfTe ietfTe = null;
281 Tunnels tunnels = null;
282 if (yo instanceof List) {
283 List<Object> list = (List<Object>) yo;
284 ietfTe = (IetfTe) list.get(DEFAULT_INDEX);
285 }
286 if (ietfTe != null && ietfTe.te() != null) {
287 tunnels = ietfTe.te().tunnels();
288 }
289 return tunnels;
290 }
291
292 private void updateTeTunnels(Tunnels tunnels) {
293 TeTopologyKey key = getTopologyKey();
294
295 tunnels.tunnel().forEach(tunnel -> {
296 DefaultTeTunnel teTunnel = yang2TeTunnel(tunnel, key);
297 providerService.updateTeTunnel(teTunnel);
298 });
299 }
300
301 private TeTopologyKey getTopologyKey() {
302 TeTopologyKey key = null;
303 Optional<TeTopology> teTopology = topologyService.teTopologies()
304 .teTopologies()
305 .values()
306 .stream()
307 .filter(topology -> topology.flags().get(BIT_MERGED))
308 .findFirst();
309 if (teTopology.isPresent()) {
310 TeTopology topology = teTopology.get();
311 key = topology.teTopologyId();
312 }
313 return key;
314 }
315
316 private DeviceId getOwnDevice(long topologyId) {
317 DeviceId deviceId = null;
318 Optional<TeTopology> topoOpt = topologyService.teTopologies()
319 .teTopologies()
320 .values()
321 .stream()
322 .filter(tp -> tp.teTopologyId().topologyId() == topologyId)
323 .findFirst();
324
325 if (topoOpt.isPresent()) {
326 deviceId = topoOpt.get().ownerId();
327 }
328 return deviceId;
329 }
330
331 private class InternalTunnelNotificationListener implements
332 RestConfNotificationEventListener {
333
334 @Override
335 public void handleNotificationEvent(DeviceId deviceId, Object eventJsonString) {
336 ObjectNode response = toJson((String) eventJsonString);
337 if (response == null) {
338 return;
339 }
340 JsonNode teNode = response.get(TE);
341 if (teNode == null) {
342 log.error("Illegal te json object from {}", deviceId);
343 return;
344 }
345 JsonNode tunnelsNode = teNode.get(TUNNELS);
346 if (tunnelsNode == null) {
347 log.error("Illegal tunnel json object from {}", deviceId);
348 return;
349 }
350
351 Tunnels tunnels = getYangTunnelsObject((ObjectNode) tunnelsNode);
352 if (tunnels == null) {
353 return;
354 }
355 updateTeTunnels(tunnels);
356 }
357 }
358}