blob: 442b65f07c4165a85544ea372d1918fef19120e8 [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;
39import org.onosproject.tetopology.management.api.TeTopologyProvider;
40import org.onosproject.tetopology.management.api.TeTopologyProviderRegistry;
41import org.onosproject.tetopology.management.api.TeTopologyProviderService;
Henry Yu4b4a7eb2016-11-09 20:07:53 -050042import org.onosproject.tetopology.management.api.TeTopologyService;
43import org.onosproject.tetopology.management.api.link.NetworkLink;
44import org.onosproject.tetopology.management.api.link.NetworkLinkKey;
45import org.onosproject.tetopology.management.api.node.NetworkNode;
46import org.onosproject.tetopology.management.api.node.NetworkNodeKey;
47import org.onosproject.teyang.utils.topology.LinkConverter;
Yixiao Chen5ece00f2016-09-14 16:23:24 -040048import org.onosproject.teyang.utils.topology.NetworkConverter;
Henry Yu4b4a7eb2016-11-09 20:07:53 -050049import org.onosproject.teyang.utils.topology.NodeConverter;
Yixiao Chen5ece00f2016-09-14 16:23:24 -040050import org.onosproject.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.network.rev20151208.IetfNetwork;
51import org.onosproject.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.network.rev20151208.ietfnetwork.networks.Network;
52import org.onosproject.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.network.topology.rev20151208.IetfNetworkTopology;
53import org.onosproject.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.te.topology.rev20160708.IetfTeTopology;
Henry Yu4b4a7eb2016-11-09 20:07:53 -050054import org.onosproject.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.te.topology.rev20160708.ietftetopology.TeLinkEvent;
55import org.onosproject.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.te.topology.rev20160708.ietftetopology.TeNodeEvent;
Yixiao Chen5ece00f2016-09-14 16:23:24 -040056import org.onosproject.yms.ych.YangCodecHandler;
57import org.onosproject.yms.ych.YangProtocolEncodingFormat;
58import org.onosproject.yms.ych.YangResourceIdentifierType;
59import org.onosproject.yms.ydt.YmsOperationType;
60import org.onosproject.yms.ymsm.YmsService;
61import org.slf4j.Logger;
62
Henry Yu4b4a7eb2016-11-09 20:07:53 -050063import java.io.IOException;
64import java.io.InputStream;
65import java.io.StringWriter;
66import java.nio.charset.StandardCharsets;
67import java.util.HashSet;
68import java.util.List;
69import java.util.Set;
70import java.util.concurrent.ExecutorService;
71import java.util.concurrent.Executors;
72
73import static org.onlab.util.Tools.groupedThreads;
74import static org.onosproject.net.config.NetworkConfigEvent.Type.CONFIG_ADDED;
75import static org.onosproject.net.config.NetworkConfigEvent.Type.CONFIG_UPDATED;
76import static org.onosproject.net.config.basics.SubjectFactories.APP_SUBJECT_FACTORY;
77import static org.slf4j.LoggerFactory.getLogger;
Yixiao Chen5ece00f2016-09-14 16:23:24 -040078
79/**
80 * Provider for IETF TE Topology that use RESTCONF as means of communication.
81 */
82@Component(immediate = true)
83public class TeTopologyRestconfProvider extends AbstractProvider
84 implements TeTopologyProvider {
85 private static final String APP_NAME = "org.onosproject.teprovider.topology";
86 private static final String RESTCONF = "restconf";
Henry Yu4b4a7eb2016-11-09 20:07:53 -050087 private static final String PROVIDER =
88 "org.onosproject.teprovider.restconf.domain";
Yixiao Chen5ece00f2016-09-14 16:23:24 -040089 private static final String IETF_NETWORK_URI = "ietf-network:networks";
Henry Yu4b4a7eb2016-11-09 20:07:53 -050090 private static final String IETF_NETWORKS_PREFIX =
91 "{\"ietf-network:networks\":";
92 private static final String TE_NOTIFICATION_PREFIX =
93 "{\"ietf-te-topology:ietf-te-topology\":";
94 private static final String TE_LINK_EVENT_PREFIX =
95 "{\"ietf-te-topology:te-link-event\":";
96 private static final String TE_NODE_EVENT_PREFIX =
97 "{\"ietf-te-topology:te-node-event\":";
98 private static final String IETF_NOTIFICATION_URI = "netconf";
Yixiao Chen5ece00f2016-09-14 16:23:24 -040099 private static final String JSON = "json";
100 private static final String E_DEVICE_NULL = "Restconf device is null";
101
102 private final Logger log = getLogger(getClass());
103
104 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
105 protected DeviceProviderRegistry deviceProviderRegistry;
106
107 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
108 protected TeTopologyProviderRegistry topologyProviderRegistry;
109
110 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
111 protected RestConfSBController restconfClient;
112
113 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
114 protected NetworkConfigRegistry cfgService;
115
116 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
117 protected CoreService coreService;
118
119 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
120 protected YmsService ymsService;
121
Henry Yu4b4a7eb2016-11-09 20:07:53 -0500122 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
123 protected TeTopologyService teTopologyService;
124
Yixiao Chen5ece00f2016-09-14 16:23:24 -0400125 private YangCodecHandler codecHandler;
126
127 private TeTopologyProviderService topologyProviderService;
128
129 private final ExecutorService executor =
130 Executors.newFixedThreadPool(5, groupedThreads("onos/restconfsbprovider",
Henry Yu4b4a7eb2016-11-09 20:07:53 -0500131 "device-installer-%d", log));
Yixiao Chen5ece00f2016-09-14 16:23:24 -0400132
133 private final ConfigFactory<ApplicationId, RestconfServerConfig> factory =
134 new ConfigFactory<ApplicationId, RestconfServerConfig>(APP_SUBJECT_FACTORY,
135 RestconfServerConfig.class,
136 "restconfDevices",
137 true) {
138 @Override
139 public RestconfServerConfig createConfig() {
140 return new RestconfServerConfig();
141 }
142 };
143
144 private final NetworkConfigListener cfgLister = new InternalNetworkConfigListener();
145 private ApplicationId appId;
146
147 private Set<DeviceId> addedDevices = new HashSet<>();
148
149 @Activate
150 public void activate() {
151 // Get the codec handler.
152 codecHandler = ymsService.getYangCodecHandler();
153 // Register all three IETF Topology YANG model schema with YMS.
154 codecHandler.addDeviceSchema(IetfNetwork.class);
155 codecHandler.addDeviceSchema(IetfNetworkTopology.class);
156 codecHandler.addDeviceSchema(IetfTeTopology.class);
157 // Register JSON CODEC functions
158 codecHandler.registerOverriddenCodec(new JsonYdtCodec(ymsService),
Shankara-Huaweid5823ab2016-11-22 10:14:52 +0530159 YangProtocolEncodingFormat.JSON);
Yixiao Chen5ece00f2016-09-14 16:23:24 -0400160
161 appId = coreService.registerApplication(APP_NAME);
162 topologyProviderService = topologyProviderRegistry.register(this);
163 cfgService.registerConfigFactory(factory);
164 cfgService.addListener(cfgLister);
165 executor.execute(TeTopologyRestconfProvider.this::connectDevices);
166 log.info("Started");
167 }
168
169 @Deactivate
170 public void deactivate() {
171 cfgService.removeListener(cfgLister);
172 restconfClient.getDevices().keySet().forEach(this::deviceRemoved);
173 topologyProviderRegistry.unregister(this);
174 cfgService.unregisterConfigFactory(factory);
175 log.info("Stopped");
176 }
177
178 /**
179 * Creates an instance of TeTopologyRestconf provider.
180 */
181 public TeTopologyRestconfProvider() {
182 super(new ProviderId(RESTCONF, PROVIDER));
183 }
184
185 private void deviceAdded(RestSBDevice nodeId) {
186 Preconditions.checkNotNull(nodeId, E_DEVICE_NULL);
187 nodeId.setActive(true);
188 addedDevices.add(nodeId.deviceId());
189 }
190
191 private void deviceRemoved(DeviceId deviceId) {
192 Preconditions.checkNotNull(deviceId, E_DEVICE_NULL);
193 restconfClient.removeDevice(deviceId);
194 }
195
196 private void connectDevices() {
197
Henry Yu4b4a7eb2016-11-09 20:07:53 -0500198 RestconfServerConfig cfg = cfgService.getConfig(appId,
199 RestconfServerConfig.class);
Yixiao Chen5ece00f2016-09-14 16:23:24 -0400200 try {
201 if (cfg != null && cfg.getDevicesAddresses() != null) {
202 //Precomputing the devices to be removed
Henry Yu4b4a7eb2016-11-09 20:07:53 -0500203 Set<RestSBDevice> toBeRemoved = new HashSet<>(restconfClient.
204 getDevices().values());
Yixiao Chen5ece00f2016-09-14 16:23:24 -0400205 toBeRemoved.removeAll(cfg.getDevicesAddresses());
206 //Adding new devices
207 for (RestSBDevice device : cfg.getDevicesAddresses()) {
208 device.setActive(false);
209 restconfClient.addDevice(device);
210 deviceAdded(device);
211 }
212
213 //Removing devices not wanted anymore
214 toBeRemoved.forEach(device -> deviceRemoved(device.deviceId()));
215 }
216 } catch (ConfigException e) {
217 log.error("Configuration error {}", e);
218 }
219
220 // Discover the topology from RESTCONF server
221 addedDevices.forEach(this::retrieveTopology);
222 addedDevices.clear();
223 }
224
225 private void retrieveTopology(DeviceId deviceId) {
226 // Retrieve IETF Network at top level.
Henry Yu4b4a7eb2016-11-09 20:07:53 -0500227 InputStream jsonStream = restconfClient.get(deviceId,
228 IETF_NETWORK_URI,
229 JSON);
Yixiao Chen5ece00f2016-09-14 16:23:24 -0400230 if (jsonStream == null) {
Henry Yu4b4a7eb2016-11-09 20:07:53 -0500231 log.warn("Unable to retrieve network Topology from restconf " +
232 "server {}", deviceId);
Yixiao Chen5ece00f2016-09-14 16:23:24 -0400233 return;
234 }
235
236 // Need to convert Input stream to String.
237 StringWriter writer = new StringWriter();
238 try {
239 IOUtils.copy(jsonStream, writer, StandardCharsets.UTF_8);
240 } catch (IOException e) {
Henry Yu4b4a7eb2016-11-09 20:07:53 -0500241 log.warn("There is an exception {} for copy jsonStream to " +
242 "stringWriter for restconf {}",
Yixiao Chen5ece00f2016-09-14 16:23:24 -0400243 e.getMessage(), deviceId);
244 return;
245 }
246 String jsonString = writer.toString();
Henry Yu4b4a7eb2016-11-09 20:07:53 -0500247 String networkLevelJsonString = removePrefixTagFromJson(jsonString,
248 IETF_NETWORKS_PREFIX);
Yixiao Chen5ece00f2016-09-14 16:23:24 -0400249
Henry Yu4b4a7eb2016-11-09 20:07:53 -0500250 YangCompositeEncodingImpl yce =
251 new YangCompositeEncodingImpl(YangResourceIdentifierType.URI,
252 IETF_NETWORK_URI,
253 networkLevelJsonString);
Yixiao Chen5ece00f2016-09-14 16:23:24 -0400254
255 Object yo = codecHandler.decode(yce,
Shankara-Huaweid5823ab2016-11-22 10:14:52 +0530256 YangProtocolEncodingFormat.JSON,
Yixiao Chen5ece00f2016-09-14 16:23:24 -0400257 YmsOperationType.QUERY_REPLY);
258
259 if ((yo == null)) {
260 log.error("YMS decoder returns {} for restconf {}", yo, deviceId);
261 return;
262 }
263
264 // YMS returns an ArrayList in a single Object (i.e. yo in this case)
265 // this means yo is actually an ArrayList of size 1
266 IetfNetwork ietfNetwork = ((List<IetfNetwork>) yo).get(0);
267
268 if (ietfNetwork.networks() != null &&
269 ietfNetwork.networks().network() != null) {
270 //Convert the YO to TE Core data and update TE Core.
271 for (Network nw : ietfNetwork.networks().network()) {
272 topologyProviderService.networkUpdated(
273 NetworkConverter.yang2TeSubsystemNetwork(nw));
274 }
275 }
276
277 //TODO: Uncomment when YMS fixes the issue in NetworkState translation (network-ref)
278// org.onosproject.tetopology.management.api.Networks networks =
279// NetworkConverter.yang2TeSubsystemNetworks(ietfNetwork.networks(),
280// ietfNetwork.networksState());
281// if (networks == null || networks.networks() == null) {
282// log.error("Yang2Te returns null for restconf {}", deviceId);
283// return;
284// }
285// for (org.onosproject.tetopology.management.api.Network network : networks.networks()) {
286// topologyProviderService.networkUpdated(network);
287// }
288
Henry Yu4b4a7eb2016-11-09 20:07:53 -0500289 RestConfNotificationEventListener<String> callBackListener =
290 new InternalRestconfNotificationEventListener();
291 restconfClient.enableNotifications(deviceId, IETF_NOTIFICATION_URI,
292 "application/json",
293 callBackListener);
Yixiao Chen5ece00f2016-09-14 16:23:24 -0400294 }
295
Henry Yu4b4a7eb2016-11-09 20:07:53 -0500296 private String removePrefixTagFromJson(String jsonString, String prefixTag) {
297 if (jsonString.startsWith(prefixTag)) {
298 return jsonString.substring(prefixTag.length(), jsonString.length() - 1);
Yixiao Chen5ece00f2016-09-14 16:23:24 -0400299 }
Yixiao Chen5ece00f2016-09-14 16:23:24 -0400300 return jsonString;
301 }
302
Yixiao Chen5ece00f2016-09-14 16:23:24 -0400303 private class InternalNetworkConfigListener implements NetworkConfigListener {
304
305 @Override
306 public void event(NetworkConfigEvent event) {
307 executor.execute(TeTopologyRestconfProvider.this::connectDevices);
308 }
309
310 @Override
311 public boolean isRelevant(NetworkConfigEvent event) {
312 return event.configClass().equals(RestconfServerConfig.class) &&
313 (event.type() == CONFIG_ADDED ||
314 event.type() == CONFIG_UPDATED);
315 }
316 }
317
Henry Yu4b4a7eb2016-11-09 20:07:53 -0500318 private class InternalRestconfNotificationEventListener implements
319 RestConfNotificationEventListener<String> {
320 @Override
321 public void handleNotificationEvent(DeviceId deviceId,
322 String eventJsonString) {
323 log.debug("New notification: {} for device: {}",
324 eventJsonString, deviceId.toString());
325
326 String teEventString = removePrefixTagFromJson(eventJsonString,
327 TE_NOTIFICATION_PREFIX);
328 if (teEventString.startsWith(TE_LINK_EVENT_PREFIX)) {
329 String linkString = removePrefixTagFromJson(teEventString,
330 TE_LINK_EVENT_PREFIX);
331 log.debug("link event={}", linkString);
332 handleRestconfLinkNotification(linkString);
333 } else if (teEventString.startsWith(TE_NODE_EVENT_PREFIX)) {
334 String nodeString = removePrefixTagFromJson(teEventString,
335 TE_NODE_EVENT_PREFIX);
336 log.debug("node event={}", nodeString);
337 handleRestconfNodeNotification(nodeString);
338 }
339 }
340
341 private void handleRestconfLinkNotification(String linkString) {
342 YangCompositeEncodingImpl yce =
343 new YangCompositeEncodingImpl(YangResourceIdentifierType.URI,
344 "ietf-te-topology:te-link-event",
345 linkString);
346 Object yo = codecHandler.decode(yce,
347 YangProtocolEncodingFormat.JSON,
348 YmsOperationType.NOTIFICATION);
349
350 if (yo == null) {
351 log.error("YMS decoder error");
352 return;
353 }
354
355 NetworkLinkKey linkKey = LinkConverter.yangLinkEvent2NetworkLinkKey(
356 (TeLinkEvent) yo);
357
358 NetworkLink networkLink = LinkConverter.yangLinkEvent2NetworkLink(
359 (TeLinkEvent) yo,
360 teTopologyService);
361
362 topologyProviderService.linkUpdated(linkKey, networkLink);
363 }
364
365 private void handleRestconfNodeNotification(String nodeString) {
366 YangCompositeEncodingImpl yce =
367 new YangCompositeEncodingImpl(YangResourceIdentifierType.URI,
368 "ietf-te-topology:te-node-event",
369 nodeString);
370 Object yo = codecHandler.decode(yce,
371 YangProtocolEncodingFormat.JSON,
372 YmsOperationType.NOTIFICATION);
373
374 if (yo == null) {
375 log.error("YMS decoder error");
376 return;
377 }
378
379 NetworkNodeKey nodeKey = NodeConverter.yangNodeEvent2NetworkNodeKey(
380 (TeNodeEvent) yo);
381
382 NetworkNode networkNode = NodeConverter.yangNodeEvent2NetworkNode(
383 (TeNodeEvent) yo,
384 teTopologyService);
385
386 topologyProviderService.nodeUpdated(nodeKey, networkNode);
387 }
388 }
Yixiao Chen5ece00f2016-09-14 16:23:24 -0400389}