blob: 8fdf49ce5c890a5963df62eee42457ad9fc5358a [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 Yu4b4a7eb2016-11-09 20:07:53 -050056import org.onosproject.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.te.topology.rev20160708.ietftetopology.TeLinkEvent;
57import org.onosproject.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.te.topology.rev20160708.ietftetopology.TeNodeEvent;
Yixiao Chen5ece00f2016-09-14 16:23:24 -040058import org.onosproject.yms.ych.YangCodecHandler;
59import org.onosproject.yms.ych.YangProtocolEncodingFormat;
60import org.onosproject.yms.ych.YangResourceIdentifierType;
61import org.onosproject.yms.ydt.YmsOperationType;
62import org.onosproject.yms.ymsm.YmsService;
63import org.slf4j.Logger;
64
Henry Yu4b4a7eb2016-11-09 20:07:53 -050065import java.io.IOException;
66import java.io.InputStream;
67import java.io.StringWriter;
68import java.nio.charset.StandardCharsets;
69import java.util.HashSet;
70import java.util.List;
71import java.util.Set;
72import java.util.concurrent.ExecutorService;
73import java.util.concurrent.Executors;
74
75import static org.onlab.util.Tools.groupedThreads;
76import static org.onosproject.net.config.NetworkConfigEvent.Type.CONFIG_ADDED;
77import static org.onosproject.net.config.NetworkConfigEvent.Type.CONFIG_UPDATED;
78import static org.onosproject.net.config.basics.SubjectFactories.APP_SUBJECT_FACTORY;
79import static org.slf4j.LoggerFactory.getLogger;
Yixiao Chen5ece00f2016-09-14 16:23:24 -040080
81/**
82 * Provider for IETF TE Topology that use RESTCONF as means of communication.
83 */
84@Component(immediate = true)
85public class TeTopologyRestconfProvider extends AbstractProvider
86 implements TeTopologyProvider {
87 private static final String APP_NAME = "org.onosproject.teprovider.topology";
88 private static final String RESTCONF = "restconf";
Henry Yu4b4a7eb2016-11-09 20:07:53 -050089 private static final String PROVIDER =
90 "org.onosproject.teprovider.restconf.domain";
Yixiao Chen5ece00f2016-09-14 16:23:24 -040091 private static final String IETF_NETWORK_URI = "ietf-network:networks";
Henry Yu4b4a7eb2016-11-09 20:07:53 -050092 private static final String IETF_NETWORKS_PREFIX =
93 "{\"ietf-network:networks\":";
94 private static final String TE_NOTIFICATION_PREFIX =
95 "{\"ietf-te-topology:ietf-te-topology\":";
96 private static final String TE_LINK_EVENT_PREFIX =
97 "{\"ietf-te-topology:te-link-event\":";
98 private static final String TE_NODE_EVENT_PREFIX =
99 "{\"ietf-te-topology:te-node-event\":";
100 private static final String IETF_NOTIFICATION_URI = "netconf";
Yixiao Chen5ece00f2016-09-14 16:23:24 -0400101 private static final String JSON = "json";
102 private static final String E_DEVICE_NULL = "Restconf device is null";
103
104 private final Logger log = getLogger(getClass());
105
106 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
107 protected DeviceProviderRegistry deviceProviderRegistry;
108
109 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
110 protected TeTopologyProviderRegistry topologyProviderRegistry;
111
112 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
113 protected RestConfSBController restconfClient;
114
115 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
116 protected NetworkConfigRegistry cfgService;
117
118 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
119 protected CoreService coreService;
120
121 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
122 protected YmsService ymsService;
123
Henry Yu4b4a7eb2016-11-09 20:07:53 -0500124 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
125 protected TeTopologyService teTopologyService;
126
Yixiao Chen5ece00f2016-09-14 16:23:24 -0400127 private YangCodecHandler codecHandler;
128
129 private TeTopologyProviderService topologyProviderService;
130
131 private final ExecutorService executor =
132 Executors.newFixedThreadPool(5, groupedThreads("onos/restconfsbprovider",
Henry Yu4b4a7eb2016-11-09 20:07:53 -0500133 "device-installer-%d", log));
Yixiao Chen5ece00f2016-09-14 16:23:24 -0400134
135 private final ConfigFactory<ApplicationId, RestconfServerConfig> factory =
136 new ConfigFactory<ApplicationId, RestconfServerConfig>(APP_SUBJECT_FACTORY,
137 RestconfServerConfig.class,
138 "restconfDevices",
139 true) {
140 @Override
141 public RestconfServerConfig createConfig() {
142 return new RestconfServerConfig();
143 }
144 };
145
146 private final NetworkConfigListener cfgLister = new InternalNetworkConfigListener();
147 private ApplicationId appId;
148
149 private Set<DeviceId> addedDevices = new HashSet<>();
150
151 @Activate
152 public void activate() {
153 // Get the codec handler.
154 codecHandler = ymsService.getYangCodecHandler();
155 // Register all three IETF Topology YANG model schema with YMS.
156 codecHandler.addDeviceSchema(IetfNetwork.class);
157 codecHandler.addDeviceSchema(IetfNetworkTopology.class);
158 codecHandler.addDeviceSchema(IetfTeTopology.class);
159 // Register JSON CODEC functions
chengfan9d60b6e2016-12-01 11:06:39 +0800160 codecHandler.registerOverriddenCodec(new DefaultJsonCodec(ymsService),
Shankara-Huaweid5823ab2016-11-22 10:14:52 +0530161 YangProtocolEncodingFormat.JSON);
Yixiao Chen5ece00f2016-09-14 16:23:24 -0400162
163 appId = coreService.registerApplication(APP_NAME);
164 topologyProviderService = topologyProviderRegistry.register(this);
165 cfgService.registerConfigFactory(factory);
166 cfgService.addListener(cfgLister);
167 executor.execute(TeTopologyRestconfProvider.this::connectDevices);
168 log.info("Started");
169 }
170
171 @Deactivate
172 public void deactivate() {
173 cfgService.removeListener(cfgLister);
174 restconfClient.getDevices().keySet().forEach(this::deviceRemoved);
175 topologyProviderRegistry.unregister(this);
176 cfgService.unregisterConfigFactory(factory);
177 log.info("Stopped");
178 }
179
180 /**
181 * Creates an instance of TeTopologyRestconf provider.
182 */
183 public TeTopologyRestconfProvider() {
184 super(new ProviderId(RESTCONF, PROVIDER));
185 }
186
187 private void deviceAdded(RestSBDevice nodeId) {
188 Preconditions.checkNotNull(nodeId, E_DEVICE_NULL);
189 nodeId.setActive(true);
190 addedDevices.add(nodeId.deviceId());
191 }
192
193 private void deviceRemoved(DeviceId deviceId) {
194 Preconditions.checkNotNull(deviceId, E_DEVICE_NULL);
195 restconfClient.removeDevice(deviceId);
196 }
197
198 private void connectDevices() {
199
Henry Yu4b4a7eb2016-11-09 20:07:53 -0500200 RestconfServerConfig cfg = cfgService.getConfig(appId,
201 RestconfServerConfig.class);
Yixiao Chen5ece00f2016-09-14 16:23:24 -0400202 try {
203 if (cfg != null && cfg.getDevicesAddresses() != null) {
204 //Precomputing the devices to be removed
Henry Yu4b4a7eb2016-11-09 20:07:53 -0500205 Set<RestSBDevice> toBeRemoved = new HashSet<>(restconfClient.
206 getDevices().values());
Yixiao Chen5ece00f2016-09-14 16:23:24 -0400207 toBeRemoved.removeAll(cfg.getDevicesAddresses());
208 //Adding new devices
209 for (RestSBDevice device : cfg.getDevicesAddresses()) {
210 device.setActive(false);
211 restconfClient.addDevice(device);
212 deviceAdded(device);
213 }
214
215 //Removing devices not wanted anymore
216 toBeRemoved.forEach(device -> deviceRemoved(device.deviceId()));
217 }
218 } catch (ConfigException e) {
219 log.error("Configuration error {}", e);
220 }
221
222 // Discover the topology from RESTCONF server
223 addedDevices.forEach(this::retrieveTopology);
224 addedDevices.clear();
225 }
226
227 private void retrieveTopology(DeviceId deviceId) {
228 // Retrieve IETF Network at top level.
Henry Yu4b4a7eb2016-11-09 20:07:53 -0500229 InputStream jsonStream = restconfClient.get(deviceId,
230 IETF_NETWORK_URI,
231 JSON);
Yixiao Chen5ece00f2016-09-14 16:23:24 -0400232 if (jsonStream == null) {
Henry Yu4b4a7eb2016-11-09 20:07:53 -0500233 log.warn("Unable to retrieve network Topology from restconf " +
234 "server {}", deviceId);
Yixiao Chen5ece00f2016-09-14 16:23:24 -0400235 return;
236 }
237
238 // Need to convert Input stream to String.
239 StringWriter writer = new StringWriter();
240 try {
241 IOUtils.copy(jsonStream, writer, StandardCharsets.UTF_8);
242 } catch (IOException e) {
Henry Yu4b4a7eb2016-11-09 20:07:53 -0500243 log.warn("There is an exception {} for copy jsonStream to " +
244 "stringWriter for restconf {}",
Yixiao Chen5ece00f2016-09-14 16:23:24 -0400245 e.getMessage(), deviceId);
246 return;
247 }
248 String jsonString = writer.toString();
Henry Yu4b4a7eb2016-11-09 20:07:53 -0500249 String networkLevelJsonString = removePrefixTagFromJson(jsonString,
250 IETF_NETWORKS_PREFIX);
Yixiao Chen5ece00f2016-09-14 16:23:24 -0400251
Henry Yu4b4a7eb2016-11-09 20:07:53 -0500252 YangCompositeEncodingImpl yce =
253 new YangCompositeEncodingImpl(YangResourceIdentifierType.URI,
254 IETF_NETWORK_URI,
255 networkLevelJsonString);
Yixiao Chen5ece00f2016-09-14 16:23:24 -0400256
257 Object yo = codecHandler.decode(yce,
Shankara-Huaweid5823ab2016-11-22 10:14:52 +0530258 YangProtocolEncodingFormat.JSON,
Yixiao Chen5ece00f2016-09-14 16:23:24 -0400259 YmsOperationType.QUERY_REPLY);
260
261 if ((yo == null)) {
262 log.error("YMS decoder returns {} for restconf {}", yo, deviceId);
263 return;
264 }
265
266 // YMS returns an ArrayList in a single Object (i.e. yo in this case)
267 // this means yo is actually an ArrayList of size 1
268 IetfNetwork ietfNetwork = ((List<IetfNetwork>) yo).get(0);
269
270 if (ietfNetwork.networks() != null &&
271 ietfNetwork.networks().network() != null) {
272 //Convert the YO to TE Core data and update TE Core.
273 for (Network nw : ietfNetwork.networks().network()) {
274 topologyProviderService.networkUpdated(
Hesam Rahimi39bdc002016-11-10 15:01:26 -0500275 NetworkConverter.yang2TeSubsystemNetwork(nw, ietfNetwork.networks()));
Yixiao Chen5ece00f2016-09-14 16:23:24 -0400276 }
277 }
278
279 //TODO: Uncomment when YMS fixes the issue in NetworkState translation (network-ref)
280// org.onosproject.tetopology.management.api.Networks networks =
281// NetworkConverter.yang2TeSubsystemNetworks(ietfNetwork.networks(),
282// ietfNetwork.networksState());
283// if (networks == null || networks.networks() == null) {
284// log.error("Yang2Te returns null for restconf {}", deviceId);
285// return;
286// }
287// for (org.onosproject.tetopology.management.api.Network network : networks.networks()) {
288// topologyProviderService.networkUpdated(network);
289// }
290
Henry Yu4b4a7eb2016-11-09 20:07:53 -0500291 RestConfNotificationEventListener<String> callBackListener =
292 new InternalRestconfNotificationEventListener();
293 restconfClient.enableNotifications(deviceId, IETF_NOTIFICATION_URI,
294 "application/json",
295 callBackListener);
Yixiao Chen5ece00f2016-09-14 16:23:24 -0400296 }
297
Henry Yu4b4a7eb2016-11-09 20:07:53 -0500298 private String removePrefixTagFromJson(String jsonString, String prefixTag) {
299 if (jsonString.startsWith(prefixTag)) {
300 return jsonString.substring(prefixTag.length(), jsonString.length() - 1);
Yixiao Chen5ece00f2016-09-14 16:23:24 -0400301 }
Yixiao Chen5ece00f2016-09-14 16:23:24 -0400302 return jsonString;
303 }
304
Yixiao Chen5ece00f2016-09-14 16:23:24 -0400305 private class InternalNetworkConfigListener implements NetworkConfigListener {
306
307 @Override
308 public void event(NetworkConfigEvent event) {
309 executor.execute(TeTopologyRestconfProvider.this::connectDevices);
310 }
311
312 @Override
313 public boolean isRelevant(NetworkConfigEvent event) {
314 return event.configClass().equals(RestconfServerConfig.class) &&
315 (event.type() == CONFIG_ADDED ||
316 event.type() == CONFIG_UPDATED);
317 }
318 }
319
Henry Yu4b4a7eb2016-11-09 20:07:53 -0500320 private class InternalRestconfNotificationEventListener implements
321 RestConfNotificationEventListener<String> {
322 @Override
323 public void handleNotificationEvent(DeviceId deviceId,
324 String eventJsonString) {
325 log.debug("New notification: {} for device: {}",
326 eventJsonString, deviceId.toString());
327
328 String teEventString = removePrefixTagFromJson(eventJsonString,
329 TE_NOTIFICATION_PREFIX);
330 if (teEventString.startsWith(TE_LINK_EVENT_PREFIX)) {
331 String linkString = removePrefixTagFromJson(teEventString,
332 TE_LINK_EVENT_PREFIX);
333 log.debug("link event={}", linkString);
334 handleRestconfLinkNotification(linkString);
335 } else if (teEventString.startsWith(TE_NODE_EVENT_PREFIX)) {
336 String nodeString = removePrefixTagFromJson(teEventString,
337 TE_NODE_EVENT_PREFIX);
338 log.debug("node event={}", nodeString);
339 handleRestconfNodeNotification(nodeString);
340 }
341 }
342
343 private void handleRestconfLinkNotification(String linkString) {
344 YangCompositeEncodingImpl yce =
345 new YangCompositeEncodingImpl(YangResourceIdentifierType.URI,
346 "ietf-te-topology:te-link-event",
347 linkString);
348 Object yo = codecHandler.decode(yce,
349 YangProtocolEncodingFormat.JSON,
350 YmsOperationType.NOTIFICATION);
351
352 if (yo == null) {
353 log.error("YMS decoder error");
354 return;
355 }
356
357 NetworkLinkKey linkKey = LinkConverter.yangLinkEvent2NetworkLinkKey(
358 (TeLinkEvent) yo);
359
360 NetworkLink networkLink = LinkConverter.yangLinkEvent2NetworkLink(
361 (TeLinkEvent) yo,
362 teTopologyService);
363
364 topologyProviderService.linkUpdated(linkKey, networkLink);
365 }
366
367 private void handleRestconfNodeNotification(String nodeString) {
368 YangCompositeEncodingImpl yce =
369 new YangCompositeEncodingImpl(YangResourceIdentifierType.URI,
370 "ietf-te-topology:te-node-event",
371 nodeString);
372 Object yo = codecHandler.decode(yce,
373 YangProtocolEncodingFormat.JSON,
374 YmsOperationType.NOTIFICATION);
375
376 if (yo == null) {
377 log.error("YMS decoder error");
378 return;
379 }
380
381 NetworkNodeKey nodeKey = NodeConverter.yangNodeEvent2NetworkNodeKey(
382 (TeNodeEvent) yo);
383
384 NetworkNode networkNode = NodeConverter.yangNodeEvent2NetworkNode(
385 (TeNodeEvent) yo,
386 teTopologyService);
387
388 topologyProviderService.nodeUpdated(nodeKey, networkNode);
389 }
390 }
Yixiao Chen5ece00f2016-09-14 16:23:24 -0400391}