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
8 package org.opendaylight.graph.impl;
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;
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;
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;
53 * This Class Implements the DataStoreService interface providing the methods
54 * required to manage the network representation elements in the datastore.
56 * @author Olivier Dugeon
57 * @author Philippe Niger
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);
66 private final Map<GraphKey, ConnectedGraphImpl> graphs = new HashMap<>();
67 private final DataBroker dataBroker;
69 private TransactionChain chain = null;
73 public ConnectedGraphServer(@Reference final DataBroker dataBroker) {
74 this.dataBroker = requireNonNull(dataBroker);
75 initTransactionChain();
76 initOperationalGraphModel();
77 LOG.info("Graph Model Server started");
81 * Remove the Operation Graph Model and destroy the transaction chain.
87 destroyOperationalGraphModel();
88 destroyTransactionChain();
92 * Reset a transaction chain by closing the current chain and starting a new one.
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);
102 * Initialize GraphModel tree at Data Store top-level.
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>() {
111 public void onSuccess(final CommitInfo result) {
112 LOG.trace("Transaction {} committed successfully", trans.getIdentifier());
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);
120 }, MoreExecutors.directExecutor());
124 * Destroy the current operational topology data. Note a valid transaction must be provided.
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>() {
133 public void onSuccess(final CommitInfo result) {
134 LOG.trace("Operational GraphModel removed {}", GRAPH_TOPOLOGY_IDENTIFIER);
138 public void onFailure(final Throwable throwable) {
139 LOG.error("Unable to reset operational GraphModel {} (transaction {})", GRAPH_TOPOLOGY_IDENTIFIER,
140 trans.getIdentifier(), throwable);
142 }, MoreExecutors.directExecutor());
144 /* Clear Connected Graph */
145 for (ConnectedGraph graph : graphs.values()) {
146 ((ConnectedGraphImpl) graph).clear();
152 * Destroy the current transaction chain.
154 private synchronized void destroyTransactionChain() {
156 LOG.debug("Destroy transaction chain for GraphModel {}", this);
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
166 protected synchronized void resetTransactionChain() {
167 LOG.debug("Resetting transaction chain for Graph builder");
168 destroyTransactionChain();
169 initTransactionChain();
173 * DataStore Instance Identifier creation for the various Graph components.
175 private static InstanceIdentifier<Graph> getGraphInstanceIdentifier(final String name) {
176 return GRAPH_TOPOLOGY_IDENTIFIER.child(Graph.class, new GraphKey(name));
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());
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());
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());
192 * Add Graph or Graph components to the Data Store.
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
199 private synchronized <T extends DataObject> void addToDataStore(final InstanceIdentifier<T> id, final T data,
201 final ReadWriteTransaction trans = chain.newReadWriteTransaction();
202 trans.put(LogicalDatastoreType.OPERATIONAL, id, data);
203 trans.commit().addCallback(new FutureCallback<CommitInfo>() {
205 public void onSuccess(final CommitInfo result) {
206 LOG.info("GraphModel: {} has been published in operational datastore ", info);
210 public void onFailure(final Throwable throwable) {
211 LOG.error("GrahModel: Cannot write {} to the operational datastore (transaction: {})", info,
212 trans.getIdentifier());
214 }, MoreExecutors.directExecutor());
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.
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
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();
231 trans.delete(LogicalDatastoreType.OPERATIONAL, old);
233 trans.put(LogicalDatastoreType.OPERATIONAL, id, data);
234 trans.commit().addCallback(new FutureCallback<CommitInfo>() {
236 public void onSuccess(final CommitInfo result) {
237 LOG.info("GraphModel: {} has been published in operational datastore ", info);
241 public void onFailure(final Throwable throwable) {
242 LOG.error("GrahModel: Cannot write {} to the operational datastore (transaction: {})", info,
243 trans.getIdentifier());
245 }, MoreExecutors.directExecutor());
249 * Remove Graph or Graph components to the Data Store.
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
255 private synchronized <T extends DataObject> void removeFromDataStore(final InstanceIdentifier<T> id,
257 final ReadWriteTransaction trans = chain.newReadWriteTransaction();
258 trans.delete(LogicalDatastoreType.OPERATIONAL, id);
259 trans.commit().addCallback(new FutureCallback<CommitInfo>() {
261 public void onSuccess(final CommitInfo result) {
262 LOG.info("GraphModel: {} has been deleted in operational datastore ", info);
266 public void onFailure(final Throwable throwable) {
267 LOG.error("GraphModel: Cannot delete {} to the operational datastore (transaction: {})", info,
268 trans.getIdentifier());
270 }, MoreExecutors.directExecutor());
274 * Clear Graph. This method is used by the Connected Graph to clear associated Graph.
276 * @param graph Graph associated to the Connected Graph
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());
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).
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
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 */
298 oldId = getVertexInstanceIdentifier(graph, old);
300 updateToDataStore(getVertexInstanceIdentifier(graph, vertex), vertex, oldId,
301 "Vertex(" + vertex.getName() + ")");
305 * Remove Vertex to existing Graph. This method is called when a Connected Vertex is removed (See deleteVertex()
306 * method from ConnectedGraph Interface).
308 * @param graph Graph where the vertex is stored
309 * @param vertex Vertex to be removed
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() + ")");
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).
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
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 */
331 oldId = getEdgeInstanceIdentifier(graph, old);
333 updateToDataStore(getEdgeInstanceIdentifier(graph, edge), edge, oldId, "Edge(" + edge.getName() + ")");
337 * Remove Edge to existing Graph. This method is called when a Connected Edge is removed (See deleteEdge()
338 * method from ConnectedGraph Interface).
340 * @param graph Graph where the edge is stored
341 * @param edge Edge to be removed
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() + ")");
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).
353 * @param graph Graph where the prefix will be stored
354 * @param prefix Prefix to be interted in the graph
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() + ")");
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).
366 * @param graph Graph where the prefix is stored
367 * @param prefix Prefix to be removed
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() + ")");
376 public synchronized void onFailure(final Throwable cause) {
377 LOG.error("GraphModel builder for {} failed", GRAPH_TOPOLOGY_IDENTIFIER, cause);
381 public void onSuccess(final Empty result) {
382 LOG.info("GraphModel builder for {} shut down", GRAPH_TOPOLOGY_IDENTIFIER);
386 public List<ConnectedGraph> getConnectedGraphs() {
387 return new ArrayList<>(graphs.values());
391 public ConnectedGraph getConnectedGraph(final GraphKey key) {
392 return graphs.get(key);
396 public ConnectedGraph getConnectedGraph(final String name) {
397 return graphs.get(new GraphKey(name));
401 public Graph getGraph(final GraphKey key) {
402 if (graphs.containsKey(key)) {
403 return graphs.get(key).getGraph();
410 public Graph getGraph(final String name) {
411 return getGraph(new GraphKey(name));
415 public ConnectedGraph createConnectedGraph(final String name, final DomainScope scope) {
416 Graph graph = new GraphBuilder()
418 .setDomainScope(scope)
420 addToDataStore(getGraphInstanceIdentifier(name), graph, "Graph(" + name + ")");
421 ConnectedGraphImpl cgraph = new ConnectedGraphImpl(graph, this);
422 graphs.put(graph.key(), cgraph);
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);
436 public void deleteGraph(final GraphKey key) {
437 checkArgument(key != null, "Provided Graph Key is a null object");
438 ConnectedGraphImpl cgraph = graphs.get(key);
440 * Remove the corresponding Connected Graph which will delete the graph
441 * by calling clearGraph() method (see above)
443 if (cgraph != null) {