X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?a=blobdiff_plain;f=southbound%2Fsouthbound-impl%2Fsrc%2Fmain%2Fjava%2Forg%2Fopendaylight%2Fovsdb%2Fsouthbound%2Fovsdb%2Ftransact%2FTransactUtils.java;h=09e90faee1a6dc785dc50493552177071eac9aeb;hb=50e7f939993fa9da3c80a1c8d166629a4868af9c;hp=b944715abb0f4837c0282f7f91e343b17df442e4;hpb=7466d0ccf78e5967e41257a8810ae8b9d1ac8925;p=ovsdb.git diff --git a/southbound/southbound-impl/src/main/java/org/opendaylight/ovsdb/southbound/ovsdb/transact/TransactUtils.java b/southbound/southbound-impl/src/main/java/org/opendaylight/ovsdb/southbound/ovsdb/transact/TransactUtils.java index b944715ab..09e90faee 100644 --- a/southbound/southbound-impl/src/main/java/org/opendaylight/ovsdb/southbound/ovsdb/transact/TransactUtils.java +++ b/southbound/southbound-impl/src/main/java/org/opendaylight/ovsdb/southbound/ovsdb/transact/TransactUtils.java @@ -7,41 +7,278 @@ */ 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.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.OvsdbBridgeAugmentation; +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 org.opendaylight.yangtools.yang.binding.KeyedInstanceIdentifier; public class TransactUtils { - private static final Logger LOG = LoggerFactory.getLogger(TransactUtils.class); + private static Predicate> hasDataBefore() { + return new Predicate>() { + @Override + public boolean apply(@Nullable DataObjectModification input) { + return input != null && input.getDataBefore() != null; + } + }; + } + + private static Predicate> hasDataBeforeAndDataAfter() { + return new Predicate>() { + @Override + public boolean apply(@Nullable DataObjectModification input) { + return input != null && input.getDataBefore() != null && input.getDataAfter() != null; + } + }; + } + + private static Predicate> hasNoDataBefore() { + return new Predicate>() { + @Override + public boolean apply(@Nullable DataObjectModification input) { + return input != null && input.getDataBefore() == null; + } + }; + } + + private static Predicate> hasDataAfterAndMatchesFilter( + final Predicate> filter) { + return new Predicate>() { + @Override + public boolean apply(@Nullable DataObjectModification input) { + return input != null && input.getDataAfter() != null && filter.apply(input); + } + }; + } + + private static Predicate> matchesEverything() { + return new Predicate>() { + @Override + public boolean apply(@Nullable DataObjectModification input) { + return true; + } + }; + } + + private static Predicate> modificationIsDeletion() { + return new Predicate>() { + @Override + public boolean apply(@Nullable DataObjectModification input) { + return input != null && input.getModificationType() == DataObjectModification + .ModificationType.DELETE; + } + }; + } + + private static Predicate> + modificationIsDeletionAndHasDataBefore() { + return new Predicate>() { + @Override + public boolean apply(@Nullable DataObjectModification input) { + return input != null && input.getModificationType() == DataObjectModification + .ModificationType.DELETE && input.getDataBefore() != null; + } + }; + } + + public static Map,Node> extractNode( + Map, DataObject> changes) { + Map,Node> result + = new HashMap<>(); + if (changes != null) { + for (Entry, DataObject> created : changes.entrySet()) { + if (created.getValue() instanceof Node) { + Node value = (Node) created.getValue(); + Class type = created.getKey().getTargetType(); + if (type.equals(Node.class)) { + @SuppressWarnings("unchecked") // Actually checked above + InstanceIdentifier iid = (InstanceIdentifier) created.getKey(); + result.put(iid, value); + } + } + } + } + return result; + } + + public static Map,T> extractCreated( + AsyncDataChangeEvent, DataObject> changes,Class klazz) { + return extract(changes.getCreatedData(),klazz); + } - public static Map,OvsdbBridgeAugmentation> extractOvsdbManagedNodeCreate( - AsyncDataChangeEvent, DataObject> changes) { - return extractOvsdbManagedNode(changes.getCreatedData()); + /** + * 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 The type of changes we're interested in. + * @param The type of changes to process. + * @return The created instances, mapped by instance identifier. + */ + public static Map, T> extractCreated( + Collection> changes, Class clazz) { + return extractCreatedOrUpdated(changes, clazz, hasNoDataBefore()); } - public static Map,OvsdbBridgeAugmentation> extractOvsdbManagedNode( - AsyncDataChangeEvent, DataObject> changes) { - return extractOvsdbManagedNode(changes.getUpdatedData()); + public static Map,T> extractUpdated( + AsyncDataChangeEvent,DataObject> changes,Class klazz) { + return extract(changes.getUpdatedData(),klazz); } - public static Set> extractOvsdbManagedNodeRemoved( - AsyncDataChangeEvent, DataObject> changes) { - Set> result = new HashSet>(); + /** + * 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 The type of changes we're interested in. + * @param The type of changes to process. + * @return The updated instances, mapped by instance identifier. + */ + public static Map, T> extractUpdated( + Collection> changes, Class 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 The type of changes we're interested in. + * @param The type of changes to process. + * @return The created or updated instances which satisfy the filter, mapped by instance identifier. + */ + public static Map, T> extractCreatedOrUpdated( + Collection> changes, Class clazz, + Predicate> filter) { + Map, T> result = new HashMap<>(); + for (Map.Entry, DataObjectModification> entry : extractDataObjectModifications(changes, + clazz, hasDataAfterAndMatchesFilter(filter)).entrySet()) { + result.put(entry.getKey(), entry.getValue().getDataAfter()); + } + return result; + } + + public static Map,T> extractCreatedOrUpdated( + AsyncDataChangeEvent,DataObject> changes,Class klazz) { + Map,T> result = extractUpdated(changes,klazz); + result.putAll(extractCreated(changes,klazz)); + 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 The type of changes we're interested in. + * @param The type of changes to process. + * @return The created or updated instances, mapped by instance identifier. + */ + public static Map, T> extractCreatedOrUpdated( + Collection> changes, Class clazz) { + return extractCreatedOrUpdated(changes, clazz, matchesEverything()); + } + + public static Map, T> extractCreatedOrUpdatedOrRemoved( + AsyncDataChangeEvent, DataObject> changes, + Class klazz) { + Map,T> result = extractCreatedOrUpdated(changes,klazz); + result.putAll(extractRemovedObjects(changes, klazz)); + 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 The type of changes we're interested in. + * @param The type of changes to process. + * @return The created, updated or removed instances, mapped by instance identifier. + */ + public static Map, T> + extractCreatedOrUpdatedOrRemoved( + Collection> changes, Class clazz) { + Map, T> result = extractCreatedOrUpdated(changes, clazz); + result.putAll(extractRemovedObjects(changes, clazz)); + return result; + } + + public static Map,T> extractOriginal( + AsyncDataChangeEvent,DataObject> changes,Class 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 The type of changes we're interested in. + * @param The type of changes to process. + * @return The original instances, mapped by instance identifier. + */ + public static Map, T> extractOriginal( + Collection> changes, Class clazz) { + Map, T> result = new HashMap<>(); + for (Map.Entry, DataObjectModification> entry : + extractDataObjectModifications(changes, clazz, hasDataBefore()).entrySet()) { + result.put(entry.getKey(), entry.getValue().getDataBefore()); + } + return result; + } + + public static Set> extractRemoved( + AsyncDataChangeEvent,DataObject> changes,Class klazz) { + Set> result = new HashSet<>(); if (changes != null && changes.getRemovedPaths() != null) { for (InstanceIdentifier iid : changes.getRemovedPaths()) { - if (iid.getTargetType().equals(OvsdbBridgeAugmentation.class)) { + if (iid.getTargetType().equals(klazz)) { @SuppressWarnings("unchecked") // Actually checked above - InstanceIdentifier iidn = (InstanceIdentifier)iid; + InstanceIdentifier iidn = (InstanceIdentifier)iid; result.add(iidn); } } @@ -49,26 +286,142 @@ public class TransactUtils { return result; } - public static Map,OvsdbBridgeAugmentation> extractOvsdbManagedNodeOriginal( - AsyncDataChangeEvent, DataObject> changes) { - return extractOvsdbManagedNode(changes.getOriginalData()); + /** + * 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 The type of changes we're interested in. + * @param The type of changes to process. + * @return The instance identifiers of removed instances. + */ + public static Set> extractRemoved( + Collection> changes, Class 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 The type of changes we're interested in. + * @param The type of changes to process. + * @return The modifications, mapped by instance identifier. + */ + private static Map, DataObjectModification> + extractDataObjectModifications(Collection> changes, Class clazz, + Predicate> filter) { + List> dataObjectModifications = new ArrayList<>(); + List> paths = new ArrayList<>(); + if (changes != null) { + for (DataTreeModification 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 The type of changes we're interested in. + * @return The modifications, mapped by instance identifier. + */ + private static Map, DataObjectModification> + extractDataObjectModifications( + Collection> changes, + Collection> paths, Class clazz, + Predicate> filter) { + Map, DataObjectModification> result = new HashMap<>(); + Queue> remainingChanges = new LinkedList<>(changes); + Queue> remainingPaths = new LinkedList<>(paths); + while (!remainingChanges.isEmpty()) { + DataObjectModification change = remainingChanges.remove(); + InstanceIdentifier path = remainingPaths.remove(); + // Is the change relevant? + if (clazz.isAssignableFrom(change.getDataType()) && filter.apply((DataObjectModification) change)) { + result.put((InstanceIdentifier) path, (DataObjectModification) change); + } + // Add any children to the queue + for (DataObjectModification 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 & ChildOf, K extends Identifier, T extends DataObject> + InstanceIdentifier extendPath( + InstanceIdentifier path, + DataObjectModification child) { + Class item = (Class) child.getDataType(); + if (child.getIdentifier() instanceof InstanceIdentifier.IdentifiableItem) { + K key = (K) ((InstanceIdentifier.IdentifiableItem) child.getIdentifier()).getKey(); + KeyedInstanceIdentifier extendedPath = path.child(item, key); + return extendedPath; + } else { + InstanceIdentifier extendedPath = path.child(item); + return extendedPath; + } + } - public static Map,OvsdbBridgeAugmentation> extractOvsdbManagedNode( - Map, DataObject> changes) { - Map,OvsdbBridgeAugmentation> result - = new HashMap,OvsdbBridgeAugmentation>(); - if (changes != null && changes.entrySet() != null) { + public static Map, T> extractRemovedObjects( + AsyncDataChangeEvent, DataObject> changes, + Class klazz) { + Set> iids = extractRemoved(changes, klazz); + 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 The type of changes we're interested in. + * @param The type of changes to process. + * @return The removed instances, keyed by instance identifier. + */ + public static Map, T> extractRemovedObjects( + Collection> changes, Class clazz) { + Map, T> result = new HashMap<>(); + for (Map.Entry, DataObjectModification> entry : + extractDataObjectModifications(changes, clazz, modificationIsDeletionAndHasDataBefore()).entrySet()) { + result.put(entry.getKey(), entry.getValue().getDataBefore()); + } + return result; + } + + public static Map,T> extract( + Map, DataObject> changes, Class klazz) { + Map,T> result = new HashMap<>(); + if (changes != null) { for (Entry, DataObject> created : changes.entrySet()) { - if (created.getValue() instanceof OvsdbBridgeAugmentation) { - OvsdbBridgeAugmentation value = (OvsdbBridgeAugmentation) created.getValue(); + if (klazz.isInstance(created.getValue())) { + @SuppressWarnings("unchecked") + T value = (T) created.getValue(); Class type = created.getKey().getTargetType(); - if (type.equals(OvsdbBridgeAugmentation.class)) { + if (type.equals(klazz)) { @SuppressWarnings("unchecked") // Actually checked above - InstanceIdentifier iid = (InstanceIdentifier) created.getKey(); - OvsdbBridgeAugmentation ovsdbManagedNode = (OvsdbBridgeAugmentation) value; - result.put(iid, ovsdbManagedNode); + InstanceIdentifier iid = (InstanceIdentifier) created.getKey(); + result.put(iid, value); } } } @@ -76,4 +429,91 @@ public class TransactUtils { return result; } + public static List extractInsert(TransactionBuilder transaction, GenericTableSchema schema) { + List operations = transaction.getOperations(); + List 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 > void stampInstanceIdentifier(TransactionBuilder transaction, + InstanceIdentifier iid, TableSchema tableSchema, + ColumnSchema> columnSchema) { + transaction.add(stampInstanceIdentifierMutation(transaction,iid, + tableSchema,columnSchema)); + } + + public static > Mutate stampInstanceIdentifierMutation(TransactionBuilder transaction, + InstanceIdentifier iid, TableSchema tableSchema, + ColumnSchema> columnSchema) { + Map externalIdsMap = ImmutableMap.of(SouthboundConstants.IID_EXTERNAL_ID_KEY, + SouthboundUtil.serializeInstanceIdentifier(iid)); + Mutate 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 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. + *

+ * 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" + *

+ * @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(); + } }