fix registerEndpoint in port delete for base Eps
[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 method for synced requests for write 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 netconfSyncedWrite(@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 = write(VbdNetconfTransaction.NODE_DATA_BROKER_MAP.get(vppIid).getKey(), command, retryCounter);
99         VbdNetconfTransaction.NODE_DATA_BROKER_MAP.get(vppIid).getValue().unlock();
100         return result;
101     }
102
103     /***
104      * Netconf wrapper method for synced requests for write operation on a Netconf Device
105      * @param vppIid        destination node
106      * @param command       routing command that needs to be executed
107      * @param retryCounter  retry counter, will repeat the operation for specified amount of times if transaction fails
108      * @return true if transaction is successful, false otherwise
109      */
110     public static boolean netconfSyncedWrite(@Nonnull final InstanceIdentifier<Node> vppIid,
111         @Nonnull final RoutingCommand command, byte retryCounter) {
112         VbdNetconfTransaction.NODE_DATA_BROKER_MAP.get(vppIid).getValue().lock();
113         boolean result = write(VbdNetconfTransaction.NODE_DATA_BROKER_MAP.get(vppIid).getKey(), command, retryCounter);
114         VbdNetconfTransaction.NODE_DATA_BROKER_MAP.get(vppIid).getValue().unlock();
115         return result;
116     }
117
118     /***
119      * Netconf wrapper method for synced requests for delete operation on a Netconf Device
120      * @param vppIid        destination node
121      * @param iid           path for Data to be written to
122      * @param retryCounter  retry counter, will repeat the operation for specified amount of times if transaction fails
123      * @param <T>           data type
124      * @return true if transaction is successful, false otherwise
125      */
126     public static <T extends DataObject> boolean netconfSyncedDelete(@Nonnull final InstanceIdentifier<Node> vppIid,
127         @Nonnull final InstanceIdentifier<T> iid, byte retryCounter) {
128         VbdNetconfTransaction.NODE_DATA_BROKER_MAP.get(vppIid).getValue().lock();
129         boolean result = deleteIfExists(vppIid, iid, retryCounter);
130         VbdNetconfTransaction.NODE_DATA_BROKER_MAP.get(vppIid).getValue().unlock();
131         return result;
132     }
133
134     public static <T extends DataObject> boolean netconfSyncedDelete(@Nonnull final InstanceIdentifier<Node> vppIid,
135             @Nonnull Set<InstanceIdentifier<T>> iids , byte retryCounter) {
136             VbdNetconfTransaction.NODE_DATA_BROKER_MAP.get(vppIid).getValue().lock();
137             boolean result = deleteIfExists(vppIid, iids, retryCounter);
138             VbdNetconfTransaction.NODE_DATA_BROKER_MAP.get(vppIid).getValue().unlock();
139             return result;
140         }
141
142     /***
143      * Netconf wrapper method for synced requests for delete operation on a Netconf Device
144      * @param vppIid        destination node
145      * @param command       config command that needs to be executed
146      * @param retryCounter  retry counter, will repeat the operation for specified amount of times if transaction fails
147      * @return true if transaction is successful, false otherwise
148      */
149     public static boolean netconfSyncedDelete(@Nonnull final InstanceIdentifier<Node> vppIid,
150         @Nonnull final ConfigCommand command, byte retryCounter) {
151         VbdNetconfTransaction.NODE_DATA_BROKER_MAP.get(vppIid).getValue().lock();
152         boolean result = deleteIfExists(vppIid, command.getIid(), retryCounter);
153         VbdNetconfTransaction.NODE_DATA_BROKER_MAP.get(vppIid).getValue().unlock();
154         return result;
155     }
156
157     /***
158      * Netconf wrapper method for synced requests for delete operation on a Netconf Device
159      * @param vppIid        destination node
160      * @param command       routing command that needs to be executed
161      * @param retryCounter  retry counter, will repeat the operation for specified amount of times if transaction fails
162      * @return true if transaction is successful, false otherwise
163      */
164     public static boolean netconfSyncedDelete(@Nonnull final InstanceIdentifier<Node> vppIid,
165         @Nonnull final RoutingCommand command, byte retryCounter) {
166         VbdNetconfTransaction.NODE_DATA_BROKER_MAP.get(vppIid).getValue().lock();
167         boolean result = deleteIfExists(vppIid, command.getIid(), retryCounter);
168         VbdNetconfTransaction.NODE_DATA_BROKER_MAP.get(vppIid).getValue().unlock();
169         return result;
170     }
171
172     /**
173      * Use {@link ConfigCommand} to put data into netconf transaction and submit. Transaction is restarted if failed
174      *
175      * @param mountpoint   to access remote device
176      * @param command      config command with data, datastore type and iid
177      * @param retryCounter number of attempts
178      * @return true if transaction is successful, false otherwise
179      */
180     private static boolean write(final DataBroker mountpoint, final ConfigCommand command, byte retryCounter) {
181         LOG.trace("Netconf WRITE transaction started. RetryCounter: {}", retryCounter);
182         Preconditions.checkNotNull(mountpoint);
183         final ReadWriteTransaction rwTx = mountpoint.newReadWriteTransaction();
184         try {
185             command.execute(rwTx);
186             final CheckedFuture<Void, TransactionCommitFailedException> futureTask = rwTx.submit();
187             futureTask.get();
188             LOG.trace("Netconf WRITE transaction done for command {}", command);
189             return true;
190         } catch (Exception e) {
191             // Retry
192             if (retryCounter > 0) {
193                 LOG.warn("Netconf WRITE transaction failed to {}. Restarting transaction ... ", e.getMessage());
194                 return write(mountpoint, command, --retryCounter);
195             } else {
196                 LOG.warn("Netconf WRITE transaction unsuccessful. Maximal number of attempts reached. Trace: {}", e);
197                 return false;
198             }
199         }
200     }
201
202     /**
203      * Write data to remote device. Transaction is restarted if failed
204      *
205      * @param mountpoint   to access remote device
206      * @param iid          data identifier
207      * @param data         to write
208      * @param retryCounter number of attempts
209      * @param <T>          generic data type. Has to be child of {@link DataObject}
210      * @return true if transaction is successful, false otherwise
211      */
212     private static <T extends DataObject> boolean write(final DataBroker mountpoint, final InstanceIdentifier<T> iid,
213         final T data, byte retryCounter) {
214         LOG.trace("Netconf WRITE transaction started. RetryCounter: {}", retryCounter);
215         Preconditions.checkNotNull(mountpoint);
216         final ReadWriteTransaction rwTx = mountpoint.newReadWriteTransaction();
217         try {
218             rwTx.put(LogicalDatastoreType.CONFIGURATION, iid, data, true);
219             final CheckedFuture<Void, TransactionCommitFailedException> futureTask = rwTx.submit();
220             futureTask.get();
221             LOG.trace("Netconf WRITE transaction done for {}", iid);
222             return true;
223         } catch (Exception e) {
224             // Retry
225             if (retryCounter > 0) {
226                 LOG.warn("Netconf WRITE transaction failed to {}. Restarting transaction ... ", e.getMessage());
227                 return write(mountpoint, iid, data, --retryCounter);
228             } else {
229                 LOG.warn("Netconf WRITE transaction unsuccessful. Maximal number of attempts reached. Trace: {}", e);
230                 return false;
231             }
232         }
233     }
234
235     /**
236      * Merge data to remote device. Transaction is restarted if failed
237      *
238      * @param mountpoint   to access remote device
239      * @param iid          data identifier
240      * @param data         to merge
241      * @param retryCounter number of attempts
242      * @param <T>          generic data type. Has to be child of {@link DataObject}
243      * @return true if transaction is successful, false otherwise
244      */
245     private static <T extends DataObject> boolean merge(final DataBroker mountpoint, final InstanceIdentifier<T> iid,
246                                                         final T data, byte retryCounter) {
247         LOG.trace("Netconf MERGE transaction started. RetryCounter: {}", retryCounter);
248         Preconditions.checkNotNull(mountpoint);
249         final ReadWriteTransaction rwTx = mountpoint.newReadWriteTransaction();
250         try {
251             rwTx.merge(LogicalDatastoreType.CONFIGURATION, iid, data, true);
252             final CheckedFuture<Void, TransactionCommitFailedException> futureTask = rwTx.submit();
253             futureTask.get();
254             LOG.trace("Netconf MERGE transaction done for {}", iid);
255             return true;
256         } catch (Exception e) {
257             // Retry
258             if (retryCounter > 0) {
259                 LOG.warn("Netconf MERGE transaction failed to {}. Restarting transaction ... ", e.getMessage());
260                 return write(mountpoint, iid, data, --retryCounter);
261             } else {
262                 LOG.warn("Netconf MERGE transaction unsuccessful. Maximal number of attempts reached. Trace: {}", e);
263                 return false;
264             }
265         }
266     }
267
268     private static <T extends DataObject> boolean write(final DataBroker mountpoint,
269             @Nonnull final Map<InstanceIdentifier<T>, T> data, byte retryCounter) {
270         LOG.trace("Netconf WRITE transaction started. RetryCounter: {}", retryCounter);
271         Preconditions.checkNotNull(mountpoint);
272         Preconditions.checkNotNull(data);
273         Preconditions.checkArgument(!data.isEmpty());
274         final ReadWriteTransaction rwTx = mountpoint.newReadWriteTransaction();
275         try {
276             data.forEach((k, v) -> rwTx.put(LogicalDatastoreType.CONFIGURATION, k, v, true));
277             final CheckedFuture<Void, TransactionCommitFailedException> futureTask = rwTx.submit();
278             futureTask.get();
279             LOG.trace("Netconf WRITE transaction done for {}", data);
280             return true;
281         } catch (Exception e) {
282             // Retry
283             if (retryCounter > 0) {
284                 LOG.warn("Netconf WRITE transaction failed to {}. Restarting transaction ... ", e.getMessage());
285                 return write(mountpoint, data, --retryCounter);
286             } else {
287                 LOG.warn("Netconf WRITE transaction unsuccessful. Maximal number of attempts reached. Trace: {}", e);
288                 return false;
289             }
290         }
291     }
292
293     /**
294      * Read data from remote device. Transaction is restarted if failed.
295      *
296      * @param datastoreType {@link LogicalDatastoreType}
297      * @param vppIid        destination node
298      * @param iid           data identifier
299      * @param retryCounter  number of attempts
300      * @param <T>           generic data type. Has to be child of {@link DataObject}
301      * @return optional data object if successful, {@link Optional#absent()} if failed
302      */
303     public static synchronized <T extends DataObject> Optional<T> read(final InstanceIdentifier<Node> vppIid,
304         final LogicalDatastoreType datastoreType, final InstanceIdentifier<T> iid, byte retryCounter) {
305         LOG.trace("Netconf READ transaction started. RetryCounter: {}", retryCounter);
306         Preconditions.checkNotNull(vppIid);
307         DataBroker mountpoint = VbdNetconfTransaction.NODE_DATA_BROKER_MAP.get(vppIid).getKey();
308         final ReadOnlyTransaction rTx = mountpoint.newReadOnlyTransaction();
309         Optional<T> data;
310         try {
311             final CheckedFuture<Optional<T>, ReadFailedException> futureData =
312                 rTx.read(datastoreType, iid);
313             data = futureData.get();
314             LOG.trace("Netconf READ transaction done. Data present: {}", data.isPresent());
315             return data;
316         } catch (Exception e) {
317             // Retry
318             if (retryCounter > 0) {
319                 LOG.warn("Netconf READ transaction failed to {}. Restarting transaction ... ", e.getMessage());
320                 rTx.close();
321                 return read(vppIid, datastoreType, iid, --retryCounter);
322             } else {
323                 LOG.warn("Netconf READ transaction unsuccessful. Maximal number of attempts reached. Trace: {}", e);
324                 return Optional.absent();
325             }
326         }
327     }
328
329     /**
330      * Remove data from remote device using {@link ConfigCommand}
331      * @param vppIid       destination node
332      * @param command      config command with data, datastore type and iid
333      * @param retryCounter number of attempts
334      * @return true if transaction is successful, false otherwise
335      */
336     private static boolean deleteIfExists(final InstanceIdentifier<Node> vppIid, final AbstractInterfaceCommand command,
337         byte retryCounter) {
338         Preconditions.checkNotNull(vppIid);
339         InstanceIdentifier<Interface> iid = VppIidFactory.getInterfaceIID(command.getInterfaceBuilder().getKey());
340         return deleteIfExists(vppIid, iid, retryCounter);
341     }
342
343     /**
344      * Remove data from remote device. Data presence is verified before removal. Transaction is restarted if failed.
345      * @param vppIid       destination node
346      * @param iid          data identifier
347      * @param retryCounter number of attempts
348      * @param <T>          generic data type. Has to be child of {@link DataObject}
349      * @return true if transaction is successful, false otherwise
350      */
351     private static <T extends DataObject> boolean deleteIfExists(final InstanceIdentifier<Node> vppIid,
352         final InstanceIdentifier<T> iid, byte retryCounter) {
353         LOG.trace("Netconf DELETE transaction started. Data will be read at first. RetryCounter: {}", retryCounter);
354         Preconditions.checkNotNull(vppIid);
355         DataBroker mountpoint = VbdNetconfTransaction.NODE_DATA_BROKER_MAP.get(vppIid).getKey();
356         final Optional<T> optionalObject = read(vppIid, LogicalDatastoreType.CONFIGURATION, iid, RETRY_COUNT);
357         if (!optionalObject.isPresent()) {
358             LOG.warn("Netconf DELETE transaction aborted. Data to remove are not present or cannot be read. Iid: {}",
359                 iid);
360             // Return true, this state is not considered as an error
361             return true;
362         }
363         final ReadWriteTransaction rwTx = mountpoint.newReadWriteTransaction();
364         try {
365             rwTx.delete(LogicalDatastoreType.CONFIGURATION, iid);
366             final CheckedFuture<Void, TransactionCommitFailedException> futureTask = rwTx.submit();
367             futureTask.get();
368             LOG.trace("Netconf DELETE transaction done for {}", iid);
369             return true;
370         } catch (Exception e) {
371             // Retry
372             if (retryCounter > 0) {
373                 LOG.warn("Netconf DELETE transaction failed to {}. Restarting transaction ... ", e.getMessage());
374                 return deleteIfExists(vppIid, iid, --retryCounter);
375             } else {
376                 LOG.warn("Netconf DELETE transaction unsuccessful. Maximal number of attempts reached. Trace: {}", e);
377                 return false;
378             }
379         }
380     }
381
382     private static <T extends DataObject> boolean deleteIfExists(final InstanceIdentifier<Node> vppIid,
383             final Set<InstanceIdentifier<T>> iids, byte retryCounter) {
384         LOG.trace("Netconf DELETE transaction started. Data will be read at first. RetryCounter: {}", retryCounter);
385         Preconditions.checkNotNull(vppIid);
386         final ReadWriteTransaction rwTx =
387             VbdNetconfTransaction.NODE_DATA_BROKER_MAP.get(vppIid).getKey().newReadWriteTransaction();
388         Set<InstanceIdentifier<T>> alreadyRemoved = new HashSet<>();
389         for (InstanceIdentifier<T> iid : iids) {
390             short microReadRetries = 3;
391             while (microReadRetries > 0) {
392                 try {
393                     if (rwTx.read(LogicalDatastoreType.CONFIGURATION, iid).get().isPresent()) {
394                         rwTx.delete(LogicalDatastoreType.CONFIGURATION, iid);
395                     } else {
396                         LOG.warn("Node {} does not exist. It won't be removed.", iid.getPathArguments());
397                         alreadyRemoved.add(iid);
398                     }
399                     break;
400                 } catch (InterruptedException | ExecutionException e) {
401                     LOG.warn("Failed to read {}. Retrying... ", iid.getPathArguments());
402                     microReadRetries--;
403                 }
404             }
405         }
406         alreadyRemoved.forEach(t -> iids.remove(t));
407         try {
408             final CheckedFuture<Void, TransactionCommitFailedException> futureTask = rwTx.submit();
409             futureTask.get();
410             LOG.trace("Netconf DELETE transaction done for {}", iids);
411             return true;
412         } catch (Exception e) {
413             // Retry
414             if (retryCounter > 0) {
415                 LOG.warn("Netconf DELETE transaction failed to {}. Restarting transaction ... ", e.getMessage());
416                 return deleteIfExists(vppIid, iids, --retryCounter);
417             } else {
418                 LOG.warn("Netconf DELETE transaction unsuccessful. Maximal number of attempts reached. Trace: {}", e);
419                 return false;
420             }
421         }
422     }
423 }