ovsdb enable checkstyle on error
[ovsdb.git] / southbound / southbound-impl / src / main / java / org / opendaylight / ovsdb / southbound / ovsdb / transact / TransactUtils.java
index 2d849b35fdbee8cbc09f4dffeb26e61f73b2be51..09e90faee1a6dc785dc50493552177071eac9aeb 100644 (file)
  */
 package org.opendaylight.ovsdb.southbound.ovsdb.transact;
 
+import static org.opendaylight.ovsdb.lib.operations.Operations.op;
+
+import com.google.common.base.Predicate;
+import com.google.common.base.Predicates;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+import java.util.ArrayList;
+import java.util.Collection;
 import java.util.HashMap;
 import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
+import java.util.Queue;
 import java.util.Set;
-
+import javax.annotation.Nullable;
+import org.opendaylight.controller.md.sal.binding.api.DataObjectModification;
+import org.opendaylight.controller.md.sal.binding.api.DataTreeModification;
 import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent;
+import org.opendaylight.ovsdb.lib.notation.Mutation;
+import org.opendaylight.ovsdb.lib.notation.Mutator;
+import org.opendaylight.ovsdb.lib.notation.OvsdbSet;
+import org.opendaylight.ovsdb.lib.notation.UUID;
+import org.opendaylight.ovsdb.lib.operations.Insert;
+import org.opendaylight.ovsdb.lib.operations.Mutate;
+import org.opendaylight.ovsdb.lib.operations.Operation;
+import org.opendaylight.ovsdb.lib.operations.TransactionBuilder;
+import org.opendaylight.ovsdb.lib.schema.ColumnSchema;
+import org.opendaylight.ovsdb.lib.schema.GenericTableSchema;
+import org.opendaylight.ovsdb.lib.schema.TableSchema;
+import org.opendaylight.ovsdb.southbound.SouthboundConstants;
+import org.opendaylight.ovsdb.southbound.SouthboundMapper;
+import org.opendaylight.ovsdb.southbound.SouthboundUtil;
 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
+import org.opendaylight.yangtools.yang.binding.ChildOf;
 import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.Identifiable;
+import org.opendaylight.yangtools.yang.binding.Identifier;
 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import com.google.common.base.Predicates;
