7f6c937e3d894fce33bb5c5f3c7112db0193ae11
[ovsdb.git] / hwvtepsouthbound / hwvtepsouthbound-impl / src / main / java / org / opendaylight / ovsdb / hwvtepsouthbound / transact / TransactCommandAggregator.java
1 /*
2  * Copyright (c) 2015, 2017 China Telecom Beijing Research Institute 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
9 package org.opendaylight.ovsdb.hwvtepsouthbound.transact;
10
11 import org.apache.commons.lang3.tuple.Pair;
12 import org.opendaylight.controller.md.sal.binding.api.DataObjectModification;
13 import org.opendaylight.controller.md.sal.binding.api.DataTreeModification;
14 import org.opendaylight.ovsdb.lib.operations.TransactionBuilder;
15 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.HwvtepGlobalAugmentation;
16 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.PhysicalSwitchAugmentation;
17 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.hwvtep.global.attributes.RemoteUcastMacs;
18 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
19 import org.opendaylight.yangtools.yang.binding.DataObject;
20 import org.opendaylight.yangtools.yang.binding.Identifiable;
21 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
22 import org.slf4j.Logger;
23 import org.slf4j.LoggerFactory;
24
25 import java.util.ArrayList;
26 import java.util.Collection;
27 import java.util.HashMap;
28 import java.util.List;
29 import java.util.Map;
30 import java.util.Objects;
31
32 public class TransactCommandAggregator implements TransactCommand {
33     private static final Logger LOG = LoggerFactory.getLogger(TransactCommandAggregator.class);
34
35     private List<TransactCommand> commands = new ArrayList<>();
36     private final HwvtepOperationalState operationalState;
37     /* stores the modified and deleted data for each child type of each node id
38        Map<nodeid , Pair < updated, deleted >
39        each updated/ deleted contains Map < child type, List<ChildData>>
40        child type is the child of hwvtep Global augmentation
41      */
42     private final Map<InstanceIdentifier<Node>,
43             Pair<Map<Class<? extends Identifiable>, List<Identifiable>>,
44                 Map<Class<? extends Identifiable>, List<Identifiable>>>> modifiedData = new HashMap<>();
45
46
47     public TransactCommandAggregator(HwvtepOperationalState state, Collection<DataTreeModification<Node>> changes) {
48         this.operationalState = state;
49         onDataTreeChanged(changes);
50         commands.add(new PhysicalSwitchUpdateCommand(state,changes));
51         commands.add(new PhysicalSwitchRemoveCommand(state,changes));
52         commands.add(new LogicalSwitchUpdateCommand(state,changes));
53         commands.add(new LogicalSwitchRemoveCommand(state,changes));
54         commands.add(new PhysicalPortUpdateCommand(state,changes));
55         commands.add(new PhysicalPortRemoveCommand(state,changes));
56         commands.add(new McastMacsRemoteUpdateCommand(state,changes));
57         commands.add(new McastMacsRemoteRemoveCommand(state,changes));
58         commands.add(new McastMacsLocalUpdateCommand(state,changes));
59         commands.add(new McastMacsLocalRemoveCommand(state,changes));
60         commands.add(new UcastMacsRemoteUpdateCommand(state,changes));
61         commands.add(new UcastMacsRemoteRemoveCommand(state,changes));
62         commands.add(new UcastMacsLocalUpdateCommand(state,changes));
63         commands.add(new UcastMacsLocalRemoveCommand(state,changes));
64         commands.add(new TunnelUpdateCommand(state,changes));
65         commands.add(new TunnelRemoveCommand(state,changes));
66         commands.add(new LogicalRouterUpdateCommand(state,changes));
67         commands.add(new LogicalRouterRemoveCommand(state,changes));
68     }
69
70     @Override
71     public void execute(TransactionBuilder transaction) {
72         for (TransactCommand command:commands) {
73             try {
74                 command.execute(transaction);
75             } catch (NullPointerException e) {
76                 LOG.error("Execution of command {} failed with the following exception."
77                         + " Continuing the execution of remaining commands", command, e);
78             }
79         }
80     }
81
82     @Override
83     public void onConfigUpdate(TransactionBuilder transaction, InstanceIdentifier nodeIid, Identifiable data,
84                                InstanceIdentifier key,
85                                Object... extraData) {
86     }
87
88     @Override
89     public void doDeviceTransaction(TransactionBuilder transaction, InstanceIdentifier nodeIid, Identifiable data,
90                                     InstanceIdentifier key,
91                                     Object... extraData) {
92     }
93
94     private void onDataTreeChanged(final Collection<DataTreeModification<Node>> changes) {
95         boolean readOperationalNodes = false;
96         for (DataTreeModification<Node> change : changes) {
97             final InstanceIdentifier<Node> key = change.getRootPath().getRootIdentifier();
98             final DataObjectModification<Node> mod = change.getRootNode();
99             final Map<Class<? extends Identifiable>, List<Identifiable>> updatedData = new HashMap<>();
100             final Map<Class<? extends Identifiable>, List<Identifiable>> deletedData = new HashMap<>();
101             extractDataChanged(key, mod, updatedData, deletedData);
102             modifiedData.put(key, Pair.of(updatedData, deletedData));
103             operationalState.setModifiedData(modifiedData);
104             if (!isMacOnlyUpdate(updatedData, deletedData)) {
105                 readOperationalNodes = true;
106             }
107         }
108         if (readOperationalNodes) {
109             operationalState.readOperationalNodes();
110         }
111     }
112
113     private boolean isMacOnlyUpdate(final Map<Class<? extends Identifiable>, List<Identifiable>> updatedData,
114                                     final Map<Class<? extends Identifiable>, List<Identifiable>> deletedData) {
115         return (updatedData.containsKey(RemoteUcastMacs.class) && updatedData.size() == 1)
116                 || (deletedData.containsKey(RemoteUcastMacs.class) && deletedData.size() == 1);
117     }
118
119     private void extractDataChanged(final InstanceIdentifier<Node> key,
120                                     final DataObjectModification<Node> mod,
121                                     final Map<Class<? extends Identifiable>, List<Identifiable>> updatedData,
122                                     final Map<Class<? extends Identifiable>, List<Identifiable>> deletedData) {
123
124         extractDataChanged(mod.getModifiedChildren(), updatedData, deletedData);
125         DataObjectModification<HwvtepGlobalAugmentation> aug = mod.getModifiedAugmentation(
126                 HwvtepGlobalAugmentation.class);
127         if (aug != null && getModificationType(aug) != null) {
128             extractDataChanged(aug.getModifiedChildren(), updatedData, deletedData);
129         }
130         DataObjectModification<PhysicalSwitchAugmentation> psAug = mod.getModifiedAugmentation(
131                 PhysicalSwitchAugmentation.class);
132         if (psAug != null && getModificationType(psAug) != null) {
133             extractDataChanged(psAug.getModifiedChildren(), updatedData, deletedData);
134         }
135     }
136
137     private void extractDataChanged(final Collection<DataObjectModification<? extends DataObject>> children,
138                                     final Map<Class<? extends Identifiable>, List<Identifiable>> updatedData,
139                                     final Map<Class<? extends Identifiable>, List<Identifiable>> deletedData) {
140         if (children == null) {
141             return;
142         }
143         for (DataObjectModification<? extends DataObject> child : children) {
144             DataObjectModification.ModificationType type = getModificationType(child);
145             if (type == null) {
146                 continue;
147             }
148             InstanceIdentifier instanceIdentifier = null;
149             Class<? extends Identifiable> childClass = (Class<? extends Identifiable>) child.getDataType();
150             InstanceIdentifier.PathArgument pathArgument = child.getIdentifier();
151             switch (type) {
152                 case WRITE:
153                 case SUBTREE_MODIFIED:
154                     DataObject dataAfter = child.getDataAfter();
155                     if (!(dataAfter instanceof Identifiable)) {
156                         continue;
157                     }
158                     DataObject before = child.getDataBefore();
159                     if (Objects.equals(dataAfter, before)) {
160                         /*
161                         in cluster reboot scenarios,
162                         application rewrites the data tx.put( logicalswitchiid, logicalswitch )
163                         that time it fires the update again ignoring such updates here
164                          */
165                         continue;
166                     }
167                     Identifiable identifiable = (Identifiable) dataAfter;
168                     addToUpdatedData(updatedData, childClass, identifiable);
169                     break;
170                 case DELETE:
171                     DataObject dataBefore = child.getDataBefore();
172                     if (!(dataBefore instanceof Identifiable)) {
173                         continue;
174                     }
175                     addToUpdatedData(deletedData, childClass, (Identifiable)dataBefore);
176                     break;
177             }
178         }
179     }
180
181     private void addToUpdatedData(Map<Class<? extends Identifiable>, List<Identifiable>> updatedData,
182                                   Class<? extends Identifiable> childClass, Identifiable identifiable) {
183         updatedData.computeIfAbsent(childClass, (cls) -> new ArrayList<>());
184         updatedData.get(childClass).add(identifiable);
185     }
186
187     private DataObjectModification.ModificationType getModificationType(
188             DataObjectModification<? extends DataObject> mod) {
189         try {
190             return mod.getModificationType();
191         } catch (IllegalStateException e) {
192             //not sure why this getter throws this exception, could be some mdsal bug
193             LOG.warn("Failed to get the modification type for mod {}", mod);
194         }
195         return null;
196     }
197 }