Update MRI projects for Aluminium
[bgpcep.git] / graph / graph-impl / src / main / java / org / opendaylight / graph / impl / ConnectedGraphServer.java
1 /*
2  * Copyright (c) 2019 Orange. 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.graph.impl;
10
11 import static java.util.Objects.requireNonNull;
12
13 import com.google.common.base.Preconditions;
14 import com.google.common.util.concurrent.FluentFuture;
15 import com.google.common.util.concurrent.FutureCallback;
16 import com.google.common.util.concurrent.MoreExecutors;
17 import java.util.ArrayList;
18 import java.util.HashMap;
19 import org.opendaylight.graph.ConnectedGraph;
20 import org.opendaylight.graph.ConnectedGraphProvider;
21 import org.opendaylight.mdsal.binding.api.DataBroker;
22 import org.opendaylight.mdsal.binding.api.ReadWriteTransaction;
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.params.xml.ns.yang.graph.rev191125.GraphTopology;
30 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.graph.rev191125.GraphTopologyBuilder;
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.graph.rev191125.graph.topology.Graph;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.graph.rev191125.graph.topology.Graph.DomainScope;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.graph.rev191125.graph.topology.GraphBuilder;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.graph.rev191125.graph.topology.GraphKey;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.graph.rev191125.graph.topology.graph.Edge;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.graph.rev191125.graph.topology.graph.Prefix;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.graph.rev191125.graph.topology.graph.Vertex;
38 import org.opendaylight.yangtools.yang.binding.DataObject;
39 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
40 import org.slf4j.Logger;
41 import org.slf4j.LoggerFactory;
42
43 /**
44  * This Class Implements the DataStoreService interface providing the methods
45  * required to manage the network representation elements in the datastore.
46  *
47  *
48  * @author Olivier Dugeon
49  * @author Philippe Niger
50  */
51
52 public class ConnectedGraphServer implements ConnectedGraphProvider, TransactionChainListener {
53
54     private static final Logger LOG = LoggerFactory.getLogger(ConnectedGraphServer.class);
55     private final DataBroker dataBroker;
56     private final InstanceIdentifier<GraphTopology> graphTopologyIdentifier;
57     private TransactionChain chain = null;
58
59     private final HashMap<GraphKey, ConnectedGraphImpl> graphs = new HashMap<>();
60
61     public ConnectedGraphServer(final DataBroker dataBroker) {
62         LOG.info("Create Graph Model Server");
63         this.dataBroker = dataBroker;
64         this.graphTopologyIdentifier = InstanceIdentifier.builder(GraphTopology.class).build();
65     }
66
67     /**
68      * Initialization of the Graph Model Server. This method is called through the blueprint.
69      */
70     public void init() {
71         initTransactionChain();
72         initOperationalGraphModel();
73     }
74
75     /**
76      * Reset a transaction chain by closing the current chain and starting a new one.
77      */
78     private synchronized void initTransactionChain() {
79         LOG.debug("Initializing transaction chain for Graph Model Server {}", this);
80         Preconditions.checkState(this.chain == null, "Transaction chain has to be closed before being initialized");
81         this.chain = this.dataBroker.createMergingTransactionChain(this);
82     }
83
84     /**
85      * Initialize GraphModel tree at Data Store top-level.
86      */
87     private synchronized void initOperationalGraphModel() {
88         requireNonNull(this.chain, "A valid transaction chain must be provided.");
89         final WriteTransaction trans = this.chain.newWriteOnlyTransaction();
90         LOG.info("Create Graph Model at top level in Operational DataStore: {}", this.graphTopologyIdentifier);
91         trans.put(LogicalDatastoreType.OPERATIONAL, this.graphTopologyIdentifier,
92                 new GraphTopologyBuilder().build());
93         trans.put(LogicalDatastoreType.CONFIGURATION, this.graphTopologyIdentifier,
94                 new GraphTopologyBuilder().build());
95         LOG.info("Create Graph Model at top level in Configuration DataStore: {}", this.graphTopologyIdentifier);
96         trans.commit().addCallback(new FutureCallback<CommitInfo>() {
97             @Override
98             public void onSuccess(final CommitInfo result) {
99                 LOG.trace("Transaction {} committed successfully", trans.getIdentifier());
100             }
101
102             @Override
103             public void onFailure(final Throwable throwable) {
104                 LOG.error("Failed to initialize GraphModel {} (transaction {}) by listener {}",
105                         ConnectedGraphServer.this.graphTopologyIdentifier, trans.getIdentifier(),
106                         ConnectedGraphServer.this, throwable);
107             }
108         }, MoreExecutors.directExecutor());
109     }
110
111     /**
112      * Destroy the current operational topology data. Note a valid transaction must be provided.
113      */
114     private synchronized FluentFuture<? extends CommitInfo> destroyOperationalGraphModel() {
115         requireNonNull(this.chain, "A valid transaction chain must be provided.");
116         final WriteTransaction trans = this.chain.newWriteOnlyTransaction();
117         trans.delete(LogicalDatastoreType.OPERATIONAL, this.graphTopologyIdentifier);
118         trans.delete(LogicalDatastoreType.CONFIGURATION, this.graphTopologyIdentifier);
119         final FluentFuture<? extends CommitInfo> future = trans.commit();
120         future.addCallback(new FutureCallback<CommitInfo>() {
121             @Override
122             public void onSuccess(final CommitInfo result) {
123                 LOG.trace("Operational GraphModel removed {}", ConnectedGraphServer.this.graphTopologyIdentifier);
124             }
125
126             @Override
127             public void onFailure(final Throwable throwable) {
128                 LOG.error("Unable to reset operational GraphModel {} (transaction {})",
129                         ConnectedGraphServer.this.graphTopologyIdentifier, trans.getIdentifier(), throwable);
130             }
131         }, MoreExecutors.directExecutor());
132
133         /* Clear Connected Graph */
134         for (ConnectedGraph graph : graphs.values()) {
135             ((ConnectedGraphImpl) graph).clear();
136         }
137         return future;
138     }
139
140     /**
141      * Destroy the current transaction chain.
142      */
143     private synchronized void destroyTransactionChain() {
144         if (this.chain != null) {
145             LOG.debug("Destroy transaction chain for GraphModel {}", this);
146             this.chain = null;
147         }
148     }
149
150     /**
151      * Reset the transaction chain only so that the PingPong transaction chain
152      * will become usable again. However, there will be data loss if we do not
153      * apply the previous failed transaction again
154      */
155     protected synchronized void resetTransactionChain() {
156         LOG.debug("Resetting transaction chain for Graph builder");
157         destroyTransactionChain();
158         initTransactionChain();
159     }
160
161     /**
162      * Remove the Operation Graph Model and destroy the transaction chain.
163      */
164     public void close() {
165         destroyOperationalGraphModel();
166         destroyTransactionChain();
167     }
168
169     /**
170      *  DataStore Instance Identifier creation for the various Graph components.
171      */
172     private InstanceIdentifier<Graph> getGraphInstanceIdentifier(final String name) {
173         GraphKey graphKey = new GraphKey(name);
174         return this.graphTopologyIdentifier.child(Graph.class, graphKey);
175     }
176
177     private InstanceIdentifier<Vertex> getVertexInstanceIdentifier(final Graph graph, final Vertex vertex) {
178         return this.graphTopologyIdentifier.child(Graph.class, graph.key()).child(Vertex.class, vertex.key());
179     }
180
181     private InstanceIdentifier<Edge> getEdgeInstanceIdentifier(final Graph graph, final Edge edge) {
182         return this.graphTopologyIdentifier.child(Graph.class, graph.key()).child(Edge.class, edge.key());
183     }
184
185     private InstanceIdentifier<Prefix> getPrefixInstanceIdentifier(final Graph graph, final Prefix prefix) {
186         return this.graphTopologyIdentifier.child(Graph.class, graph.key()).child(Prefix.class, prefix.key());
187     }
188
189     /**
190      * Add Graph or Graph components to the Data Store.
191      *
192      * @param <T>   As a generic method, T must be a Graph, Vertex, Edge or Prefix.
193      * @param id    Instance Identifier of the Data Object
194      * @param data  Data Object (Graph, Vertex, Edge or Prefix)
195      * @param info  Information to be logged
196      */
197     private synchronized <T extends DataObject> void addToDataStore(final InstanceIdentifier<T> id, final T data,
198             final String info) {
199         final ReadWriteTransaction trans = this.chain.newReadWriteTransaction();
200         trans.put(LogicalDatastoreType.OPERATIONAL, id, data);
201         trans.commit().addCallback(new FutureCallback<CommitInfo>() {
202             @Override
203             public void onSuccess(final CommitInfo result) {
204                 LOG.info("GraphModel: {} has been published in operational datastore ", info);
205             }
206
207             @Override
208             public void onFailure(final Throwable throwable) {
209                 LOG.error("GrahModel: Cannot write {} to the operational datastore (transaction: {})", info,
210                         trans.getIdentifier());
211             }
212         }, MoreExecutors.directExecutor());
213     }
214
215     /**
216      * Update Graph components (Vertex, Edge or Prefix ) to the Data Store. Old value identified by its Instance ID
217      * will be remove first before adding the new value.
218      *
219      * @param <T>   As a generic method, T must be a Vertex, Edge or Prefix.
220      * @param id    Instance Identifier of the Data Object
221      * @param data  Data Object (Vertex, Edge or Prefix)
222      * @param old   Instance Identifier of the previous version of the Data Object
223      * @param info  Information to be logged
224      */
225     private synchronized <T extends DataObject> void updateToDataStore(final InstanceIdentifier<T> id, final T data,
226             final InstanceIdentifier<T> old, final String info) {
227         final ReadWriteTransaction trans = this.chain.newReadWriteTransaction();
228         if (old != null) {
229             trans.delete(LogicalDatastoreType.OPERATIONAL, old);
230         }
231         trans.put(LogicalDatastoreType.OPERATIONAL, id, data);
232         trans.commit().addCallback(new FutureCallback<CommitInfo>() {
233             @Override
234             public void onSuccess(final CommitInfo result) {
235                 LOG.info("GraphModel: {} has been published in operational datastore ", info);
236             }
237
238             @Override
239             public void onFailure(final Throwable throwable) {
240                 LOG.error("GrahModel: Cannot write {} to the operational datastore (transaction: {})", info,
241                         trans.getIdentifier());
242             }
243         }, MoreExecutors.directExecutor());
244     }
245
246     /**
247      * Remove Graph or Graph components to the Data Store.
248      *
249      * @param <T>  As a generic method, T must be a Graph, Vertex, Edge or Prefix.
250      * @param id   Instance Identifier of the Data Object
251      * @param info Information to be logged
252      */
253     private synchronized <T extends DataObject> void removeFromDataStore(final InstanceIdentifier<T> id,
254             final String info) {
255         final ReadWriteTransaction trans = this.chain.newReadWriteTransaction();
256         trans.delete(LogicalDatastoreType.OPERATIONAL, id);
257         trans.commit().addCallback(new FutureCallback<CommitInfo>() {
258             @Override
259             public void onSuccess(final CommitInfo result) {
260                 LOG.info("GraphModel: {} has been deleted in operational datastore ", info);
261             }
262
263             @Override
264             public void onFailure(final Throwable throwable) {
265                 LOG.error("GraphModel: Cannot delete {} to the operational datastore (transaction: {})", info,
266                         trans.getIdentifier());
267             }
268         }, MoreExecutors.directExecutor());
269     }
270
271     /**
272      * Clear Graph. This method is used by the Connected Graph to clear associated Graph.
273      *
274      * @param graph Graph associated to the Connected Graph
275      */
276     public void clearGraph(final Graph graph) {
277         Preconditions.checkArgument(graph != null, "Provided Graph is a null object");
278         removeFromDataStore(getGraphInstanceIdentifier(graph.getName()), "Graph(" + graph.getName() + ")");
279     }
280
281     /**
282      * Add Vertex to existing Graph. Old vertex is remove first. This method is called when a Connected Vertex is
283      * created (See addVertex() method from ConnectedGraph Interface).
284      *
285      * @param graph   Graph where the vertex will be stored
286      * @param vertex  Vertex to be inserted in the graph
287      * @param old     Old vertex when performing an update. Must be null for a simple addition
288      */
289     public void addVertex(final Graph graph, final Vertex vertex, final Vertex old) {
290         Preconditions.checkArgument(graph != null, "Provided Graph is a null object");
291         Preconditions.checkArgument(vertex != null, "Provided Vertex is a null object");
292         InstanceIdentifier<Vertex> oldId = null;
293         /* Remove old Vertex if it exists before storing the new Vertex */
294         if (old != null) {
295             oldId = getVertexInstanceIdentifier(graph, old);
296         }
297         updateToDataStore(getVertexInstanceIdentifier(graph, vertex), vertex, oldId,
298                 "Vertex(" + vertex.getName() + ")");
299     }
300
301     /**
302      * Remove Vertex to existing Graph. This method is called when a Connected Vertex is removed (See deleteVertex()
303      * method from ConnectedGraph Interface).
304      *
305      * @param graph   Graph where the vertex is stored
306      * @param vertex  Vertex to be removed
307      */
308     public void deleteVertex(final Graph graph, final Vertex vertex) {
309         Preconditions.checkArgument(graph != null, "Provided Graph is a null object");
310         Preconditions.checkArgument(vertex != null, "Provided Vertex is a null object");
311         removeFromDataStore(getVertexInstanceIdentifier(graph, vertex), "Vertex(" + vertex.getName() + ")");
312     }
313
314     /**
315      * Add Edge to existing Graph. Old edge is remove first. This method is called when a Connected Edge is
316      * created (See addEdge() method from ConnectedGraph Interface).
317      *
318      * @param graph  Graph where the edge will be stored
319      * @param edge   Edge to be inserted in the graph
320      * @param old    Old edge when performing an update. Must be null for a simple addition
321      */
322     public void addEdge(final Graph graph, final Edge edge, final Edge old) {
323         Preconditions.checkArgument(graph != null, "Provided Graph is a null object");
324         Preconditions.checkArgument(edge != null, "Provided Edge is a null object");
325         InstanceIdentifier<Edge> oldId = null;
326         /* Remove old Edge if it exists before storing the new Edge */
327         if (old != null) {
328             oldId = getEdgeInstanceIdentifier(graph, old);
329         }
330         updateToDataStore(getEdgeInstanceIdentifier(graph, edge), edge, oldId, "Edge(" + edge.getName() + ")");
331     }
332
333     /**
334      * Remove Edge to existing Graph. This method is called when a Connected Edge is removed (See deleteEdge()
335      * method from ConnectedGraph Interface).
336      *
337      * @param graph  Graph where the edge is stored
338      * @param edge   Edge to be removed
339      */
340     public void deleteEdge(final Graph graph, final Edge edge) {
341         Preconditions.checkArgument(graph != null, "Provided Graph is a null object");
342         Preconditions.checkArgument(edge != null, "Provided Edge is a null object");
343         removeFromDataStore(getEdgeInstanceIdentifier(graph, edge), "Edge(" + edge.getName() + ")");
344     }
345
346     /**
347      * Add Prefix to existing Graph. This method is called when a Prefix is added to a Connected Vertex
348      * (See addPrefix() method from ConnectedGraph Interface).
349      *
350      * @param graph  Graph where the prefix will be stored
351      * @param prefix Prefix to be interted in the graph
352      */
353     public void addPrefix(final Graph graph, final Prefix prefix) {
354         Preconditions.checkArgument(graph != null, "Provided Graph is a null object");
355         Preconditions.checkArgument(prefix != null, "Provided Prefix is a null object");
356         addToDataStore(getPrefixInstanceIdentifier(graph, prefix), prefix, "Prefix(" + prefix.getPrefix() + ")");
357     }
358
359     /**
360      * Remove Prefix to existing Graph. This method is called when a Prefix is removed from a Connected Vertex
361      * (See deletePrefix() method from ConnectedGraph Interface).
362      *
363      * @param graph  Graph where the prefix is stored
364      * @param prefix Prefix to be removed
365      */
366     public void deletePrefix(final Graph graph, final Prefix prefix) {
367         Preconditions.checkArgument(graph != null, "Provided Graph is a null object");
368         Preconditions.checkArgument(prefix != null, "Provided Prefix is a null object");
369         removeFromDataStore(getPrefixInstanceIdentifier(graph, prefix), "Prefix(" + prefix.getPrefix() + ")");
370     }
371
372     @Override
373     public final synchronized void onTransactionChainFailed(final TransactionChain transactionChain,
374             final Transaction transaction, final Throwable cause) {
375         LOG.error("GraphModel builder for {} failed in transaction: {} ", this.graphTopologyIdentifier,
376                 transaction != null ? transaction.getIdentifier() : null, cause);
377     }
378
379     @Override
380     public final void onTransactionChainSuccessful(final TransactionChain transactionChain) {
381         LOG.info("GraphModel builder for {} shut down", this.graphTopologyIdentifier);
382     }
383
384     @Override
385     public ArrayList<ConnectedGraph> getConnectedGraphs() {
386         return new ArrayList<>(this.graphs.values());
387     }
388
389     @Override
390     public ConnectedGraph getConnectedGraph(final GraphKey key) {
391         return graphs.get(key);
392     }
393
394     @Override
395     public ConnectedGraph getConnectedGraph(final String name) {
396         return graphs.get(new GraphKey(name));
397     }
398
399     @Override
400     public Graph getGraph(final GraphKey key) {
401         if (graphs.containsKey(key)) {
402             return graphs.get(key).getGraph();
403         } else {
404             return null;
405         }
406     }
407
408     @Override
409     public Graph getGraph(final String name) {
410         return getGraph(new GraphKey(name));
411     }
412
413     @Override
414     public ConnectedGraph createConnectedGraph(final String name, final DomainScope scope) {
415         Graph graph = new GraphBuilder()
416                 .setName(name)
417                 .setDomainScope(scope)
418                 .build();
419         addToDataStore(getGraphInstanceIdentifier(name), graph, "Graph(" + name + ")");
420         ConnectedGraphImpl cgraph = new ConnectedGraphImpl(graph, this);
421         graphs.put(graph.key(), cgraph);
422         return cgraph;
423     }
424
425     @Override
426     public ConnectedGraph addGraph(final Graph graph) {
427         Preconditions.checkArgument(graph != null, "Provided Graph is a null object");
428         addToDataStore(getGraphInstanceIdentifier(graph.getName()), graph, "Graph(" + graph.getName() + ")");
429         ConnectedGraphImpl cgraph = new ConnectedGraphImpl(graph, this);
430         graphs.put(graph.key(), cgraph);
431         return cgraph;
432     }
433
434     @Override
435     public void deleteGraph(final GraphKey key) {
436         Preconditions.checkArgument(key != null, "Provided Graph Key is a null object");
437         ConnectedGraphImpl cgraph = graphs.remove(key);
438         /*
439          * Remove the corresponding Connected Graph which will delete the graph
440          * by calling clearGraph() method (see above)
441          */
442         if (cgraph != null) {
443             cgraph.clear();
444         }
445     }
446 }