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