2 * Copyright (c) 2019 Orange. All rights reserved.
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
9 package org.opendaylight.graph.impl;
11 import static java.util.Objects.requireNonNull;
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;
44 * This Class Implements the DataStoreService interface providing the methods
45 * required to manage the network representation elements in the datastore.
48 * @author Olivier Dugeon
49 * @author Philippe Niger
52 public class ConnectedGraphServer implements ConnectedGraphProvider, TransactionChainListener {
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;
59 private final HashMap<GraphKey, ConnectedGraphImpl> graphs = new HashMap<>();
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();
68 * Initialization of the Graph Model Server. This method is called through the blueprint.
71 initTransactionChain();
72 initOperationalGraphModel();
76 * Reset a transaction chain by closing the current chain and starting a new one.
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);
85 * Initialize GraphModel tree at Data Store top-level.
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>() {
98 public void onSuccess(final CommitInfo result) {
99 LOG.trace("Transaction {} committed successfully", trans.getIdentifier());
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);
108 }, MoreExecutors.directExecutor());
112 * Destroy the current operational topology data. Note a valid transaction must be provided.
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>() {
122 public void onSuccess(final CommitInfo result) {
123 LOG.trace("Operational GraphModel removed {}", ConnectedGraphServer.this.graphTopologyIdentifier);
127 public void onFailure(final Throwable throwable) {
128 LOG.error("Unable to reset operational GraphModel {} (transaction {})",
129 ConnectedGraphServer.this.graphTopologyIdentifier, trans.getIdentifier(), throwable);
131 }, MoreExecutors.directExecutor());
133 /* Clear Connected Graph */
134 for (ConnectedGraph graph : graphs.values()) {
135 ((ConnectedGraphImpl) graph).clear();
141 * Destroy the current transaction chain.
143 private synchronized void destroyTransactionChain() {
144 if (this.chain != null) {
145 LOG.debug("Destroy transaction chain for GraphModel {}", this);
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
155 protected synchronized void resetTransactionChain() {
156 LOG.debug("Resetting transaction chain for Graph builder");
157 destroyTransactionChain();
158 initTransactionChain();
162 * Remove the Operation Graph Model and destroy the transaction chain.
164 public void close() {
165 destroyOperationalGraphModel();
166 destroyTransactionChain();
170 * DataStore Instance Identifier creation for the various Graph components.
172 private InstanceIdentifier<Graph> getGraphInstanceIdentifier(final String name) {
173 GraphKey graphKey = new GraphKey(name);
174 return this.graphTopologyIdentifier.child(Graph.class, graphKey);
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());
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());
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());
190 * Add Graph or Graph components to the Data Store.
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
197 private synchronized <T extends DataObject> void addToDataStore(final InstanceIdentifier<T> id, final T data,
199 final ReadWriteTransaction trans = this.chain.newReadWriteTransaction();
200 trans.put(LogicalDatastoreType.OPERATIONAL, id, data);
201 trans.commit().addCallback(new FutureCallback<CommitInfo>() {
203 public void onSuccess(final CommitInfo result) {
204 LOG.info("GraphModel: {} has been published in operational datastore ", info);
208 public void onFailure(final Throwable throwable) {
209 LOG.error("GrahModel: Cannot write {} to the operational datastore (transaction: {})", info,
210 trans.getIdentifier());
212 }, MoreExecutors.directExecutor());
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.
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
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();
229 trans.delete(LogicalDatastoreType.OPERATIONAL, old);
231 trans.put(LogicalDatastoreType.OPERATIONAL, id, data);
232 trans.commit().addCallback(new FutureCallback<CommitInfo>() {
234 public void onSuccess(final CommitInfo result) {
235 LOG.info("GraphModel: {} has been published in operational datastore ", info);
239 public void onFailure(final Throwable throwable) {
240 LOG.error("GrahModel: Cannot write {} to the operational datastore (transaction: {})", info,
241 trans.getIdentifier());
243 }, MoreExecutors.directExecutor());
247 * Remove Graph or Graph components to the Data Store.
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
253 private synchronized <T extends DataObject> void removeFromDataStore(final InstanceIdentifier<T> id,
255 final ReadWriteTransaction trans = this.chain.newReadWriteTransaction();
256 trans.delete(LogicalDatastoreType.OPERATIONAL, id);
257 trans.commit().addCallback(new FutureCallback<CommitInfo>() {
259 public void onSuccess(final CommitInfo result) {
260 LOG.info("GraphModel: {} has been deleted in operational datastore ", info);
264 public void onFailure(final Throwable throwable) {
265 LOG.error("GraphModel: Cannot delete {} to the operational datastore (transaction: {})", info,
266 trans.getIdentifier());
268 }, MoreExecutors.directExecutor());
272 * Clear Graph. This method is used by the Connected Graph to clear associated Graph.
274 * @param graph Graph associated to the Connected Graph
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() + ")");
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).
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
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 */
295 oldId = getVertexInstanceIdentifier(graph, old);
297 updateToDataStore(getVertexInstanceIdentifier(graph, vertex), vertex, oldId,
298 "Vertex(" + vertex.getName() + ")");
302 * Remove Vertex to existing Graph. This method is called when a Connected Vertex is removed (See deleteVertex()
303 * method from ConnectedGraph Interface).
305 * @param graph Graph where the vertex is stored
306 * @param vertex Vertex to be removed
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() + ")");
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).
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
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 */
328 oldId = getEdgeInstanceIdentifier(graph, old);
330 updateToDataStore(getEdgeInstanceIdentifier(graph, edge), edge, oldId, "Edge(" + edge.getName() + ")");
334 * Remove Edge to existing Graph. This method is called when a Connected Edge is removed (See deleteEdge()
335 * method from ConnectedGraph Interface).
337 * @param graph Graph where the edge is stored
338 * @param edge Edge to be removed
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() + ")");
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).
350 * @param graph Graph where the prefix will be stored
351 * @param prefix Prefix to be interted in the graph
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() + ")");
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).
363 * @param graph Graph where the prefix is stored
364 * @param prefix Prefix to be removed
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() + ")");
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);
380 public final void onTransactionChainSuccessful(final TransactionChain transactionChain) {
381 LOG.info("GraphModel builder for {} shut down", this.graphTopologyIdentifier);
385 public ArrayList<ConnectedGraph> getConnectedGraphs() {
386 return new ArrayList<>(this.graphs.values());
390 public ConnectedGraph getConnectedGraph(final GraphKey key) {
391 return graphs.get(key);
395 public ConnectedGraph getConnectedGraph(final String name) {
396 return graphs.get(new GraphKey(name));
400 public Graph getGraph(final GraphKey key) {
401 if (graphs.containsKey(key)) {
402 return graphs.get(key).getGraph();
409 public Graph getGraph(final String name) {
410 return getGraph(new GraphKey(name));
414 public ConnectedGraph createConnectedGraph(final String name, final DomainScope scope) {
415 Graph graph = new GraphBuilder()
417 .setDomainScope(scope)
419 addToDataStore(getGraphInstanceIdentifier(name), graph, "Graph(" + name + ")");
420 ConnectedGraphImpl cgraph = new ConnectedGraphImpl(graph, this);
421 graphs.put(graph.key(), cgraph);
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);
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);
439 * Remove the corresponding Connected Graph which will delete the graph
440 * by calling clearGraph() method (see above)
442 if (cgraph != null) {