2 * Copyright (c) 2016 Cisco Systems, Inc. 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.groupbasedpolicy.renderer.vpp.util;
11 import java.util.HashSet;
14 import java.util.concurrent.ExecutionException;
16 import javax.annotation.Nonnull;
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;
35 import com.google.common.base.Optional;
36 import com.google.common.base.Preconditions;
37 import com.google.common.util.concurrent.CheckedFuture;
39 public class GbpNetconfTransaction {
41 public static final byte RETRY_COUNT = 3;
42 private static final Logger LOG = LoggerFactory.getLogger(GbpNetconfTransaction.class);
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
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();
57 write(VbdNetconfTransaction.NODE_DATA_BROKER_MAP.get(vppIid).getKey(), iid, data, retryCounter);
58 VbdNetconfTransaction.NODE_DATA_BROKER_MAP.get(vppIid).getValue().unlock();
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();
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
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();
83 merge(VbdNetconfTransaction.NODE_DATA_BROKER_MAP.get(vppIid).getKey(), iid, data, retryCounter);
84 VbdNetconfTransaction.NODE_DATA_BROKER_MAP.get(vppIid).getValue().unlock();
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
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();
99 write(VbdNetconfTransaction.NODE_DATA_BROKER_MAP.get(vppIid).getKey(), command, retryCounter);
100 VbdNetconfTransaction.NODE_DATA_BROKER_MAP.get(vppIid).getValue().unlock();
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
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();
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
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();
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
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();
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();
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
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();
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
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();
189 * Use {@link ConfigCommand} to put data into netconf transaction and submit. Transaction is restarted if failed
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
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();
201 command.execute(rwTx);
202 final CheckedFuture<Void, TransactionCommitFailedException> futureTask = rwTx.submit();
204 LOG.trace("Netconf WRITE transaction done for command {}", command);
206 } catch (Exception e) {
208 if (retryCounter > 0) {
209 LOG.warn("Netconf WRITE transaction failed to {}. Restarting transaction ... ", e.getMessage());
210 return write(mountpoint, command, --retryCounter);
212 LOG.warn("Netconf WRITE transaction unsuccessful. Maximal number of attempts reached. Trace: {}", e);
219 * Write data to remote device. Transaction is restarted if failed
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
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();
234 rwTx.put(LogicalDatastoreType.CONFIGURATION, iid, data, true);
235 final CheckedFuture<Void, TransactionCommitFailedException> futureTask = rwTx.submit();
237 LOG.trace("Netconf WRITE transaction done for {}", iid);
239 } catch (Exception e) {
241 if (retryCounter > 0) {
242 LOG.warn("Netconf WRITE transaction failed to {}. Restarting transaction ... ", e.getMessage());
243 return write(mountpoint, iid, data, --retryCounter);
245 LOG.warn("Netconf WRITE transaction unsuccessful. Maximal number of attempts reached. Trace: {}", e);
252 * Merge data to remote device. Transaction is restarted if failed
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
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();
267 rwTx.merge(LogicalDatastoreType.CONFIGURATION, iid, data, true);
268 final CheckedFuture<Void, TransactionCommitFailedException> futureTask = rwTx.submit();
270 LOG.trace("Netconf MERGE transaction done for {}", iid);
272 } catch (Exception e) {
274 if (retryCounter > 0) {
275 LOG.warn("Netconf MERGE transaction failed to {}. Restarting transaction ... ", e.getMessage());
276 return write(mountpoint, iid, data, --retryCounter);
278 LOG.warn("Netconf MERGE transaction unsuccessful. Maximal number of attempts reached. Trace: {}", e);
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();
292 data.forEach((k, v) -> rwTx.put(LogicalDatastoreType.CONFIGURATION, k, v, true));
293 final CheckedFuture<Void, TransactionCommitFailedException> futureTask = rwTx.submit();
295 LOG.trace("Netconf WRITE transaction done for {}", data);
297 } catch (Exception e) {
299 if (retryCounter > 0) {
300 LOG.warn("Netconf WRITE transaction failed to {}. Restarting transaction ... ", e.getMessage());
301 return write(mountpoint, data, --retryCounter);
303 LOG.warn("Netconf WRITE transaction unsuccessful. Maximal number of attempts reached. Trace: {}", e);
310 * Read data from remote device. Transaction is restarted if failed.
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
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();
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());
332 } catch (Exception e) {
334 if (retryCounter > 0) {
335 LOG.warn("Netconf READ transaction failed to {}. Restarting transaction ... ", e.getMessage());
337 return read(vppIid, datastoreType, iid, --retryCounter);
339 LOG.warn("Netconf READ transaction unsuccessful. Maximal number of attempts reached. Trace: {}", e);
340 return Optional.absent();
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
352 private static boolean deleteIfExists(final InstanceIdentifier<Node> vppIid, final AbstractInterfaceCommand command,
354 Preconditions.checkNotNull(vppIid);
355 InstanceIdentifier<Interface> iid = VppIidFactory.getInterfaceIID(command.getInterfaceBuilder().getKey());
356 return deleteIfExists(vppIid, iid, retryCounter);
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
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: {}",
376 // Return true, this state is not considered as an error
379 final ReadWriteTransaction rwTx = mountpoint.newReadWriteTransaction();
381 rwTx.delete(LogicalDatastoreType.CONFIGURATION, iid);
382 final CheckedFuture<Void, TransactionCommitFailedException> futureTask = rwTx.submit();
384 LOG.trace("Netconf DELETE transaction done for {}", iid);
386 } catch (Exception e) {
388 if (retryCounter > 0) {
389 LOG.warn("Netconf DELETE transaction failed to {}. Restarting transaction ... ", e.getMessage());
390 return deleteIfExists(vppIid, iid, --retryCounter);
392 LOG.warn("Netconf DELETE transaction unsuccessful. Maximal number of attempts reached. Trace: {}", e);
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) {
409 if (rwTx.read(LogicalDatastoreType.CONFIGURATION, iid).get().isPresent()) {
410 rwTx.delete(LogicalDatastoreType.CONFIGURATION, iid);
412 LOG.warn("Node {} does not exist. It won't be removed.", iid.getPathArguments());
413 alreadyRemoved.add(iid);
416 } catch (InterruptedException | ExecutionException e) {
417 LOG.warn("Failed to read {}. Retrying... ", iid.getPathArguments());
422 alreadyRemoved.forEach(t -> iids.remove(t));
424 final CheckedFuture<Void, TransactionCommitFailedException> futureTask = rwTx.submit();
426 LOG.trace("Netconf DELETE transaction done for {}", iids);
428 } catch (Exception e) {
430 if (retryCounter > 0) {
431 LOG.warn("Netconf DELETE transaction failed to {}. Restarting transaction ... ", e.getMessage());
432 return deleteIfExists(vppIid, iids, --retryCounter);
434 LOG.warn("Netconf DELETE transaction unsuccessful. Maximal number of attempts reached. Trace: {}", e);