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