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