Honeynode Enhancements
[transportpce.git] / tests / honeynode / honeynode-plugin-impl / src / main / java / io / fd / honeycomb / transportpce / device / read / DeviceReaderFactory.java
1 /*
2  * Copyright (c) 2018 Orange and/or its affiliates.
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 package io.fd.honeycomb.transportpce.device.read;
17
18 import com.google.common.collect.Sets;
19 import com.google.common.util.concurrent.Futures;
20 import com.google.inject.Inject;
21 import com.google.inject.name.Named;
22
23 import java.io.File;
24 import java.io.IOException;
25 import java.text.DateFormat;
26 import java.text.SimpleDateFormat;
27 import java.util.ArrayList;
28 import java.util.List;
29 import java.util.Set;
30 import java.util.concurrent.ExecutionException;
31 import java.util.concurrent.Future;
32
33 import org.opendaylight.controller.config.util.capability.Capability;
34 import org.opendaylight.controller.config.util.capability.YangModuleCapability;
35 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
36 import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
37 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
38 import org.opendaylight.yang.gen.v1.http.org.openroadm.device.rev170206.org.openroadm.device.container.OrgOpenroadmDevice;
39 import org.opendaylight.yang.gen.v1.http.org.openroadm.device.rev170206.org.openroadm.device.container.OrgOpenroadmDeviceBuilder;
40 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.netmod.notification.rev080714.Netconf;
41 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.netmod.notification.rev080714.netconf.Streams;
42 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.netmod.notification.rev080714.netconf.StreamsBuilder;
43 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.NetconfState;
44 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.NetconfStateBuilder;
45 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.Yang;
46 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.netconf.state.Schemas;
47 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.netconf.state.SchemasBuilder;
48 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.netconf.state.schemas.Schema;
49 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.netconf.state.schemas.Schema.Location;
50 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.netconf.state.schemas.Schema.Location.Enumeration;
51 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.netconf.state.schemas.SchemaBuilder;
52 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
53 import org.opendaylight.yangtools.yang.model.api.Module;
54 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
55 import org.opendaylight.yangtools.yang.model.repo.api.RevisionSourceIdentifier;
56 import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceFilter;
57 import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceRepresentation;
58 import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
59 import org.opendaylight.yangtools.yang.model.repo.api.YangTextSchemaSource;
60 import org.opendaylight.yangtools.yang.model.repo.spi.PotentialSchemaSource;
61 import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceListener;
62 import org.opendaylight.yangtools.yang.model.repo.util.FilesystemSchemaSourceCache;
63 import org.opendaylight.yangtools.yang.parser.repo.SharedSchemaRepository;
64 import org.opendaylight.yangtools.yang.parser.util.TextToASTTransformer;
65 import org.slf4j.Logger;
66 import org.slf4j.LoggerFactory;
67
68 import io.fd.honeycomb.translate.read.ReaderFactory;
69 import io.fd.honeycomb.translate.read.registry.ModifiableReaderRegistryBuilder;
70 import io.fd.honeycomb.translate.util.read.BindingBrokerReader;
71 import io.fd.honeycomb.transportpce.device.configuration.DeviceConfiguration;
72 import io.fd.honeycomb.transportpce.device.configuration.NetconfConfiguration;
73
74 /**
75  * @author Martial COULIBALY ( martial.coulibaly@gfi.com ) on behalf of Orange
76  */
77 public final class DeviceReaderFactory implements ReaderFactory {
78
79     private static final Logger LOG = LoggerFactory.getLogger(DeviceReaderFactory.class);
80     public static final InstanceIdentifier<OrgOpenroadmDevice> DEVICE_CONTAINER_ID = InstanceIdentifier
81             .create(OrgOpenroadmDevice.class);
82     private static final String YANG_MODELS = "yang";
83
84     @Inject
85     @Named("device-databroker")
86     private DataBroker dataBroker;
87
88     @Inject
89     private DeviceConfiguration deviceConfiguration;
90
91     @Inject
92     private NetconfConfiguration netconfConfiguration;
93
94     @Override
95     public void init(final ModifiableReaderRegistryBuilder registry) {
96         registry.add(new BindingBrokerReader<>(DEVICE_CONTAINER_ID, dataBroker, LogicalDatastoreType.OPERATIONAL,
97                 OrgOpenroadmDeviceBuilder.class));
98         if (writeXMLDataToOper()) {
99             writeNetconfState();
100             writeNetconfStream();
101             loadConfigData();
102         }
103     }
104
105     /**
106      * Write xml data from {@link DeviceConfiguration} to operational data.
107      *
108      */
109     public boolean writeXMLDataToOper() {
110         Boolean res = false;
111         LOG.info("writting xml file data to oper datastore");
112         OrgOpenroadmDevice device = this.deviceConfiguration.getDataDevice();
113         if (device != null) {
114             String deviceId = device.getInfo().getNodeId();
115             LOG.info("Getting device info from xml file for device '{}'", deviceId);
116             OrgOpenroadmDeviceBuilder result = new OrgOpenroadmDeviceBuilder(device);
117             InstanceIdentifier<OrgOpenroadmDevice> iid = InstanceIdentifier.create(OrgOpenroadmDevice.class);
118             WriteTransaction writeTx = this.dataBroker.newWriteOnlyTransaction();
119             if (writeTx != null) {
120                 LOG.info("WriteTransaction is ok, copy device info to oper datastore");
121                 writeTx.put(LogicalDatastoreType.OPERATIONAL, iid, result.build());
122                 Future<Void> future = writeTx.submit();
123                 try {
124                     Futures.getChecked(future, ExecutionException.class);
125                     LOG.info("device '{}' writed to oper datastore", deviceId);
126                     res = true;
127                 } catch (ExecutionException e) {
128                     LOG.error("Failed to write Element '{}' to oper datastore", deviceId);
129                 }
130             } else {
131                 LOG.error("WriteTransaction object is null");
132             }
133         } else {
134             LOG.error("device data operation gets from xml file is null !");
135         }
136         return res;
137     }
138
139     /**
140      * Load data to config device datastore.
141      *
142      */
143     public boolean loadConfigData() {
144         Boolean res = false;
145         LOG.info("loading device configuration info from xml file...");
146         String xml = this.deviceConfiguration.getConfigDevice();
147         LOG.info("device info gets from xml file !");
148         if (xml != null) {
149             OrgOpenroadmDevice result = this.deviceConfiguration.getDeviceFromXML(xml);
150             if (result != null) {
151                 LOG.info("OrgOpenroadmDevice info gets : {}", result.getInfo().getNodeId());
152                 WriteTransaction writeTx = this.dataBroker.newWriteOnlyTransaction();
153                 if (writeTx != null) {
154                     LOG.info("WriteTransaction is ok, copy device info to config datastore");
155                     writeTx.put(LogicalDatastoreType.CONFIGURATION, DEVICE_CONTAINER_ID, result);
156                     Future<Void> future = writeTx.submit();
157                     try {
158                         Futures.getChecked(future, ExecutionException.class);
159                         LOG.info("device writed to config datastore");
160                     } catch (ExecutionException e) {
161                         LOG.error("Failed to write device to config datastore");
162                     }
163                 } else {
164                     LOG.error("WriteTransaction object is null");
165                 }
166             } else {
167                 LOG.error("device gets from xml is null !!");
168             }
169         } else {
170             LOG.error("device ID from input is not the same from xml file");
171         }
172         return res;
173     }
174
175     /**
176      * write {@link Streams} data to operational device datastore.
177      *
178      * @return result {@link Boolean}
179      */
180     public boolean writeNetconfStream() {
181         Boolean result = false;
182         LOG.info("writting netconf stream to oper datastore");
183         Streams streams = this.netconfConfiguration.getNetconfStreamsData();
184         if (streams != null) {
185             LOG.info("Netconf Data gets from xml file is present");
186             InstanceIdentifier<Streams> iid = InstanceIdentifier.create(Netconf.class).child(Streams.class);
187             Streams netconfStreams = new StreamsBuilder(streams).build();
188             WriteTransaction writeTx = this.dataBroker.newWriteOnlyTransaction();
189             if (writeTx != null) {
190                 LOG.info("WriteTransaction is ok");
191                 writeTx.put(LogicalDatastoreType.OPERATIONAL, iid, netconfStreams);
192                 Future<Void> future = writeTx.submit();
193                 try {
194                     Futures.getChecked(future, ExecutionException.class);
195                     LOG.info("netconf stream writed to oper datastore");
196                     result = true;
197                 } catch (ExecutionException e) {
198                     LOG.error("Failed to write netconf stream to oper datastore");
199                 }
200             } else {
201                 LOG.error("WriteTransaction object is null");
202             }
203         } else {
204             LOG.error("Netconf data gets from xml file is null !");
205         }
206         return result;
207     }
208
209     /**
210      * Write {@link NetconfState} data to operational device datastore.
211      *
212      * @return result {@link Boolean}
213      */
214     public boolean writeNetconfState() {
215         Boolean res = false;
216         LOG.info("writting netconf state to oper datastore");
217         final SharedSchemaRepository schemaRepo = new SharedSchemaRepository("honeynode-simulator");
218         final Set<Capability> capabilities = parseSchemasToModuleCapabilities(schemaRepo);
219         final Set<Capability> transformedCapabilities = Sets.newHashSet(capabilities);
220         DummyMonitoringService monitor = new DummyMonitoringService(transformedCapabilities);
221         List<Schema> schemaList = new ArrayList<Schema>();
222         List<Location> locationList = new ArrayList<Location>();
223         Location location = new Location(Enumeration.NETCONF);
224         locationList.add(location);
225         Schema schematobuild = null;
226         for (final Schema schema : monitor.getSchemas().getSchema()) {
227             schematobuild = new SchemaBuilder().setIdentifier(schema.getIdentifier())
228                     .setNamespace(schema.getNamespace()).setVersion(schema.getVersion()).setFormat(Yang.class)
229                     .setLocation(locationList).build();
230             schemaList.add(schematobuild);
231         }
232         Schemas schemas = new SchemasBuilder().setSchema(schemaList).build();
233         NetconfState netconfState = new NetconfStateBuilder().setSchemas(schemas).build();
234         if (netconfState != null) {
235             InstanceIdentifier<NetconfState> iid = InstanceIdentifier.create(NetconfState.class);
236             WriteTransaction writeTx = this.dataBroker.newWriteOnlyTransaction();
237             if (writeTx != null) {
238                 LOG.info("WriteTransaction is ok, copy device info to oper datastore");
239                 writeTx.put(LogicalDatastoreType.OPERATIONAL, iid, netconfState);
240                 Future<Void> future = writeTx.submit();
241                 try {
242                     Futures.getChecked(future, ExecutionException.class);
243                     LOG.info("netconf state writed to oper datastore");
244                     res = true;
245                 } catch (ExecutionException e) {
246                     LOG.error("Failed to write netconf state to oper datastore");
247                 }
248             } else {
249                 LOG.error("WriteTransaction object is null");
250             }
251         } else {
252             LOG.error("device data operation gets from xml file is null !");
253         }
254         return res;
255     }
256
257     private Set<Capability> parseSchemasToModuleCapabilities(final SharedSchemaRepository consumer) {
258         final Set<SourceIdentifier> loadedSources = Sets.newHashSet();
259         consumer.registerSchemaSourceListener(TextToASTTransformer.create(consumer, consumer));
260         consumer.registerSchemaSourceListener(new SchemaSourceListener() {
261             @Override
262             public void schemaSourceEncountered(final SchemaSourceRepresentation schemaSourceRepresentation) {
263             }
264
265             @Override
266             public void schemaSourceRegistered(final Iterable<PotentialSchemaSource<?>> potentialSchemaSources) {
267                 for (final PotentialSchemaSource<?> potentialSchemaSource : potentialSchemaSources) {
268                     loadedSources.add(potentialSchemaSource.getSourceIdentifier());
269                 }
270             }
271
272             @Override
273             public void schemaSourceUnregistered(final PotentialSchemaSource<?> potentialSchemaSource) {
274             }
275         });
276         LOG.info("Loading models from directory.");
277         ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
278         File models = new File(classLoader.getResource(YANG_MODELS).getFile());
279         if (models.exists() && models.isDirectory()) {
280             LOG.info("folder '{}' exists !", models.getAbsolutePath());
281             final FilesystemSchemaSourceCache<YangTextSchemaSource> cache = new FilesystemSchemaSourceCache<>(consumer,
282                     YangTextSchemaSource.class, models);
283             consumer.registerSchemaSourceListener(cache);
284         } else {
285             LOG.warn("folder '{}' not exists !", models.getAbsolutePath());
286             LOG.info("Custom module loading skipped.");
287         }
288         SchemaContext schemaContext;
289         try {
290             // necessary for creating mdsal data stores and operations
291             schemaContext = consumer.createSchemaContextFactory(SchemaSourceFilter.ALWAYS_ACCEPT)
292                     .createSchemaContext(loadedSources).get();
293         } catch (final InterruptedException | ExecutionException e) {
294             throw new RuntimeException("Cannot parse schema context", e);
295         }
296
297         final Set<Capability> capabilities = Sets.newHashSet();
298
299         for (final Module module : schemaContext.getModules()) {
300             for (final Module subModule : module.getSubmodules()) {
301                 addModuleCapability(consumer, capabilities, subModule);
302             }
303             addModuleCapability(consumer, capabilities, module);
304         }
305         return capabilities;
306     }
307
308     private static void addModuleCapability(final SharedSchemaRepository consumer, final Set<Capability> capabilities,
309             final Module module) {
310         DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
311
312         // to convert Date to String, use format method of SimpleDateFormat class.
313         String revision = dateFormat.format(module.getRevision());
314         final SourceIdentifier moduleSourceIdentifier = RevisionSourceIdentifier.create(module.getName(), revision);
315         try {
316             final String moduleContent = new String(
317                     consumer.getSchemaSource(moduleSourceIdentifier, YangTextSchemaSource.class).get().read());
318             capabilities.add(new YangModuleCapability(module, moduleContent));
319             // IOException would be thrown in creating SchemaContext already
320         } catch (ExecutionException | InterruptedException | IOException e) {
321             LOG.warn("Cannot retrieve schema source for module {} from schema repository",
322                     moduleSourceIdentifier.toString(), e);
323         }
324     }
325 }