Bug 6692: remove InstanceIdentifierCodec instance from SBU
[ovsdb.git] / southbound / southbound-impl / src / main / java / org / opendaylight / ovsdb / southbound / ovsdb / transact / TransactUtils.java
1 /*
2  * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
3  *
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
7  */
8 package org.opendaylight.ovsdb.southbound.ovsdb.transact;
9
10 import static org.opendaylight.ovsdb.lib.operations.Operations.op;
11
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;
24 import java.util.Map;
25 import java.util.Map.Entry;
26 import java.util.Queue;
27 import java.util.Set;
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.InstanceIdentifierCodec;
44 import org.opendaylight.ovsdb.southbound.SouthboundConstants;
45 import org.opendaylight.ovsdb.southbound.SouthboundMapper;
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;
53
54 public class TransactUtils {
55
56     private TransactUtils() { }
57
58     private static <T extends DataObject> Predicate<DataObjectModification<T>> hasDataBefore() {
59         return new Predicate<DataObjectModification<T>>() {
60             @Override
61             public boolean apply(@Nullable DataObjectModification<T> input) {
62                 return input != null && input.getDataBefore() != null;
63             }
64         };
65     }
66
67     private static <T extends DataObject> Predicate<DataObjectModification<T>> hasDataBeforeAndDataAfter() {
68         return new Predicate<DataObjectModification<T>>() {
69             @Override
70             public boolean apply(@Nullable DataObjectModification<T> input) {
71                 return input != null && input.getDataBefore() != null && input.getDataAfter() != null;
72             }
73         };
74     }
75
76     private static <T extends DataObject> Predicate<DataObjectModification<T>> hasNoDataBefore() {
77         return new Predicate<DataObjectModification<T>>() {
78             @Override
79             public boolean apply(@Nullable DataObjectModification<T> input) {
80                 return input != null && input.getDataBefore() == null;
81             }
82         };
83     }
84
85     private static <T extends DataObject> Predicate<DataObjectModification<T>> hasDataAfterAndMatchesFilter(
86             final Predicate<DataObjectModification<T>> filter) {
87         return new Predicate<DataObjectModification<T>>() {
88             @Override
89             public boolean apply(@Nullable DataObjectModification<T> input) {
90                 return input != null && input.getDataAfter() != null && filter.apply(input);
91             }
92         };
93     }
94
95     private static <T extends DataObject> Predicate<DataObjectModification<T>> matchesEverything() {
96         return new Predicate<DataObjectModification<T>>() {
97             @Override
98             public boolean apply(@Nullable DataObjectModification<T> input) {
99                 return true;
100             }
101         };
102     }
103
104     private static <T extends DataObject> Predicate<DataObjectModification<T>> modificationIsDeletion() {
105         return new Predicate<DataObjectModification<T>>() {
106             @Override
107             public boolean apply(@Nullable DataObjectModification<T> input) {
108                 return input != null && input.getModificationType() == DataObjectModification
109                         .ModificationType.DELETE;
110             }
111         };
112     }
113
114     private static <T extends DataObject> Predicate<DataObjectModification<T>>
115         modificationIsDeletionAndHasDataBefore() {
116         return new Predicate<DataObjectModification<T>>() {
117             @Override
118             public boolean apply(@Nullable DataObjectModification<T> input) {
119                 return input != null && input.getModificationType() == DataObjectModification
120                         .ModificationType.DELETE && input.getDataBefore() != null;
121             }
122         };
123     }
124
125     public static Map<InstanceIdentifier<Node>,Node> extractNode(
126             Map<InstanceIdentifier<?>, DataObject> changes) {
127         Map<InstanceIdentifier<Node>,Node> result
128             = new HashMap<>();
129         if (changes != null) {
130             for (Entry<InstanceIdentifier<?>, DataObject> created : changes.entrySet()) {
131                 if (created.getValue() instanceof Node) {
132                     Node value = (Node) created.getValue();
133                     Class<?> type = created.getKey().getTargetType();
134                     if (type.equals(Node.class)) {
135                         // Actually checked above
136                         @SuppressWarnings("unchecked")
137                         InstanceIdentifier<Node> iid = (InstanceIdentifier<Node>) created.getKey();
138                         result.put(iid, value);
139                     }
140                 }
141             }
142         }
143         return result;
144     }
145
146     public static <T extends DataObject> Map<InstanceIdentifier<T>,T> extractCreated(
147             AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> changes,Class<T> klazz) {
148         return extract(changes.getCreatedData(),klazz);
149     }
150
151     /**
152      * Extract all the instances of {@code clazz} which were created in the given set of modifications.
153      *
154      * @param changes The changes to process.
155      * @param clazz The class we're interested in.
156      * @param <T> The type of changes we're interested in.
157      * @param <U> The type of changes to process.
158      * @return The created instances, mapped by instance identifier.
159      */
160     public static <T extends DataObject, U extends DataObject> Map<InstanceIdentifier<T>, T> extractCreated(
161             Collection<DataTreeModification<U>> changes, Class<T> clazz) {
162         return extractCreatedOrUpdated(changes, clazz, hasNoDataBefore());
163     }
164
165     public static <T extends DataObject> Map<InstanceIdentifier<T>,T> extractUpdated(
166             AsyncDataChangeEvent<InstanceIdentifier<?>,DataObject> changes,Class<T> klazz) {
167         return extract(changes.getUpdatedData(),klazz);
168     }
169
170     /**
171      * Extract all the instances of {@code clazz} which were updated in the given set of modifications.
172      *
173      * @param changes The changes to process.
174      * @param clazz The class we're interested in.
175      * @param <T> The type of changes we're interested in.
176      * @param <U> The type of changes to process.
177      * @return The updated instances, mapped by instance identifier.
178      */
179     public static <T extends DataObject, U extends DataObject> Map<InstanceIdentifier<T>, T> extractUpdated(
180             Collection<DataTreeModification<U>> changes, Class<T> clazz) {
181         return extractCreatedOrUpdated(changes, clazz, hasDataBeforeAndDataAfter());
182     }
183
184     /**
185      * Extract all the instance of {@code clazz} which were created or updated in the given set of modifications, and
186      * which satisfy the given filter.
187      *
188      * @param changes The changes to process.
189      * @param clazz The class we're interested in.
190      * @param filter The filter the changes must satisfy.
191      * @param <T> The type of changes we're interested in.
192      * @param <U> The type of changes to process.
193      * @return The created or updated instances which satisfy the filter, mapped by instance identifier.
194      */
195     public static <T extends DataObject, U extends DataObject> Map<InstanceIdentifier<T>, T> extractCreatedOrUpdated(
196             Collection<DataTreeModification<U>> changes, Class<T> clazz,
197             Predicate<DataObjectModification<T>> filter) {
198         Map<InstanceIdentifier<T>, T> result = new HashMap<>();
199         for (Map.Entry<InstanceIdentifier<T>, DataObjectModification<T>> entry : extractDataObjectModifications(changes,
200                 clazz, hasDataAfterAndMatchesFilter(filter)).entrySet()) {
201             result.put(entry.getKey(), entry.getValue().getDataAfter());
202         }
203         return result;
204     }
205
206     public static <T extends DataObject> Map<InstanceIdentifier<T>,T> extractCreatedOrUpdated(
207             AsyncDataChangeEvent<InstanceIdentifier<?>,DataObject> changes,Class<T> klazz) {
208         Map<InstanceIdentifier<T>,T> result = extractUpdated(changes,klazz);
209         result.putAll(extractCreated(changes,klazz));
210         return result;
211     }
212
213     /**
214      * Extract all the instances of {@code clazz} which were created or updated in the given set of modifications.
215      *
216      * @param changes The changes to process.
217      * @param clazz The class we're interested in.
218      * @param <T> The type of changes we're interested in.
219      * @param <U> The type of changes to process.
220      * @return The created or updated instances, mapped by instance identifier.
221      */
222     public static <T extends DataObject, U extends DataObject> Map<InstanceIdentifier<T>, T> extractCreatedOrUpdated(
223             Collection<DataTreeModification<U>> changes, Class<T> clazz) {
224         return extractCreatedOrUpdated(changes, clazz, matchesEverything());
225     }
226
227     public static <T extends DataObject> Map<InstanceIdentifier<T>, T> extractCreatedOrUpdatedOrRemoved(
228             AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> changes,
229             Class<T> klazz) {
230         Map<InstanceIdentifier<T>,T> result = extractCreatedOrUpdated(changes,klazz);
231         result.putAll(extractRemovedObjects(changes, klazz));
232         return result;
233     }
234
235     /**
236      * Extract all the instances of {@code clazz} which were created, updated, or removed in the given set of
237      * modifications. For instances which were created or updated, the new instances are returned; for instances
238      * which were removed, the old instances are returned.
239      *
240      * @param changes The changes to process.
241      * @param clazz The class we're interested in.
242      * @param <T> The type of changes we're interested in.
243      * @param <U> The type of changes to process.
244      * @return The created, updated or removed instances, mapped by instance identifier.
245      */
246     public static <T extends DataObject, U extends DataObject> Map<InstanceIdentifier<T>, T>
247         extractCreatedOrUpdatedOrRemoved(
248             Collection<DataTreeModification<U>> changes, Class<T> clazz) {
249         Map<InstanceIdentifier<T>, T> result = extractCreatedOrUpdated(changes, clazz);
250         result.putAll(extractRemovedObjects(changes, clazz));
251         return result;
252     }
253
254     public static <T extends DataObject> Map<InstanceIdentifier<T>,T> extractOriginal(
255             AsyncDataChangeEvent<InstanceIdentifier<?>,DataObject> changes,Class<T> klazz) {
256         return extract(changes.getOriginalData(),klazz);
257     }
258
259     /**
260      * Extract the original instances of class {@code clazz} in the given set of modifications.
261      *
262      * @param changes The changes to process.
263      * @param clazz The class we're interested in.
264      * @param <T> The type of changes we're interested in.
265      * @param <U> The type of changes to process.
266      * @return The original instances, mapped by instance identifier.
267      */
268     public static <T extends DataObject, U extends DataObject> Map<InstanceIdentifier<T>, T> extractOriginal(
269             Collection<DataTreeModification<U>> changes, Class<T> clazz) {
270         Map<InstanceIdentifier<T>, T> result = new HashMap<>();
271         for (Map.Entry<InstanceIdentifier<T>, DataObjectModification<T>> entry :
272                 extractDataObjectModifications(changes, clazz, hasDataBefore()).entrySet()) {
273             result.put(entry.getKey(), entry.getValue().getDataBefore());
274         }
275         return result;
276     }
277
278     public static <T extends DataObject> Set<InstanceIdentifier<T>> extractRemoved(
279             AsyncDataChangeEvent<InstanceIdentifier<?>,DataObject> changes,Class<T> klazz) {
280         Set<InstanceIdentifier<T>> result = new HashSet<>();
281         if (changes != null && changes.getRemovedPaths() != null) {
282             for (InstanceIdentifier<?> iid : changes.getRemovedPaths()) {
283                 if (iid.getTargetType().equals(klazz)) {
284                     // Actually checked above
285                     @SuppressWarnings("unchecked")
286                     InstanceIdentifier<T> iidn = (InstanceIdentifier<T>)iid;
287                     result.add(iidn);
288                 }
289             }
290         }
291         return result;
292     }
293
294     /**
295      * Extract the instance identifier of removed instances of {@code clazz} from the given set of modifications.
296      *
297      * @param changes The changes to process.
298      * @param clazz The class we're interested in.
299      * @param <T> The type of changes we're interested in.
300      * @param <U> The type of changes to process.
301      * @return The instance identifiers of removed instances.
302      */
303     public static <T extends DataObject, U extends DataObject> Set<InstanceIdentifier<T>> extractRemoved(
304             Collection<DataTreeModification<U>> changes, Class<T> clazz) {
305         return extractDataObjectModifications(changes, clazz, modificationIsDeletion()).keySet();
306     }
307
308     /**
309      * Extract all the modifications affecting instances of {@code clazz} which are present in the given set of
310      * modifications and satisfy the given filter.
311      *
312      * @param changes The changes to process.
313      * @param clazz The class we're interested in.
314      * @param filter The filter the changes must satisfy.
315      * @param <T> The type of changes we're interested in.
316      * @param <U> The type of changes to process.
317      * @return The modifications, mapped by instance identifier.
318      */
319     private static <T extends DataObject, U extends DataObject> Map<InstanceIdentifier<T>, DataObjectModification<T>>
320         extractDataObjectModifications(Collection<DataTreeModification<U>> changes, Class<T> clazz,
321                                        Predicate<DataObjectModification<T>> filter) {
322         List<DataObjectModification<? extends DataObject>> dataObjectModifications = new ArrayList<>();
323         List<InstanceIdentifier<? extends DataObject>> paths = new ArrayList<>();
324         if (changes != null) {
325             for (DataTreeModification<? extends DataObject> change : changes) {
326                 dataObjectModifications.add(change.getRootNode());
327                 paths.add(change.getRootPath().getRootIdentifier());
328             }
329         }
330         return extractDataObjectModifications(dataObjectModifications, paths, clazz, filter);
331     }
332
333     /**
334      * Extract all the modifications affecting instances of {@code clazz} which are present in the given set of
335      * modifications and satisfy the given filter.
336      *
337      * @param changes The changes to process.
338      * @param paths The paths of the changes.
339      * @param clazz The class we're interested in.
340      * @param filter The filter the changes must satisfy.
341      * @param <T> The type of changes we're interested in.
342      * @return The modifications, mapped by instance identifier.
343      */
344     private static <T extends DataObject> Map<InstanceIdentifier<T>, DataObjectModification<T>>
345         extractDataObjectModifications(
346             Collection<DataObjectModification<? extends DataObject>> changes,
347             Collection<InstanceIdentifier<? extends DataObject>> paths, Class<T> clazz,
348             Predicate<DataObjectModification<T>> filter) {
349         Map<InstanceIdentifier<T>, DataObjectModification<T>> result = new HashMap<>();
350         Queue<DataObjectModification<? extends DataObject>> remainingChanges = new LinkedList<>(changes);
351         Queue<InstanceIdentifier<? extends DataObject>> remainingPaths = new LinkedList<>(paths);
352         while (!remainingChanges.isEmpty()) {
353             DataObjectModification<? extends DataObject> change = remainingChanges.remove();
354             InstanceIdentifier<? extends DataObject> path = remainingPaths.remove();
355             // Is the change relevant?
356             if (clazz.isAssignableFrom(change.getDataType()) && filter.apply((DataObjectModification<T>) change)) {
357                 result.put((InstanceIdentifier<T>) path, (DataObjectModification<T>) change);
358             }
359             // Add any children to the queue
360             for (DataObjectModification<? extends DataObject> child : change.getModifiedChildren()) {
361                 remainingChanges.add(child);
362                 remainingPaths.add(extendPath(path, child));
363             }
364         }
365         return result;
366     }
367
368     /**
369      * Extends the given instance identifier path to include the given child. Augmentations are treated in the same way
370      * as children; keyed children are handled correctly.
371      *
372      * @param path The current path.
373      * @param child The child modification to include.
374      * @return The extended path.
375      */
376     private static <N extends Identifiable<K> & ChildOf<? super T>, K extends Identifier<N>, T extends DataObject>
377         InstanceIdentifier<? extends DataObject> extendPath(
378             InstanceIdentifier path,
379             DataObjectModification child) {
380         Class<N> item = (Class<N>) child.getDataType();
381         if (child.getIdentifier() instanceof InstanceIdentifier.IdentifiableItem) {
382             K key = (K) ((InstanceIdentifier.IdentifiableItem) child.getIdentifier()).getKey();
383             KeyedInstanceIdentifier<N, K> extendedPath = path.child(item, key);
384             return extendedPath;
385         } else {
386             InstanceIdentifier<N> extendedPath = path.child(item);
387             return extendedPath;
388         }
389     }
390
391     public static <T extends DataObject> Map<InstanceIdentifier<T>, T> extractRemovedObjects(
392             AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> changes,
393             Class<T> klazz) {
394         Set<InstanceIdentifier<T>> iids = extractRemoved(changes, klazz);
395         return Maps.filterKeys(extractOriginal(changes, klazz),Predicates.in(iids));
396     }
397
398     /**
399      * Extract the removed instances of {@code clazz} from the given set of modifications.
400      *
401      * @param changes The changes to process.
402      * @param clazz The class we're interested in.
403      * @param <T> The type of changes we're interested in.
404      * @param <U> The type of changes to process.
405      * @return The removed instances, keyed by instance identifier.
406      */
407     public static <T extends DataObject, U extends DataObject> Map<InstanceIdentifier<T>, T> extractRemovedObjects(
408             Collection<DataTreeModification<U>> changes, Class<T> clazz) {
409         Map<InstanceIdentifier<T>, T> result = new HashMap<>();
410         for (Map.Entry<InstanceIdentifier<T>, DataObjectModification<T>> entry :
411                 extractDataObjectModifications(changes, clazz, modificationIsDeletionAndHasDataBefore()).entrySet()) {
412             result.put(entry.getKey(), entry.getValue().getDataBefore());
413         }
414         return result;
415     }
416
417     public static <T extends DataObject> Map<InstanceIdentifier<T>,T> extract(
418             Map<InstanceIdentifier<?>, DataObject> changes, Class<T> klazz) {
419         Map<InstanceIdentifier<T>,T> result = new HashMap<>();
420         if (changes != null) {
421             for (Entry<InstanceIdentifier<?>, DataObject> created : changes.entrySet()) {
422                 if (klazz.isInstance(created.getValue())) {
423                     @SuppressWarnings("unchecked")
424                     T value = (T) created.getValue();
425                     Class<?> type = created.getKey().getTargetType();
426                     if (type.equals(klazz)) {
427                         // Actually checked above
428                         @SuppressWarnings("unchecked")
429                         InstanceIdentifier<T> iid = (InstanceIdentifier<T>) created.getKey();
430                         result.put(iid, value);
431                     }
432                 }
433             }
434         }
435         return result;
436     }
437
438     public static List<Insert> extractInsert(TransactionBuilder transaction, GenericTableSchema schema) {
439         List<Operation> operations = transaction.getOperations();
440         List<Insert> inserts = new ArrayList<>();
441         for (Operation operation : operations) {
442             if (operation instanceof Insert && operation.getTableSchema().equals(schema)) {
443                 inserts.add((Insert) operation);
444             }
445         }
446         return inserts;
447     }
448
449     /**
450      * Extract the NamedUuid from the Insert.
451      * If the Insert does not have a NamedUuid set, a random one will be
452      * generated, set, and returned.
453      *
454      * @param insert - Insert from which to extract the NamedUuid
455      * @return UUID - NamedUUID of the Insert
456      */
457     public static UUID extractNamedUuid(Insert insert) {
458         String uuidString = insert.getUuidName() != null
459                 ? insert.getUuidName() : SouthboundMapper.getRandomUuid();
460         insert.setUuidName(uuidString);
461         return new UUID(uuidString);
462     }
463
464     public static <T extends TableSchema<T>> void stampInstanceIdentifier(TransactionBuilder transaction,
465             InstanceIdentifier<?> iid, TableSchema<T> tableSchema, ColumnSchema<T, Map<String, String>> columnSchema,
466             InstanceIdentifierCodec instanceIdentifierCodec) {
467         transaction.add(
468                 stampInstanceIdentifierMutation(transaction, iid, tableSchema, columnSchema, instanceIdentifierCodec));
469     }
470
471     public static <T extends TableSchema<T>> Mutate<T> stampInstanceIdentifierMutation(TransactionBuilder transaction,
472             InstanceIdentifier<?> iid, TableSchema<T> tableSchema, ColumnSchema<T, Map<String, String>> columnSchema,
473             InstanceIdentifierCodec instanceIdentifierCodec) {
474         Map<String,String> externalIdsMap = ImmutableMap.of(SouthboundConstants.IID_EXTERNAL_ID_KEY,
475                 instanceIdentifierCodec.serialize(iid));
476         Mutate<T> mutate = op.mutate(tableSchema)
477                 .addMutation(columnSchema,
478                     Mutator.INSERT,
479                     externalIdsMap);
480         Mutation deleteIidMutation = new Mutation(columnSchema.getName(),
481                 Mutator.DELETE,
482                 OvsdbSet.fromSet(Sets.newHashSet(SouthboundConstants.IID_EXTERNAL_ID_KEY)));
483         List<Mutation> mutations = Lists.newArrayList(Sets.newHashSet(deleteIidMutation));
484         mutations.addAll(mutate.getMutations());
485         mutate.setMutations(mutations);
486         return mutate;
487     }
488
489     /**
490      * This method builds a string by concatenating the 2 character
491      * hexadecimal representation of each byte from the input byte array.
492      * <p>
493      * For example: an input byte array containing:
494      *   bytes[0] = 'a'
495      *   bytes[1] = 'b'
496      *   bytes[2] = 'c'
497      *   bytes[3] = '-'
498      *   bytes[4] = '1'
499      *   bytes[5] = '2'
500      *   bytes[6] = '3'
501      * returns the string "6162632d313233"
502      * </p>
503      * @param bytes
504      *            The byte array to convert to string
505      * @return The hexadecimal representation of the byte array. If bytes is
506      *         null, the string "" is returned
507      */
508     public static String bytesToHexString(byte[] bytes) {
509
510         if (bytes == null) {
511             return "";
512         }
513
514         StringBuffer buf = new StringBuffer();
515         for (byte b : bytes) {
516             short u8byte = (short) (b & 0xff);
517             String tmp = Integer.toHexString(u8byte);
518             if (tmp.length() == 1) {
519                 buf.append("0");
520             }
521             buf.append(tmp);
522         }
523         return buf.toString();
524     }
525 }