ovsdb enable checkstyle on error
[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.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;
53
54 public class TransactUtils {
55     private static <T extends DataObject> Predicate<DataObjectModification<T>> hasDataBefore() {
56         return new Predicate<DataObjectModification<T>>() {
57             @Override
58             public boolean apply(@Nullable DataObjectModification<T> input) {
59                 return input != null && input.getDataBefore() != null;
60             }
61         };
62     }
63
64     private static <T extends DataObject> Predicate<DataObjectModification<T>> hasDataBeforeAndDataAfter() {
65         return new Predicate<DataObjectModification<T>>() {
66             @Override
67             public boolean apply(@Nullable DataObjectModification<T> input) {
68                 return input != null && input.getDataBefore() != null && input.getDataAfter() != null;
69             }
70         };
71     }
72
73     private static <T extends DataObject> Predicate<DataObjectModification<T>> hasNoDataBefore() {
74         return new Predicate<DataObjectModification<T>>() {
75             @Override
76             public boolean apply(@Nullable DataObjectModification<T> input) {
77                 return input != null && input.getDataBefore() == null;
78             }
79         };
80     }
81
82     private static <T extends DataObject> Predicate<DataObjectModification<T>> hasDataAfterAndMatchesFilter(
83             final Predicate<DataObjectModification<T>> filter) {
84         return new Predicate<DataObjectModification<T>>() {
85             @Override
86             public boolean apply(@Nullable DataObjectModification<T> input) {
87                 return input != null && input.getDataAfter() != null && filter.apply(input);
88             }
89         };
90     }
91
92     private static <T extends DataObject> Predicate<DataObjectModification<T>> matchesEverything() {
93         return new Predicate<DataObjectModification<T>>() {
94             @Override
95             public boolean apply(@Nullable DataObjectModification<T> input) {
96                 return true;
97             }
98         };
99     }
100
101     private static <T extends DataObject> Predicate<DataObjectModification<T>> modificationIsDeletion() {
102         return new Predicate<DataObjectModification<T>>() {
103             @Override
104             public boolean apply(@Nullable DataObjectModification<T> input) {
105                 return input != null && input.getModificationType() == DataObjectModification
106                         .ModificationType.DELETE;
107             }
108         };
109     }
110
111     private static <T extends DataObject> Predicate<DataObjectModification<T>>
112         modificationIsDeletionAndHasDataBefore() {
113         return new Predicate<DataObjectModification<T>>() {
114             @Override
115             public boolean apply(@Nullable DataObjectModification<T> input) {
116                 return input != null && input.getModificationType() == DataObjectModification
117                         .ModificationType.DELETE && input.getDataBefore() != null;
118             }
119         };
120     }
121
122     public static Map<InstanceIdentifier<Node>,Node> extractNode(
123             Map<InstanceIdentifier<?>, DataObject> changes) {
124         Map<InstanceIdentifier<Node>,Node> result
125             = new HashMap<>();
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);
135                     }
136                 }
137             }
138         }
139         return result;
140     }
141
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);
145     }
146
147     /**
148      * Extract all the instances of {@code clazz} which were created in the given set of modifications.
149      *
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.
155      */
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());
159     }
160
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);
164     }
165
166     /**
167      * Extract all the instances of {@code clazz} which were updated in the given set of modifications.
168      *
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.
174      */
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());
178     }
179
180     /**
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.
183      *
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.
190      */
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());
198         }
199         return result;
200     }
201
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));
206         return result;
207     }
208
209     /**
210      * Extract all the instances of {@code clazz} which were created or updated in the given set of modifications.
211      *
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.
217      */
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());
221     }
222
223     public static <T extends DataObject> Map<InstanceIdentifier<T>, T> extractCreatedOrUpdatedOrRemoved(
224             AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> changes,
225             Class<T> klazz) {
226         Map<InstanceIdentifier<T>,T> result = extractCreatedOrUpdated(changes,klazz);
227         result.putAll(extractRemovedObjects(changes, klazz));
228         return result;
229     }
230
231     /**
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.
235      *
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.
241      */
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));
247         return result;
248     }
249
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);
253     }
254
255     /**
256      * Extract the original instances of class {@code clazz} in the given set of modifications.
257      *
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.
263      */
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());
270         }
271         return result;
272     }
273
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;
282                     result.add(iidn);
283                 }
284             }
285         }
286         return result;
287     }
288
289     /**
290      * Extract the instance identifier of removed instances of {@code clazz} from the given set of modifications.
291      *
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.
297      */
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();
301     }
302
303     /**
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.
306      *
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.
313      */
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());
323             }
324         }
325         return extractDataObjectModifications(dataObjectModifications, paths, clazz, filter);
326     }
327
328     /**
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.
331      *
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.
338      */
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);
353             }
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));
358             }
359         }
360         return result;
361     }
362
363     /**
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.
366      *
367      * @param path The current path.
368      * @param child The child modification to include.
369      * @return The extended path.
370      */
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);
379             return extendedPath;
380         } else {
381             InstanceIdentifier<N> extendedPath = path.child(item);
382             return extendedPath;
383         }
384     }
385
386     public static <T extends DataObject> Map<InstanceIdentifier<T>, T> extractRemovedObjects(
387             AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> changes,
388             Class<T> klazz) {
389         Set<InstanceIdentifier<T>> iids = extractRemoved(changes, klazz);
390         return Maps.filterKeys(extractOriginal(changes, klazz),Predicates.in(iids));
391     }
392
393     /**
394      * Extract the removed instances of {@code clazz} from the given set of modifications.
395      *
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.
401      */
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());
408         }
409         return result;
410     }
411
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);
425                     }
426                 }
427             }
428         }
429         return result;
430     }
431
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);
438             }
439         }
440         return inserts;
441     }
442
443     /**
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.
447      *
448      * @param insert - Insert from which to extract the NamedUuid
449      * @return UUID - NamedUUID of the Insert
450      */
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);
456     }
457
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));
463     }
464
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,
472                     Mutator.INSERT,
473                     externalIdsMap);
474         Mutation deleteIidMutation = new Mutation(columnSchema.getName(),
475                 Mutator.DELETE,
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);
480         return mutate;
481     }
482
483     /**
484      * This method builds a string by concatenating the 2 character
485      * hexadecimal representation of each byte from the input byte array.
486      * <p>
487      * For example: an input byte array containing:
488      *   bytes[0] = 'a'
489      *   bytes[1] = 'b'
490      *   bytes[2] = 'c'
491      *   bytes[3] = '-'
492      *   bytes[4] = '1'
493      *   bytes[5] = '2'
494      *   bytes[6] = '3'
495      * returns the string "6162632d313233"
496      * </p>
497      * @param bytes
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
501      */
502     public static String bytesToHexString(byte[] bytes) {
503
504         if (bytes == null) {
505             return "";
506         }
507
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) {
513                 buf.append("0");
514             }
515             buf.append(tmp);
516         }
517         return buf.toString();
518     }
519 }