Bump upstreams
[lispflowmapping.git] / mappingservice / dsbackend / src / main / java / org / opendaylight / lispflowmapping / dsbackend / DataStoreBackEnd.java
1 /*
2  * Copyright (c) 2015 Cisco Systems, Inc.  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.lispflowmapping.dsbackend;
9
10 import static java.util.Objects.requireNonNull;
11
12 import com.google.common.annotations.VisibleForTesting;
13 import com.google.common.util.concurrent.FutureCallback;
14 import com.google.common.util.concurrent.ListenableFuture;
15 import com.google.common.util.concurrent.MoreExecutors;
16 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
17 import java.util.ArrayList;
18 import java.util.Date;
19 import java.util.List;
20 import java.util.Optional;
21 import java.util.concurrent.ExecutionException;
22 import org.opendaylight.lispflowmapping.lisp.util.LispAddressStringifier;
23 import org.opendaylight.mdsal.binding.api.DataBroker;
24 import org.opendaylight.mdsal.binding.api.ReadTransaction;
25 import org.opendaylight.mdsal.binding.api.TransactionChain;
26 import org.opendaylight.mdsal.binding.api.WriteTransaction;
27 import org.opendaylight.mdsal.common.api.CommitInfo;
28 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
29 import org.opendaylight.yang.gen.v1.urn.opendaylight.lfm.lisp.proto.rev151105.XtrId;
30 import org.opendaylight.yang.gen.v1.urn.opendaylight.lfm.mappingservice.rev150906.MappingDatabase;
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.lfm.mappingservice.rev150906.MappingOrigin;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.lfm.mappingservice.rev150906.db.instance.AuthenticationKey;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.lfm.mappingservice.rev150906.db.instance.Mapping;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.lfm.mappingservice.rev150906.db.instance.mapping.XtrIdMapping;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.lfm.mappingservice.rev150906.mapping.database.LastUpdated;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.lfm.mappingservice.rev150906.mapping.database.LastUpdatedBuilder;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.lfm.mappingservice.rev150906.mapping.database.VirtualNetworkIdentifier;
38 import org.opendaylight.yangtools.yang.binding.DataObject;
39 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
40 import org.opendaylight.yangtools.yang.common.Empty;
41 import org.slf4j.Logger;
42 import org.slf4j.LoggerFactory;
43
44 /**
45  * Stores data coming from the mapping database RPCs into the MD-SAL datastore.
46  *
47  * @author Lorand Jakab
48  */
49 public class DataStoreBackEnd {
50     private static final Logger LOG = LoggerFactory.getLogger(DataStoreBackEnd.class);
51     private static final InstanceIdentifier<MappingDatabase> DATABASE_ROOT =
52             InstanceIdentifier.create(MappingDatabase.class);
53     private static final InstanceIdentifier<LastUpdated> LAST_UPDATED =
54             InstanceIdentifier.create(MappingDatabase.class).child(LastUpdated.class);
55
56     private final TransactionChain configTxChain;
57     private final TransactionChain operTxChain;
58
59     @SuppressFBWarnings(value = "MC_OVERRIDABLE_METHOD_CALL_IN_CONSTRUCTOR", justification = "Non-final for mocking")
60     public DataStoreBackEnd(DataBroker broker) {
61         LOG.debug("Creating DataStoreBackEnd transaction chain...");
62         configTxChain = broker.createMergingTransactionChain();
63         operTxChain = broker.createMergingTransactionChain();
64         configTxChain.addCallback(new FutureCallback<Empty>() {
65             @Override
66             public void onSuccess(Empty result) {
67                 onTransactionChainSuccessful(configTxChain);
68             }
69
70             @Override
71             public void onFailure(Throwable cause) {
72                 onTransactionChainFailed(configTxChain, cause);
73             }
74         });
75         operTxChain.addCallback(new FutureCallback<Empty>() {
76             @Override
77             public void onSuccess(Empty result) {
78                 onTransactionChainSuccessful(operTxChain);
79             }
80
81             @Override
82             public void onFailure(Throwable cause) {
83                 onTransactionChainFailed(operTxChain, cause);
84             }
85         });
86     }
87
88     public void addAuthenticationKey(AuthenticationKey authenticationKey) {
89         if (LOG.isDebugEnabled()) {
90             LOG.debug("MD-SAL: Adding authentication key '{}' for {}",
91                     authenticationKey.getMappingAuthkey().getKeyString(),
92                     LispAddressStringifier.getString(authenticationKey.getEid()));
93         }
94
95         InstanceIdentifier<AuthenticationKey> path = InstanceIdentifierUtil
96                 .createAuthenticationKeyIid(authenticationKey.getEid());
97         writePutTransaction(path, authenticationKey, LogicalDatastoreType.CONFIGURATION,
98                 "Adding authentication key to MD-SAL datastore failed");
99     }
100
101     public void addMapping(Mapping mapping) {
102         if (LOG.isDebugEnabled()) {
103             LOG.debug("MD-SAL: Adding mapping for {}",
104                     LispAddressStringifier.getString(mapping.getMappingRecord().getEid()));
105         }
106
107         InstanceIdentifier<Mapping> path = InstanceIdentifierUtil
108                 .createMappingIid(mapping.getMappingRecord().getEid(), mapping.getOrigin());
109         writePutTransaction(path, mapping, getDestinationDatastore(mapping),
110                 "Adding mapping to MD-SAL datastore failed");
111     }
112
113     // This method assumes that it is only called for southbound originated Map-Registers
114     public void addXtrIdMapping(XtrIdMapping mapping) {
115         XtrId xtrId = mapping.getMappingRecord().getXtrId();
116         requireNonNull(xtrId, "Make sure you only call addXtrIdMapping when the MappingRecord contains an xTR-ID");
117         if (LOG.isDebugEnabled()) {
118             LOG.debug("MD-SAL: Adding mapping for {}, xTR-ID {}",
119                     LispAddressStringifier.getString(mapping.getMappingRecord().getEid()), xtrId);
120         }
121
122         InstanceIdentifier<XtrIdMapping> path = InstanceIdentifierUtil
123                 .createXtrIdMappingIid(mapping.getMappingRecord().getEid(), MappingOrigin.Southbound, xtrId);
124         writePutTransaction(path, mapping, LogicalDatastoreType.OPERATIONAL,
125                 "Adding xTR-ID mapping to MD-SAL datastore failed");
126     }
127
128     public void removeAuthenticationKey(AuthenticationKey authenticationKey) {
129         if (LOG.isDebugEnabled()) {
130             LOG.debug("MD-SAL: Removing authentication key for {}",
131                     LispAddressStringifier.getString(authenticationKey.getEid()));
132         }
133
134         InstanceIdentifier<AuthenticationKey> path = InstanceIdentifierUtil
135                 .createAuthenticationKeyIid(authenticationKey.getEid());
136         deleteTransaction(path, LogicalDatastoreType.CONFIGURATION,
137                 "Deleting authentication key from MD-SAL datastore failed");
138     }
139
140     public void removeMapping(Mapping mapping) {
141         if (LOG.isDebugEnabled()) {
142             LOG.debug("MD-SAL: Removing mapping for {}",
143                     LispAddressStringifier.getString(mapping.getMappingRecord().getEid()));
144         }
145
146         InstanceIdentifier<Mapping> path = InstanceIdentifierUtil
147                 .createMappingIid(mapping.getMappingRecord().getEid(), mapping.getOrigin());
148         deleteTransaction(path, getDestinationDatastore(mapping), "Deleting mapping from MD-SAL datastore failed");
149     }
150
151     public void removeXtrIdMapping(XtrIdMapping mapping) {
152         XtrId xtrId = mapping.getMappingRecord().getXtrId();
153         requireNonNull(xtrId, "Make sure you only call addXtrIdMapping when the MappingRecord contains an xTR-ID");
154         if (LOG.isDebugEnabled()) {
155             LOG.debug("MD-SAL: Removing mapping for {}, xTR-ID {}",
156                     LispAddressStringifier.getString(mapping.getMappingRecord().getEid()), xtrId);
157         }
158
159         InstanceIdentifier<XtrIdMapping> path = InstanceIdentifierUtil
160                 .createXtrIdMappingIid(mapping.getMappingRecord().getEid(), MappingOrigin.Southbound, xtrId);
161         deleteTransaction(path, LogicalDatastoreType.OPERATIONAL,
162                 "Deleting xTR-ID mapping from MD-SAL datastore failed");
163     }
164
165     public void removeAllDatastoreContent() {
166         LOG.debug("MD-SAL: Removing all mapping database datastore content (mappings and keys)");
167         removeAllConfigDatastoreContent();
168         removeAllOperationalDatastoreContent();
169     }
170
171     public void removeAllConfigDatastoreContent() {
172         deleteTransaction(DATABASE_ROOT, LogicalDatastoreType.CONFIGURATION,
173                 "Removal of all database content in config datastore failed");
174     }
175
176     public void removeAllOperationalDatastoreContent() {
177         deleteTransaction(DATABASE_ROOT, LogicalDatastoreType.OPERATIONAL,
178                 "Removal of all database content in operational datastore failed");
179     }
180
181     public void updateAuthenticationKey(AuthenticationKey authenticationKey) {
182         if (LOG.isDebugEnabled()) {
183             LOG.debug("MD-SAL: Updating authentication key for {} with '{}'",
184                     LispAddressStringifier.getString(authenticationKey.getEid()),
185                     authenticationKey.getMappingAuthkey().getKeyString());
186         }
187
188         InstanceIdentifier<AuthenticationKey> path = InstanceIdentifierUtil
189                 .createAuthenticationKeyIid(authenticationKey.getEid());
190         writePutTransaction(path, authenticationKey, LogicalDatastoreType.CONFIGURATION,
191                 "Updating authentication key in MD-SAL datastore failed");
192     }
193
194     public void updateMapping(Mapping mapping) {
195         if (LOG.isDebugEnabled()) {
196             LOG.debug("MD-SAL: Updating mapping for {}",
197                     LispAddressStringifier.getString(mapping.getMappingRecord().getEid()));
198         }
199
200         InstanceIdentifier<Mapping> path = InstanceIdentifierUtil
201                 .createMappingIid(mapping.getMappingRecord().getEid(), mapping.getOrigin());
202         writePutTransaction(path, mapping, getDestinationDatastore(mapping),
203                 "Updating mapping in MD-SAL datastore failed");
204     }
205
206     public List<Mapping> getAllMappings() {
207         List<Mapping> mappings = getAllMappings(LogicalDatastoreType.CONFIGURATION);
208         mappings.addAll(getAllMappings(LogicalDatastoreType.OPERATIONAL));
209         return mappings;
210     }
211
212     public List<Mapping> getAllMappings(LogicalDatastoreType logicalDataStore) {
213         LOG.debug("MD-SAL: Get all mappings from {} datastore",
214                 logicalDataStore == LogicalDatastoreType.CONFIGURATION ? "config" : "operational");
215         List<Mapping> mappings = new ArrayList<>();
216         MappingDatabase mdb = readTransaction(DATABASE_ROOT, logicalDataStore);
217
218         if (mdb != null && mdb.getVirtualNetworkIdentifier() != null) {
219             for (VirtualNetworkIdentifier id : mdb.nonnullVirtualNetworkIdentifier().values()) {
220                 List<Mapping> ms = new ArrayList<>(id.nonnullMapping().values());
221                 if (ms != null) {
222                     mappings.addAll(ms);
223                 }
224             }
225         }
226
227         return mappings;
228     }
229
230     public List<AuthenticationKey> getAllAuthenticationKeys() {
231         LOG.debug("MD-SAL: Get all authentication keys from datastore");
232         List<AuthenticationKey> authKeys = new ArrayList<>();
233         MappingDatabase mdb = readTransaction(DATABASE_ROOT, LogicalDatastoreType.CONFIGURATION);
234
235         if (mdb != null && mdb.getVirtualNetworkIdentifier() != null) {
236             for (VirtualNetworkIdentifier id : mdb.nonnullVirtualNetworkIdentifier().values()) {
237                 List<AuthenticationKey> keys = new ArrayList<>(id.nonnullAuthenticationKey().values());
238                 if (keys != null) {
239                     authKeys.addAll(keys);
240                 }
241             }
242         }
243
244         return authKeys;
245     }
246
247     public void saveLastUpdateTimestamp() {
248         Long timestamp = System.currentTimeMillis();
249         LOG.debug("MD-SAL: Saving last update timestamp to operational datastore: {}", new Date(timestamp));
250         writePutTransaction(LAST_UPDATED, new LastUpdatedBuilder().setLastUpdated(timestamp).build(),
251                 LogicalDatastoreType.OPERATIONAL, "Couldn't save last update timestamp to operational datastore");
252     }
253
254     public void removeLastUpdateTimestamp() {
255         LOG.debug("MD-SAL: Removing last update timestamp from operational datastore");
256         deleteTransaction(LAST_UPDATED, LogicalDatastoreType.OPERATIONAL,
257                 "Couldn't remove last update timestamp from operational datastore");
258     }
259
260     public Long getLastUpdateTimestamp() {
261         LastUpdated lastUpdated = readTransaction(LAST_UPDATED, LogicalDatastoreType.OPERATIONAL);
262         if (lastUpdated != null && lastUpdated.getLastUpdated() != null) {
263             Long timestamp = lastUpdated.getLastUpdated();
264             LOG.debug("MD-SAL: Retrieved last update timestamp from operational datastore: {}",
265                     new Date(timestamp).toString());
266             return timestamp;
267         } else {
268             LOG.debug("MD-SAL: Couldn't retrieve last update timestamp from operational datastore");
269             return null;
270         }
271     }
272
273     private static LogicalDatastoreType getDestinationDatastore(Mapping mapping) {
274         return mapping.getOrigin().equals(MappingOrigin.Southbound) ? LogicalDatastoreType.OPERATIONAL
275                 : LogicalDatastoreType.CONFIGURATION;
276     }
277
278     private TransactionChain getChain(LogicalDatastoreType logicalDatastoreType) {
279         return switch (logicalDatastoreType) {
280             case CONFIGURATION -> configTxChain;
281             case OPERATIONAL -> operTxChain;
282         };
283     }
284
285     private <U extends DataObject> void writePutTransaction(InstanceIdentifier<U> addIID, U data,
286             LogicalDatastoreType logicalDatastoreType, String errMsg) {
287         WriteTransaction writeTx = getChain(logicalDatastoreType).newWriteOnlyTransaction();
288         // TODO: is is a utility method, hence we do not have enough lifecycle knowledge to use plain put()
289         writeTx.mergeParentStructurePut(logicalDatastoreType, addIID, data);
290         writeTx.commit().addCallback(new FutureCallback<CommitInfo>() {
291             @Override
292             public void onSuccess(CommitInfo result) {
293                 // No-op
294             }
295
296             @Override
297             public void onFailure(Throwable throwable) {
298                 LOG.error("Transaction failed:", throwable);
299             }
300         }, MoreExecutors.directExecutor());
301     }
302
303     private <U extends DataObject> U readTransaction(InstanceIdentifier<U> readIID,
304             LogicalDatastoreType logicalDatastoreType) {
305         final ListenableFuture<Optional<U>> readFuture;
306         try (ReadTransaction readTx = getChain(logicalDatastoreType).newReadOnlyTransaction()) {
307             readFuture = readTx.read(logicalDatastoreType, readIID);
308         }
309         try {
310             Optional<U> optionalDataObject = readFuture.get();
311             if (optionalDataObject != null && optionalDataObject.isPresent()) {
312                 return optionalDataObject.orElseThrow();
313             } else {
314                 LOG.debug("{}: Failed to read", Thread.currentThread().getStackTrace()[1]);
315             }
316         } catch (InterruptedException | ExecutionException e) {
317             LOG.warn("Failed to ....", e);
318         }
319         return null;
320     }
321
322     private <U extends DataObject> void deleteTransaction(InstanceIdentifier<U> deleteIID,
323             LogicalDatastoreType logicalDatastoreType, String errMsg) {
324         WriteTransaction writeTx = getChain(logicalDatastoreType).newWriteOnlyTransaction();
325         writeTx.delete(logicalDatastoreType, deleteIID);
326         writeTx.commit().addCallback(new FutureCallback<CommitInfo>() {
327             @Override
328             public void onSuccess(CommitInfo result) {
329             }
330
331             @Override
332             public void onFailure(Throwable throwable) {
333                 LOG.error("Transaction failed:", throwable);
334             }
335         }, MoreExecutors.directExecutor());
336     }
337
338     @VisibleForTesting
339     void onTransactionChainFailed(TransactionChain chain, Throwable cause) {
340         LOG.error("Broken chain {} in DataStoreBackEnd, cause {}", chain, cause.getMessage());
341     }
342
343     @VisibleForTesting
344     void onTransactionChainSuccessful(TransactionChain chain) {
345         LOG.info("DataStoreBackEnd closed successfully, chain {}", chain);
346     }
347
348     public void closeTransactionChain() {
349         LOG.debug("Closing DataStoreBackEnd transaction chain...");
350         configTxChain.close();
351         operTxChain.close();
352     }
353 }