ca2922d681e6b8527c5ba79599842f8f1b8d480d
[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.AbstractConfigCommand;
20 import org.opendaylight.groupbasedpolicy.renderer.vpp.commands.AbstractInterfaceCommand;
21 import org.opendaylight.groupbasedpolicy.renderer.vpp.commands.interfaces.ConfigCommand;
22 import org.opendaylight.groupbasedpolicy.renderer.vpp.commands.lisp.AbstractLispCommand;
23 import org.opendaylight.vbd.impl.transaction.VbdNetconfTransaction;
24 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.Interface;
25 import org.opendaylight.yangtools.yang.binding.DataObject;
26 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
27 import org.slf4j.Logger;
28 import org.slf4j.LoggerFactory;
29
30 import com.google.common.base.Optional;
31 import com.google.common.base.Preconditions;
32 import com.google.common.util.concurrent.CheckedFuture;
33
34 public class GbpNetconfTransaction {
35
36     public static final byte RETRY_COUNT = 3;
37     private static final Logger LOG = LoggerFactory.getLogger(GbpNetconfTransaction.class);
38
39     /***
40      * Netconf wrapper for write and delete operation on a Netconf Device
41      * @param mountpoint    netconf device
42      * @param iid           path for Data to be written to
43      * @param data          data to be written
44      * @param retryCounter  retry counter, will repeat the operation for specified amount of times if transaction fails
45      * @param <T>           data type
46      * @return true if transaction is successful, false otherwise
47      */
48     public static <T extends DataObject> boolean netconfSyncedWrite(@Nonnull final DataBroker mountpoint,
49         @Nonnull final InstanceIdentifier<T> iid, @Nonnull final T data, byte retryCounter) {
50         VbdNetconfTransaction.REENTRANT_LOCK.lock();
51         boolean result = write(mountpoint, iid, data, retryCounter);
52         VbdNetconfTransaction.REENTRANT_LOCK.unlock();
53         return result;
54     }
55
56     /***
57      * Netconf wrapper for merge operation on a Netconf Device
58      * @param mountpoint    netconf device
59      * @param iid           path for Data to be merged to
60      * @param data          data to be merged
61      * @param retryCounter  retry counter, will repeat the operation for specified amount of times if transaction fails
62      * @param <T>           data type
63      * @return true if transaction is successful, false otherwise
64      */
65     public static <T extends DataObject> boolean netconfSyncedMerge(@Nonnull final DataBroker mountpoint,
66                                                                     @Nonnull final InstanceIdentifier<T> iid, @Nonnull final T data, byte retryCounter) {
67         VbdNetconfTransaction.REENTRANT_LOCK.lock();
68         boolean result = merge(mountpoint, iid, data, retryCounter);
69         VbdNetconfTransaction.REENTRANT_LOCK.unlock();
70         return result;
71     }
72
73     /***
74      * Netconf wrapper method for synced requests for write operation on a Netconf Device
75      * @param mountpoint    netconf device
76      * @param command       config command that needs to be executed
77      * @param retryCounter  retry counter, will repeat the operation for specified amount of times if transaction fails
78      * @return true if transaction is successful, false otherwise
79      */
80     public static boolean netconfSyncedWrite(@Nonnull final DataBroker mountpoint, @Nonnull final ConfigCommand command,
81         byte retryCounter) {
82         VbdNetconfTransaction.REENTRANT_LOCK.lock();
83         boolean result = write(mountpoint, command, retryCounter);
84         VbdNetconfTransaction.REENTRANT_LOCK.unlock();
85         return result;
86     }
87
88     /***
89      * Netconf wrapper method for synced requests for write operation on a Netconf Device
90      * @param mountpoint    netconf device
91      * @param command       abstract lisp command that needs to be executed
92      * @param retryCounter  retry counter, will repeat the operation for specified amount of times if transaction fails
93      * @return true if transaction is successful, false otherwise
94      */
95     public static <T extends DataObject> boolean netconfSyncedWrite(@Nonnull final DataBroker mountpoint,
96                                                                     @Nonnull final AbstractLispCommand<T> command,
97                                                                     byte retryCounter) {
98         VbdNetconfTransaction.REENTRANT_LOCK.lock();
99         boolean result = write(mountpoint, command, retryCounter);
100         VbdNetconfTransaction.REENTRANT_LOCK.unlock();
101         return result;
102     }
103
104     /***
105      * Netconf wrapper method for synced requests for delete operation on a Netconf Device
106      * @param mountpoint    netconf device
107      * @param iid           path for Data to be written to
108      * @param retryCounter  retry counter, will repeat the operation for specified amount of times if transaction fails
109      * @param <T>           data type
110      * @return true if transaction is successful, false otherwise
111      */
112     public static <T extends DataObject> boolean netconfSyncedDelete(@Nonnull final DataBroker mountpoint,
113         @Nonnull final InstanceIdentifier<T> iid, byte retryCounter) {
114         VbdNetconfTransaction.REENTRANT_LOCK.lock();
115         boolean result = deleteIfExists(mountpoint, iid, retryCounter);
116         VbdNetconfTransaction.REENTRANT_LOCK.unlock();
117         return result;
118     }
119
120     /***
121      * Netconf wrapper method for synced requests for delete operation on a Netconf Device
122      * @param mountpoint    netconf device
123      * @param command       config command that needs to be executed
124      * @param retryCounter  retry counter, will repeat the operation for specified amount of times if transaction fails
125      * @return true if transaction is successful, false otherwise
126      */
127     public static boolean netconfSyncedDelete(@Nonnull final DataBroker mountpoint,
128                                               @Nonnull final AbstractInterfaceCommand command, byte retryCounter) {
129         VbdNetconfTransaction.REENTRANT_LOCK.lock();
130         boolean result = deleteIfExists(mountpoint, command, retryCounter);
131         VbdNetconfTransaction.REENTRANT_LOCK.unlock();
132         return result;
133     }
134
135     /***
136      * Netconf wrapper method for synced requests for delete operation on a Netconf Device
137      * @param mountpoint    netconf device
138      * @param command       routing command that needs to be executed
139      * @param retryCounter  retry counter, will repeat the operation for specified amount of times if transaction fails
140      * @return true if transaction is successful, false otherwise
141      */
142     public static boolean netconfSyncedDelete(@Nonnull final DataBroker mountpoint,
143         @Nonnull final AbstractConfigCommand command, byte retryCounter) {
144         VbdNetconfTransaction.REENTRANT_LOCK.lock();
145         boolean result = deleteIfExists(mountpoint, command, retryCounter);
146         VbdNetconfTransaction.REENTRANT_LOCK.unlock();
147         return result;
148     }
149
150     /**
151      * Use {@link ConfigCommand} to put data into netconf transaction and submit. Transaction is restarted if failed
152      *
153      * @param mountpoint   to access remote device
154      * @param command      config command with data, datastore type and iid
155      * @param retryCounter number of attempts
156      * @return true if transaction is successful, false otherwise
157      */
158     private static boolean write(final DataBroker mountpoint, final ConfigCommand command, byte retryCounter) {
159         LOG.trace("Netconf WRITE transaction started. RetryCounter: {}", retryCounter);
160         Preconditions.checkNotNull(mountpoint);
161         final ReadWriteTransaction rwTx = mountpoint.newReadWriteTransaction();
162         try {
163             command.execute(rwTx);
164             final CheckedFuture<Void, TransactionCommitFailedException> futureTask = rwTx.submit();
165             futureTask.get();
166             LOG.trace("Netconf WRITE transaction done for command {}", command);
167             return true;
168         } catch (Exception e) {
169             // Retry
170             if (retryCounter > 0) {
171                 LOG.warn("Netconf WRITE transaction failed to {}. Restarting transaction ... ", e.getMessage());
172                 return write(mountpoint, command, --retryCounter);
173             } else {
174                 LOG.warn("Netconf WRITE transaction unsuccessful. Maximal number of attempts reached. Trace: {}", e);
175                 return false;
176             }
177         }
178     }
179
180     /**
181      * Write data to remote device. Transaction is restarted if failed
182      *
183      * @param mountpoint   to access remote device
184      * @param iid          data identifier
185      * @param data         to write
186      * @param retryCounter number of attempts
187      * @param <T>          generic data type. Has to be child of {@link DataObject}
188      * @return true if transaction is successful, false otherwise
189      */
190     private static <T extends DataObject> boolean write(final DataBroker mountpoint, final InstanceIdentifier<T> iid,
191         final T data, byte retryCounter) {
192         LOG.trace("Netconf WRITE transaction started. RetryCounter: {}", retryCounter);
193         Preconditions.checkNotNull(mountpoint);
194         final ReadWriteTransaction rwTx = mountpoint.newReadWriteTransaction();
195         try {
196             rwTx.put(LogicalDatastoreType.CONFIGURATION, iid, data, true);
197             final CheckedFuture<Void, TransactionCommitFailedException> futureTask = rwTx.submit();
198             futureTask.get();
199             LOG.trace("Netconf WRITE transaction done for {}", iid);
200             return true;
201         } catch (Exception e) {
202             // Retry
203             if (retryCounter > 0) {
204                 LOG.warn("Netconf WRITE transaction failed to {}. Restarting transaction ... ", e.getMessage());
205                 return write(mountpoint, iid, data, --retryCounter);
206             } else {
207                 LOG.warn("Netconf WRITE transaction unsuccessful. Maximal number of attempts reached. Trace: {}", e);
208                 return false;
209             }
210         }
211     }
212
213     /**
214      * Merge data to remote device. Transaction is restarted if failed
215      *
216      * @param mountpoint   to access remote device
217      * @param iid          data identifier
218      * @param data         to merge
219      * @param retryCounter number of attempts
220      * @param <T>          generic data type. Has to be child of {@link DataObject}
221      * @return true if transaction is successful, false otherwise
222      */
223     private static <T extends DataObject> boolean merge(final DataBroker mountpoint, final InstanceIdentifier<T> iid,
224                                                         final T data, byte retryCounter) {
225         LOG.trace("Netconf MERGE transaction started. RetryCounter: {}", retryCounter);
226         Preconditions.checkNotNull(mountpoint);
227         final ReadWriteTransaction rwTx = mountpoint.newReadWriteTransaction();
228         try {
229             rwTx.merge(LogicalDatastoreType.CONFIGURATION, iid, data, true);
230             final CheckedFuture<Void, TransactionCommitFailedException> futureTask = rwTx.submit();
231             futureTask.get();
232             LOG.trace("Netconf MERGE transaction done for {}", iid);
233             return true;
234         } catch (Exception e) {
235             // Retry
236             if (retryCounter > 0) {
237                 LOG.warn("Netconf MERGE transaction failed to {}. Restarting transaction ... ", e.getMessage());
238                 return write(mountpoint, iid, data, --retryCounter);
239             } else {
240                 LOG.warn("Netconf MERGE transaction unsuccessful. Maximal number of attempts reached. Trace: {}", e);
241                 return false;
242             }
243         }
244     }
245
246     /**
247      * Use {@link AbstractLispCommand} to put data into netconf transaction and submit. Transaction is restarted if failed
248      *
249      * @param mountpoint   to access remote device
250      * @param command      abstract lisp command with data, datastore type and iid
251      * @param retryCounter number of attempts
252      * @return true if transaction is successful, false otherwise
253      */
254     private static <T extends DataObject> boolean write(final DataBroker mountpoint,
255                                                         final AbstractLispCommand<T> command,
256                                                         byte retryCounter) {
257         LOG.trace("Netconf WRITE transaction started. RetryCounter: {}", retryCounter);
258         Preconditions.checkNotNull(mountpoint);
259         final ReadWriteTransaction rwTx = mountpoint.newReadWriteTransaction();
260         try {
261             command.execute(rwTx);
262             final CheckedFuture<Void, TransactionCommitFailedException> futureTask = rwTx.submit();
263             futureTask.get();
264             LOG.trace("Netconf WRITE transaction done for command {}", command);
265             return true;
266         } catch (Exception e) {
267             // Retry
268             if (retryCounter > 0) {
269                 LOG.warn("Netconf WRITE transaction failed to {}. Restarting transaction ... ", e.getMessage());
270                 rwTx.cancel();
271                 return write(mountpoint, command, --retryCounter);
272             } else {
273                 LOG.warn("Netconf WRITE transaction unsuccessful. Maximal number of attempts reached. Trace: {}", e);
274                 return false;
275             }
276         }
277     }
278
279     /**
280      * Read data from remote device. Transaction is restarted if failed.
281      *
282      * @param mountpoint    to access remote device
283      * @param datastoreType {@link LogicalDatastoreType}
284      * @param iid           data identifier
285      * @param retryCounter  number of attempts
286      * @param <T>           generic data type. Has to be child of {@link DataObject}
287      * @return optional data object if successful, {@link Optional#absent()} if failed
288      */
289     public static synchronized <T extends DataObject> Optional<T> read(final DataBroker mountpoint,
290         final LogicalDatastoreType datastoreType, final InstanceIdentifier<T> iid, byte retryCounter) {
291         LOG.trace("Netconf READ transaction started. RetryCounter: {}", retryCounter);
292         Preconditions.checkNotNull(mountpoint);
293         final ReadOnlyTransaction rTx = mountpoint.newReadOnlyTransaction();
294         Optional<T> data;
295         try {
296             final CheckedFuture<Optional<T>, ReadFailedException> futureData =
297                 rTx.read(datastoreType, iid);
298             data = futureData.get();
299             LOG.trace("Netconf READ transaction done. Data present: {}", data.isPresent());
300             return data;
301         } catch (Exception e) {
302             // Retry
303             if (retryCounter > 0) {
304                 LOG.warn("Netconf READ transaction failed to {}. Restarting transaction ... ", e.getMessage());
305                 rTx.close();
306                 return read(mountpoint, datastoreType, iid, --retryCounter);
307             } else {
308                 LOG.warn("Netconf READ transaction unsuccessful. Maximal number of attempts reached. Trace: {}", e);
309                 return Optional.absent();
310             }
311         }
312     }
313
314     /**
315      * Remove data from remote device using {@link ConfigCommand}
316      *
317      * @param mountpoint   to access remote device
318      * @param command      config command with data, datastore type and iid
319      * @param retryCounter number of attempts
320      * @return true if transaction is successful, false otherwise
321      */
322     private static boolean deleteIfExists(final DataBroker mountpoint,
323                                           final AbstractInterfaceCommand command,
324                                           byte retryCounter) {
325         Preconditions.checkNotNull(mountpoint);
326         InstanceIdentifier<Interface> iid = VppIidFactory.getInterfaceIID(command.getInterfaceBuilder().getKey());
327         return deleteIfExists(mountpoint, iid, retryCounter);
328     }
329
330     /**
331      * Remove data from remote device using {@link ConfigCommand}
332      *
333      * @param mountpoint   to access remote device
334      * @param command      config command with data, datastore type and iid
335      * @param retryCounter number of attempts
336      * @return true if transaction is successful, false otherwise
337      */
338     private static boolean deleteIfExists(final DataBroker mountpoint, final AbstractConfigCommand command,
339         byte retryCounter) {
340         Preconditions.checkNotNull(mountpoint);
341
342         return deleteIfExists(mountpoint, command.getIid(), retryCounter);
343     }
344
345     /**
346      * Remove data from remote device. Data presence is verified before removal. Transaction is restarted if failed.
347      *
348      * @param mountpoint   to access remote device
349      * @param iid          data identifier
350      * @param retryCounter number of attempts
351      * @param <T>          generic data type. Has to be child of {@link DataObject}
352      * @return true if transaction is successful, false otherwise
353      */
354     private static <T extends DataObject> boolean deleteIfExists(final DataBroker mountpoint,
355         final InstanceIdentifier<T> iid, byte retryCounter) {
356         LOG.trace("Netconf DELETE transaction started. Data will be read at first. RetryCounter: {}", retryCounter);
357         Preconditions.checkNotNull(mountpoint);
358         final Optional<T> optionalObject = read(mountpoint, LogicalDatastoreType.CONFIGURATION, iid, RETRY_COUNT);
359         if (!optionalObject.isPresent()) {
360             LOG.warn("Netconf DELETE transaction aborted. Data to remove are not present or cannot be read. Iid: {}",
361                 iid);
362             // Return true, this state is not considered as an error
363             return true;
364         }
365         final ReadWriteTransaction rwTx = mountpoint.newReadWriteTransaction();
366         try {
367             rwTx.delete(LogicalDatastoreType.CONFIGURATION, iid);
368             final CheckedFuture<Void, TransactionCommitFailedException> futureTask = rwTx.submit();
369             futureTask.get();
370             LOG.trace("Netconf DELETE transaction done for {}", iid);
371             return true;
372         } catch (Exception e) {
373             // Retry
374             if (retryCounter > 0) {
375                 LOG.warn("Netconf DELETE transaction failed to {}. Restarting transaction ... ", e.getMessage());
376                 return deleteIfExists(mountpoint, iid, --retryCounter);
377             } else {
378                 LOG.warn("Netconf DELETE transaction unsuccessful. Maximal number of attempts reached. Trace: {}", e);
379                 return false;
380             }
381         }
382     }
383 }