-import com.google.common.collect.Maps;
+import org.opendaylight.yangtools.yang.binding.KeyedInstanceIdentifier;
 
 public class TransactUtils {
-    private static final Logger LOG = LoggerFactory.getLogger(TransactUtils.class);
+    private static <T extends DataObject> Predicate<DataObjectModification<T>> hasDataBefore() {
+        return new Predicate<DataObjectModification<T>>() {
+            @Override
+            public boolean apply(@Nullable DataObjectModification<T> input) {
+                return input != null && input.getDataBefore() != null;
+            }
+        };
+    }
+
+    private static <T extends DataObject> Predicate<DataObjectModification<T>> hasDataBeforeAndDataAfter() {
+        return new Predicate<DataObjectModification<T>>() {
+            @Override
+            public boolean apply(@Nullable DataObjectModification<T> input) {
+                return input != null && input.getDataBefore() != null && input.getDataAfter() != null;
+            }
+        };
+    }
+
+    private static <T extends DataObject> Predicate<DataObjectModification<T>> hasNoDataBefore() {
+        return new Predicate<DataObjectModification<T>>() {
+            @Override
+            public boolean apply(@Nullable DataObjectModification<T> input) {
+                return input != null && input.getDataBefore() == null;
+            }
+        };
+    }
+
+    private static <T extends DataObject> Predicate<DataObjectModification<T>> hasDataAfterAndMatchesFilter(
+            final Predicate<DataObjectModification<T>> filter) {
+        return new Predicate<DataObjectModification<T>>() {
+            @Override
+            public boolean apply(@Nullable DataObjectModification<T> input) {
+                return input != null && input.getDataAfter() != null && filter.apply(input);
+            }
+        };
+    }
+
+    private static <T extends DataObject> Predicate<DataObjectModification<T>> matchesEverything() {
+        return new Predicate<DataObjectModification<T>>() {
+            @Override
+            public boolean apply(@Nullable DataObjectModification<T> input) {
+                return true;
+            }
+        };
+    }
+
+    private static <T extends DataObject> Predicate<DataObjectModification<T>> modificationIsDeletion() {
+        return new Predicate<DataObjectModification<T>>() {
+            @Override
+            public boolean apply(@Nullable DataObjectModification<T> input) {
+                return input != null && input.getModificationType() == DataObjectModification
+                        .ModificationType.DELETE;
+            }
+        };
+    }
+
+    private static <T extends DataObject> Predicate<DataObjectModification<T>>
+        modificationIsDeletionAndHasDataBefore() {
+        return new Predicate<DataObjectModification<T>>() {
+            @Override
+            public boolean apply(@Nullable DataObjectModification<T> input) {
+                return input != null && input.getModificationType() == DataObjectModification
+                        .ModificationType.DELETE && input.getDataBefore() != null;
+            }
+        };
+    }
 
     public static Map<InstanceIdentifier<Node>,Node> extractNode(
             Map<InstanceIdentifier<?>, DataObject> changes) {
         Map<InstanceIdentifier<Node>,Node> result
-            = new HashMap<InstanceIdentifier<Node>,Node>();
-        if (changes != null && changes.entrySet() != null) {
+            = new HashMap<>();
+        if (changes != null) {
             for (Entry<InstanceIdentifier<?>, DataObject> created : changes.entrySet()) {
                 if (created.getValue() instanceof Node) {
                     Node value = (Node) created.getValue();
@@ -51,11 +144,61 @@ public class TransactUtils {
         return extract(changes.getCreatedData(),klazz);
     }
 
+    /**
+     * Extract all the instances of {@code clazz} which were created in the given set of modifications.
+     *
+     * @param changes The changes to process.
+     * @param clazz The class we're interested in.
+     * @param <T> The type of changes we're interested in.
+     * @param <U> The type of changes to process.
+     * @return The created instances, mapped by instance identifier.
+     */
+    public static <T extends DataObject, U extends DataObject> Map<InstanceIdentifier<T>, T> extractCreated(
+            Collection<DataTreeModification<U>> changes, Class<T> clazz) {
+        return extractCreatedOrUpdated(changes, clazz, hasNoDataBefore());
+    }
+
     public static <T extends DataObject> Map<InstanceIdentifier<T>,T> extractUpdated(
             AsyncDataChangeEvent<InstanceIdentifier<?>,DataObject> changes,Class<T> klazz) {
         return extract(changes.getUpdatedData(),klazz);
     }
 
+    /**
+     * Extract all the instances of {@code clazz} which were updated in the given set of modifications.
+     *
+     * @param changes The changes to process.
+     * @param clazz The class we're interested in.
+     * @param <T> The type of changes we're interested in.
+     * @param <U> The type of changes to process.
+     * @return The updated instances, mapped by instance identifier.
+     */
+    public static <T extends DataObject, U extends DataObject> Map<InstanceIdentifier<T>, T> extractUpdated(
+            Collection<DataTreeModification<U>> changes, Class<T> clazz) {
+        return extractCreatedOrUpdated(changes, clazz, hasDataBeforeAndDataAfter());
+    }
+
+    /**
+     * Extract all the instance of {@code clazz} which were created or updated in the given set of modifications, and
+     * which satisfy the given filter.
+     *
+     * @param changes The changes to process.
+     * @param clazz The class we're interested in.
+     * @param filter The filter the changes must satisfy.
+     * @param <T> The type of changes we're interested in.
+     * @param <U> The type of changes to process.
+     * @return The created or updated instances which satisfy the filter, mapped by instance identifier.
+     */
+    public static <T extends DataObject, U extends DataObject> Map<InstanceIdentifier<T>, T> extractCreatedOrUpdated(
+            Collection<DataTreeModification<U>> changes, Class<T> clazz,
+            Predicate<DataObjectModification<T>> filter) {
+        Map<InstanceIdentifier<T>, T> result = new HashMap<>();
+        for (Map.Entry<InstanceIdentifier<T>, DataObjectModification<T>> entry : extractDataObjectModifications(changes,
+                clazz, hasDataAfterAndMatchesFilter(filter)).entrySet()) {
+            result.put(entry.getKey(), entry.getValue().getDataAfter());
+        }
+        return result;
+    }
+
     public static <T extends DataObject> Map<InstanceIdentifier<T>,T> extractCreatedOrUpdated(
             AsyncDataChangeEvent<InstanceIdentifier<?>,DataObject> changes,Class<T> klazz) {
         Map<InstanceIdentifier<T>,T> result = extractUpdated(changes,klazz);
@@ -63,6 +206,20 @@ public class TransactUtils {
         return result;
     }
 
+    /**
+     * Extract all the instances of {@code clazz} which were created or updated in the given set of modifications.
+     *
+     * @param changes The changes to process.
+     * @param clazz The class we're interested in.
+     * @param <T> The type of changes we're interested in.
+     * @param <U> The type of changes to process.
+     * @return The created or updated instances, mapped by instance identifier.
+     */
+    public static <T extends DataObject, U extends DataObject> Map<InstanceIdentifier<T>, T> extractCreatedOrUpdated(
+            Collection<DataTreeModification<U>> changes, Class<T> clazz) {
+        return extractCreatedOrUpdated(changes, clazz, matchesEverything());
+    }
+
     public static <T extends DataObject> Map<InstanceIdentifier<T>, T> extractCreatedOrUpdatedOrRemoved(
             AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> changes,
             Class<T> klazz) {
@@ -71,14 +228,52 @@ public class TransactUtils {
         return result;
     }
 
+    /**
+     * Extract all the instances of {@code clazz} which were created, updated, or removed in the given set of
+     * modifications. For instances which were created or updated, the new instances are returned; for instances
+     * which were removed, the old instances are returned.
+     *
+     * @param changes The changes to process.
+     * @param clazz The class we're interested in.
+     * @param <T> The type of changes we're interested in.
+     * @param <U> The type of changes to process.
+     * @return The created, updated or removed instances, mapped by instance identifier.
+     */
+    public static <T extends DataObject, U extends DataObject> Map<InstanceIdentifier<T>, T>
+        extractCreatedOrUpdatedOrRemoved(
+            Collection<DataTreeModification<U>> changes, Class<T> clazz) {
+        Map<InstanceIdentifier<T>, T> result = extractCreatedOrUpdated(changes, clazz);
+        result.putAll(extractRemovedObjects(changes, clazz));
+        return result;
+    }
+
     public static <T extends DataObject> Map<InstanceIdentifier<T>,T> extractOriginal(
             AsyncDataChangeEvent<InstanceIdentifier<?>,DataObject> changes,Class<T> klazz) {
         return extract(changes.getOriginalData(),klazz);
     }
 
+    /**
+     * Extract the original instances of class {@code clazz} in the given set of modifications.
+     *
+     * @param changes The changes to process.
+     * @param clazz The class we're interested in.
+     * @param <T> The type of changes we're interested in.
+     * @param <U> The type of changes to process.
+     * @return The original instances, mapped by instance identifier.
+     */
+    public static <T extends DataObject, U extends DataObject> Map<InstanceIdentifier<T>, T> extractOriginal(
+            Collection<DataTreeModification<U>> changes, Class<T> clazz) {
+        Map<InstanceIdentifier<T>, T> result = new HashMap<>();
+        for (Map.Entry<InstanceIdentifier<T>, DataObjectModification<T>> entry :
+                extractDataObjectModifications(changes, clazz, hasDataBefore()).entrySet()) {
+            result.put(entry.getKey(), entry.getValue().getDataBefore());
+        }
+        return result;
+    }
+
     public static <T extends DataObject> Set<InstanceIdentifier<T>> extractRemoved(
             AsyncDataChangeEvent<InstanceIdentifier<?>,DataObject> changes,Class<T> klazz) {
-        Set<InstanceIdentifier<T>> result = new HashSet<InstanceIdentifier<T>>();
+        Set<InstanceIdentifier<T>> result = new HashSet<>();
         if (changes != null && changes.getRemovedPaths() != null) {
             for (InstanceIdentifier<?> iid : changes.getRemovedPaths()) {
                 if (iid.getTargetType().equals(klazz)) {
@@ -91,6 +286,103 @@ public class TransactUtils {
         return result;
     }
 
+    /**
+     * Extract the instance identifier of removed instances of {@code clazz} from the given set of modifications.
+     *
+     * @param changes The changes to process.
+     * @param clazz The class we're interested in.
+     * @param <T> The type of changes we're interested in.
+     * @param <U> The type of changes to process.
+     * @return The instance identifiers of removed instances.
+     */
+    public static <T extends DataObject, U extends DataObject> Set<InstanceIdentifier<T>> extractRemoved(
+            Collection<DataTreeModification<U>> changes, Class<T> clazz) {
+        return extractDataObjectModifications(changes, clazz, modificationIsDeletion()).keySet();
+    }
+
+    /**
+     * Extract all the modifications affecting instances of {@code clazz} which are present in the given set of
+     * modifications and satisfy the given filter.
+     *
+     * @param changes The changes to process.
+     * @param clazz The class we're interested in.
+     * @param filter The filter the changes must satisfy.
+     * @param <T> The type of changes we're interested in.
+     * @param <U> The type of changes to process.
+     * @return The modifications, mapped by instance identifier.
+     */
+    private static <T extends DataObject, U extends DataObject> Map<InstanceIdentifier<T>, DataObjectModification<T>>
+        extractDataObjectModifications(Collection<DataTreeModification<U>> changes, Class<T> clazz,
+                                       Predicate<DataObjectModification<T>> filter) {
+        List<DataObjectModification<? extends DataObject>> dataObjectModifications = new ArrayList<>();
+        List<InstanceIdentifier<? extends DataObject>> paths = new ArrayList<>();
+        if (changes != null) {
+            for (DataTreeModification<? extends DataObject> change : changes) {
+                dataObjectModifications.add(change.getRootNode());
+                paths.add(change.getRootPath().getRootIdentifier());
+            }
+        }
+        return extractDataObjectModifications(dataObjectModifications, paths, clazz, filter);
+    }
+
+    /**
+     * Extract all the modifications affecting instances of {@code clazz} which are present in the given set of
+     * modifications and satisfy the given filter.
+     *
+     * @param changes The changes to process.
+     * @param paths The paths of the changes.
+     * @param clazz The class we're interested in.
+     * @param filter The filter the changes must satisfy.
+     * @param <T> The type of changes we're interested in.
+     * @return The modifications, mapped by instance identifier.
+     */
+    private static <T extends DataObject> Map<InstanceIdentifier<T>, DataObjectModification<T>>
+        extractDataObjectModifications(
+            Collection<DataObjectModification<? extends DataObject>> changes,
+            Collection<InstanceIdentifier<? extends DataObject>> paths, Class<T> clazz,
+            Predicate<DataObjectModification<T>> filter) {
+        Map<InstanceIdentifier<T>, DataObjectModification<T>> result = new HashMap<>();
+        Queue<DataObjectModification<? extends DataObject>> remainingChanges = new LinkedList<>(changes);
+        Queue<InstanceIdentifier<? extends DataObject>> remainingPaths = new LinkedList<>(paths);
+        while (!remainingChanges.isEmpty()) {
+            DataObjectModification<? extends DataObject> change = remainingChanges.remove();
+            InstanceIdentifier<? extends DataObject> path = remainingPaths.remove();
+            // Is the change relevant?
+            if (clazz.isAssignableFrom(change.getDataType()) && filter.apply((DataObjectModification<T>) change)) {
+                result.put((InstanceIdentifier<T>) path, (DataObjectModification<T>) change);
+            }
+            // Add any children to the queue
+            for (DataObjectModification<? extends DataObject> child : change.getModifiedChildren()) {
+                remainingChanges.add(child);
+                remainingPaths.add(extendPath(path, child));
+            }
+        }
+        return result;
+    }
+
+    /**
+     * Extends the given instance identifier path to include the given child. Augmentations are treated in the same way
+     * as children; keyed children are handled correctly.
+     *
+     * @param path The current path.
+     * @param child The child modification to include.
+     * @return The extended path.
+     */
+    private static <N extends Identifiable<K> & ChildOf<? super T>, K extends Identifier<N>, T extends DataObject>
+        InstanceIdentifier<? extends DataObject> extendPath(
+            InstanceIdentifier path,
+            DataObjectModification child) {
+        Class<N> item = (Class<N>) child.getDataType();
+        if (child.getIdentifier() instanceof InstanceIdentifier.IdentifiableItem) {
+            K key = (K) ((InstanceIdentifier.IdentifiableItem) child.getIdentifier()).getKey();
+            KeyedInstanceIdentifier<N, K> extendedPath = path.child(item, key);
+            return extendedPath;
+        } else {
+            InstanceIdentifier<N> extendedPath = path.child(item);
+            return extendedPath;
+        }
+    }
+
     public static <T extends DataObject> Map<InstanceIdentifier<T>, T> extractRemovedObjects(
             AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> changes,
             Class<T> klazz) {
@@ -98,10 +390,29 @@ public class TransactUtils {
         return Maps.filterKeys(extractOriginal(changes, klazz),Predicates.in(iids));
     }
 
+    /**
+     * Extract the removed instances of {@code clazz} from the given set of modifications.
+     *
+     * @param changes The changes to process.
+     * @param clazz The class we're interested in.
+     * @param <T> The type of changes we're interested in.
+     * @param <U> The type of changes to process.
+     * @return The removed instances, keyed by instance identifier.
+     */
+    public static <T extends DataObject, U extends DataObject> Map<InstanceIdentifier<T>, T> extractRemovedObjects(
+            Collection<DataTreeModification<U>> changes, Class<T> clazz) {
+        Map<InstanceIdentifier<T>, T> result = new HashMap<>();
+        for (Map.Entry<InstanceIdentifier<T>, DataObjectModification<T>> entry :
+                extractDataObjectModifications(changes, clazz, modificationIsDeletionAndHasDataBefore()).entrySet()) {
+            result.put(entry.getKey(), entry.getValue().getDataBefore());
+        }
+        return result;
+    }
+
     public static <T extends DataObject> Map<InstanceIdentifier<T>,T> extract(
             Map<InstanceIdentifier<?>, DataObject> changes, Class<T> klazz) {
-        Map<InstanceIdentifier<T>,T> result = new HashMap<InstanceIdentifier<T>,T>();
-        if (changes != null && changes.entrySet() != null) {
+        Map<InstanceIdentifier<T>,T> result = new HashMap<>();
+        if (changes != null) {
             for (Entry<InstanceIdentifier<?>, DataObject> created : changes.entrySet()) {
                 if (klazz.isInstance(created.getValue())) {
                     @SuppressWarnings("unchecked")
@@ -117,4 +428,92 @@ public class TransactUtils {
         }
         return result;
     }
+
+    public static List<Insert> extractInsert(TransactionBuilder transaction, GenericTableSchema schema) {
+        List<Operation> operations = transaction.getOperations();
+        List<Insert> inserts = new ArrayList<>();
+        for (Operation operation : operations) {
+            if (operation instanceof Insert && operation.getTableSchema().equals(schema)) {
+                inserts.add((Insert) operation);
+            }
+        }
+        return inserts;
+    }
+
+    /**
+     * Extract the NamedUuid from the Insert.
+     * If the Insert does not have a NamedUuid set, a random one will be
+     * generated, set, and returned.
+     *
+     * @param insert - Insert from which to extract the NamedUuid
+     * @return UUID - NamedUUID of the Insert
+     */
+    public static UUID extractNamedUuid(Insert insert) {
+        String uuidString = insert.getUuidName() != null
+                ? insert.getUuidName() : SouthboundMapper.getRandomUuid();
+        insert.setUuidName(uuidString);
+        return new UUID(uuidString);
+    }
+
+    public static <T  extends TableSchema<T>> void stampInstanceIdentifier(TransactionBuilder transaction,
+            InstanceIdentifier<?> iid, TableSchema<T> tableSchema,
+            ColumnSchema<T, Map<String,String>> columnSchema) {
+        transaction.add(stampInstanceIdentifierMutation(transaction,iid,
+                tableSchema,columnSchema));
+    }
+
+    public static <T  extends TableSchema<T>> Mutate<T> stampInstanceIdentifierMutation(TransactionBuilder transaction,
+            InstanceIdentifier<?> iid, TableSchema<T> tableSchema,
+            ColumnSchema<T, Map<String,String>> columnSchema) {
+        Map<String,String> externalIdsMap = ImmutableMap.of(SouthboundConstants.IID_EXTERNAL_ID_KEY,
+                SouthboundUtil.serializeInstanceIdentifier(iid));
+        Mutate<T> mutate = op.mutate(tableSchema)
+                .addMutation(columnSchema,
+                    Mutator.INSERT,
+                    externalIdsMap);
+        Mutation deleteIidMutation = new Mutation(columnSchema.getName(),
+                Mutator.DELETE,
+                OvsdbSet.fromSet(Sets.newHashSet(SouthboundConstants.IID_EXTERNAL_ID_KEY)));
+        List<Mutation> mutations = Lists.newArrayList(Sets.newHashSet(deleteIidMutation));
+        mutations.addAll(mutate.getMutations());
+        mutate.setMutations(mutations);
+        return mutate;
+    }
+
+    /**
+     * This method builds a string by concatenating the 2 character
+     * hexadecimal representation of each byte from the input byte array.
+     * <p>
+     * For example: an input byte array containing:
+     *   bytes[0] = 'a'
+     *   bytes[1] = 'b'
+     *   bytes[2] = 'c'
+     *   bytes[3] = '-'
+     *   bytes[4] = '1'
+     *   bytes[5] = '2'
+     *   bytes[6] = '3'
+     * returns the string "6162632d313233"
+     * </p>
+     * @param bytes
+     *            The byte array to convert to string
+     * @return The hexadecimal representation of the byte array. If bytes is
+     *         null, the string "" is returned
+     */
+    public static String bytesToHexString(byte[] bytes) {
+
+        if (bytes == null) {
+            return "";
+        }
+
+        StringBuffer buf = new StringBuffer();
+        for (int i = 0; i < bytes.length; i++) {
+            short u8byte = (short) (bytes[i] & 0xff);
+            String tmp = Integer.toHexString(u8byte);
+            if (tmp.length() == 1) {
+                buf.append("0");
+            }
+            buf.append(tmp);
+        }
+        return buf.toString();
+    }
 }