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