blob: ff6f2295b633b345c8e1618d23197b150b5a7c2b [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;
38import org.onosproject.provider.te.utils.YangCompositeEncodingImpl;
39import org.onosproject.tetopology.management.api.TeTopology;
40import org.onosproject.tetopology.management.api.TeTopologyKey;
41import org.onosproject.tetopology.management.api.TeTopologyService;
42import org.onosproject.tetunnel.api.TeTunnelProviderService;
43import org.onosproject.tetunnel.api.TeTunnelService;
44import org.onosproject.tetunnel.api.tunnel.DefaultTeTunnel;
45import org.onosproject.tetunnel.api.tunnel.TeTunnel;
46import org.onosproject.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.te.rev20160705.IetfTe;
47import org.onosproject.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.te.rev20160705.ietfte.tunnelsgrouping.Tunnels;
48import org.onosproject.yms.ych.YangCodecHandler;
49import org.onosproject.yms.ych.YangCompositeEncoding;
50import org.onosproject.yms.ymsm.YmsService;
51import org.slf4j.Logger;
52import org.slf4j.LoggerFactory;
53
54import javax.ws.rs.core.MediaType;
55import java.io.ByteArrayInputStream;
56import java.io.InputStream;
57import java.util.List;
58import java.util.Optional;
59
60import static com.google.common.base.Preconditions.checkNotNull;
61import static com.google.common.base.Preconditions.checkState;
62import static org.onosproject.provider.te.utils.CodecTools.jsonToString;
63import static org.onosproject.provider.te.utils.CodecTools.toJson;
64import static org.onosproject.tetopology.management.api.TeTopology.BIT_MERGED;
65import static org.onosproject.teyang.utils.tunnel.TunnelConverter.buildIetfTe;
66import static org.onosproject.teyang.utils.tunnel.TunnelConverter.yang2TeTunnel;
67import static org.onosproject.yms.ych.YangProtocolEncodingFormat.JSON;
68import static org.onosproject.yms.ych.YangResourceIdentifierType.URI;
69import static org.onosproject.yms.ydt.YmsOperationType.EDIT_CONFIG_REQUEST;
70import static org.onosproject.yms.ydt.YmsOperationType.QUERY_REPLY;
71
72
73/**
74 * Provider which uses RESTCONF to do cross-domain tunnel creation/deletion/
75 * update/deletion and so on operations on the domain networks.
76 */
77
78@Component(immediate = true)
79public class TeTunnelRestconfProvider extends AbstractProvider
80 implements TunnelProvider {
81
82 private final Logger log = LoggerFactory.getLogger(getClass());
83
84 private static final String SCHEMA = "ietf";
85 private static final String IETF = "ietf";
86 private static final String TE = "te";
87 private static final int DEFAULT_INDEX = 1;
88 private static final String TUNNELS = "tunnels";
89 private static final String TUNNELS_URL = IETF + ":" + TE + "/" + TUNNELS;
90 private static final String MEDIA_TYPE_JSON = "json";
91
92 private static final String SHOULD_IN_ONE = "Tunnel should be setup in one topo";
93 private static final String PROVIDER_ID = "org.onosproject.provider.ietf";
94 private static final String RESTCONF_ROOT = "/onos/restconf";
95
96 private final RestConfNotificationEventListener listener =
97 new InternalTunnelNotificationListener();
98
99 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
100 protected RestConfSBController controller;
101
102 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
103 protected YmsService ymsService;
104
105 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
106 protected TeTunnelService tunnelService;
107
108 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
109 protected TeTunnelProviderService providerService;
110
111 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
112 protected TeTopologyService topologyService;
113
114 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
115 protected TunnelProviderRegistry tunnelProviderRegistry;
116
117 private YangCodecHandler codecHandler;
118
119 @Activate
120 public void activate() {
121 tunnelProviderRegistry.register(this);
122 codecHandler = ymsService.getYangCodecHandler();
123 codecHandler.addDeviceSchema(IetfTe.class);
124 collectInitialTunnels();
125 subscribe();
126 log.info("Started");
127 }
128
129 @Deactivate
130 public void deactivate() {
131 tunnelProviderRegistry.unregister(this);
132 unsubscribe();
133 log.info("Stopped");
134
135 }
136
137 public TeTunnelRestconfProvider() {
138 super(new ProviderId(SCHEMA, PROVIDER_ID));
139 }
140
141 private void collectInitialTunnels() {
142 for (DeviceId deviceId : controller.getDevices().keySet()) {
143 ObjectNode jsonNodes = executeGetRequest(deviceId);
144 if (jsonNodes == null) {
145 continue;
146 }
147 ObjectNode tunnelsNode = (ObjectNode) jsonNodes.get(TUNNELS);
148 if (tunnelsNode == null) {
149 continue;
150 }
151 Tunnels teTunnels = getYangTunnelsObject(tunnelsNode);
152 if (teTunnels == null) {
153 continue;
154 }
155 updateTeTunnels(teTunnels);
156 }
157 }
158
159 private void subscribe() {
160 for (DeviceId deviceId : controller.getDevices().keySet()) {
161 try {
162 controller.enableNotifications(deviceId, TUNNELS_URL,
163 MEDIA_TYPE_JSON,
164 listener);
165 } catch (Exception e) {
166 log.error("Failed to subscribe for {} : {}", deviceId,
167 e.getMessage());
168 }
169 }
170 }
171
172 private void unsubscribe() {
173 controller.getDevices()
174 .keySet()
175 .forEach(deviceId -> controller
176 .removeNotificationListener(deviceId));
177 }
178
179 @Override
180 public void setupTunnel(Tunnel tunnel, Path path) {
181 TeTunnel teTunnel = tunnelService.getTeTunnel(tunnel.tunnelId());
182 long tid = teTunnel.srcNode().topologyId();
183 checkState(tid == teTunnel.dstNode().topologyId(), SHOULD_IN_ONE);
184 setupTunnel(getOwnDevice(tid), tunnel, path);
185 }
186
187 @Override
188 public void setupTunnel(ElementId srcElement, Tunnel tunnel, Path path) {
189 TeTunnel teTunnel = tunnelService.getTeTunnel(tunnel.tunnelId());
190
191 IetfTe ietfTe = buildIetfTe(teTunnel);
192
193 YangCompositeEncoding encoding = codecHandler.
194 encodeCompositeOperation(RESTCONF_ROOT, null, ietfTe,
195 JSON, EDIT_CONFIG_REQUEST);
196 String identifier = encoding.getResourceIdentifier();
197 String resourceInformation = encoding.getResourceInformation();
198
199 if (srcElement == null) {
200 log.error("Can't find remote device for tunnel : {}", tunnel);
201 return;
202 }
203 log.info("Create tunnel get here");
204
205 controller.post((DeviceId) srcElement, identifier,
206 new ByteArrayInputStream(resourceInformation.getBytes()),
207 MediaType.APPLICATION_JSON, ObjectNode.class);
208 }
209
210 @Override
211 public void releaseTunnel(Tunnel tunnel) {
212 //TODO implement release tunnel method
213 }
214
215 @Override
216 public void releaseTunnel(ElementId srcElement, Tunnel tunnel) {
217 //TODO implement release tunnel with src method
218 }
219
220 @Override
221 public void updateTunnel(Tunnel tunnel, Path path) {
222 //TODO implement update tunnel method
223
224 }
225
226 @Override
227 public void updateTunnel(ElementId srcElement, Tunnel tunnel, Path path) {
228 //TODO implement update tunnel with src method
229 }
230
231 @Override
232 public TunnelId tunnelAdded(TunnelDescription tunnel) {
233 //TODO implement tunnel add method when te tunnel app merged to core
234 return null;
235 }
236
237 @Override
238 public void tunnelRemoved(TunnelDescription tunnel) {
239 //TODO implement tunnel remove method when te tunnel app merged to core
240
241 }
242
243 @Override
244 public void tunnelUpdated(TunnelDescription tunnel) {
245 //TODO implement tunnel update method when te tunnel app merged to core
246 }
247
248 @Override
249 public Tunnel tunnelQueryById(TunnelId tunnelId) {
250 return null;
251 }
252
253 private ObjectNode executeGetRequest(DeviceId deviceId) {
254 //the request url is ietf-te:te/tunnels
255 //the response node will begin with tunnels
256 //be careful here to when get the tunnels data
257 InputStream resultStream =
258 controller.get(deviceId, TUNNELS_URL, MEDIA_TYPE_JSON);
259 return toJson(resultStream);
260 }
261
262 private Tunnels getYangTunnelsObject(ObjectNode tunnelsNode) {
263 checkNotNull(tunnelsNode, "Input object node should not be null");
264
265 YangCompositeEncoding yce =
266 new YangCompositeEncodingImpl(URI,
267 TUNNELS_URL,
268 jsonToString(tunnelsNode));
269
270 Object yo = codecHandler.decode(yce, JSON, QUERY_REPLY);
271
272 if (yo == null) {
273 log.error("YMS decoder returns null");
274 return null;
275 }
276 IetfTe ietfTe = null;
277 Tunnels tunnels = null;
278 if (yo instanceof List) {
279 List<Object> list = (List<Object>) yo;
280 ietfTe = (IetfTe) list.get(DEFAULT_INDEX);
281 }
282 if (ietfTe != null && ietfTe.te() != null) {
283 tunnels = ietfTe.te().tunnels();
284 }
285 return tunnels;
286 }
287
288 private void updateTeTunnels(Tunnels tunnels) {
289 TeTopologyKey key = getTopologyKey();
290
291 tunnels.tunnel().forEach(tunnel -> {
292 DefaultTeTunnel teTunnel = yang2TeTunnel(tunnel, key);
293 providerService.updateTeTunnel(teTunnel);
294 });
295 }
296
297 private TeTopologyKey getTopologyKey() {
298 TeTopologyKey key = null;
299 Optional<TeTopology> teTopology = topologyService.teTopologies()
300 .teTopologies()
301 .values()
302 .stream()
303 .filter(topology -> topology.flags().get(BIT_MERGED))
304 .findFirst();
305 if (teTopology.isPresent()) {
306 TeTopology topology = teTopology.get();
307 key = topology.teTopologyId();
308 }
309 return key;
310 }
311
312 private DeviceId getOwnDevice(long topologyId) {
313 DeviceId deviceId = null;
314 Optional<TeTopology> topoOpt = topologyService.teTopologies()
315 .teTopologies()
316 .values()
317 .stream()
318 .filter(tp -> tp.teTopologyId().topologyId() == topologyId)
319 .findFirst();
320
321 if (topoOpt.isPresent()) {
322 deviceId = topoOpt.get().ownerId();
323 }
324 return deviceId;
325 }
326
327 private class InternalTunnelNotificationListener implements
328 RestConfNotificationEventListener {
329
330 @Override
331 public void handleNotificationEvent(DeviceId deviceId, Object eventJsonString) {
332 ObjectNode response = toJson((String) eventJsonString);
333 if (response == null) {
334 return;
335 }
336 JsonNode teNode = response.get(TE);
337 if (teNode == null) {
338 log.error("Illegal te json object from {}", deviceId);
339 return;
340 }
341 JsonNode tunnelsNode = teNode.get(TUNNELS);
342 if (tunnelsNode == null) {
343 log.error("Illegal tunnel json object from {}", deviceId);
344 return;
345 }
346
347 Tunnels tunnels = getYangTunnelsObject((ObjectNode) tunnelsNode);
348 if (tunnels == null) {
349 return;
350 }
351 updateTeTunnels(tunnels);
352 }
353 }
354}