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 java.util.ArrayList;
13 import java.util.Collection;
14 import java.util.HashMap;
15 import java.util.HashSet;
16 import java.util.LinkedList;
17 import java.util.List;
19 import java.util.Map.Entry;
20 import java.util.Queue;
23 import javax.annotation.Nullable;
25 import com.google.common.base.Predicate;
26 import org.opendaylight.controller.md.sal.binding.api.DataObjectModification;
27 import org.opendaylight.controller.md.sal.binding.api.DataTreeModification;
28 import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent;
29 import org.opendaylight.ovsdb.lib.notation.Mutation;
30 import org.opendaylight.ovsdb.lib.notation.Mutator;
31 import org.opendaylight.ovsdb.lib.notation.OvsdbSet;
32 import org.opendaylight.ovsdb.lib.notation.UUID;
33 import org.opendaylight.ovsdb.lib.operations.Insert;
34 import org.opendaylight.ovsdb.lib.operations.Mutate;
35 import org.opendaylight.ovsdb.lib.operations.Operation;
36 import org.opendaylight.ovsdb.lib.operations.TransactionBuilder;
37 import org.opendaylight.ovsdb.lib.schema.ColumnSchema;
38 import org.opendaylight.ovsdb.lib.schema.GenericTableSchema;
39 import org.opendaylight.ovsdb.lib.schema.TableSchema;
40 import org.opendaylight.ovsdb.southbound.SouthboundConstants;
41 import org.opendaylight.ovsdb.southbound.SouthboundMapper;
42 import org.opendaylight.ovsdb.southbound.SouthboundUtil;
43 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
45 import org.opendaylight.yangtools.yang.binding.ChildOf;
46 import org.opendaylight.yangtools.yang.binding.DataObject;
47 import org.opendaylight.yangtools.yang.binding.Identifiable;
48 import org.opendaylight.yangtools.yang.binding.Identifier;
49 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
50 import org.opendaylight.yangtools.yang.binding.KeyedInstanceIdentifier;
51 import org.slf4j.Logger;
52 import org.slf4j.LoggerFactory;
54 import com.google.common.base.Predicates;
55 import com.google.common.collect.ImmutableMap;
56 import com.google.common.collect.Lists;
57 import com.google.common.collect.Maps;
58 import com.google.common.collect.Sets;
60 public class TransactUtils {
61 private static final Logger LOG = LoggerFactory.getLogger(TransactUtils.class);
63 private static <T extends DataObject> Predicate<DataObjectModification<T>> hasDataBefore() {
64 return new Predicate<DataObjectModification<T>>() {
66 public boolean apply(@Nullable DataObjectModification<T> input) {
67 return input != null && input.getDataBefore() != null;
72 private static <T extends DataObject> Predicate<DataObjectModification<T>> hasDataBeforeAndDataAfter() {
73 return new Predicate<DataObjectModification<T>>() {
75 public boolean apply(@Nullable DataObjectModification<T> input) {
76 return input != null && input.getDataBefore() != null && input.getDataAfter() != null;
81 private static <T extends DataObject> Predicate<DataObjectModification<T>> hasNoDataBefore() {
82 return new Predicate<DataObjectModification<T>>() {
84 public boolean apply(@Nullable DataObjectModification<T> input) {
85 return input != null && input.getDataBefore() == null;
90 private static <T extends DataObject> Predicate<DataObjectModification<T>> hasDataAfterAndMatchesFilter(
91 final Predicate<DataObjectModification<T>> filter) {
92 return new Predicate<DataObjectModification<T>>() {
94 public boolean apply(@Nullable DataObjectModification<T> input) {
95 return input != null && input.getDataAfter() != null && filter.apply(input);
100 private static <T extends DataObject> Predicate<DataObjectModification<T>> matchesEverything() {
101 return new Predicate<DataObjectModification<T>>() {
103 public boolean apply(@Nullable DataObjectModification<T> input) {
109 private static <T extends DataObject> Predicate<DataObjectModification<T>> modificationIsDeletion() {
110 return new Predicate<DataObjectModification<T>>() {
112 public boolean apply(@Nullable DataObjectModification<T> input) {
113 return input != null && input.getModificationType() == DataObjectModification
114 .ModificationType.DELETE;
119 private static <T extends DataObject> Predicate<DataObjectModification<T>> modificationIsDeletionAndHasDataBefore
121 return new Predicate<DataObjectModification<T>>() {
123 public boolean apply(@Nullable DataObjectModification<T> input) {
124 return input != null && input.getModificationType() == DataObjectModification
125 .ModificationType.DELETE && input.getDataBefore() != null;
130 public static Map<InstanceIdentifier<Node>,Node> extractNode(
131 Map<InstanceIdentifier<?>, DataObject> changes) {
132 Map<InstanceIdentifier<Node>,Node> result
134 if (changes != null) {
135 for (Entry<InstanceIdentifier<?>, DataObject> created : changes.entrySet()) {
136 if (created.getValue() instanceof Node) {
137 Node value = (Node) created.getValue();
138 Class<?> type = created.getKey().getTargetType();
139 if (type.equals(Node.class)) {
140 @SuppressWarnings("unchecked") // Actually checked above
141 InstanceIdentifier<Node> iid = (InstanceIdentifier<Node>) created.getKey();
142 result.put(iid, value);
150 public static <T extends DataObject> Map<InstanceIdentifier<T>,T> extractCreated(
151 AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> changes,Class<T> klazz) {
152 return extract(changes.getCreatedData(),klazz);
156 * Extract all the instances of {@code clazz} which were created in the given set of modifications.
158 * @param changes The changes to process.
159 * @param clazz The class we're interested in.
160 * @param <T> The type of changes we're interested in.
161 * @param <U> The type of changes to process.
162 * @return The created instances, mapped by instance identifier.
164 public static <T extends DataObject, U extends DataObject> Map<InstanceIdentifier<T>, T> extractCreated(
165 Collection<DataTreeModification<U>> changes, Class<T> clazz) {
166 return extractCreatedOrUpdated(changes, clazz, hasNoDataBefore());
170 * Extract all the instance of {@code clazz} which were created or updated in the given set of modifications, and
171 * which satisfy the given filter.
173 * @param changes The changes to process.
174 * @param clazz The class we're interested in.
175 * @param filter The filter the changes must satisfy.
176 * @param <T> The type of changes we're interested in.
177 * @param <U> The type of changes to process.
178 * @return The created or updated instances which satisfy the filter, mapped by instance identifier.
180 public static <T extends DataObject, U extends DataObject> Map<InstanceIdentifier<T>, T> extractCreatedOrUpdated(
181 Collection<DataTreeModification<U>> changes, Class<T> clazz,
182 Predicate<DataObjectModification<T>> filter) {
183 Map<InstanceIdentifier<T>, T> result = new HashMap<>();
184 for (Map.Entry<InstanceIdentifier<T>, DataObjectModification<T>> entry : extractDataObjectModifications(changes,
185 clazz, hasDataAfterAndMatchesFilter(filter)).entrySet()) {
186 result.put(entry.getKey(), entry.getValue().getDataAfter());
191 public static <T extends DataObject> Map<InstanceIdentifier<T>,T> extractUpdated(
192 AsyncDataChangeEvent<InstanceIdentifier<?>,DataObject> changes,Class<T> klazz) {
193 return extract(changes.getUpdatedData(),klazz);
197 * Extract all the instances of {@code clazz} which were updated in the given set of modifications.
199 * @param changes The changes to process.
200 * @param clazz The class we're interested in.
201 * @param <T> The type of changes we're interested in.
202 * @param <U> The type of changes to process.
203 * @return The updated instances, mapped by instance identifier.
205 public static <T extends DataObject, U extends DataObject> Map<InstanceIdentifier<T>, T> extractUpdated(
206 Collection<DataTreeModification<U>> changes, Class<T> clazz) {
207 return extractCreatedOrUpdated(changes, clazz, hasDataBeforeAndDataAfter());
210 public static <T extends DataObject> Map<InstanceIdentifier<T>,T> extractCreatedOrUpdated(
211 AsyncDataChangeEvent<InstanceIdentifier<?>,DataObject> changes,Class<T> klazz) {
212 Map<InstanceIdentifier<T>,T> result = extractUpdated(changes,klazz);
213 result.putAll(extractCreated(changes,klazz));
218 * Extract all the instances of {@code clazz} which were created or updated in the given set of modifications.
220 * @param changes The changes to process.
221 * @param clazz The class we're interested in.
222 * @param <T> The type of changes we're interested in.
223 * @param <U> The type of changes to process.
224 * @return The created or updated instances, mapped by instance identifier.
226 public static <T extends DataObject, U extends DataObject> Map<InstanceIdentifier<T>, T> extractCreatedOrUpdated(
227 Collection<DataTreeModification<U>> changes, Class<T> clazz) {
228 return extractCreatedOrUpdated(changes, clazz, matchesEverything());
231 public static <T extends DataObject> Map<InstanceIdentifier<T>, T> extractCreatedOrUpdatedOrRemoved(
232 AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> changes,
234 Map<InstanceIdentifier<T>,T> result = extractCreatedOrUpdated(changes,klazz);
235 result.putAll(extractRemovedObjects(changes, klazz));
240 * Extract all the instances of {@code clazz} which were created, updated, or removed in the given set of
241 * modifications. For instances which were created or updated, the new instances are returned; for instances
242 * which were removed, the old instances are returned.
244 * @param changes The changes to process.
245 * @param clazz The class we're interested in.
246 * @param <T> The type of changes we're interested in.
247 * @param <U> The type of changes to process.
248 * @return The created, updated or removed instances, mapped by instance identifier.
250 public static <T extends DataObject, U extends DataObject> Map<InstanceIdentifier<T>, T>
251 extractCreatedOrUpdatedOrRemoved(
252 Collection<DataTreeModification<U>> changes, Class<T> clazz) {
253 Map<InstanceIdentifier<T>, T> result = extractCreatedOrUpdated(changes, clazz);
254 result.putAll(extractRemovedObjects(changes, clazz));
258 public static <T extends DataObject> Map<InstanceIdentifier<T>,T> extractOriginal(
259 AsyncDataChangeEvent<InstanceIdentifier<?>,DataObject> changes,Class<T> klazz) {
260 return extract(changes.getOriginalData(),klazz);
264 * Extract the original instances of class {@code clazz} in the given set of modifications.
266 * @param changes The changes to process.
267 * @param clazz The class we're interested in.
268 * @param <T> The type of changes we're interested in.
269 * @param <U> The type of changes to process.
270 * @return The original instances, mapped by instance identifier.
272 public static <T extends DataObject, U extends DataObject> Map<InstanceIdentifier<T>, T> extractOriginal(
273 Collection<DataTreeModification<U>> changes, Class<T> clazz) {
274 Map<InstanceIdentifier<T>, T> result = new HashMap<>();
275 for (Map.Entry<InstanceIdentifier<T>, DataObjectModification<T>> entry :
276 extractDataObjectModifications(changes, clazz, hasDataBefore()).entrySet()) {
277 result.put(entry.getKey(), entry.getValue().getDataBefore());
282 public static <T extends DataObject> Set<InstanceIdentifier<T>> extractRemoved(
283 AsyncDataChangeEvent<InstanceIdentifier<?>,DataObject> changes,Class<T> klazz) {
284 Set<InstanceIdentifier<T>> result = new HashSet<>();
285 if (changes != null && changes.getRemovedPaths() != null) {
286 for (InstanceIdentifier<?> iid : changes.getRemovedPaths()) {
287 if (iid.getTargetType().equals(klazz)) {
288 @SuppressWarnings("unchecked") // Actually checked above
289 InstanceIdentifier<T> iidn = (InstanceIdentifier<T>)iid;
298 * Extract the instance identifier of removed instances of {@code clazz} from the given set of modifications.
300 * @param changes The changes to process.
301 * @param clazz The class we're interested in.
302 * @param <T> The type of changes we're interested in.
303 * @param <U> The type of changes to process.
304 * @return The instance identifiers of removed instances.
306 public static <T extends DataObject, U extends DataObject> Set<InstanceIdentifier<T>> extractRemoved(
307 Collection<DataTreeModification<U>> changes, Class<T> clazz) {
308 return extractDataObjectModifications(changes, clazz, modificationIsDeletion()).keySet();
312 * Extract all the modifications affecting instances of {@code clazz} which are present in the given set of
313 * modifications and satisfy the given filter.
315 * @param changes The changes to process.
316 * @param clazz The class we're interested in.
317 * @param filter The filter the changes must satisfy.
318 * @param <T> The type of changes we're interested in.
319 * @param <U> The type of changes to process.
320 * @return The modifications, mapped by instance identifier.
322 private static <T extends DataObject, U extends DataObject> Map<InstanceIdentifier<T>, DataObjectModification<T>>
323 extractDataObjectModifications(
324 Collection<DataTreeModification<U>> changes, Class<T> clazz,
325 Predicate<DataObjectModification<T>> filter) {
326 List<DataObjectModification<? extends DataObject>> dataObjectModifications = new ArrayList<>();
327 List<InstanceIdentifier<? extends DataObject>> paths = new ArrayList<>();
328 if (changes != null) {
329 for (DataTreeModification<? extends DataObject> change : changes) {
330 dataObjectModifications.add(change.getRootNode());
331 paths.add(change.getRootPath().getRootIdentifier());
334 return extractDataObjectModifications(dataObjectModifications, paths, clazz, filter);
338 * Extract all the modifications affecting instances of {@code clazz} which are present in the given set of
339 * modifications and satisfy the given filter.
341 * @param changes The changes to process.
342 * @param paths The paths of the changes.
343 * @param clazz The class we're interested in.
344 * @param filter The filter the changes must satisfy.
345 * @param <T> The type of changes we're interested in.
346 * @return The modifications, mapped by instance identifier.
348 private static <T extends DataObject> Map<InstanceIdentifier<T>, DataObjectModification<T>>
349 extractDataObjectModifications(
350 Collection<DataObjectModification<? extends DataObject>> changes,
351 Collection<InstanceIdentifier<? extends DataObject>> paths, Class<T> clazz,
352 Predicate<DataObjectModification<T>> filter) {
353 Map<InstanceIdentifier<T>, DataObjectModification<T>> result = new HashMap<>();
354 Queue<DataObjectModification<? extends DataObject>> remainingChanges = new LinkedList<>(changes);
355 Queue<InstanceIdentifier<? extends DataObject>> remainingPaths = new LinkedList<>(paths);
356 while (!remainingChanges.isEmpty()) {
357 DataObjectModification<? extends DataObject> change = remainingChanges.remove();
358 InstanceIdentifier<? extends DataObject> path = remainingPaths.remove();
359 // Is the change relevant?
360 if (clazz.isAssignableFrom(change.getDataType()) && filter.apply((DataObjectModification<T>) change)) {
361 result.put((InstanceIdentifier<T>) path, (DataObjectModification<T>) change);
363 // Add any children to the queue
364 for (DataObjectModification<? extends DataObject> child : change.getModifiedChildren()) {
365 remainingChanges.add(child);
366 remainingPaths.add(extendPath(path, child));
373 * Extends the given instance identifier path to include the given child. Augmentations are treated in the same way
374 * as children; keyed children are handled correctly.
376 * @param path The current path.
377 * @param child The child modification to include.
378 * @return The extended path.
380 private static <N extends Identifiable<K> & ChildOf<? super T>, K extends Identifier<N>, T extends DataObject>
381 InstanceIdentifier<? extends DataObject> extendPath(
382 InstanceIdentifier path,
383 DataObjectModification child) {
384 Class<N> item = (Class<N>) child.getDataType();
385 if (child.getIdentifier() instanceof InstanceIdentifier.IdentifiableItem) {
386 K key = (K) ((InstanceIdentifier.IdentifiableItem) child.getIdentifier()).getKey();
387 KeyedInstanceIdentifier<N, K> extendedPath = path.child(item, key);
388 LOG.debug("Building a new child iid for {} with {} and key {}, resulting in {}",
389 path, item, extendedPath);
392 InstanceIdentifier<N> extendedPath = path.child(item);
393 LOG.debug("Building a new child iid for {} with {}, resulting in {}",
394 path, item, extendedPath);
399 public static <T extends DataObject> Map<InstanceIdentifier<T>, T> extractRemovedObjects(
400 AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> changes,
402 Set<InstanceIdentifier<T>> iids = extractRemoved(changes, klazz);
403 return Maps.filterKeys(extractOriginal(changes, klazz),Predicates.in(iids));
407 * Extract the removed instances of {@code clazz} from the given set of modifications.
409 * @param changes The changes to process.
410 * @param clazz The class we're interested in.
411 * @param <T> The type of changes we're interested in.
412 * @param <U> The type of changes to process.
413 * @return The removed instances, keyed by instance identifier.
415 public static <T extends DataObject, U extends DataObject> Map<InstanceIdentifier<T>, T> extractRemovedObjects(
416 Collection<DataTreeModification<U>> changes, Class<T> clazz) {
417 Map<InstanceIdentifier<T>, T> result = new HashMap<>();
418 for (Map.Entry<InstanceIdentifier<T>, DataObjectModification<T>> entry :
419 extractDataObjectModifications(changes, clazz, modificationIsDeletionAndHasDataBefore()).entrySet()) {
420 result.put(entry.getKey(), entry.getValue().getDataBefore());
425 public static <T extends DataObject> Map<InstanceIdentifier<T>,T> extract(
426 Map<InstanceIdentifier<?>, DataObject> changes, Class<T> klazz) {
427 Map<InstanceIdentifier<T>,T> result = new HashMap<>();
428 if (changes != null) {
429 for (Entry<InstanceIdentifier<?>, DataObject> created : changes.entrySet()) {
430 if (klazz.isInstance(created.getValue())) {
431 @SuppressWarnings("unchecked")
432 T value = (T) created.getValue();
433 Class<?> type = created.getKey().getTargetType();
434 if (type.equals(klazz)) {
435 @SuppressWarnings("unchecked") // Actually checked above
436 InstanceIdentifier<T> iid = (InstanceIdentifier<T>) created.getKey();
437 result.put(iid, value);
445 public static List<Insert> extractInsert(TransactionBuilder transaction, GenericTableSchema schema) {
446 List<Operation> operations = transaction.getOperations();
447 List<Insert> inserts = new ArrayList<>();
448 for (Operation operation : operations) {
449 if (operation instanceof Insert && operation.getTableSchema().equals(schema)) {
450 inserts.add((Insert) operation);
457 * Extract the NamedUuid from the Insert.
458 * If the Insert does not have a NamedUuid set, a random one will be
459 * generated, set, and returned.
461 * @param insert - Insert from which to extract the NamedUuid
462 * @return UUID - NamedUUID of the Insert
464 public static UUID extractNamedUuid(Insert insert) {
465 String uuidString = insert.getUuidName() != null
466 ? insert.getUuidName() : SouthboundMapper.getRandomUUID();
467 insert.setUuidName(uuidString);
468 return new UUID(uuidString);
471 public static <T extends TableSchema<T>> void stampInstanceIdentifier(TransactionBuilder transaction,
472 InstanceIdentifier<?> iid, TableSchema<T> tableSchema,
473 ColumnSchema<T, Map<String,String>> columnSchema) {
474 transaction.add(stampInstanceIdentifierMutation(transaction,iid,
475 tableSchema,columnSchema));
478 public static <T extends TableSchema<T>> Mutate<T> stampInstanceIdentifierMutation(TransactionBuilder transaction,
479 InstanceIdentifier<?> iid, TableSchema<T> tableSchema,
480 ColumnSchema<T, Map<String,String>> columnSchema) {
481 Map<String,String> externalIdsMap = ImmutableMap.of(SouthboundConstants.IID_EXTERNAL_ID_KEY,
482 SouthboundUtil.serializeInstanceIdentifier(iid));
483 Mutate<T> mutate = op.mutate(tableSchema)
484 .addMutation(columnSchema,
487 Mutation deleteIidMutation = new Mutation(columnSchema.getName(),
489 OvsdbSet.fromSet(Sets.newHashSet(SouthboundConstants.IID_EXTERNAL_ID_KEY)));
490 List<Mutation> mutations = Lists.newArrayList(Sets.newHashSet(deleteIidMutation));
491 mutations.addAll(mutate.getMutations());
492 mutate.setMutations(mutations);
497 * This method builds a string by concatenating the 2 character
498 * hexadecimal representation of each byte from the input byte array.
500 * For example: an input byte array containing:
508 * returns the string "6162632d313233"
511 * The byte array to convert to string
512 * @return The hexadecimal representation of the byte array. If bytes is
513 * null, the string "" is returned
515 public static String bytesToHexString(byte[] bytes) {
521 StringBuffer buf = new StringBuffer();
522 for (int i = 0; i < bytes.length; i++) {
523 short u8byte = (short) (bytes[i] & 0xff);
524 String tmp = Integer.toHexString(u8byte);
525 if (tmp.length() == 1) {
530 return buf.toString();