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.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;
45 * This Class Implements the DataStoreService interface providing the methods
46 * required to manage the network representation elements in the datastore.
49 * @author Olivier Dugeon
50 * @author Philippe Niger
53 public class ConnectedGraphServer implements ConnectedGraphProvider, TransactionChainListener {
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;
60 private final HashMap<GraphKey, ConnectedGraphImpl> graphs = new HashMap<>();
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();
69 * Initialization of the Graph Model Server. This method is called through the blueprint.
72 initTransactionChain();
73 initOperationalGraphModel();
77 * Reset a transaction chain by closing the current chain and starting a new one.
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);
86 * Initialize GraphModel tree at Data Store top-level.
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>() {
99 public void onSuccess(final CommitInfo result) {
100 LOG.trace("Transaction {} committed successfully", trans.getIdentifier());
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);
109 }, MoreExecutors.directExecutor());
113 * Destroy the current operational topology data. Note a valid transaction must be provided.
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>() {
123 public void onSuccess(final CommitInfo result) {
124 LOG.trace("Operational GraphModel removed {}", ConnectedGraphServer.this.graphTopologyIdentifier);
128 public void onFailure(final Throwable throwable) {
129 LOG.error("Unable to reset operational GraphModel {} (transaction {})",
130 ConnectedGraphServer.this.graphTopologyIdentifier, trans.getIdentifier(), throwable);
132 }, MoreExecutors.directExecutor());
134 /* Clear Connected Graph */
135 for (ConnectedGraph graph : graphs.values()) {
136 ((ConnectedGraphImpl) graph).clear();
142 * Destroy the current transaction chain.
144 private synchronized void destroyTransactionChain() {
145 if (this.chain != null) {
146 LOG.debug("Destroy transaction chain for GraphModel {}", this);
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
156 protected synchronized void resetTransactionChain() {
157 LOG.debug("Resetting transaction chain for Graph builder");
158 destroyTransactionChain();
159 initTransactionChain();
163 * Remove the Operation Graph Model and destroy the transaction chain.
165 public void close() {
166 destroyOperationalGraphModel();
167 destroyTransactionChain();
171 * DataStore Instance Identifier creation for the various Graph components.
173 private InstanceIdentifier<Graph> getGraphInstanceIdentifier(String name) {
174 GraphKey graphKey = new GraphKey(name);
175 return this.graphTopologyIdentifier.child(Graph.class, graphKey);
178 private InstanceIdentifier<Vertex> getVertexInstanceIdentifier(Graph graph, final Vertex vertex) {
179 return this.graphTopologyIdentifier.child(Graph.class, graph.key()).child(Vertex.class, vertex.key());
182 private InstanceIdentifier<Edge> getEdgeInstanceIdentifier(Graph graph, final Edge edge) {
183 return this.graphTopologyIdentifier.child(Graph.class, graph.key()).child(Edge.class, edge.key());
186 private InstanceIdentifier<Prefix> getPrefixInstanceIdentifier(Graph graph, final Prefix prefix) {
187 return this.graphTopologyIdentifier.child(Graph.class, graph.key()).child(Prefix.class, prefix.key());
191 * Add Graph or Graph components to the Data Store.
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
198 private synchronized <T extends DataObject> void addToDataStore(final InstanceIdentifier<T> id, final T data,
200 final ReadWriteTransaction trans = this.chain.newReadWriteTransaction();
201 trans.put(LogicalDatastoreType.OPERATIONAL, id, data);
202 trans.commit().addCallback(new FutureCallback<CommitInfo>() {
204 public void onSuccess(final CommitInfo result) {
205 LOG.info("GraphModel: {} has been published in operational datastore ", info);
209 public void onFailure(final Throwable throwable) {
210 LOG.error("GrahModel: Cannot write {} to the operational datastore (transaction: {})", info,
211 trans.getIdentifier());
213 }, MoreExecutors.directExecutor());
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.
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
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();
230 trans.delete(LogicalDatastoreType.OPERATIONAL, old);
232 trans.put(LogicalDatastoreType.OPERATIONAL, id, data);
233 trans.commit().addCallback(new FutureCallback<CommitInfo>() {
235 public void onSuccess(final CommitInfo result) {
236 LOG.info("GraphModel: {} has been published in operational datastore ", info);
240 public void onFailure(final Throwable throwable) {
241 LOG.error("GrahModel: Cannot write {} to the operational datastore (transaction: {})", info,
242 trans.getIdentifier());
244 }, MoreExecutors.directExecutor());
248 * Remove Graph or Graph components to the Data Store.
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
254 private synchronized <T extends DataObject> void removeFromDataStore(final InstanceIdentifier<T> id,
256 final ReadWriteTransaction trans = this.chain.newReadWriteTransaction();
257 trans.delete(LogicalDatastoreType.OPERATIONAL, id);
258 trans.commit().addCallback(new FutureCallback<CommitInfo>() {
260 public void onSuccess(final CommitInfo result) {
261 LOG.info("GraphModel: {} has been deleted in operational datastore ", info);
265 public void onFailure(final Throwable throwable) {
266 LOG.error("GraphModel: Cannot delete {} to the operational datastore (transaction: {})", info,
267 trans.getIdentifier());
269 }, MoreExecutors.directExecutor());
273 * Clear Graph. This method is used by the Connected Graph to clear associated Graph.
275 * @param graph Graph associated to the Connected Graph
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() + ")");
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).
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
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 */
296 oldId = getVertexInstanceIdentifier(graph, old);
298 updateToDataStore(getVertexInstanceIdentifier(graph, vertex), vertex, oldId,
299 "Vertex(" + vertex.getName() + ")");
303 * Remove Vertex to existing Graph. This method is called when a Connected Vertex is removed (See deleteVertex()
304 * method from ConnectedGraph Interface).
306 * @param graph Graph where the vertex is stored
307 * @param vertex Vertex to be removed
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() + ")");
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).
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
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 */
329 oldId = getEdgeInstanceIdentifier(graph, old);
331 updateToDataStore(getEdgeInstanceIdentifier(graph, edge), edge, oldId, "Edge(" + edge.getName() + ")");
335 * Remove Edge to existing Graph. This method is called when a Connected Edge is removed (See deleteEdge()
336 * method from ConnectedGraph Interface).
338 * @param graph Graph where the edge is stored
339 * @param edge Edge to be removed
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() + ")");
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).
351 * @param graph Graph where the prefix will be stored
352 * @param prefix Prefix to be interted in the graph
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() + ")");
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).
364 * @param graph Graph where the prefix is stored
365 * @param prefix Prefix to be removed
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() + ")");
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);
381 public final void onTransactionChainSuccessful(final TransactionChain transactionChain) {
382 LOG.info("GraphModel builder for {} shut down", this.graphTopologyIdentifier);
386 public ArrayList<ConnectedGraph> getConnectedGraphs() {
387 return new ArrayList<ConnectedGraph>(this.graphs.values());
391 public ConnectedGraph getConnectedGraph(GraphKey key) {
392 return graphs.get(key);
396 public ConnectedGraph getConnectedGraph(String name) {
397 return graphs.get(new GraphKey(name));
401 public Graph getGraph(GraphKey key) {
402 if (graphs.containsKey(key)) {
403 return graphs.get(key).getGraph();
410 public Graph getGraph(String name) {
411 return getGraph(new GraphKey(name));
415 public ConnectedGraph createConnectedGraph(String name, DomainScope scope) {
416 Graph graph = new GraphBuilder()
418 .setDomainScope(scope)
419 .setEdge(Collections.emptyList())
420 .setVertex(Collections.emptyList())
421 .setPrefix(Collections.emptyList())
423 addToDataStore(getGraphInstanceIdentifier(name), graph, "Graph(" + name + ")");
424 ConnectedGraphImpl cgraph = new ConnectedGraphImpl(graph, this);
425 graphs.put(graph.key(), cgraph);
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);
439 public void deleteGraph(GraphKey key) {
440 Preconditions.checkArgument(key != null, "Provided Graph Key is a null object");
441 ConnectedGraphImpl cgraph = graphs.remove(key);
443 * Remove the corresponding Connected Graph which will delete the graph
444 * by calling clearGraph() method (see above)
446 if (cgraph != null) {