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