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