26b9581fa0675e1eed73f19a85e2612bd8964f08
[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 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;
18 import java.util.Map;
19 import java.util.Map.Entry;
20 import java.util.Queue;
21 import java.util.Set;
22
23 import javax.annotation.Nullable;
24
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;
44
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;
53
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;
59
60 public class TransactUtils {
61     private static final Logger LOG = LoggerFactory.getLogger(TransactUtils.class);
62
63     private static <T extends DataObject> Predicate<DataObjectModification<T>> hasDataBefore() {
64         return new Predicate<DataObjectModification<T>>() {
65             @Override
66             public boolean apply(@Nullable DataObjectModification<T> input) {
67                 return input != null && input.getDataBefore() != null;
68             }
69         };
70     }
71
72     private static <T extends DataObject> Predicate<DataObjectModification<T>> hasDataBeforeAndDataAfter() {
73         return new Predicate<DataObjectModification<T>>() {
74             @Override
75             public boolean apply(@Nullable DataObjectModification<T> input) {
76                 return input != null && input.getDataBefore() != null && input.getDataAfter() != null;
77             }
78         };
79     }
80
81     private static <T extends DataObject> Predicate<DataObjectModification<T>> hasNoDataBefore() {
82         return new Predicate<DataObjectModification<T>>() {
83             @Override
84             public boolean apply(@Nullable DataObjectModification<T> input) {
85                 return input != null && input.getDataBefore() == null;
86             }
87         };
88     }
89
90     private static <T extends DataObject> Predicate<DataObjectModification<T>> hasDataAfterAndMatchesFilter(
91             final Predicate<DataObjectModification<T>> filter) {
92         return new Predicate<DataObjectModification<T>>() {
93             @Override
94             public boolean apply(@Nullable DataObjectModification<T> input) {
95                 return input != null && input.getDataAfter() != null && filter.apply(input);
96             }
97         };
98     }
99
100     private static <T extends DataObject> Predicate<DataObjectModification<T>> matchesEverything() {
101         return new Predicate<DataObjectModification<T>>() {
102             @Override
103             public boolean apply(@Nullable DataObjectModification<T> input) {
104                 return true;
105             }
106         };
107     }
108
109     private static <T extends DataObject> Predicate<DataObjectModification<T>> modificationIsDeletion() {
110         return new Predicate<DataObjectModification<T>>() {
111             @Override
112             public boolean apply(@Nullable DataObjectModification<T> input) {
113                 return input != null && input.getModificationType() == DataObjectModification
114                         .ModificationType.DELETE;
115             }
116         };
117     }
118
119     private static <T extends DataObject> Predicate<DataObjectModification<T>> modificationIsDeletionAndHasDataBefore
120             () {
121         return new Predicate<DataObjectModification<T>>() {
122             @Override
123             public boolean apply(@Nullable DataObjectModification<T> input) {
124                 return input != null && input.getModificationType() == DataObjectModification
125                         .ModificationType.DELETE && input.getDataBefore() != null;
126             }
127         };
128     }
129
130     public static Map<InstanceIdentifier<Node>,Node> extractNode(
131             Map<InstanceIdentifier<?>, DataObject> changes) {
132         Map<InstanceIdentifier<Node>,Node> result
133             = new HashMap<>();
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);
143                     }
144                 }
145             }
146         }
147         return result;
148     }
149
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);
153     }
154
155     /**
156      * Extract all the instances of {@code clazz} which were created in the given set of modifications.
157      *
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.
163      */
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());
167     }
168
169     /**
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.
172      *
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.
179      */
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());
187         }
188         return result;
189     }
190
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);
194     }
195
196     /**
197      * Extract all the instances of {@code clazz} which were updated in the given set of modifications.
198      *
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.
204      */
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());
208     }
209
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));
214         return result;
215     }
216
217     /**
218      * Extract all the instances of {@code clazz} which were created or updated in the given set of modifications.
219      *
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.
225      */
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());
229     }
230
231     public static <T extends DataObject> Map<InstanceIdentifier<T>, T> extractCreatedOrUpdatedOrRemoved(
232             AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> changes,
233             Class<T> klazz) {
234         Map<InstanceIdentifier<T>,T> result = extractCreatedOrUpdated(changes,klazz);
235         result.putAll(extractRemovedObjects(changes, klazz));
236         return result;
237     }
238
239     /**
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.
243      *
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.
249      */
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));
255         return result;
256     }
257
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);
261     }
262
263     /**
264      * Extract the original instances of class {@code clazz} in the given set of modifications.
265      *
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.
271      */
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());
278         }
279         return result;
280     }
281
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;
290                     result.add(iidn);
291                 }
292             }
293         }
294         return result;
295     }
296
297     /**
298      * Extract the instance identifier of removed instances of {@code clazz} from the given set of modifications.
299      *
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.
305      */
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();
309     }
310
311     /**
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.
314      *
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.
321      */
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());
332             }
333         }
334         return extractDataObjectModifications(dataObjectModifications, paths, clazz, filter);
335     }
336
337     /**
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.
340      *
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.
347      */
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);
362             }
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));
367             }
368         }
369         return result;
370     }
371
372     /**
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.
375      *
376      * @param path The current path.
377      * @param child The child modification to include.
378      * @return The extended path.
379      */
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);
390             return extendedPath;
391         } else {
392             InstanceIdentifier<N> extendedPath = path.child(item);
393             LOG.debug("Building a new child iid for {} with {}, resulting in {}",
394                     path, item, extendedPath);
395             return extendedPath;
396         }
397     }
398
399     public static <T extends DataObject> Map<InstanceIdentifier<T>, T> extractRemovedObjects(
400             AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> changes,
401             Class<T> klazz) {
402         Set<InstanceIdentifier<T>> iids = extractRemoved(changes, klazz);
403         return Maps.filterKeys(extractOriginal(changes, klazz),Predicates.in(iids));
404     }
405
406     /**
407      * Extract the removed instances of {@code clazz} from the given set of modifications.
408      *
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.
414      */
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());
421         }
422         return result;
423     }
424
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);
438                     }
439                 }
440             }
441         }
442         return result;
443     }
444
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);
451             }
452         }
453         return inserts;
454     }
455
456     /**
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.
460      *
461      * @param insert - Insert from which to extract the NamedUuid
462      * @return UUID - NamedUUID of the Insert
463      */
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);
469     }
470
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));
476     }
477
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,
485                     Mutator.INSERT,
486                     externalIdsMap);
487         Mutation deleteIidMutation = new Mutation(columnSchema.getName(),
488                 Mutator.DELETE,
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);
493         return mutate;
494     }
495 }