blob: c75f01d10bc4b5be6b69377db7ec9d2bdfca8fbc [file] [log] [blame]
Yixiao Chen5ece00f2016-09-14 16:23:24 -04001/*
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 */
16package org.onosproject.provider.te.topology;
17
Henry Yu4b4a7eb2016-11-09 20:07:53 -050018import com.google.common.base.Preconditions;
Yixiao Chen5ece00f2016-09-14 16:23:24 -040019import org.apache.commons.io.IOUtils;
20import org.apache.felix.scr.annotations.Activate;
21import org.apache.felix.scr.annotations.Component;
22import org.apache.felix.scr.annotations.Deactivate;
23import org.apache.felix.scr.annotations.Reference;
24import org.apache.felix.scr.annotations.ReferenceCardinality;
25import org.onosproject.core.ApplicationId;
26import org.onosproject.core.CoreService;
27import org.onosproject.incubator.net.config.basics.ConfigException;
28import org.onosproject.net.DeviceId;
29import org.onosproject.net.config.ConfigFactory;
30import org.onosproject.net.config.NetworkConfigEvent;
31import org.onosproject.net.config.NetworkConfigListener;
32import org.onosproject.net.config.NetworkConfigRegistry;
33import org.onosproject.net.device.DeviceProviderRegistry;
34import org.onosproject.net.provider.AbstractProvider;
35import org.onosproject.net.provider.ProviderId;
36import org.onosproject.protocol.rest.RestSBDevice;
Henry Yu4b4a7eb2016-11-09 20:07:53 -050037import org.onosproject.protocol.restconf.RestConfNotificationEventListener;
Yixiao Chen5ece00f2016-09-14 16:23:24 -040038import org.onosproject.protocol.restconf.RestConfSBController;
chengfan9d60b6e2016-12-01 11:06:39 +080039import org.onosproject.provider.te.utils.DefaultJsonCodec;
40import org.onosproject.provider.te.utils.YangCompositeEncodingImpl;
Yixiao Chen5ece00f2016-09-14 16:23:24 -040041import org.onosproject.tetopology.management.api.TeTopologyProvider;
42import org.onosproject.tetopology.management.api.TeTopologyProviderRegistry;
43import org.onosproject.tetopology.management.api.TeTopologyProviderService;
Henry Yu4b4a7eb2016-11-09 20:07:53 -050044import org.onosproject.tetopology.management.api.TeTopologyService;
45import org.onosproject.tetopology.management.api.link.NetworkLink;
46import org.onosproject.tetopology.management.api.link.NetworkLinkKey;
47import org.onosproject.tetopology.management.api.node.NetworkNode;
48import org.onosproject.tetopology.management.api.node.NetworkNodeKey;
49import org.onosproject.teyang.utils.topology.LinkConverter;
Yixiao Chen5ece00f2016-09-14 16:23:24 -040050import org.onosproject.teyang.utils.topology.NetworkConverter;
Henry Yu4b4a7eb2016-11-09 20:07:53 -050051import org.onosproject.teyang.utils.topology.NodeConverter;
Yixiao Chen5ece00f2016-09-14 16:23:24 -040052import org.onosproject.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.network.rev20151208.IetfNetwork;
53import org.onosproject.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.network.rev20151208.ietfnetwork.networks.Network;
54import org.onosproject.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.network.topology.rev20151208.IetfNetworkTopology;
55import org.onosproject.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.te.topology.rev20160708.IetfTeTopology;
Henry Yu8ac364b2016-12-15 18:24:20 -050056import org.onosproject.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.te.topology.rev20160708.ietftetopology
57 .IetfTeTopologyEvent;
Henry Yu4b4a7eb2016-11-09 20:07:53 -050058import org.onosproject.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.te.topology.rev20160708.ietftetopology.TeLinkEvent;
59import org.onosproject.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.te.topology.rev20160708.ietftetopology.TeNodeEvent;
Yixiao Chen5ece00f2016-09-14 16:23:24 -040060import org.onosproject.yms.ych.YangCodecHandler;
61import org.onosproject.yms.ych.YangProtocolEncodingFormat;
62import org.onosproject.yms.ych.YangResourceIdentifierType;
63import org.onosproject.yms.ydt.YmsOperationType;
64import org.onosproject.yms.ymsm.YmsService;
65import org.slf4j.Logger;
66
Henry Yu4b4a7eb2016-11-09 20:07:53 -050067import java.io.IOException;
68import java.io.InputStream;
69import java.io.StringWriter;
70import java.nio.charset.StandardCharsets;
71import java.util.HashSet;
72import java.util.List;
73import java.util.Set;
74import java.util.concurrent.ExecutorService;
75import java.util.concurrent.Executors;
76
77import static org.onlab.util.Tools.groupedThreads;
78import static org.onosproject.net.config.NetworkConfigEvent.Type.CONFIG_ADDED;
79import static org.onosproject.net.config.NetworkConfigEvent.Type.CONFIG_UPDATED;
80import static org.onosproject.net.config.basics.SubjectFactories.APP_SUBJECT_FACTORY;
81import static org.slf4j.LoggerFactory.getLogger;
Yixiao Chen5ece00f2016-09-14 16:23:24 -040082
83/**
84 * Provider for IETF TE Topology that use RESTCONF as means of communication.
85 */
86@Component(immediate = true)
87public class TeTopologyRestconfProvider extends AbstractProvider
88 implements TeTopologyProvider {
Henry Yu8ac364b2016-12-15 18:24:20 -050089 private static final String APP_NAME = "org.onosproject.teprovider";
Yixiao Chen5ece00f2016-09-14 16:23:24 -040090 private static final String RESTCONF = "restconf";
Henry Yu4b4a7eb2016-11-09 20:07:53 -050091 private static final String PROVIDER =
92 "org.onosproject.teprovider.restconf.domain";
Yixiao Chen5ece00f2016-09-14 16:23:24 -040093 private static final String IETF_NETWORK_URI = "ietf-network:networks";
Henry Yu4b4a7eb2016-11-09 20:07:53 -050094 private static final String IETF_NETWORKS_PREFIX =
95 "{\"ietf-network:networks\":";
96 private static final String TE_NOTIFICATION_PREFIX =
97 "{\"ietf-te-topology:ietf-te-topology\":";
98 private static final String TE_LINK_EVENT_PREFIX =
99 "{\"ietf-te-topology:te-link-event\":";
100 private static final String TE_NODE_EVENT_PREFIX =
101 "{\"ietf-te-topology:te-node-event\":";
102 private static final String IETF_NOTIFICATION_URI = "netconf";
Yixiao Chen5ece00f2016-09-14 16:23:24 -0400103 private static final String JSON = "json";
104 private static final String E_DEVICE_NULL = "Restconf device is null";
105
106 private final Logger log = getLogger(getClass());
107
108 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
109 protected DeviceProviderRegistry deviceProviderRegistry;
110
111 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
112 protected TeTopologyProviderRegistry topologyProviderRegistry;
113
114 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
115 protected RestConfSBController restconfClient;
116
117 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
118 protected NetworkConfigRegistry cfgService;
119
120 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
121 protected CoreService coreService;
122
123 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
124 protected YmsService ymsService;
125
Henry Yu4b4a7eb2016-11-09 20:07:53 -0500126 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
127 protected TeTopologyService teTopologyService;
128
Yixiao Chen5ece00f2016-09-14 16:23:24 -0400129 private YangCodecHandler codecHandler;
130
131 private TeTopologyProviderService topologyProviderService;
132
133 private final ExecutorService executor =
134 Executors.newFixedThreadPool(5, groupedThreads("onos/restconfsbprovider",
Henry Yu4b4a7eb2016-11-09 20:07:53 -0500135 "device-installer-%d", log));
Yixiao Chen5ece00f2016-09-14 16:23:24 -0400136
137 private final ConfigFactory<ApplicationId, RestconfServerConfig> factory =
138 new ConfigFactory<ApplicationId, RestconfServerConfig>(APP_SUBJECT_FACTORY,
139 RestconfServerConfig.class,
140 "restconfDevices",
141 true) {
142 @Override
143 public RestconfServerConfig createConfig() {
144 return new RestconfServerConfig();
145 }
146 };
147
148 private final NetworkConfigListener cfgLister = new InternalNetworkConfigListener();
149 private ApplicationId appId;
150
151 private Set<DeviceId> addedDevices = new HashSet<>();
152
153 @Activate
154 public void activate() {
155 // Get the codec handler.
156 codecHandler = ymsService.getYangCodecHandler();
157 // Register all three IETF Topology YANG model schema with YMS.
158 codecHandler.addDeviceSchema(IetfNetwork.class);
159 codecHandler.addDeviceSchema(IetfNetworkTopology.class);
160 codecHandler.addDeviceSchema(IetfTeTopology.class);
161 // Register JSON CODEC functions
chengfan9d60b6e2016-12-01 11:06:39 +0800162 codecHandler.registerOverriddenCodec(new DefaultJsonCodec(ymsService),
Shankara-Huaweid5823ab2016-11-22 10:14:52 +0530163 YangProtocolEncodingFormat.JSON);
Yixiao Chen5ece00f2016-09-14 16:23:24 -0400164
165 appId = coreService.registerApplication(APP_NAME);
166 topologyProviderService = topologyProviderRegistry.register(this);
167 cfgService.registerConfigFactory(factory);
168 cfgService.addListener(cfgLister);
169 executor.execute(TeTopologyRestconfProvider.this::connectDevices);
170 log.info("Started");
171 }
172
173 @Deactivate
174 public void deactivate() {
175 cfgService.removeListener(cfgLister);
176 restconfClient.getDevices().keySet().forEach(this::deviceRemoved);
177 topologyProviderRegistry.unregister(this);
178 cfgService.unregisterConfigFactory(factory);
179 log.info("Stopped");
180 }
181
182 /**
183 * Creates an instance of TeTopologyRestconf provider.
184 */
185 public TeTopologyRestconfProvider() {
186 super(new ProviderId(RESTCONF, PROVIDER));
187 }
188
189 private void deviceAdded(RestSBDevice nodeId) {
190 Preconditions.checkNotNull(nodeId, E_DEVICE_NULL);
191 nodeId.setActive(true);
192 addedDevices.add(nodeId.deviceId());
193 }
194
195 private void deviceRemoved(DeviceId deviceId) {
196 Preconditions.checkNotNull(deviceId, E_DEVICE_NULL);
197 restconfClient.removeDevice(deviceId);
198 }
199
200 private void connectDevices() {
201
Henry Yu4b4a7eb2016-11-09 20:07:53 -0500202 RestconfServerConfig cfg = cfgService.getConfig(appId,
203 RestconfServerConfig.class);
Yixiao Chen5ece00f2016-09-14 16:23:24 -0400204 try {
205 if (cfg != null && cfg.getDevicesAddresses() != null) {
206 //Precomputing the devices to be removed
Henry Yu4b4a7eb2016-11-09 20:07:53 -0500207 Set<RestSBDevice> toBeRemoved = new HashSet<>(restconfClient.
208 getDevices().values());
Yixiao Chen5ece00f2016-09-14 16:23:24 -0400209 toBeRemoved.removeAll(cfg.getDevicesAddresses());
210 //Adding new devices
211 for (RestSBDevice device : cfg.getDevicesAddresses()) {
212 device.setActive(false);
213 restconfClient.addDevice(device);
214 deviceAdded(device);
215 }
216
217 //Removing devices not wanted anymore
218 toBeRemoved.forEach(device -> deviceRemoved(device.deviceId()));
219 }
220 } catch (ConfigException e) {
221 log.error("Configuration error {}", e);
222 }
223
224 // Discover the topology from RESTCONF server
225 addedDevices.forEach(this::retrieveTopology);
226 addedDevices.clear();
227 }
228
229 private void retrieveTopology(DeviceId deviceId) {
230 // Retrieve IETF Network at top level.
Henry Yu4b4a7eb2016-11-09 20:07:53 -0500231 InputStream jsonStream = restconfClient.get(deviceId,
232 IETF_NETWORK_URI,
233 JSON);
Yixiao Chen5ece00f2016-09-14 16:23:24 -0400234 if (jsonStream == null) {
Henry Yu4b4a7eb2016-11-09 20:07:53 -0500235 log.warn("Unable to retrieve network Topology from restconf " +
236 "server {}", deviceId);
Yixiao Chen5ece00f2016-09-14 16:23:24 -0400237 return;
238 }
239
240 // Need to convert Input stream to String.
241 StringWriter writer = new StringWriter();
242 try {
243 IOUtils.copy(jsonStream, writer, StandardCharsets.UTF_8);
244 } catch (IOException e) {
Henry Yu4b4a7eb2016-11-09 20:07:53 -0500245 log.warn("There is an exception {} for copy jsonStream to " +
246 "stringWriter for restconf {}",
Yixiao Chen5ece00f2016-09-14 16:23:24 -0400247 e.getMessage(), deviceId);
248 return;
249 }
250 String jsonString = writer.toString();
Henry Yu4b4a7eb2016-11-09 20:07:53 -0500251 String networkLevelJsonString = removePrefixTagFromJson(jsonString,
252 IETF_NETWORKS_PREFIX);
Yixiao Chen5ece00f2016-09-14 16:23:24 -0400253
Henry Yu4b4a7eb2016-11-09 20:07:53 -0500254 YangCompositeEncodingImpl yce =
255 new YangCompositeEncodingImpl(YangResourceIdentifierType.URI,
256 IETF_NETWORK_URI,
257 networkLevelJsonString);
Yixiao Chen5ece00f2016-09-14 16:23:24 -0400258
259 Object yo = codecHandler.decode(yce,
Shankara-Huaweid5823ab2016-11-22 10:14:52 +0530260 YangProtocolEncodingFormat.JSON,
Yixiao Chen5ece00f2016-09-14 16:23:24 -0400261 YmsOperationType.QUERY_REPLY);
262
263 if ((yo == null)) {
264 log.error("YMS decoder returns {} for restconf {}", yo, deviceId);
265 return;
266 }
267
268 // YMS returns an ArrayList in a single Object (i.e. yo in this case)
269 // this means yo is actually an ArrayList of size 1
270 IetfNetwork ietfNetwork = ((List<IetfNetwork>) yo).get(0);
271
272 if (ietfNetwork.networks() != null &&
273 ietfNetwork.networks().network() != null) {
274 //Convert the YO to TE Core data and update TE Core.
275 for (Network nw : ietfNetwork.networks().network()) {
276 topologyProviderService.networkUpdated(
Hesam Rahimi39bdc002016-11-10 15:01:26 -0500277 NetworkConverter.yang2TeSubsystemNetwork(nw, ietfNetwork.networks()));
Yixiao Chen5ece00f2016-09-14 16:23:24 -0400278 }
279 }
280
281 //TODO: Uncomment when YMS fixes the issue in NetworkState translation (network-ref)
282// org.onosproject.tetopology.management.api.Networks networks =
283// NetworkConverter.yang2TeSubsystemNetworks(ietfNetwork.networks(),
284// ietfNetwork.networksState());
285// if (networks == null || networks.networks() == null) {
286// log.error("Yang2Te returns null for restconf {}", deviceId);
287// return;
288// }
289// for (org.onosproject.tetopology.management.api.Network network : networks.networks()) {
290// topologyProviderService.networkUpdated(network);
291// }
292
Henry Yu4b4a7eb2016-11-09 20:07:53 -0500293 RestConfNotificationEventListener<String> callBackListener =
294 new InternalRestconfNotificationEventListener();
295 restconfClient.enableNotifications(deviceId, IETF_NOTIFICATION_URI,
296 "application/json",
297 callBackListener);
Yixiao Chen5ece00f2016-09-14 16:23:24 -0400298 }
299
Henry Yu4b4a7eb2016-11-09 20:07:53 -0500300 private String removePrefixTagFromJson(String jsonString, String prefixTag) {
301 if (jsonString.startsWith(prefixTag)) {
302 return jsonString.substring(prefixTag.length(), jsonString.length() - 1);
Yixiao Chen5ece00f2016-09-14 16:23:24 -0400303 }
Yixiao Chen5ece00f2016-09-14 16:23:24 -0400304 return jsonString;
305 }
306
Yixiao Chen5ece00f2016-09-14 16:23:24 -0400307 private class InternalNetworkConfigListener implements NetworkConfigListener {
308
309 @Override
310 public void event(NetworkConfigEvent event) {
311 executor.execute(TeTopologyRestconfProvider.this::connectDevices);
312 }
313
314 @Override
315 public boolean isRelevant(NetworkConfigEvent event) {
316 return event.configClass().equals(RestconfServerConfig.class) &&
317 (event.type() == CONFIG_ADDED ||
318 event.type() == CONFIG_UPDATED);
319 }
320 }
321
Henry Yu4b4a7eb2016-11-09 20:07:53 -0500322 private class InternalRestconfNotificationEventListener implements
323 RestConfNotificationEventListener<String> {
324 @Override
325 public void handleNotificationEvent(DeviceId deviceId,
326 String eventJsonString) {
327 log.debug("New notification: {} for device: {}",
328 eventJsonString, deviceId.toString());
329
330 String teEventString = removePrefixTagFromJson(eventJsonString,
331 TE_NOTIFICATION_PREFIX);
332 if (teEventString.startsWith(TE_LINK_EVENT_PREFIX)) {
333 String linkString = removePrefixTagFromJson(teEventString,
334 TE_LINK_EVENT_PREFIX);
335 log.debug("link event={}", linkString);
336 handleRestconfLinkNotification(linkString);
337 } else if (teEventString.startsWith(TE_NODE_EVENT_PREFIX)) {
338 String nodeString = removePrefixTagFromJson(teEventString,
339 TE_NODE_EVENT_PREFIX);
340 log.debug("node event={}", nodeString);
341 handleRestconfNodeNotification(nodeString);
342 }
343 }
344
345 private void handleRestconfLinkNotification(String linkString) {
Henry Yu4b4a7eb2016-11-09 20:07:53 -0500346
Henry Yu8ac364b2016-12-15 18:24:20 -0500347 IetfTeTopologyEvent event = convertJson2IetfTeTopologyEvent(
348 "ietf-te-topology:te-link-event",
349 linkString);
350 if (event == null) {
351 log.error("ERROR: json to YO conversion failure");
Henry Yu4b4a7eb2016-11-09 20:07:53 -0500352 return;
353 }
354
Henry Yu8ac364b2016-12-15 18:24:20 -0500355 if (event.type() != IetfTeTopologyEvent.Type.TE_LINK_EVENT) {
356 log.error("ERROR: wrong YO event type: {}", event.type());
357 return;
358 }
359
360 TeLinkEvent teLinkEvent = event.subject().teLinkEvent();
361
362 log.debug("TeLinkEvent: {}", teLinkEvent);
363
Henry Yu4b4a7eb2016-11-09 20:07:53 -0500364 NetworkLinkKey linkKey = LinkConverter.yangLinkEvent2NetworkLinkKey(
Henry Yu8ac364b2016-12-15 18:24:20 -0500365 teLinkEvent);
Henry Yu4b4a7eb2016-11-09 20:07:53 -0500366
367 NetworkLink networkLink = LinkConverter.yangLinkEvent2NetworkLink(
Henry Yu8ac364b2016-12-15 18:24:20 -0500368 teLinkEvent,
Henry Yu4b4a7eb2016-11-09 20:07:53 -0500369 teTopologyService);
370
Henry Yu8ac364b2016-12-15 18:24:20 -0500371 if (networkLink == null) {
372 log.error("ERROR: yangLinkEvent2NetworkLink returns null");
373 return;
374 }
375
376 log.debug("networkLink: {}", networkLink);
377
Henry Yu4b4a7eb2016-11-09 20:07:53 -0500378 topologyProviderService.linkUpdated(linkKey, networkLink);
379 }
380
Henry Yu8ac364b2016-12-15 18:24:20 -0500381 private IetfTeTopologyEvent convertJson2IetfTeTopologyEvent(String uriString,
382 String jsonBody) {
383
Henry Yu4b4a7eb2016-11-09 20:07:53 -0500384 YangCompositeEncodingImpl yce =
385 new YangCompositeEncodingImpl(YangResourceIdentifierType.URI,
Henry Yu8ac364b2016-12-15 18:24:20 -0500386 uriString,
387 jsonBody);
Henry Yu4b4a7eb2016-11-09 20:07:53 -0500388 Object yo = codecHandler.decode(yce,
389 YangProtocolEncodingFormat.JSON,
390 YmsOperationType.NOTIFICATION);
391
392 if (yo == null) {
393 log.error("YMS decoder error");
Henry Yu8ac364b2016-12-15 18:24:20 -0500394 return null;
395 }
396
397 if (!(yo instanceof IetfTeTopologyEvent)) {
398 log.error("ERROR: YO is not IetfTeTopologyEvent");
399 return null;
400 }
401
402 return (IetfTeTopologyEvent) yo;
403 }
404
405 private void handleRestconfNodeNotification(String nodeString) {
406
407 IetfTeTopologyEvent event = convertJson2IetfTeTopologyEvent(
408 "ietf-te-topology:te-node-event",
409 nodeString);
410
411 if (event == null) {
412 log.error("ERROR: json to YO conversion failure");
Henry Yu4b4a7eb2016-11-09 20:07:53 -0500413 return;
414 }
415
Henry Yu8ac364b2016-12-15 18:24:20 -0500416 if (event.type() != IetfTeTopologyEvent.Type.TE_NODE_EVENT) {
417 log.error("ERROR: wrong YO event type: {}", event.type());
418 return;
419 }
420
421 TeNodeEvent teNodeEvent = event.subject().teNodeEvent();
422
423 log.debug("TeNodeEvent: {}", teNodeEvent);
424
Henry Yu4b4a7eb2016-11-09 20:07:53 -0500425 NetworkNodeKey nodeKey = NodeConverter.yangNodeEvent2NetworkNodeKey(
Henry Yu8ac364b2016-12-15 18:24:20 -0500426 teNodeEvent);
Henry Yu4b4a7eb2016-11-09 20:07:53 -0500427
428 NetworkNode networkNode = NodeConverter.yangNodeEvent2NetworkNode(
Henry Yu8ac364b2016-12-15 18:24:20 -0500429 teNodeEvent,
Henry Yu4b4a7eb2016-11-09 20:07:53 -0500430 teTopologyService);
431
Henry Yu8ac364b2016-12-15 18:24:20 -0500432 if (networkNode == null) {
433 log.error("ERROR: yangNodeEvent2NetworkNode returns null");
434 return;
435 }
436
Henry Yu4b4a7eb2016-11-09 20:07:53 -0500437 topologyProviderService.nodeUpdated(nodeKey, networkNode);
438 }
439 }
Yixiao Chen5ece00f2016-09-14 16:23:24 -0400440}