2 * Copyright (c) 2014 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
8 package org.opendaylight.ovsdb.southbound.ovsdb.transact;
10 import static org.opendaylight.ovsdb.lib.operations.Operations.op;
12 import com.google.common.base.Predicate;
13 import com.google.common.base.Predicates;
14 import com.google.common.collect.ImmutableMap;
15 import com.google.common.collect.Lists;
16 import com.google.common.collect.Maps;
17 import com.google.common.collect.Sets;
18 import java.util.ArrayList;
19 import java.util.Collection;
20 import java.util.HashMap;
21 import java.util.HashSet;
22 import java.util.LinkedList;
23 import java.util.List;
25 import java.util.Map.Entry;
26 import java.util.Queue;
28 import javax.annotation.Nullable;
29 import org.opendaylight.controller.md.sal.binding.api.DataObjectModification;
30 import org.opendaylight.controller.md.sal.binding.api.DataTreeModification;
31 import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent;
32 import org.opendaylight.ovsdb.lib.notation.Mutation;
33 import org.opendaylight.ovsdb.lib.notation.Mutator;
34 import org.opendaylight.ovsdb.lib.notation.OvsdbSet;
35 import org.opendaylight.ovsdb.lib.notation.UUID;
36 import org.opendaylight.ovsdb.lib.operations.Insert;
37 import org.opendaylight.ovsdb.lib.operations.Mutate;
38 import org.opendaylight.ovsdb.lib.operations.Operation;
39 import org.opendaylight.ovsdb.lib.operations.TransactionBuilder;
40 import org.opendaylight.ovsdb.lib.schema.ColumnSchema;
41 import org.opendaylight.ovsdb.lib.schema.GenericTableSchema;
42 import org.opendaylight.ovsdb.lib.schema.TableSchema;
43 import org.opendaylight.ovsdb.southbound.SouthboundConstants;
44 import org.opendaylight.ovsdb.southbound.SouthboundMapper;
45 import org.opendaylight.ovsdb.southbound.SouthboundUtil;
46 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
47 import org.opendaylight.yangtools.yang.binding.ChildOf;
48 import org.opendaylight.yangtools.yang.binding.DataObject;
49 import org.opendaylight.yangtools.yang.binding.Identifiable;
50 import org.opendaylight.yangtools.yang.binding.Identifier;
51 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
52 import org.opendaylight.yangtools.yang.binding.KeyedInstanceIdentifier;
54 public class TransactUtils {
55 private static <T extends DataObject> Predicate<DataObjectModification<T>> hasDataBefore() {
56 return new Predicate<DataObjectModification<T>>() {
58 public boolean apply(@Nullable DataObjectModification<T> input) {
59 return input != null && input.getDataBefore() != null;
64 private static <T extends DataObject> Predicate<DataObjectModification<T>> hasDataBeforeAndDataAfter() {
65 return new Predicate<DataObjectModification<T>>() {
67 public boolean apply(@Nullable DataObjectModification<T> input) {
68 return input != null && input.getDataBefore() != null && input.getDataAfter() != null;
73 private static <T extends DataObject> Predicate<DataObjectModification<T>> hasNoDataBefore() {
74 return new Predicate<DataObjectModification<T>>() {
76 public boolean apply(@Nullable DataObjectModification<T> input) {
77 return input != null && input.getDataBefore() == null;
82 private static <T extends DataObject> Predicate<DataObjectModification<T>> hasDataAfterAndMatchesFilter(
83 final Predicate<DataObjectModification<T>> filter) {
84 return new Predicate<DataObjectModification<T>>() {
86 public boolean apply(@Nullable DataObjectModification<T> input) {
87 return input != null && input.getDataAfter() != null && filter.apply(input);
92 private static <T extends DataObject> Predicate<DataObjectModification<T>> matchesEverything() {
93 return new Predicate<DataObjectModification<T>>() {
95 public boolean apply(@Nullable DataObjectModification<T> input) {
101 private static <T extends DataObject> Predicate<DataObjectModification<T>> modificationIsDeletion() {
102 return new Predicate<DataObjectModification<T>>() {
104 public boolean apply(@Nullable DataObjectModification<T> input) {
105 return input != null && input.getModificationType() == DataObjectModification
106 .ModificationType.DELETE;
111 private static <T extends DataObject> Predicate<DataObjectModification<T>>
112 modificationIsDeletionAndHasDataBefore() {
113 return new Predicate<DataObjectModification<T>>() {
115 public boolean apply(@Nullable DataObjectModification<T> input) {
116 return input != null && input.getModificationType() == DataObjectModification
117 .ModificationType.DELETE && input.getDataBefore() != null;
122 public static Map<InstanceIdentifier<Node>,Node> extractNode(
123 Map<InstanceIdentifier<?>, DataObject> changes) {
124 Map<InstanceIdentifier<Node>,Node> result
126 if (changes != null) {
127 for (Entry<InstanceIdentifier<?>, DataObject> created : changes.entrySet()) {
128 if (created.getValue() instanceof Node) {
129 Node value = (Node) created.getValue();
130 Class<?> type = created.getKey().getTargetType();
131 if (type.equals(Node.class)) {
132 @SuppressWarnings("unchecked") // Actually checked above
133 InstanceIdentifier<Node> iid = (InstanceIdentifier<Node>) created.getKey();
134 result.put(iid, value);
142 public static <T extends DataObject> Map<InstanceIdentifier<T>,T> extractCreated(
143 AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> changes,Class<T> klazz) {
144 return extract(changes.getCreatedData(),klazz);
148 * Extract all the instances of {@code clazz} which were created in the given set of modifications.
150 * @param changes The changes to process.
151 * @param clazz The class we're interested in.
152 * @param <T> The type of changes we're interested in.
153 * @param <U> The type of changes to process.
154 * @return The created instances, mapped by instance identifier.
156 public static <T extends DataObject, U extends DataObject> Map<InstanceIdentifier<T>, T> extractCreated(
157 Collection<DataTreeModification<U>> changes, Class<T> clazz) {
158 return extractCreatedOrUpdated(changes, clazz, hasNoDataBefore());
161 public static <T extends DataObject> Map<InstanceIdentifier<T>,T> extractUpdated(
162 AsyncDataChangeEvent<InstanceIdentifier<?>,DataObject> changes,Class<T> klazz) {
163 return extract(changes.getUpdatedData(),klazz);
167 * Extract all the instances of {@code clazz} which were updated in the given set of modifications.
169 * @param changes The changes to process.
170 * @param clazz The class we're interested in.
171 * @param <T> The type of changes we're interested in.
172 * @param <U> The type of changes to process.
173 * @return The updated instances, mapped by instance identifier.
175 public static <T extends DataObject, U extends DataObject> Map<InstanceIdentifier<T>, T> extractUpdated(
176 Collection<DataTreeModification<U>> changes, Class<T> clazz) {
177 return extractCreatedOrUpdated(changes, clazz, hasDataBeforeAndDataAfter());
181 * Extract all the instance of {@code clazz} which were created or updated in the given set of modifications, and
182 * which satisfy the given filter.
184 * @param changes The changes to process.
185 * @param clazz The class we're interested in.
186 * @param filter The filter the changes must satisfy.
187 * @param <T> The type of changes we're interested in.
188 * @param <U> The type of changes to process.
189 * @return The created or updated instances which satisfy the filter, mapped by instance identifier.
191 public static <T extends DataObject, U extends DataObject> Map<InstanceIdentifier<T>, T> extractCreatedOrUpdated(
192 Collection<DataTreeModification<U>> changes, Class<T> clazz,
193 Predicate<DataObjectModification<T>> filter) {
194 Map<InstanceIdentifier<T>, T> result = new HashMap<>();
195 for (Map.Entry<InstanceIdentifier<T>, DataObjectModification<T>> entry : extractDataObjectModifications(changes,
196 clazz, hasDataAfterAndMatchesFilter(filter)).entrySet()) {
197 result.put(entry.getKey(), entry.getValue().getDataAfter());
202 public static <T extends DataObject> Map<InstanceIdentifier<T>,T> extractCreatedOrUpdated(
203 AsyncDataChangeEvent<InstanceIdentifier<?>,DataObject> changes,Class<T> klazz) {
204 Map<InstanceIdentifier<T>,T> result = extractUpdated(changes,klazz);
205 result.putAll(extractCreated(changes,klazz));
210 * Extract all the instances of {@code clazz} which were created or updated in the given set of modifications.
212 * @param changes The changes to process.
213 * @param clazz The class we're interested in.
214 * @param <T> The type of changes we're interested in.
215 * @param <U> The type of changes to process.
216 * @return The created or updated instances, mapped by instance identifier.
218 public static <T extends DataObject, U extends DataObject> Map<InstanceIdentifier<T>, T> extractCreatedOrUpdated(
219 Collection<DataTreeModification<U>> changes, Class<T> clazz) {
220 return extractCreatedOrUpdated(changes, clazz, matchesEverything());
223 public static <T extends DataObject> Map<InstanceIdentifier<T>, T> extractCreatedOrUpdatedOrRemoved(
224 AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> changes,
226 Map<InstanceIdentifier<T>,T> result = extractCreatedOrUpdated(changes,klazz);
227 result.putAll(extractRemovedObjects(changes, klazz));
232 * Extract all the instances of {@code clazz} which were created, updated, or removed in the given set of
233 * modifications. For instances which were created or updated, the new instances are returned; for instances
234 * which were removed, the old instances are returned.
236 * @param changes The changes to process.
237 * @param clazz The class we're interested in.
238 * @param <T> The type of changes we're interested in.
239 * @param <U> The type of changes to process.
240 * @return The created, updated or removed instances, mapped by instance identifier.
242 public static <T extends DataObject, U extends DataObject> Map<InstanceIdentifier<T>, T>
243 extractCreatedOrUpdatedOrRemoved(
244 Collection<DataTreeModification<U>> changes, Class<T> clazz) {
245 Map<InstanceIdentifier<T>, T> result = extractCreatedOrUpdated(changes, clazz);
246 result.putAll(extractRemovedObjects(changes, clazz));
250 public static <T extends DataObject> Map<InstanceIdentifier<T>,T> extractOriginal(
251 AsyncDataChangeEvent<InstanceIdentifier<?>,DataObject> changes,Class<T> klazz) {
252 return extract(changes.getOriginalData(),klazz);
256 * Extract the original instances of class {@code clazz} in the given set of modifications.
258 * @param changes The changes to process.
259 * @param clazz The class we're interested in.
260 * @param <T> The type of changes we're interested in.
261 * @param <U> The type of changes to process.
262 * @return The original instances, mapped by instance identifier.
264 public static <T extends DataObject, U extends DataObject> Map<InstanceIdentifier<T>, T> extractOriginal(
265 Collection<DataTreeModification<U>> changes, Class<T> clazz) {
266 Map<InstanceIdentifier<T>, T> result = new HashMap<>();
267 for (Map.Entry<InstanceIdentifier<T>, DataObjectModification<T>> entry :
268 extractDataObjectModifications(changes, clazz, hasDataBefore()).entrySet()) {
269 result.put(entry.getKey(), entry.getValue().getDataBefore());
274 public static <T extends DataObject> Set<InstanceIdentifier<T>> extractRemoved(
275 AsyncDataChangeEvent<InstanceIdentifier<?>,DataObject> changes,Class<T> klazz) {
276 Set<InstanceIdentifier<T>> result = new HashSet<>();
277 if (changes != null && changes.getRemovedPaths() != null) {
278 for (InstanceIdentifier<?> iid : changes.getRemovedPaths()) {
279 if (iid.getTargetType().equals(klazz)) {
280 @SuppressWarnings("unchecked") // Actually checked above
281 InstanceIdentifier<T> iidn = (InstanceIdentifier<T>)iid;
290 * Extract the instance identifier of removed instances of {@code clazz} from the given set of modifications.
292 * @param changes The changes to process.
293 * @param clazz The class we're interested in.
294 * @param <T> The type of changes we're interested in.
295 * @param <U> The type of changes to process.
296 * @return The instance identifiers of removed instances.
298 public static <T extends DataObject, U extends DataObject> Set<InstanceIdentifier<T>> extractRemoved(
299 Collection<DataTreeModification<U>> changes, Class<T> clazz) {
300 return extractDataObjectModifications(changes, clazz, modificationIsDeletion()).keySet();
304 * Extract all the modifications affecting instances of {@code clazz} which are present in the given set of
305 * modifications and satisfy the given filter.
307 * @param changes The changes to process.
308 * @param clazz The class we're interested in.
309 * @param filter The filter the changes must satisfy.
310 * @param <T> The type of changes we're interested in.
311 * @param <U> The type of changes to process.
312 * @return The modifications, mapped by instance identifier.
314 private static <T extends DataObject, U extends DataObject> Map<InstanceIdentifier<T>, DataObjectModification<T>>
315 extractDataObjectModifications(Collection<DataTreeModification<U>> changes, Class<T> clazz,
316 Predicate<DataObjectModification<T>> filter) {
317 List<DataObjectModification<? extends DataObject>> dataObjectModifications = new ArrayList<>();
318 List<InstanceIdentifier<? extends DataObject>> paths = new ArrayList<>();
319 if (changes != null) {
320 for (DataTreeModification<? extends DataObject> change : changes) {
321 dataObjectModifications.add(change.getRootNode());
322 paths.add(change.getRootPath().getRootIdentifier());
325 return extractDataObjectModifications(dataObjectModifications, paths, clazz, filter);
329 * Extract all the modifications affecting instances of {@code clazz} which are present in the given set of
330 * modifications and satisfy the given filter.
332 * @param changes The changes to process.
333 * @param paths The paths of the changes.
334 * @param clazz The class we're interested in.
335 * @param filter The filter the changes must satisfy.
336 * @param <T> The type of changes we're interested in.
337 * @return The modifications, mapped by instance identifier.
339 private static <T extends DataObject> Map<InstanceIdentifier<T>, DataObjectModification<T>>
340 extractDataObjectModifications(
341 Collection<DataObjectModification<? extends DataObject>> changes,
342 Collection<InstanceIdentifier<? extends DataObject>> paths, Class<T> clazz,
343 Predicate<DataObjectModification<T>> filter) {
344 Map<InstanceIdentifier<T>, DataObjectModification<T>> result = new HashMap<>();
345 Queue<DataObjectModification<? extends DataObject>> remainingChanges = new LinkedList<>(changes);
346 Queue<InstanceIdentifier<? extends DataObject>> remainingPaths = new LinkedList<>(paths);
347 while (!remainingChanges.isEmpty()) {
348 DataObjectModification<? extends DataObject> change = remainingChanges.remove();
349 InstanceIdentifier<? extends DataObject> path = remainingPaths.remove();
350 // Is the change relevant?
351 if (clazz.isAssignableFrom(change.getDataType()) && filter.apply((DataObjectModification<T>) change)) {
352 result.put((InstanceIdentifier<T>) path, (DataObjectModification<T>) change);
354 // Add any children to the queue
355 for (DataObjectModification<? extends DataObject> child : change.getModifiedChildren()) {
356 remainingChanges.add(child);
357 remainingPaths.add(extendPath(path, child));
364 * Extends the given instance identifier path to include the given child. Augmentations are treated in the same way
365 * as children; keyed children are handled correctly.
367 * @param path The current path.
368 * @param child The child modification to include.
369 * @return The extended path.
371 private static <N extends Identifiable<K> & ChildOf<? super T>, K extends Identifier<N>, T extends DataObject>
372 InstanceIdentifier<? extends DataObject> extendPath(
373 InstanceIdentifier path,
374 DataObjectModification child) {
375 Class<N> item = (Class<N>) child.getDataType();
376 if (child.getIdentifier() instanceof InstanceIdentifier.IdentifiableItem) {
377 K key = (K) ((InstanceIdentifier.IdentifiableItem) child.getIdentifier()).getKey();
378 KeyedInstanceIdentifier<N, K> extendedPath = path.child(item, key);
381 InstanceIdentifier<N> extendedPath = path.child(item);
386 public static <T extends DataObject> Map<InstanceIdentifier<T>, T> extractRemovedObjects(
387 AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> changes,
389 Set<InstanceIdentifier<T>> iids = extractRemoved(changes, klazz);
390 return Maps.filterKeys(extractOriginal(changes, klazz),Predicates.in(iids));
394 * Extract the removed instances of {@code clazz} from the given set of modifications.
396 * @param changes The changes to process.
397 * @param clazz The class we're interested in.
398 * @param <T> The type of changes we're interested in.
399 * @param <U> The type of changes to process.
400 * @return The removed instances, keyed by instance identifier.
402 public static <T extends DataObject, U extends DataObject> Map<InstanceIdentifier<T>, T> extractRemovedObjects(
403 Collection<DataTreeModification<U>> changes, Class<T> clazz) {
404 Map<InstanceIdentifier<T>, T> result = new HashMap<>();
405 for (Map.Entry<InstanceIdentifier<T>, DataObjectModification<T>> entry :
406 extractDataObjectModifications(changes, clazz, modificationIsDeletionAndHasDataBefore()).entrySet()) {
407 result.put(entry.getKey(), entry.getValue().getDataBefore());
412 public static <T extends DataObject> Map<InstanceIdentifier<T>,T> extract(
413 Map<InstanceIdentifier<?>, DataObject> changes, Class<T> klazz) {
414 Map<InstanceIdentifier<T>,T> result = new HashMap<>();
415 if (changes != null) {
416 for (Entry<InstanceIdentifier<?>, DataObject> created : changes.entrySet()) {
417 if (klazz.isInstance(created.getValue())) {
418 @SuppressWarnings("unchecked")
419 T value = (T) created.getValue();
420 Class<?> type = created.getKey().getTargetType();
421 if (type.equals(klazz)) {
422 @SuppressWarnings("unchecked") // Actually checked above
423 InstanceIdentifier<T> iid = (InstanceIdentifier<T>) created.getKey();
424 result.put(iid, value);
432 public static List<Insert> extractInsert(TransactionBuilder transaction, GenericTableSchema schema) {
433 List<Operation> operations = transaction.getOperations();
434 List<Insert> inserts = new ArrayList<>();
435 for (Operation operation : operations) {
436 if (operation instanceof Insert && operation.getTableSchema().equals(schema)) {
437 inserts.add((Insert) operation);
444 * Extract the NamedUuid from the Insert.
445 * If the Insert does not have a NamedUuid set, a random one will be
446 * generated, set, and returned.
448 * @param insert - Insert from which to extract the NamedUuid
449 * @return UUID - NamedUUID of the Insert
451 public static UUID extractNamedUuid(Insert insert) {
452 String uuidString = insert.getUuidName() != null
453 ? insert.getUuidName() : SouthboundMapper.getRandomUuid();
454 insert.setUuidName(uuidString);
455 return new UUID(uuidString);
458 public static <T extends TableSchema<T>> void stampInstanceIdentifier(TransactionBuilder transaction,
459 InstanceIdentifier<?> iid, TableSchema<T> tableSchema,
460 ColumnSchema<T, Map<String,String>> columnSchema) {
461 transaction.add(stampInstanceIdentifierMutation(transaction,iid,
462 tableSchema,columnSchema));
465 public static <T extends TableSchema<T>> Mutate<T> stampInstanceIdentifierMutation(TransactionBuilder transaction,
466 InstanceIdentifier<?> iid, TableSchema<T> tableSchema,
467 ColumnSchema<T, Map<String,String>> columnSchema) {
468 Map<String,String> externalIdsMap = ImmutableMap.of(SouthboundConstants.IID_EXTERNAL_ID_KEY,
469 SouthboundUtil.serializeInstanceIdentifier(iid));
470 Mutate<T> mutate = op.mutate(tableSchema)
471 .addMutation(columnSchema,
474 Mutation deleteIidMutation = new Mutation(columnSchema.getName(),
476 OvsdbSet.fromSet(Sets.newHashSet(SouthboundConstants.IID_EXTERNAL_ID_KEY)));
477 List<Mutation> mutations = Lists.newArrayList(Sets.newHashSet(deleteIidMutation));
478 mutations.addAll(mutate.getMutations());
479 mutate.setMutations(mutations);
484 * This method builds a string by concatenating the 2 character
485 * hexadecimal representation of each byte from the input byte array.
487 * For example: an input byte array containing:
495 * returns the string "6162632d313233"
498 * The byte array to convert to string
499 * @return The hexadecimal representation of the byte array. If bytes is
500 * null, the string "" is returned
502 public static String bytesToHexString(byte[] bytes) {
508 StringBuffer buf = new StringBuffer();
509 for (int i = 0; i < bytes.length; i++) {
510 short u8byte = (short) (bytes[i] & 0xff);
511 String tmp = Integer.toHexString(u8byte);
512 if (tmp.length() == 1) {
517 return buf.toString();