bug 6578 added mdsal read retry
[ovsdb.git] / hwvtepsouthbound / hwvtepsouthbound-impl / src / main / java / org / opendaylight / ovsdb / hwvtepsouthbound / transact / PhysicalSwitchUpdateCommand.java
1 /*
2  * Copyright © 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 static org.opendaylight.ovsdb.hwvtepsouthbound.HwvtepSouthboundUtil.schemaMismatchLog;
12 import static org.opendaylight.ovsdb.lib.operations.Operations.op;
13
14 import java.util.Collection;
15 import java.util.Collections;
16 import java.util.HashMap;
17 import java.util.HashSet;
18 import java.util.List;
19 import java.util.Map;
20 import java.util.Map.Entry;
21 import java.util.Set;
22
23 import org.opendaylight.controller.md.sal.binding.api.DataObjectModification;
24 import org.opendaylight.controller.md.sal.binding.api.DataTreeModification;
25 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
26 import org.opendaylight.ovsdb.hwvtepsouthbound.HwvtepSouthboundMapper;
27 import org.opendaylight.ovsdb.lib.error.SchemaVersionMismatchException;
28 import org.opendaylight.ovsdb.lib.notation.Mutator;
29 import org.opendaylight.ovsdb.lib.notation.UUID;
30 import org.opendaylight.ovsdb.lib.operations.TransactionBuilder;
31 import org.opendaylight.ovsdb.lib.schema.typed.TyperUtils;
32 import org.opendaylight.ovsdb.schema.hardwarevtep.Global;
33 import org.opendaylight.ovsdb.schema.hardwarevtep.PhysicalSwitch;
34 import org.opendaylight.ovsdb.schema.hardwarevtep.Tunnel;
35 import org.opendaylight.ovsdb.utils.mdsal.utils.MdsalUtils;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.HwvtepPhysicalLocatorAugmentation;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.PhysicalSwitchAugmentation;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.hwvtep.physical._switch.attributes.ManagementIps;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.hwvtep.physical._switch.attributes.TunnelIps;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.hwvtep.physical._switch.attributes.Tunnels;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.hwvtep.tunnel.attributes.BfdLocalConfigs;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.hwvtep.tunnel.attributes.BfdParams;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.hwvtep.tunnel.attributes.BfdRemoteConfigs;
44 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
45 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.node.TerminationPoint;
46 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
47 import org.slf4j.Logger;
48 import org.slf4j.LoggerFactory;
49
50 import com.google.common.base.Optional;
51 import com.google.common.collect.ImmutableMap;
52 import com.google.common.collect.Sets;
53
54 public class PhysicalSwitchUpdateCommand extends AbstractTransactCommand {
55     private static final Logger LOG = LoggerFactory.getLogger(PhysicalSwitchUpdateCommand.class);
56
57     public PhysicalSwitchUpdateCommand(HwvtepOperationalState state,
58             Collection<DataTreeModification<Node>> changes) {
59         super(state, changes);
60     }
61
62     @Override
63     public void execute(TransactionBuilder transaction) {
64         Map<InstanceIdentifier<Node>, PhysicalSwitchAugmentation> created =
65                 extractCreated(getChanges(),PhysicalSwitchAugmentation.class);
66         if (!created.isEmpty()) {
67             for (Entry<InstanceIdentifier<Node>, PhysicalSwitchAugmentation> physicalSwitchEntry:
68                 created.entrySet()) {
69                 updatePhysicalSwitch(transaction,  physicalSwitchEntry.getKey(), physicalSwitchEntry.getValue());
70             }
71         }
72         Map<InstanceIdentifier<Node>, PhysicalSwitchAugmentation> updated =
73                 extractUpdatedSwitches(getChanges(),PhysicalSwitchAugmentation.class);
74         if (!updated.isEmpty()) {
75             for (Entry<InstanceIdentifier<Node>, PhysicalSwitchAugmentation> physicalSwitchEntry:
76                 updated.entrySet()) {
77                 updatePhysicalSwitch(transaction,  physicalSwitchEntry.getKey(), physicalSwitchEntry.getValue());
78             }
79         }
80     }
81
82
83     private void updatePhysicalSwitch(TransactionBuilder transaction,
84             InstanceIdentifier<Node> iid, PhysicalSwitchAugmentation physicalSwitchAugmentation) {
85         LOG.debug("Creating a physical switch named: {}", physicalSwitchAugmentation.getHwvtepNodeName());
86         Optional<PhysicalSwitchAugmentation> operationalPhysicalSwitchOptional =
87                 getOperationalState().getPhysicalSwitchAugmentation(iid);
88         PhysicalSwitch physicalSwitch = TyperUtils.getTypedRowWrapper(transaction.getDatabaseSchema(), PhysicalSwitch.class);
89         setDescription(physicalSwitch, physicalSwitchAugmentation);
90         setManagementIps(physicalSwitch, physicalSwitchAugmentation);
91         setTunnuleIps(physicalSwitch, operationalPhysicalSwitchOptional.get());
92         try {
93             setTunnels(transaction, iid, physicalSwitch, physicalSwitchAugmentation,
94                             operationalPhysicalSwitchOptional.isPresent());
95         } catch (SchemaVersionMismatchException e) {
96             schemaMismatchLog("tunnels", "Physical_Switch", e);
97         }
98         if (!operationalPhysicalSwitchOptional.isPresent()) {
99             //create a physical switch
100             setName(physicalSwitch, physicalSwitchAugmentation, operationalPhysicalSwitchOptional);
101             String pswitchUuid = "PhysicalSwitch_" + HwvtepSouthboundMapper.getRandomUUID();
102             transaction.add(op.insert(physicalSwitch).withId(pswitchUuid));
103             transaction.add(op.comment("Physical Switch: Creating " +
104                             physicalSwitchAugmentation.getHwvtepNodeName().getValue()));
105             //update global table
106             Global global = TyperUtils.getTypedRowWrapper(transaction.getDatabaseSchema(), Global.class);
107             global.setSwitches(Collections.singleton(new UUID(pswitchUuid)));
108
109             LOG.trace("execute: create physical switch: {}", physicalSwitch);
110             transaction.add(op.mutate(global)
111                     .addMutation(global.getSwitchesColumn().getSchema(), Mutator.INSERT,
112                             global.getSwitchesColumn().getData()));
113             transaction.add(op.comment("Global: Mutating " +
114                             physicalSwitchAugmentation.getHwvtepNodeName().getValue() + " " + pswitchUuid));
115         } else {
116             PhysicalSwitchAugmentation updatedPhysicalSwitch = operationalPhysicalSwitchOptional.get();
117             String existingPhysicalSwitchName = updatedPhysicalSwitch.getHwvtepNodeName().getValue();
118             /* In case TOR devices don't allow creation of PhysicalSwitch name might be null
119              * as user is only adding configurable parameters to MDSAL like BFD params
120              * 
121              * TODO Note: Consider handling tunnel udpate/remove in separate command
122              */
123             if(existingPhysicalSwitchName == null) {
124                 existingPhysicalSwitchName = operationalPhysicalSwitchOptional.get().getHwvtepNodeName().getValue();
125             }
126             // Name is immutable, and so we *can't* update it.  So we use extraPhysicalSwitch for the schema stuff
127             PhysicalSwitch extraPhysicalSwitch = TyperUtils.getTypedRowWrapper(transaction.getDatabaseSchema(), PhysicalSwitch.class);
128             extraPhysicalSwitch.setName("");
129             LOG.trace("execute: updating physical switch: {}", physicalSwitch);
130             transaction.add(op.update(physicalSwitch)
131                     .where(extraPhysicalSwitch.getNameColumn().getSchema().opEqual(existingPhysicalSwitchName))
132                     .build());
133             transaction.add(op.comment("Physical Switch: Updating " + existingPhysicalSwitchName));
134         }
135     }
136
137     private void setName(PhysicalSwitch physicalSwitch, PhysicalSwitchAugmentation physicalSwitchAugmentation,
138             Optional<PhysicalSwitchAugmentation> operationalPhysicalSwitchOptional) {
139         if (physicalSwitchAugmentation.getHwvtepNodeName() != null) {
140             physicalSwitch.setName(physicalSwitchAugmentation.getHwvtepNodeName().getValue());
141         } else if (operationalPhysicalSwitchOptional.isPresent() && operationalPhysicalSwitchOptional.get().getHwvtepNodeName() != null) {
142             physicalSwitch.setName(operationalPhysicalSwitchOptional.get().getHwvtepNodeName().getValue());
143         }
144     }
145
146     private void setDescription(PhysicalSwitch physicalSwitch, PhysicalSwitchAugmentation physicalSwitchAugmentation) {
147         if (physicalSwitchAugmentation.getHwvtepNodeDescription() != null) {
148             physicalSwitch.setDescription(physicalSwitchAugmentation.getHwvtepNodeDescription());
149         }
150     }
151
152     private void setManagementIps(PhysicalSwitch physicalSwitch, PhysicalSwitchAugmentation physicalSwitchAugmentation) {
153         Set<String> ipSet = new HashSet<>();
154         if (physicalSwitchAugmentation.getManagementIps() != null) {
155             for (ManagementIps ip: physicalSwitchAugmentation.getManagementIps()) {
156                 ipSet.add(ip.getManagementIpsKey().getIpv4Address().getValue());
157             }
158             physicalSwitch.setManagementIps(ipSet);
159         }
160     }
161
162     private void setTunnuleIps(PhysicalSwitch physicalSwitch, PhysicalSwitchAugmentation physicalSwitchAugmentation) {
163         Set<String> ipSet = new HashSet<>();
164         if (physicalSwitchAugmentation.getTunnelIps() != null) {
165             for (TunnelIps ip: physicalSwitchAugmentation.getTunnelIps()) {
166                 ipSet.add(ip.getTunnelIpsKey().getIpv4Address().getValue());
167             }
168             physicalSwitch.setTunnelIps(ipSet);
169         }
170     }
171
172     @SuppressWarnings("unchecked")
173     private void setTunnels(TransactionBuilder transaction, InstanceIdentifier<Node> iid,
174                     PhysicalSwitch physicalSwitch, PhysicalSwitchAugmentation physicalSwitchAugmentation,
175                     boolean pSwitchExists) {
176         //TODO: revisit this code for optimizations
177         //TODO: needs more testing
178         if(physicalSwitchAugmentation.getTunnels() != null) {
179             for(Tunnels tunnel: physicalSwitchAugmentation.getTunnels()) {
180                 Optional<Tunnels> opTunnelOpt = getOperationalState().getTunnels(iid, tunnel.getKey());
181                 Tunnel newTunnel = TyperUtils.getTypedRowWrapper(transaction.getDatabaseSchema(), Tunnel.class);
182
183                 UUID localUUID = getLocatorUUID(transaction,
184                                 (InstanceIdentifier<TerminationPoint>) tunnel.getLocalLocatorRef().getValue());
185                 UUID remoteUUID = getLocatorUUID(transaction,
186                                 (InstanceIdentifier<TerminationPoint>) tunnel.getRemoteLocatorRef().getValue());
187                 if(localUUID != null && remoteUUID != null) {
188                     UUID uuid;
189                     // local and remote must exist
190                     newTunnel.setLocal(localUUID);
191                     newTunnel.setRemote(remoteUUID);
192                     setBfdParams(newTunnel, tunnel);
193                     setBfdLocalConfigs(newTunnel, tunnel);
194                     setBfdRemoteConfigs(newTunnel, tunnel);
195                     if(!opTunnelOpt.isPresent()) {
196                         String tunnelUuid = "Tunnel_" + HwvtepSouthboundMapper.getRandomUUID();
197                         transaction.add(op.insert(newTunnel).withId(tunnelUuid));
198                         transaction.add(op.comment("Tunnel: Creating " + tunnelUuid));
199                         if(!pSwitchExists) {
200                             //TODO: Figure out a way to handle this
201                             LOG.warn("Tunnel configuration requires pre-existing physicalSwitch");
202                         } else {
203                             // TODO: Can we reuse physicalSwitch instead?
204                             PhysicalSwitch pSwitch =
205                                             TyperUtils.getTypedRowWrapper(transaction.getDatabaseSchema(),
206                                                             PhysicalSwitch.class);
207                             pSwitch.setTunnels(Collections.singleton(new UUID(tunnelUuid)));
208                             pSwitch.setName(physicalSwitchAugmentation.getHwvtepNodeName().getValue());
209                             transaction.add(op.mutate(pSwitch)
210                                             .addMutation(pSwitch.getTunnels().getSchema(), Mutator.INSERT,
211                                                     pSwitch.getTunnels().getData())
212                                             .where(pSwitch.getNameColumn().getSchema().
213                                                             opEqual(pSwitch.getNameColumn().getData()))
214                                             .build());
215                             transaction.add(op.comment("PhysicalSwitch: Mutating " + tunnelUuid));
216                         }
217                         uuid = new UUID(tunnelUuid);
218                     } else {
219                         uuid = new UUID (opTunnelOpt.get().getTunnelUuid().getValue());
220                         Tunnel extraTunnel =
221                                 TyperUtils.getTypedRowWrapper(transaction.getDatabaseSchema(), Tunnel.class, null);
222                         extraTunnel.getUuidColumn().setData(uuid);
223                         transaction.add(op.update(newTunnel)
224                                         .where(extraTunnel.getUuidColumn().getSchema().opEqual(uuid))
225                                         .build());
226                         transaction.add(op.comment("Tunnel: Updating " + uuid));
227                     }
228                 }
229             }
230         }
231     }
232
233     private void setBfdParams(Tunnel tunnel, Tunnels psAugTunnel) {
234         List<BfdParams> bfdParams = psAugTunnel.getBfdParams();
235         if(bfdParams != null) {
236             Map<String, String> bfdParamMap = new HashMap<>();
237             for(BfdParams bfdParam : bfdParams) {
238                 bfdParamMap.put(bfdParam.getBfdParamKey(), bfdParam.getBfdParamValue());
239             }
240             try {
241                 tunnel.setBfdParams(ImmutableMap.copyOf(bfdParamMap));
242             } catch (NullPointerException e) {
243                 LOG.warn("Incomplete BFD Params for tunnel", e);
244             }
245         }
246     }
247
248     private void setBfdLocalConfigs(Tunnel tunnel, Tunnels psAugTunnel) {
249         List<BfdLocalConfigs> bfdLocalConfigs = psAugTunnel.getBfdLocalConfigs();
250         if(bfdLocalConfigs != null) {
251             Map<String, String> configLocalMap = new HashMap<>();
252             for(BfdLocalConfigs localConfig : bfdLocalConfigs) {
253                 configLocalMap.put(localConfig.getBfdLocalConfigKey(), localConfig.getBfdLocalConfigValue());
254             }
255             try {
256                 tunnel.setBfdConfigLocal(ImmutableMap.copyOf(configLocalMap));
257             } catch (NullPointerException e) {
258                 LOG.warn("Incomplete BFD LocalConfig for tunnel", e);
259             }
260         }
261     }
262
263     private void setBfdRemoteConfigs(Tunnel tunnel, Tunnels psAugTunnel) {
264         List<BfdRemoteConfigs> bfdRemoteConfigs = psAugTunnel.getBfdRemoteConfigs();
265         if(bfdRemoteConfigs != null) {
266             Map<String, String> configRemoteMap = new HashMap<>();
267             for(BfdRemoteConfigs remoteConfig : bfdRemoteConfigs) {
268                 configRemoteMap.put(remoteConfig.getBfdRemoteConfigKey(), remoteConfig.getBfdRemoteConfigValue());
269             }
270             try {
271                 tunnel.setBfdConfigRemote(ImmutableMap.copyOf(configRemoteMap));
272             } catch (NullPointerException e) {
273                 LOG.warn("Incomplete BFD RemoteConfig for tunnel", e);
274             }
275         }
276     }
277
278     private UUID getLocatorUUID(TransactionBuilder transaction, InstanceIdentifier<TerminationPoint> iid) {
279         UUID locatorUUID = null;
280         Optional<HwvtepPhysicalLocatorAugmentation> opLocOptional =
281                         getOperationalState().getPhysicalLocatorAugmentation(iid);
282         if (opLocOptional.isPresent()) {
283             // Get Locator UUID from operational
284             HwvtepPhysicalLocatorAugmentation locatorAug = opLocOptional.get();
285             locatorUUID = new UUID(locatorAug.getPhysicalLocatorUuid().getValue());
286         } else {
287             // TODO/FIXME: Not in operational, do we create a new one?
288             LOG.warn("Trying to create tunnel without creating physical locators first");
289             Optional<TerminationPoint> confLocOptional = new MdsalUtils(getOperationalState().getDataBroker())
290                     .readOptional(LogicalDatastoreType.CONFIGURATION, iid);
291             if (confLocOptional.isPresent()) {
292                 HwvtepPhysicalLocatorAugmentation locatorAugmentation =
293                                 confLocOptional.get().getAugmentation(HwvtepPhysicalLocatorAugmentation.class);
294                 locatorUUID = TransactUtils.createPhysicalLocator(transaction, getOperationalState(), iid);
295             } else {
296                 LOG.warn("Unable to find endpoint for tunnel. Endpoint indentifier is {}", iid);
297             }
298         }
299         return locatorUUID;
300     }
301
302     private Map<InstanceIdentifier<Node>, PhysicalSwitchAugmentation> extractCreated(
303             Collection<DataTreeModification<Node>> changes, Class<PhysicalSwitchAugmentation> class1) {
304         Map<InstanceIdentifier<Node>, PhysicalSwitchAugmentation> result = new HashMap<>();
305         if (changes != null && !changes.isEmpty()) {
306             for (DataTreeModification<Node> change : changes) {
307                 final InstanceIdentifier<Node> key = change.getRootPath().getRootIdentifier();
308                 final DataObjectModification<Node> mod = change.getRootNode();
309                 Node created = TransactUtils.getCreated(mod);
310                 if (created != null) {
311                     PhysicalSwitchAugmentation physicalSwitch = created.getAugmentation(PhysicalSwitchAugmentation.class);
312                     if (physicalSwitch != null) {
313                         result.put(key, physicalSwitch);
314                     }
315                 }
316             }
317         }
318         return result;
319     }
320
321     private Map<InstanceIdentifier<Node>, PhysicalSwitchAugmentation> extractUpdatedSwitches(
322             Collection<DataTreeModification<Node>> changes, Class<PhysicalSwitchAugmentation> class1) {
323         Map<InstanceIdentifier<Node>, PhysicalSwitchAugmentation> result = new HashMap<>();
324         if (changes != null && !changes.isEmpty()) {
325             for (DataTreeModification<Node> change : changes) {
326                 final InstanceIdentifier<Node> key = change.getRootPath().getRootIdentifier();
327                 final DataObjectModification<Node> mod = change.getRootNode();
328                 Node updated = TransactUtils.getUpdated(mod);
329                 if (updated != null) {
330                     PhysicalSwitchAugmentation physicalSwitch = updated.getAugmentation(PhysicalSwitchAugmentation.class);
331                     if (physicalSwitch != null) {
332                         result.put(key, physicalSwitch);
333                     }
334                 }
335             }
336         }
337         return result;
338     }
339
340 }