Netconf transactions synchronization GBP part
[groupbasedpolicy.git] / renderers / vpp / src / main / java / org / opendaylight / groupbasedpolicy / renderer / vpp / util / GbpNetconfTransaction.java
1 /*
2  * Copyright (c) 2016 Cisco Systems, Inc. and others. All rights reserved.
3  *
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
7  */
8
9 package org.opendaylight.groupbasedpolicy.renderer.vpp.util;
10
11 import javax.annotation.Nonnull;
12
13 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
14 import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction;
15 import org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction;
16 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
17 import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
18 import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
19 import org.opendaylight.groupbasedpolicy.renderer.vpp.commands.ConfigCommand;
20 import org.opendaylight.vbd.impl.transaction.VbdNetconfTransaction;
21 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.Interface;
22 import org.opendaylight.yangtools.yang.binding.DataObject;
23 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
24 import org.slf4j.Logger;
25 import org.slf4j.LoggerFactory;
26
27 import com.google.common.base.Optional;
28 import com.google.common.base.Preconditions;
29 import com.google.common.util.concurrent.CheckedFuture;
30
31 public class GbpNetconfTransaction {
32
33     public static final byte RETRY_COUNT = 3;
34     private static final Logger LOG = LoggerFactory.getLogger(GbpNetconfTransaction.class);
35
36     /***
37      * Netconf wrapper for write and delete operation on a Netconf Device
38      * @param mountpoint    netconf device
39      * @param iid           path for Data to be written to
40      * @param data          data to be written
41      * @param retryCounter  retry counter, will repeat the operation for specified amount of times if transaction fails
42      * @param <T>           data type
43      * @return true if transaction is successful, false otherwise
44      */
45     public static <T extends DataObject> boolean netconfSyncedWrite(@Nonnull final DataBroker mountpoint,
46         @Nonnull final InstanceIdentifier<T> iid, @Nonnull final T data, byte retryCounter) {
47         VbdNetconfTransaction.REENTRANT_LOCK.lock();
48         boolean result = write(mountpoint, iid, data, retryCounter);
49         VbdNetconfTransaction.REENTRANT_LOCK.unlock();
50         return result;
51     }
52
53     /***
54      * Netconf wrapper method for synced requests for write operation on a Netconf Device
55      * @param mountpoint    netconf device
56      * @param command       config command that needs to be executed
57      * @param retryCounter  retry counter, will repeat the operation for specified amount of times if transaction fails
58      * @return true if transaction is successful, false otherwise
59      */
60     public static boolean netconfSyncedWrite(@Nonnull final DataBroker mountpoint, @Nonnull final ConfigCommand command,
61         byte retryCounter) {
62         VbdNetconfTransaction.REENTRANT_LOCK.lock();
63         boolean result = write(mountpoint, command, retryCounter);
64         VbdNetconfTransaction.REENTRANT_LOCK.unlock();
65         return result;
66     }
67
68     /***
69      * Netconf wrapper method for synced requests for delete operation on a Netconf Device
70      * @param mountpoint    netconf device
71      * @param iid           path for Data to be written to
72      * @param retryCounter  retry counter, will repeat the operation for specified amount of times if transaction fails
73      * @param <T>           data type
74      * @return true if transaction is successful, false otherwise
75      */
76     public static <T extends DataObject> boolean netconfSyncedDelete(@Nonnull final DataBroker mountpoint,
77         @Nonnull final InstanceIdentifier<T> iid, byte retryCounter) {
78         VbdNetconfTransaction.REENTRANT_LOCK.lock();
79         boolean result = deleteIfExists(mountpoint, iid, retryCounter);
80         VbdNetconfTransaction.REENTRANT_LOCK.unlock();
81         return result;
82     }
83
84     /***
85      * Netconf wrapper method for synced requests for delete operation on a Netconf Device
86      * @param mountpoint    netconf device
87      * @param command       config command that needs to be executed
88      * @param retryCounter  retry counter, will repeat the operation for specified amount of times if transaction fails
89      * @return true if transaction is successful, false otherwise
90      */
91     public static boolean netconfSyncedDelete(@Nonnull final DataBroker mountpoint,
92         @Nonnull final ConfigCommand command, byte retryCounter) {
93         VbdNetconfTransaction.REENTRANT_LOCK.lock();
94         boolean result = deleteIfExists(mountpoint, command, retryCounter);
95         VbdNetconfTransaction.REENTRANT_LOCK.unlock();
96         return result;
97     }
98
99
100     /**
101      * Use {@link ConfigCommand} to put data into netconf transaction and submit. Transaction is restarted if failed
102      *
103      * @param mountpoint   to access remote device
104      * @param command      config command with data, datastore type and iid
105      * @param retryCounter number of attempts
106      * @return true if transaction is successful, false otherwise
107      */
108     private static boolean write(final DataBroker mountpoint, final ConfigCommand command, byte retryCounter) {
109         LOG.trace("Netconf WRITE transaction started. RetryCounter: {}", retryCounter);
110         Preconditions.checkNotNull(mountpoint);
111         final ReadWriteTransaction rwTx = mountpoint.newReadWriteTransaction();
112         try {
113             command.execute(rwTx);
114             final CheckedFuture<Void, TransactionCommitFailedException> futureTask = rwTx.submit();
115             futureTask.get();
116             LOG.trace("Netconf WRITE transaction done for command {}", command);
117             return true;
118         } catch (Exception e) {
119             // Retry
120             if (retryCounter > 0) {
121                 LOG.warn("Netconf WRITE transaction failed to {}. Restarting transaction ... ", e.getMessage());
122                 return write(mountpoint, command, --retryCounter);
123             } else {
124                 LOG.warn("Netconf WRITE transaction unsuccessful. Maximal number of attempts reached. Trace: {}", e);
125                 return false;
126             }
127         }
128     }
129
130     /**
131      * Write data to remote device. Transaction is restarted if failed
132      *
133      * @param mountpoint   to access remote device
134      * @param iid          data identifier
135      * @param data         to write
136      * @param retryCounter number of attempts
137      * @param <T>          generic data type. Has to be child of {@link DataObject}
138      * @return true if transaction is successful, false otherwise
139      */
140     private static <T extends DataObject> boolean write(final DataBroker mountpoint,
141                                                                     final InstanceIdentifier<T> iid,
142                                                                     final T data,
143                                                                     byte retryCounter) {
144         LOG.trace("Netconf WRITE transaction started. RetryCounter: {}", retryCounter);
145         Preconditions.checkNotNull(mountpoint);
146         final ReadWriteTransaction rwTx = mountpoint.newReadWriteTransaction();
147         try {
148             rwTx.put(LogicalDatastoreType.CONFIGURATION, iid, data, true);
149             final CheckedFuture<Void, TransactionCommitFailedException> futureTask = rwTx.submit();
150             futureTask.get();
151             LOG.trace("Netconf WRITE transaction done for {}", iid);
152             return true;
153         } catch (Exception e) {
154             // Retry
155             if (retryCounter > 0) {
156                 LOG.warn("Netconf WRITE transaction failed to {}. Restarting transaction ... ", e.getMessage());
157                 return write(mountpoint, iid, data, --retryCounter);
158             } else {
159                 LOG.warn("Netconf WRITE transaction unsuccessful. Maximal number of attempts reached. Trace: {}", e);
160                 return false;
161             }
162         }
163     }
164
165     /**
166      * Read data from remote device. Transaction is restarted if failed.
167      *
168      * @param mountpoint    to access remote device
169      * @param datastoreType {@link LogicalDatastoreType}
170      * @param iid           data identifier
171      * @param retryCounter  number of attempts
172      * @param <T>           generic data type. Has to be child of {@link DataObject}
173      * @return optional data object if successful, {@link Optional#absent()} if failed
174      */
175     public static synchronized <T extends DataObject> Optional<T> read(final DataBroker mountpoint,
176                                                                        final LogicalDatastoreType datastoreType,
177                                                                        final InstanceIdentifier<T> iid,
178                                                                        byte retryCounter) {
179         LOG.trace("Netconf READ transaction started. RetryCounter: {}", retryCounter);
180         Preconditions.checkNotNull(mountpoint);
181         final ReadOnlyTransaction rTx = mountpoint.newReadOnlyTransaction();
182         Optional<T> data;
183         try {
184             final CheckedFuture<Optional<T>, ReadFailedException> futureData =
185                     rTx.read(datastoreType, iid);
186             data = futureData.get();
187             LOG.trace("Netconf READ transaction done. Data present: {}", data.isPresent());
188             return data;
189         } catch (Exception e) {
190             // Retry
191             if (retryCounter > 0) {
192                 LOG.warn("Netconf READ transaction failed to {}. Restarting transaction ... ", e.getMessage());
193                 rTx.close();
194                 return read(mountpoint, datastoreType, iid, --retryCounter);
195             } else {
196                 LOG.warn("Netconf READ transaction unsuccessful. Maximal number of attempts reached. Trace: {}", e);
197                 return Optional.absent();
198             }
199         }
200     }
201
202     /**
203      * Remove data from remote device using {@link ConfigCommand}
204      *
205      * @param mountpoint   to access remote device
206      * @param command      config command with data, datastore type and iid
207      * @param retryCounter number of attempts
208      * @return true if transaction is successful, false otherwise
209      */
210     private static boolean deleteIfExists(final DataBroker mountpoint, final ConfigCommand command,
211                                               byte retryCounter) {
212         Preconditions.checkNotNull(mountpoint);
213         InstanceIdentifier<Interface> iid = VppIidFactory.getInterfaceIID(command.getInterfaceBuilder().getKey());
214         return deleteIfExists(mountpoint, iid, retryCounter);
215     }
216
217     /**
218      * Remove data from remote device. Data presence is verified before removal. Transaction is restarted if failed.
219      *
220      * @param mountpoint   to access remote device
221      * @param iid          data identifier
222      * @param retryCounter number of attempts
223      * @param <T>          generic data type. Has to be child of {@link DataObject}
224      * @return true if transaction is successful, false otherwise
225      */
226     private static <T extends DataObject> boolean deleteIfExists(final DataBroker mountpoint,
227                                                                      final InstanceIdentifier<T> iid,
228                                                                      byte retryCounter) {
229         LOG.trace("Netconf DELETE transaction started. Data will be read at first. RetryCounter: {}", retryCounter);
230         Preconditions.checkNotNull(mountpoint);
231         final Optional<T> optionalObject = read(mountpoint, LogicalDatastoreType.CONFIGURATION, iid, RETRY_COUNT);
232         if (!optionalObject.isPresent()) {
233             LOG.warn("Netconf DELETE transaction aborted. Data to remove are not present or cannot be read. Iid: {}",
234                     iid);
235             // Return true, this state is not considered as an error
236             return true;
237         }
238         final ReadWriteTransaction rwTx = mountpoint.newReadWriteTransaction();
239         try {
240             rwTx.delete(LogicalDatastoreType.CONFIGURATION, iid);
241             final CheckedFuture<Void, TransactionCommitFailedException> futureTask = rwTx.submit();
242             futureTask.get();
243             LOG.trace("Netconf DELETE transaction done for {}", iid);
244             return true;
245         } catch (Exception e) {
246             // Retry
247             if (retryCounter > 0) {
248                 LOG.warn("Netconf DELETE transaction failed to {}. Restarting transaction ... ", e.getMessage());
249                 return deleteIfExists(mountpoint, iid, --retryCounter);
250             } else {
251                 LOG.warn("Netconf DELETE transaction unsuccessful. Maximal number of attempts reached. Trace: {}", e);
252                 return false;
253             }
254         }
255     }
256 }