2 * Copyright © 2017 AT&T and others. All rights reserved.
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6 * and is available at http://www.eclipse.org/legal/epl-v10.html
9 package org.opendaylight.transportpce.inventory.job;
11 import com.fasterxml.jackson.core.JsonProcessingException;
12 import com.fasterxml.jackson.databind.ObjectMapper;
13 import com.google.common.base.Strings;
14 import com.google.common.io.Files;
16 import java.io.IOException;
17 import java.nio.charset.StandardCharsets;
18 import java.text.SimpleDateFormat;
19 import java.util.Date;
20 import java.util.Optional;
21 import java.util.concurrent.ExecutionException;
22 import org.apache.karaf.scheduler.Job;
23 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
24 import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction;
25 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
26 import org.opendaylight.mdsal.dom.api.DOMSchemaService;
27 import org.opendaylight.transportpce.common.InstanceIdentifiers;
28 import org.opendaylight.transportpce.common.Timeouts;
29 import org.opendaylight.transportpce.common.device.DeviceTransactionManager;
30 import org.opendaylight.yang.gen.v1.http.org.openroadm.device.rev170206.org.openroadm.device.container.OrgOpenroadmDevice;
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.NetconfNode;
32 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.Topology;
33 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
34 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
35 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
36 import org.slf4j.Logger;
37 import org.slf4j.LoggerFactory;
40 * Class which periodically backups the device into a file implements {@link Job}
41 * interface which is automatically registered via whitboard pattern into karaf
42 * environment. This job will persist an {@link OrgOpenroadmDevice} capable with
43 * the models provided in transportpce-models
45 public class PeriodicDeviceBackupJob implements Runnable {
47 private static final String TIMESTAMP_FORMAT = "yyyy-MM-dd_HH-mm-ss-SSS";
48 private static final String ORG_OPENROADM_DEVICE = "org-openroadm-device";
49 private static final Logger LOG = LoggerFactory.getLogger(PeriodicDeviceBackupJob.class);
51 private final DataBroker dataBroker;
52 private final DeviceTransactionManager deviceTransactionManager;
54 private final SchemaContext schemaContext;
55 private final SimpleDateFormat filenameFormatter;
58 * Folder property, where the file is placed.
60 private String folder;
62 * Prefix of device file.
64 private String filePrefix;
67 * Constructor with injected fields.
69 * @param dataBroker the datatabroker
70 * @param domSchemaService the DOM schema service
71 * @param deviceTransactionManager the device transaction manager
73 public PeriodicDeviceBackupJob(DataBroker dataBroker, DOMSchemaService domSchemaService,
74 DeviceTransactionManager deviceTransactionManager) {
75 this.dataBroker = dataBroker;
76 this.schemaContext = domSchemaService.getGlobalContext();
77 this.deviceTransactionManager = deviceTransactionManager;
78 this.filenameFormatter = new SimpleDateFormat(TIMESTAMP_FORMAT);
83 LOG.info("Running periodical device backup into {}", this.folder);
86 } catch (InterruptedException | ExecutionException e) {
87 LOG.warn("Unable to read netconf topology from the operational datastore", e);
96 public String getFolder() {
103 * @param folder the folder to set
105 public void setFolder(String folder) {
106 this.folder = folder;
110 * Gets the filePrefix.
112 * @return the filePrefix
114 public String getFilePrefix() {
115 return this.filePrefix;
119 * Sets the filePrefix.
121 * @param filePrefix the filePrefix to set
123 public void setFilePrefix(String filePrefix) {
124 this.filePrefix = filePrefix;
128 * Stores all devices into files.
130 * @throws InterruptedException interrupted exception
131 * @throws ExecutionException execution exception
133 private void backupAllDevices() throws InterruptedException, ExecutionException {
134 ReadOnlyTransaction newReadOnlyTransaction = this.dataBroker.newReadOnlyTransaction();
135 Optional<Topology> topology = newReadOnlyTransaction
136 .read(LogicalDatastoreType.OPERATIONAL, InstanceIdentifiers.NETCONF_TOPOLOGY_II).get().toJavaUtil();
137 if (!topology.isPresent()) {
138 LOG.warn("Netconf topology was not found in datastore");
142 for (Node node : topology.get().getNode()) {
143 String nodeId = getNodeId(node);
144 if (Strings.isNullOrEmpty(nodeId)) {
145 LOG.debug("{} node is not a roadm device");
148 storeRoadmDevice(nodeId);
153 * Stores a single ROADM device into a file.
155 * @param nodeId the node ID
157 private void storeRoadmDevice(String nodeId) {
158 InstanceIdentifier<OrgOpenroadmDevice> deviceIi = InstanceIdentifier.create(OrgOpenroadmDevice.class);
159 Optional<OrgOpenroadmDevice> deviceObject =
160 this.deviceTransactionManager.getDataFromDevice(nodeId, LogicalDatastoreType.OPERATIONAL, deviceIi,
161 Timeouts.DEVICE_READ_TIMEOUT, Timeouts.DEVICE_READ_TIMEOUT_UNIT);
162 if (!deviceObject.isPresent()) {
163 LOG.warn("Device object {} is not present.", nodeId);
167 // TODO: Serialization should be done via XMLDataObjectConverter when devices use the same model as
170 * XMLDataObjectConverter createWithSchemaContext =
171 * XMLDataObjectConverter.createWithSchemaContext(schemaContext, nodeSerializer); Writer
172 * writerFromDataObject = createWithSchemaContext.writerFromDataObject(deviceObject.get(),
173 * OrgOpenroadmDevice.class, createWithSchemaContext.dataContainer());
175 StringBuilder serializedDevice = new StringBuilder();
177 serializedDevice.append(new ObjectMapper().writeValueAsString(deviceObject.get()));
178 } catch (JsonProcessingException e1) {
179 LOG.error(e1.getMessage(), e1);
182 String prepareFileName = prepareFileName(nodeId);
183 File parent = new File(this.folder);
184 if (!parent.exists() && !parent.isDirectory() && !parent.mkdirs()) {
185 LOG.error("Could not create empty directory {}", this.folder);
186 throw new IllegalStateException(String.format("Could not create empty directory %s", this.folder));
189 File file = new File(parent, this.filePrefix.concat(prepareFileName));
191 throw new IllegalStateException(String.format("The file %s already exists", file));
194 Files.asCharSink(file, StandardCharsets.UTF_8).write(serializedDevice.toString());
195 } catch (IOException e) {
196 LOG.error("Could not write device backup into file {}", file);
200 private String prepareFileName(String nodeId) {
201 String format = this.filenameFormatter.format(new Date());
202 StringBuilder sb = new StringBuilder(format);
203 sb.append("__").append(nodeId).append("__").append(".xml");
204 return sb.toString();
208 * If the {@link Node} in the {@link Topology} is a {@link NetconfNode}, has
209 * capabilities of {@link PeriodicDeviceBackupJob#ORG_OPENROADM_DEVICE} and the
210 * key is not null returns the identifier of {@link OrgOpenroadmDevice} node.
212 * @param node inside the {@link Topology}
215 private static String getNodeId(Node node) {
217 LOG.trace("The node is null");
220 NetconfNode netconfNode = node.augmentation(NetconfNode.class);
221 if (netconfNode == null) {
222 LOG.trace("The node {} has not properties of NetconfNode", node);
225 if ((netconfNode.getAvailableCapabilities() == null)
226 || (netconfNode.getAvailableCapabilities().getAvailableCapability() == null)) {
227 LOG.trace("No available capabilities");
230 long count = netconfNode.getAvailableCapabilities().getAvailableCapability().stream()
231 .filter(cp -> (cp.getCapability() != null) && cp.getCapability().contains(ORG_OPENROADM_DEVICE)).count();
233 LOG.trace("The node {} has not capabilities of OpenROADMDevice", node);
236 if ((node.key() == null) || (node.key().getNodeId() == null)) {
237 LOG.trace("Node {} has invalid key", node);
240 if ("controller-config".equalsIgnoreCase(node.key().getNodeId().getValue())) {
241 LOG.info("Got controller-config instead of roadm-node");
244 return node.key().getNodeId().getValue();