From: Olivier Dugeon Date: Thu, 28 Nov 2019 14:40:07 +0000 (+0100) Subject: Graph modelisation for Path Computation Algorithm X-Git-Tag: release/aluminium~75 X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?a=commitdiff_plain;h=35df3115915882fe84f071cfb89c6a8f99b1c822;p=bgpcep.git Graph modelisation for Path Computation Algorithm Initial commit of Graph Model. This is the patch set 1/3 to implement the Path Computation Algorithms to implement a full feature PCE server in conformity to RFC5440. Details information about the yang model, REST and Java API are provided in docs/graph directory. JIRA: BGPCEP-858 Change-Id: Icf7b8320185f9d94377c76cdaecba836b7ef3bc5 Signed-off-by: Olivier Dugeon Co-authored-by: Philippe Niger Signed-off-by: Robert Varga --- diff --git a/artifacts/pom.xml b/artifacts/pom.xml index a2f5597679..5f4b429427 100644 --- a/artifacts/pom.xml +++ b/artifacts/pom.xml @@ -46,6 +46,13 @@ import pom + + ${project.groupId} + graph-artifacts + ${project.version} + import + pom + ${project.groupId} topology-artifacts diff --git a/distribution-karaf/pom.xml b/distribution-karaf/pom.xml index f560c7bd0e..b609fd6756 100644 --- a/distribution-karaf/pom.xml +++ b/distribution-karaf/pom.xml @@ -68,6 +68,13 @@ xml runtime + + org.opendaylight.bgpcep + features-graph + features + xml + runtime + org.opendaylight.bgpcep features-rsvp diff --git a/docs/graph/graph-user-guide-graph-model.rst b/docs/graph/graph-user-guide-graph-model.rst new file mode 100644 index 0000000000..43563cf4c7 --- /dev/null +++ b/docs/graph/graph-user-guide-graph-model.rst @@ -0,0 +1,220 @@ +.. _graph-user-guide-graph-model: + +Graph Model Overview +==================== +This section provides a high-level overview of the Graph Model. + +.. contents:: Contents + :depth: 2 + :local: + +Graph Theory and Path Computation +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The primary goal of Graph and Algorithms features, is to be able to compute +constrainted path among a given IP/MPLS network. Such path could be used to +enforce connectivity by means of RSVP-TE or Segment Routing TE through PCEP +protocol or simply provide a solution to answer a path request (coming from +RESTCONF API or a PcRequest message). + +In IP/MPLS networks, these path computation algorithms need to access to a +representation of the network topology which is, in general, denoted as +*Traffic Engineering Database (TED)* in reference to information convey by +the IGP Traffic Engineering routing protocols such as OSPF-TE or IS-IS-TE. +There is many way to store this TED and the IETF is in the process of +defining the corresponding yang model (draft-ietf-teas-yang-te-topo-22.txt). +But, most of these path computation algorithms have been designed with a +particular representation of network model for perfomrance efficiency: a Graph. +This latter is denoted in literature as *G(V, E)* where V, the Vertices, +represent the nodes (e.g. routers) and E, the Edges, represent the link between +nodes. There is numerous graph type, but for path computation, a Directed and +Connected Graph is recommended, again for performance efficiency. + +A Connected Graph is a particular graph type where each pair of vertices forms +the endpoints of a path. It is used because path computation algorithms need to +progress smoothly in the graph without searching for the next hops in the whole +graph (mostly for performance issue) and also because some algorithms need to +mark Vertices as visited once processed to avoid loop. Connected Graph is a +particular graph type where each pair of vertices forms the endpoints of a +path. + +In general, for modern networks (i.e. with optical links), links are considered +as full-duplex. Thus, from a resources (mostly bandwidth) point of view, going +from node *A to B* will consumes resources (mostly bandwidth) on the link from +A to B while keeping intact resources on the link from B to A. So, the graph +model needs to reflect this behaviour. The graph that represents the network +will used Directed Edges for that purposes. So, the recommended graph is a +Directed Graph (sometimes also named Oriented Graph). As a consequence, it is +necessary to create 2 edges between 2 vertices: *A to B* and *B to A*. + +For more information, reader could refer to Graph Theory +e.g. https://en.wikipedia.org/wiki/Graph_theory and Path Computation algorithms +e.g. Shortest Path First (SPF) https://en.wikipedia.org/wiki/Shortest_path_problem +and Constrainted Shortest Path First (CSPF) https://en.wikipedia.org/wiki/Constrained_Shortest_Path_First. + +Yang Model for Graph +^^^^^^^^^^^^^^^^^^^^ + +In order to manipulate the Graph within the OpenDaylight Data Store, the given +yang model has been provided. It defines Edges and Vertices. Edges are enhanced +by Edges Attributes which are define all link attributes we could collect from +real network (e.g. through BGP Link State). Vertices are enhanced by Prefixes +in order to find quickly where End Points of a path are attached in the Graph. +It also serves to store Segment Routing information when computing path for +Segment Routing TE setup. + +A new yang model is provided as an alternative to the IETF yang-te-topo model +and IETF RFC8345 for several reasons: + + * Some link and node parameters (e.g. Segment Routing, TE Metric extensions) + which are available in IP/MPLS networks, through the IGP-TE or BGP-LS + protocols (see Linkstate Routes in BGP RIB) are not present in the IETF + ted model + * Node and link identifier are represented as string in the IETF ted model + which it is not efficient when looking into a HashMap() to find node or + link by its identifier + * Even if LinkstateTopologyBuilder() provided mechanism to fulfil IETF ted + model in the datastore, the NodeHolder an TpHolder classes have been + defined as private, and thus could not be used outside the + LinkstateTopologyBuilder() class + +Graph and Algorithm have been also designed to be used by other projects +(e.g. openflow) which not control IP/MPLS network. Thus, even if the graph +model addresses in first case the IP/MPLS network, its genericity allows +it to be suitable for other network types. + +The yand model for the Graph is as follow: + +.. code-block:: console + + module: graph + +--rw graph-topology + +--rw graph* [name] + +--rw name string + +--rw domain-scope? enumeration + +--rw asn? uint32 + +--rw vertex* [vertex-id] + | +--rw vertex-id uint64 + | +--rw name? string + | +--rw router-id? inet:ip-address + | +--rw vertex-type? enumeration + | +--rw srgb + | | +--rw lower-bound? uint32 + | | +--rw range-size? uint32 + | +--rw asn? uint32 + +--rw edge* [edge-id] + | +--rw edge-id uint64 + | +--rw local-vertex-id? uint64 + | +--rw remote-vertex-id? uint64 + | +--rw name? string + | +--rw edge-attributes + | +--rw metric? uint32 + | +--rw te-metric? uint32 + | +--rw admin-group? uint32 + | +--rw local-address? inet:ip-address + | +--rw remote-address? inet:ip-address + | +--rw local-identifier? uint32 + | +--rw remote-identifier? uint32 + | +--rw max-link-bandwidth? decimal-bandwidth + | +--rw max-resv-link-bandwidth? decimal-bandwidth + | +--rw unreserved-bandwidth* [class-type] + | | +--rw class-type uint8 + | | +--rw bandwidth? decimal-bandwidth + | +--rw delay? delay + | +--rw min-max-delay + | | +--rw min-delay? delay + | | +--rw max-delay? delay + | +--rw jitter? delay + | +--rw loss? loss + | +--rw residual-bandwidth? decimal-bandwidth + | +--rw available-bandwidth? decimal-bandwidth + | +--rw utilized-bandwidth? decimal-bandwidth + | +--rw adj-sid? uint32 + | +--rw backup-adj-sid? uint32 + | +--rw srlgs* uint32 + +--rw prefix* [prefix] + +--rw prefix inet:ip-prefix + +--rw prefix-sid? uint32 + +--rw node-sid? boolean + +--rw vertex-id? uint64 + +Java Class for Connected Graph +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Yang model represents data as a Flat Tree hierarchy. However, this particular +graph representation (without a specific storage engine capabilities) is not +very useful for Path Computation due to lower performance compared to other +Graph types. Of course path computation algorithms could play with a such +Graph, but at the cost of performance issue as algorithms need to search the +neighbours of a vertices at each step when progressing in the graph. This will +decrease the performance by a factor of *N* to *N²* depending of the +algorithms. For large scale network, say 1000+ nodes, it is too high. + +Yang syntax authorizes reference to other grouping or leaf with 'leafref'. +This could allows from a Vertex to access to Edges. However, it is not possible +to achieve a cross reference between Vertex and Edge. In Connected Graph, +both Vertex and Edge must reference each together: from Vertex it is needed to +access directly at the list of Edges connected to this Vertex, and from Edge, +it is need to access directly at the source and destination Vertex. + +So, to overcome this limitation, the implemented Graph is composed of two +pieces: + + * A standard Graph modeled in yang and stored in the Data Store + * A Connected Graph version based on the yang model but stored in memory only + + +The connected version of Vertex is composed of: + +.. code-block:: java + + /* Reference to input and output Connected Edge within the Connected Graph */ + private ArrayList input = new ArrayList<>(); + private ArrayList output = new ArrayList<>(); + + /* List of Prefixes announced by this Vertex */ + private ArrayList prefixes = new ArrayList<>(); + + /* Reference to the Vertex of the standard Graph associated to the Connected Graph */ + private Vertex vertex = null; + +Where distinction is made between input and output Edges in order to respect the Directed Graph +behviour. + +The connected version of Edges is composed of: + +.. code-block:: java + + /* Reference to Source and Destination Connected Vertex within the Connected Graph */ + private ConnectedVertexImpl source; + private ConnectedVertexImpl destination; + + /* Reference to the Edge within the Graph associated to the Connected Graph */ + private Edge edge; + +Where source and destination Vertices also ease to implement the Directed Graph. + +And finally, the connected version of Graph is composed of: + +.. code-block:: java + + /* List of Connected Vertics that composed this Connected Graph */ + private final HashMap vertices = new HashMap<>(); + + /* List of Connected Edges that composed this Connected Graph */ + private final HashMap edges = new HashMap<>(); + + /* List of IP prefix attached to Vertices */ + private final HashMap prefixes = new HashMap<>(); + + /* Reference to the non connected Graph stored in DataStore */ + private Graph graph; + +Where Vertices, Edges and Prefixes are stored in *HashMap* to speed up the +access of a given element of the Graph. + +Note that the Unique Key identifier for Connected Edge and Connected Vertex +must not be equal to zero (and as a consequence the Edge and Vertex key). +This restriction is due to some algorithms that used the value 0 as a +special indication during the path computation. + diff --git a/docs/graph/graph-user-guide-manage-graph.rst b/docs/graph/graph-user-guide-manage-graph.rst new file mode 100644 index 0000000000..c4aed50e37 --- /dev/null +++ b/docs/graph/graph-user-guide-manage-graph.rst @@ -0,0 +1,382 @@ +.. _graph-user-guide-manage-graph: + +Manage Graph +============ +This section explains how to manipulate the Graph through REST API. + +.. contents:: Contents + :depth: 2 + :local: + +Concept +^^^^^^^ + +The connected Graph is not accessible through the REST API as it is not +posssible to represent connected Edges and Vertices with yang model (see +Graph Overview). Thus, Graph feature provides a new service named +ConnectedGraphProvider published in Karaf. This service maintains an +up-to-date list of Connected Graph stored in memory and allows to: + + * Get Connected Graph by name or GraphKey + * Create Connected Graph + * Add existing graph to create associated Connected Graph + * Delete existing Graph identify by its GraphKey + +Then, Connected Graph provides method to manage Vertices, Edges and Prefix. +The ConnectedGraphProvider is also in charge to maintain up to date the Graph +associated to the Connected Graph in the OpenDaylight operational Data Store. + +In fact, two graphs are stored in the Data Store: + + * Operational Graph in ``restconf/operational`` which is the graph + associated with the Connected Graph stored in memory + * Configuration Graph in ``restconf/config`` which is the graph that + could be create / modify / delete in order to produce the Connected + Graph and thus, the associated Graph stored in operational Data Store + +It is also possible to add / delete Vertices, Edges and Prefix on an existing +Graph through the REST API. + + +JAVA API +^^^^^^^^ + +Developper will refer to the Java Doc to manage Connected and standard Graph. +The main principle is as follow: + +1. First Create a Connected Graph through the ConnectedGraphProvider which is a +new service provided by the Graph feature through Karaf: + +.. code-block:: java + + ConnectedGraph cgraph = graphProvider.createConnectedGraph("example", GraphType.IntraDomain); + +Or by adding an initial graph: + +.. code-block:: java + + Graph graph = new GraphBuilder().setName("example").setGraphType(GraphType.IntraDomain).build(); + ConnectedGraph cgraph = graphProvider.addGraph(graph); + +Where graphProvider is obtained from blueprint. + +2. Once created, the Connected Graph offers various method to manage Vertices +and Edges. For example: + +.. code-block:: java + + /* Add a Vertex */ + Vertex vertex = new VertexBuilder().setVertexId(Uint64.ValueOf(1)).build(); + cgraph.addVertex(vertex); + + /* Add an Edge */ + Edge edge = new EdgeBuilder().setEdgeId(Uint64.ValueOf(1)).build(); + cgraph.addEdge(edge); + + ... + +REST API +^^^^^^^^ + +This section provides the list of REST method that could be used to manage +Graph. + +Get Graph +''''''''' + +Graphs are stored in operation and config Data Store. Thus there are accessible +through the ``graph:graph-topology`` namespace as follow: + +----- + +**URL:** ``restconf/operational/graph:graph-topology`` + +**Method:** ``GET`` + +**Response Body:** + +.. code-block:: json + + { + "graph-topology": { + "graph": [ + { + "name": "example", + "vertex": [ + { + "vertex-id": 2, + "name": "r2", + "vertex-type": "standard" + }, + { + "vertex-id": 1, + "name": "r1", + "vertex-type": "standard" + } + ], + "graph-type": "intra-domain" + } + ] + } + } + +Graphs publish in the configuration Data Store are also accessible through REST +API with the same namespace as follow: + +----- + +**URL:** ``restconf/config/graph:graph-topology`` + +**Method:** ``GET`` + +**Response Body:** + +.. code-block:: json + + { + "graph-topology": { + "graph": [ + { + "name": "example", + "vertex": [ + { + "vertex-id": 2, + "name": "r2", + "vertex-type": "standard" + }, + { + "vertex-id": 1, + "name": "r1", + "vertex-type": "standard" + } + ], + "graph-type": "intra-domain" + } + ] + } + } + +Create Graph +'''''''''''' + +Graphs could be created with PUT method. In this case, all previously +configured graphs are removed from both the configuration and operational +Data Store. This includes all modification and associated Connected Graphs. + +----- + +**URL:** ``restconf/config/graph:graph-topology`` + +**Method:** ``PUT`` + +**Content-Type:** ``application/json`` + +**Request Body:** + +.. code-block:: json + + { + "graph-topology": { + "graph": [ + { + "name": "example", + "graph-type": "intra-domain", + "vertex": [ + { + "vertex-id": 1, + "name": "r1" + }, + { + "vertex-id": 2, + "name": "r2" + } + ], + "edge": [ + { + "edge-id": 1, + "name": "r1 - r2", + "local-vertex-id": 1, + "remote-vertex-id": 2 + }, + { + "edge-id": 2, + "name": "r2 - r1", + "local-vertex-id": 2, + "remote-vertex-id": 1 + } + ] + } + ] + } + } + +@line 5: **name** The Graph identifier. Must be unique. + +@line 6: **graph-type** The type of the Graph: intra-domain or inter-domain. + +@line 7: **vertex** - List of Vertices. Each Vertex ID must be unique. + +@line 17: **edges** - List of Edges. Each Edge ID must be unique. + +@line 21: **local-vertex-id** - Vertex ID where the Edge is connected from. +The vertex ID must correspond to vertex that is present in the vertex list, +otherwise, the connection will not be estabished in the Connected Graph. + +@line 22: **remote-vertex-id** - Vertex ID where the Edge is connected to. +The vertex ID must correspond to vertex that is present in the vertex list, +otherwise, the connection will not be estabished in the Connected Graph. + +Add Graph +''''''''' + +It is also possible to add a Graph to the existing list. POST method will +be used instead of PUT. Body and URL remains the same. + +Delete Graph +'''''''''''' + +Removing a graph used the DELETE method as follow: + +----- + +**URL:** ``restconf/config/graph:graph-topology/graph/example`` + +**Method:** ``DELETE`` + +The name of the graph i.e. the Graph Key to be deleted must be provide +within the URL. + +Add Vertices +'''''''''''' + +One or more vertex could be added to a Graph. If the graph doesn't exist, +it will be automatically created. Only POST method must be used. + +----- + +**URL:** ``restconf/config/graph:graph-topology/graph/example`` + +**Method:** ``POST`` + +**Content-Type:** ``application/json`` + +**Request Body:** + +.. code-block:: json + + { + "vertex": [ + { + "vertex-id": 100, + "name": "r100", + "router-id": "192.168.1.100" + } + ] + } + +Delete Vertex +''''''''''''' + +Removing a vertex used the DELETE method as follow: + +----- + +**URL:** ``restconf/config/graph:graph-topology/graph/example/vertex/10`` + +**Method:** ``DELETE`` + +The Vertex to be deleted is identified by its Vertex Id and must be provide +within the URL. + +Add Edges +''''''''' + +One or more edges could be added to a Graph. If the graph doesn't exist, +it will be automatically created. Only POST method must be used. + +----- + +**URL:** ``restconf/config/graph:graph-topology/graph/example`` + +**Method:** ``POST`` + +**Content-Type:** ``application/json`` + +**Request Body:** + +.. code-block:: json + + { + "edge": [ + { + "edge-id": 10, + "name": "r1 - r2", + "local-vertex-id": 1, + "remote-vertex-id": 2 + }, + { + "edge-id": 20, + "name": "r2 - r1", + "local-vertex-id": 2, + "remote-vertex-id": 1 + } + ] + } + +Delete Edge +''''''''''' + +Removing an edge used the DELETE method as follow: + +----- + +**URL:** ``restconf/config/graph:graph-topology/graph/example/edge/10`` + +**Method:** ``DELETE`` + +The Edge to be deleted is identified by its Edge Id and must be provide +within the URL. + +Add Prefixes +'''''''''''' + +One or more prefixe could be added to a Graph. If the graph doesn't exist, +it will be automatically created. Only POST method must be used. + +----- + +**URL:** ``restconf/config/graph:graph-topology/graph/example`` + +**Method:** ``POST`` + +**Content-Type:** ``application/json`` + +**Request Body:** + +.. code-block:: json + + { + "prefix": [ + { + "prefix": "192.168.1.0/24", + "vertex-id": 1 + } + ] + } + +Delete Prefix +''''''''''''' + +Removing a prefix used the DELETE method as follow: + +----- + +**URL:** ``restconf/config/graph:graph-topology/graph/example/prefix/192%2e168%2e1%2e0%2f24`` + +**Method:** ``DELETE`` + +The Prefix to be deleted is identified by its Prefix Id and must be provide +within the URL. As the prefix identifier is the ip prefix, '.' and '/' must +be replace by their respective ASCII representation i.e. '%2e' for dot and +'%2f' for slash. + diff --git a/docs/graph/graph-user-guide-running-graph.rst b/docs/graph/graph-user-guide-running-graph.rst new file mode 100644 index 0000000000..6b05567d92 --- /dev/null +++ b/docs/graph/graph-user-guide-running-graph.rst @@ -0,0 +1,40 @@ +.. _graph-user-guide-running-graph: + +Running Graph +============= +This section explains how to install Graph plugin. + +1. Install Graph feature - ``feature-graph``. + Also, for sake of this sample, it is required to install RESTCONF. + In the Karaf console, type command: + + .. code-block:: console + + feature:install feature-restconf feature-graph + +2. The Graph plugin contains a default empty configuration, which is applied + after the feature starts up. One instance of Graph plugin is created + (named *graph-topology*), and its presence can be verified via REST: + + **URL:** ``restconf/config/graph:graph-topology`` + + **Method:** ``GET`` + + **Response Body:** + + .. code-block:: json + + {} + + It is also posible to access to the operational graph topology which is + also empty by default via REST: + + **URL:** ``restconf/operational/graph:graph-topology`` + + **Method:** ``GET`` + + **Response Body:** + + .. code-block:: json + + {} diff --git a/docs/graph/index.rst b/docs/graph/index.rst new file mode 100644 index 0000000000..17164f5e71 --- /dev/null +++ b/docs/graph/index.rst @@ -0,0 +1,18 @@ +.. _graph-user-guide: + +GRAPH Model User Guide +====================== +This guide contains information on how to use the OpenDaylight Graph Model plugin that is the basis +for Path Computation Algorithms. Graph and algorithms are used in the PCE Server part to compute +path. This path will then serves to fulfil Explicit Route Object (ERO) for PCResponse message in +turn of a PCRequest message. + +Note: The user should learn about Graph Theory and (Constraint) Shortest Path First algorithms. + + +.. toctree:: + :maxdepth: 1 + + graph-user-guide-graph-model + graph-user-guide-running-graph + graph-user-guide-manage-graph diff --git a/docs/index.rst b/docs/index.rst index 4fc9abaf5c..234a4e2c30 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -27,3 +27,4 @@ User Guides bgp/index bmp/index pcep/index + graph/index diff --git a/features/graph/features-graph/pom.xml b/features/graph/features-graph/pom.xml new file mode 100644 index 0000000000..220288f674 --- /dev/null +++ b/features/graph/features-graph/pom.xml @@ -0,0 +1,37 @@ + + + + 4.0.0 + + org.opendaylight.bgpcep + feature-repo-parent + 0.14.0-SNAPSHOT + ../../../feature-repo-parent + + + features-graph + feature + + + + ${project.groupId} + odl-bgpcep-graph + xml + features + + + ${project.groupId} + odl-bgpcep-graph-api + xml + features + + + diff --git a/features/graph/odl-bgpcep-graph-api/pom.xml b/features/graph/odl-bgpcep-graph-api/pom.xml new file mode 100644 index 0000000000..f86ab161e2 --- /dev/null +++ b/features/graph/odl-bgpcep-graph-api/pom.xml @@ -0,0 +1,42 @@ + + + + 4.0.0 + + org.opendaylight.bgpcep + single-feature-parent + 0.14.0-SNAPSHOT + ../../../single-feature-parent + + + odl-bgpcep-graph-api + feature + OpenDaylight :: GRAPH :: API + + + + ${project.groupId} + graph-api + + + org.opendaylight.mdsal.model + odl-mdsal-model-rfc6991 + xml + features + + + org.opendaylight.mdsal.model + odl-mdsal-model-odl-uint24 + xml + features + + + diff --git a/features/graph/odl-bgpcep-graph/pom.xml b/features/graph/odl-bgpcep-graph/pom.xml new file mode 100644 index 0000000000..99523491a4 --- /dev/null +++ b/features/graph/odl-bgpcep-graph/pom.xml @@ -0,0 +1,42 @@ + + + + 4.0.0 + + org.opendaylight.bgpcep + single-feature-parent + 0.14.0-SNAPSHOT + ../../../single-feature-parent + + + odl-bgpcep-graph + feature + OpenDaylight :: GRAPH + + + + ${project.groupId} + odl-bgpcep-graph-api + xml + features + + + org.opendaylight.controller + odl-mdsal-broker + xml + features + + + ${project.groupId} + graph-impl + + + diff --git a/features/graph/pom.xml b/features/graph/pom.xml new file mode 100644 index 0000000000..e12863fe03 --- /dev/null +++ b/features/graph/pom.xml @@ -0,0 +1,33 @@ + + + + 4.0.0 + + org.opendaylight.odlparent + odlparent-lite + 6.0.4 + + + + features-aggregator-graph + org.opendaylight.bgpcep + 0.14.0-SNAPSHOT + pom + + + features-graph + odl-bgpcep-graph-api + odl-bgpcep-graph + + + + true + true + + diff --git a/features/pom.xml b/features/pom.xml index 466207d06e..15997d31c3 100644 --- a/features/pom.xml +++ b/features/pom.xml @@ -23,6 +23,7 @@ bgp pcep + graph bgpcep-extras bmp rsvp diff --git a/graph/graph-api/pom.xml b/graph/graph-api/pom.xml new file mode 100644 index 0000000000..ed8571df2c --- /dev/null +++ b/graph/graph-api/pom.xml @@ -0,0 +1,48 @@ + + + + + + 4.0.0 + + org.opendaylight.bgpcep + binding-parent + 0.14.0-SNAPSHOT + ../../binding-parent + + + graph-api + Graph Network Topology API + bundle + ${project.artifactId} + + + + org.opendaylight.mdsal.model + odl-uint24 + + + org.opendaylight.mdsal.binding.model.ietf + rfc6991-ietf-inet-types + + + com.google.guava + guava + + + org.opendaylight.yangtools + concepts + + + org.opendaylight.yangtools + yang-common + + + diff --git a/graph/graph-api/src/main/java/org/opendaylight/graph/ConnectedEdge.java b/graph/graph-api/src/main/java/org/opendaylight/graph/ConnectedEdge.java new file mode 100644 index 0000000000..96065d3887 --- /dev/null +++ b/graph/graph-api/src/main/java/org/opendaylight/graph/ConnectedEdge.java @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2019 Orange. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.graph; + +import org.eclipse.jdt.annotation.NonNull; +import org.eclipse.jdt.annotation.Nullable; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.graph.rev191125.graph.topology.graph.Edge; + +/** + * Connected Edge class is the connected version of the Edge class from the graph yang model. + * + *

+ * It is composed of a reference to the associated Edge class from the Graph class, + * a unique Key identifier in the associated Connected Graph, + * and two references to the associated Connected Vertex in the connected Graph: source and destination. + *

+ * {@code
+ * ---------------------------                        --------------------------------
+ * | Source Connected Vertex |---- Connected Edge --->| Destination Connected Vertex |
+ * ---------------------------                        --------------------------------
+ * }
+ * 
+ * + * @author Olivier Dugeon + * @author Philippe Niger + */ +public interface ConnectedEdge { + /** + * Returns unique key associated to this Connected Edge. + * + * @return Edge Key + */ + @NonNull Long getKey(); + + /** + * Returns the Edge from the Graph associated to this connection. + * + * @return Edge associated to this connection + */ + @NonNull Edge getEdge(); + + /** + * Returns the source Connected Vertex from the Connected Graph associated to this Connected Edge. + * + * @return Source Connected Vertex + */ + @Nullable ConnectedVertex getSource(); + + /** + * Returns the destination Connected Vertex from the Connected Graph associated to this Connected Edge. + * + * @return Destination Connected Vertex + */ + @Nullable ConnectedVertex getDestination(); + +} diff --git a/graph/graph-api/src/main/java/org/opendaylight/graph/ConnectedGraph.java b/graph/graph-api/src/main/java/org/opendaylight/graph/ConnectedGraph.java new file mode 100644 index 0000000000..f163c34065 --- /dev/null +++ b/graph/graph-api/src/main/java/org/opendaylight/graph/ConnectedGraph.java @@ -0,0 +1,195 @@ +/* + * Copyright (c) 2019 Orange. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.graph; + +import java.util.List; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpPrefix; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.graph.rev191125.graph.topology.Graph; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.graph.rev191125.graph.topology.graph.Edge; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.graph.rev191125.graph.topology.graph.EdgeKey; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.graph.rev191125.graph.topology.graph.Prefix; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.graph.rev191125.graph.topology.graph.Vertex; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.graph.rev191125.graph.topology.graph.VertexKey; + +/** + * Connected Graph class is the connected version of the Graph class from the graph yang model. + * + *

+ * Connected Graph is composed by Connected Vertex, Connected Edges and Prefix. It models Unidirectional Connected + * Graph (see graph theory). So, Edge and Connected Edge are unidirectional, thus to connect bi-directionally 2 vertices + * Va and Vb, it is necessary to setup 2 edges: Va to Vb and Vb to Va. + *

+ * {@code
+ *        --------------     ---------------------------    --------------
+ *        | Connected  |---->| Connected Edge Va to Vb |--->| Connected  |
+ *   ---->|  Vertex    |     ---------------------------    |  Vertex    |---->
+ *        |            |                                    |            |
+ *        | - Key (Va) |                                    | - Key (Vb) |
+ *   <----| - Vertex   |     ---------------------------    | - Vertex   |<----
+ *        |            |<----| Connected Edge Vb to Va |<---|            |
+ *        --------------     ---------------------------    --------------
+ * }
+ * 
+ * + * @author Olivier Dugeon + * @author Philippe Niger + */ +public interface ConnectedGraph { + + /** + * Returns the Graph associated to this Connected Graph. + * + * @return Graph + */ + Graph getGraph(); + + /** + * Returns the list of Connected Vertices that form this graph. + * + * @return list of Connected Vertices + */ + List getVertices(); + + /** + * Returns the Vertex associated to the given key. + * + * @param key Unique Vertex Identifier + * @return Vertex or null if there is no Vertex associated to the given key in this graph + */ + ConnectedVertex getConnectedVertex(Long key); + + /** + * Returns the Vertex associated to the given IP address. + * + * @param address IP address of the Loopback of the Vertex + * + * @return Vertex or null if there is no Vertex associated to the given IP address in this graph + */ + ConnectedVertex getConnectedVertex(IpAddress address); + + /** + * Add Vertex in the Connected Graph. This action will automatically create the associated Connected Vertex and + * update the Graph in the DataStore. + * + * @param vertex Vertex to be added + * + * @return Connected Vertex associated to the given Vertex + */ + ConnectedVertex addVertex(Vertex vertex); + + /** + * Remove the Vertex in the Connected Graph. This action will automatically remove the associated Connected Vertex + * and update the Graph in the DataStore. + * + * @param vertexKey Unique Vertex Identifier + */ + void deleteVertex(VertexKey vertexKey); + + /** + * Returns the number of Vertices in the graph. + * + * @return number of vertices + */ + int getVerticesSize(); + + /** + * Returns the list of Connected Edges that form this graph. + * + * @return list of Connected Edges + */ + List getEdges(); + + /** + * Returns the Edge associated to the given key. + * + * @param key Unique Edge Identifier + * @return Edge or null if there is no Edge associated to the given key in this graph + */ + ConnectedEdge getConnectedEdge(Long key); + + /** + * Returns the Edge associated to the given IP address. + * + * @param address IP address of the Edge + * + * @return Edge or null if there is no Edge associated to the given IP address in this graph + */ + ConnectedEdge getConnectedEdge(IpAddress address); + + /** + * Add Edge in the Connected Graph. This action will automatically create the associated Connected Edge and + * update the Graph in the DataStore. + * + * @param edge Edge to be added + * + * @return Connected Edge associated to the given Edge + */ + ConnectedEdge addEdge(Edge edge); + + /** + * Remove the Edge in the Connected Graph. This action will automatically remove the associated Connected Edge + * and update the Graph in the DataStore. + * + * @param edgeKey Unique Edge Identifier + */ + void deleteEdge(EdgeKey edgeKey); + + /** + * Returns the number of Edges in the graph. + * + * @return number of edges + */ + int getEdgesSize(); + + /** + * Returns the list of Prefix that are stored in this graph. + * + * @return list of Prefix + */ + List getPrefixes(); + + /** + * Returns the Prefix associated to the given IP prefix. + * + * @param ippfx IPv4 or IPv6 prefix + * + * @return Prefix that match the given IPv4 or IPv6 prefix + */ + Prefix getPrefix(IpPrefix ippfx); + + /** + * Add Prefix in the Connected Graph. This action will automatically update the Graph in the DataStore. + * + * @param prefix Prefix to be added + * + */ + void addPrefix(Prefix prefix); + + /** + * Remove the Prefix in the Connected Graph. This action will automatically update the Graph in the DataStore. + * + * @param ippfx IPv4 or IPv6 prefix + */ + void deletePrefix(IpPrefix ippfx); + + /** + * Clear the Connected Graph by removing all Vertices, Edges and Prefixes. This also remove the associated Graph + * in the Datastore. + * + */ + void clear(); + + /** + * Returns the summary of the graph characteristics: number of Vertices, Edges and Prefix. + * + * @return characteristics of the Graph as a string + */ + String getSummary(); + +} diff --git a/graph/graph-api/src/main/java/org/opendaylight/graph/ConnectedGraphProvider.java b/graph/graph-api/src/main/java/org/opendaylight/graph/ConnectedGraphProvider.java new file mode 100644 index 0000000000..8f21828c40 --- /dev/null +++ b/graph/graph-api/src/main/java/org/opendaylight/graph/ConnectedGraphProvider.java @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2019 Orange. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.graph; + +import java.util.List; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.graph.rev191125.graph.topology.Graph; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.graph.rev191125.graph.topology.Graph.DomainScope; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.graph.rev191125.graph.topology.GraphKey; + +/** + * Connected Graph Provider is a new service provided by the Graph feature to manage the Connected Graph. + * + *

+ * It allows to get, create, add and delete Connected Graph. All associated Graph are automatically + * stored in the DataStore. + * + * @author Olivier Dugeon + * @author Philippe Niger + * + */ + +public interface ConnectedGraphProvider { + + /** + * Returns Graph for the given graph name. + * + * @param name Name of the Graph + * + * @return Graph + */ + Graph getGraph(String name); + + /** + * Returns the Graph for the given graph key. + * + * @param key Unique Graph Identifier + * + * @return Graph + */ + Graph getGraph(GraphKey key); + + /** + * Returns Connected Graph for the given graph name. + * + * @param name Name of the Graph + * + * @return Connected Graph + */ + ConnectedGraph getConnectedGraph(String name); + + /** + * Returns Connected Graph for the given graph key. + * + * @param key Unique Graph Identifier + * + * @return Connected Graph + */ + ConnectedGraph getConnectedGraph(GraphKey key); + + /** + * Returns all registered Connected Graphs. + * + * @return List of Connected Graph + */ + List getConnectedGraphs(); + + /** + * Create Connected Graph and associated Graph for given name and Graph Type. The associated Graph is also stored + * in the DataStore. + * + * @param name Name of the Graph + * @param scope Domain scope of the Graph (Intra or Inter domain) + * + * @return Connected Graph + */ + ConnectedGraph createConnectedGraph(String name, DomainScope scope); + + /** + * Add a Graph. This action will automatically create the associated Connected Graph and store is in the DataStore. + * + * @param graph Graph to be added + * + * @return Connected Graph + */ + ConnectedGraph addGraph(Graph graph); + + /** + * Remove a Graph. This action will automatically delete the associated Connected Graph and store is in the + * DataStore. + * + * @param key Graph Identifier + * + */ + void deleteGraph(GraphKey key); + +} diff --git a/graph/graph-api/src/main/java/org/opendaylight/graph/ConnectedVertex.java b/graph/graph-api/src/main/java/org/opendaylight/graph/ConnectedVertex.java new file mode 100644 index 0000000000..014c2ca41c --- /dev/null +++ b/graph/graph-api/src/main/java/org/opendaylight/graph/ConnectedVertex.java @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2019 Orange. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.graph; + +import java.util.List; +import org.eclipse.jdt.annotation.NonNull; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.graph.rev191125.graph.topology.graph.Edge; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.graph.rev191125.graph.topology.graph.Prefix; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.graph.rev191125.graph.topology.graph.Vertex; + +/** + * Connected Vertex class is the connected version of the Vertex class from the graph yang model. + * + *

+ * It is composed of a reference to the associated Vertex class from the Graph class, + * a unique Key identifier in the associated Connected Graph, + * and two lists to the associated Connected Edges in the connected Graph: input and output. + *

+ * {@code
+ *                              -------------
+ *                              | Connected |
+ *                         ---->|  Vertex   |---->
+ * Input Connected Edges {  ... | - Key     | ...  } Output Connected Edges
+ *                         ---->| - Vertex  |---->
+ *                              -------------
+ * }
+ * 
+ * + * @author Olivier Dugeon + * @author Philippe Niger + * + */ +public interface ConnectedVertex { + + /** + * Returns unique key associated to this Connected Vertex. + * + * @return Vertex Key + */ + @NonNull Long getKey(); + + /** + * Returns Vertex associated to this Connected Vertex. + * + * @return vertex Vertex + */ + @NonNull Vertex getVertex(); + + /** + * Returns Connected Edges that has for destination the Connected Vertex identified by its key. + * + * @param destinationKey Unique Key that identify the destination Vertex + * + * @return List of Connected Edge + */ + List getEdgeTo(Long destinationKey); + + /** + * Returns the list of incoming Edge for this Connected Vertex. + * + * @return List of Edge + */ + List getInputEdges(); + + /** + * Returns the list of incoming Connected Edge for this Connected Vertex. + * + * @return List of Connected Edge + */ + List getInputConnectedEdges(); + + /** + * Returns the list of outgoing Edge for this Connected Vertex. + * + * @return List of Edge + */ + List getOutputEdges(); + + /** + * Returns the list of outgoing Connected Edge for this Connected Vertex. + * + * @return List of Connected Edge + */ + List getOutputConnectedEdges(); + + /** + * Return the list of prefix announced by this Connected Vertex. Prefix contains the associated SID when + * Segment Routing is enable. + * + * @return List of Prefix + */ + List getPrefixes(); + +} diff --git a/graph/graph-api/src/main/yang/graph.yang b/graph/graph-api/src/main/yang/graph.yang new file mode 100644 index 0000000000..9ec45041a7 --- /dev/null +++ b/graph/graph-api/src/main/yang/graph.yang @@ -0,0 +1,291 @@ +module graph { + yang-version 1; + namespace "urn:opendaylight:params:xml:ns:yang:graph"; + prefix "graph"; + + import ietf-inet-types { prefix inet; revision-date 2013-07-15; } + import odl-uint24 { prefix uint24; } + + organization "Orange"; + contact "Philippe Niger "; + + description + "This module contains the graph data model for network topology and datastore + used in the Path Computation Algorithms. + + Copyright (c)2019 Orange. All rights reserved. + + This program and the accompanying materials are made available + under the terms of the Eclipse Public License v1.0 which + accompanies this distribution, and is available at + http://www.eclipse.org/legal/epl-v10.html"; + + revision "2019-11-25" { + description + "Initial revision."; + reference ""; + } + + typedef delay { + description "Link delay is in the range 0 - 16.777215 seconds. Larger value is also encoded with Max value."; + units "microseconds"; + type uint24:uint24; + } + + typedef loss { + description "Link loss is in the range 0 - 50.331642% (= 2^24 - 2). Larger value is also encoded with Max value."; + units "0.000003%"; + type uint24:uint24; + } + + typedef decimal-bandwidth { + description "Bandwidth in decimal format for easy comparison"; + units "bytes/second"; + type decimal64 { + fraction-digits 2; + } + } + + grouping edge-attributes { + description "Attributes associated with the Edge"; + reference "RFC 3630 & RFC 7471, RFC 3784 & RFC8570: OSPF / IS-IS Traffic Engineering (TE) & Extended Metrics"; + + leaf metric { + description "Standard Metric from the routing protocol"; + type uint32; + } + leaf te-metric { + description "Traffic Engineering Metric"; + type uint32; + } + leaf admin-group { + description "Administrative group or color of the link"; + type uint32; + } + leaf local-address { + type inet:ip-address; + } + leaf remote-address { + type inet:ip-address; + } + leaf local-identifier { + type uint32; + } + leaf remote-identifier { + type uint32; + } + leaf max-link-bandwidth { + description "Maximum bandwidth that can be use"; + type decimal-bandwidth; + } + leaf max-resv-link-bandwidth { + description "Maximum amount of bandwidth that can be reserved"; + type decimal-bandwidth; + } + list unreserved-bandwidth { + description "Unreserved bandwidth for 0-7 class type"; + max-elements "8"; + ordered-by user; + key "class-type"; + leaf class-type { + type uint8 { + range "0..7"; + } + } + leaf bandwidth { + description "Unreserved bandwidth for this class type"; + type decimal-bandwidth; + } + } + leaf delay { + description "Unidirectional Delay."; + type delay; + } + container min-max-delay { + description "Min/Max Unidirectional Delay"; + leaf min-delay { + type delay; + } + leaf max-delay { + type delay; + } + } + leaf jitter { + description "Unidirectional Delay Variation"; + type delay; + } + leaf loss { + description "Unidirectional Loss"; + type loss; + } + leaf residual-bandwidth { + description "Unidirectional Residual Bandwidth"; + type decimal-bandwidth; + } + leaf available-bandwidth { + description "Unidirectional Available Bandwidth"; + type decimal-bandwidth; + } + leaf utilized-bandwidth { + description "Unidirectional Utilized Bandwidth"; + type decimal-bandwidth; + } + leaf adj-sid { + description "Segment Routing Adjacency Identifier"; + units "MPLS label"; + type uint32; + } + leaf backup-adj-sid { + description "Segment Routing Backup Adjacency Identifier"; + units "MPLS label"; + type uint32; + } + leaf-list srlgs { + description "List of Shared Risk Link Group Attributes"; + type uint32; + } + } + + grouping edge { + description "Unidirectional Edge (link) representation for the network topology"; + leaf edge-id { + type uint64; + mandatory true; + } + leaf local-vertex-id { + description "Vertex identifier where the Edge is attached"; + type uint64; + } + leaf remote-vertex-id { + description "Vertex identifier where the Edge is going to"; + type uint64; + } + leaf name { + description "Edge name"; + type string; + } + container edge-attributes { + description "All attributes associated to the Edge"; + uses edge-attributes; + } + } + + grouping srgb { + description "Segment Routing Global Block: lower-bound + range-size"; + leaf lower-bound { + description "Lower bound of label range in SRGB. Unit MPLS label"; + type uint32; + } + leaf range-size { + description "Label range size in SRGB. Unit MPLS label"; + type uint32; + } + } + + grouping vertex { + description "Vertex (node) representation for the network topology"; + leaf vertex-id { + description "Identifier of the Vertex"; + type uint64; + mandatory true; + } + leaf name { + description "Name of the Vertex when known"; + type string; + } + leaf router-id { + description "Global unique IP Trafic Engineering Router ID"; + type inet:ip-address; + } + leaf vertex-type { + type enumeration { + enum standard { + value 0; + } + enum abr { + value 1; + } + enum asbr-in { + value 2; + } + enum asbr-out { + value 3; + } + enum pseudo { + value 4; + } + } + default standard; + } + container srgb { + description "Segment Routing Global Block"; + uses srgb; + } + leaf asn { + description "AS Number"; + type uint32; + } + } + + grouping prefix { + leaf prefix { + description "IP (v4 or v6) Prefix."; + type inet:ip-prefix; + mandatory true; + } + leaf prefix-sid { + description "Segment Routing prefix Identifier. Unit MPLS label"; + type uint32; + } + leaf node-sid { + description "Prefix is a Node Segment Routing Identifier (Node-SID)"; + type boolean; + } + leaf vertex-id { + description "Reference to the Vertex where the prefix is attached"; + type uint64; + } + } + + container graph-topology { + list graph { + description "Graph representation of the Network Topology"; + key "name"; + leaf name { + type string; + } + leaf domain-scope { + description "Network domain scope: intra or inter domain"; + type enumeration { + enum intra-domain { + value 1; + } + enum inter-domain { + value 2; + } + } + default intra-domain; + } + leaf asn { + description "AS Number"; + type uint32; + } + list vertex { + description "The list of Vertices defined for the Graph."; + key "vertex-id"; + uses vertex; + } + list edge { + description "The list of Edges defined for the Graph."; + key "edge-id"; + uses edge; + } + list prefix { + description "The list of prefixes for the Graph."; + key "prefix"; + uses prefix; + } + } + } +} + diff --git a/graph/graph-artifacts/pom.xml b/graph/graph-artifacts/pom.xml new file mode 100644 index 0000000000..d280cfcbe7 --- /dev/null +++ b/graph/graph-artifacts/pom.xml @@ -0,0 +1,61 @@ + + + + + 4.0.0 + + + org.opendaylight.odlparent + odlparent-lite + 6.0.4 + + + + org.opendaylight.bgpcep + graph-artifacts + 0.14.0-SNAPSHOT + pom + + + + + ${project.groupId} + graph-api + ${project.version} + + + ${project.groupId} + graph-impl + ${project.version} + + + + org.opendaylight.bgpcep + features-graph + features + xml + ${project.version} + + + org.opendaylight.bgpcep + odl-bgpcep-graph + features + xml + ${project.version} + + + org.opendaylight.bgpcep + odl-bgpcep-graph-api + features + xml + ${project.version} + + + + diff --git a/graph/graph-impl/pom.xml b/graph/graph-impl/pom.xml new file mode 100644 index 0000000000..2df190d15a --- /dev/null +++ b/graph/graph-impl/pom.xml @@ -0,0 +1,65 @@ + + + + + + + 4.0.0 + + org.opendaylight.bgpcep + bgpcep-parent + 0.14.0-SNAPSHOT + ../../parent + + + graph-impl + Graph Network Topology Implementation + bundle + ${project.artifactId} + + + + ${project.groupId} + graph-api + + + org.opendaylight.yangtools + concepts + + + org.opendaylight.yangtools + yang-common + + + org.opendaylight.mdsal + mdsal-common-api + + + org.opendaylight.mdsal + mdsal-binding-api + + + org.opendaylight.mdsal + yang-binding + + + org.slf4j + slf4j-api + + + com.google.guava + guava + + + org.opendaylight.mdsal.binding.model.ietf + rfc6991-ietf-inet-types + + + diff --git a/graph/graph-impl/src/main/java/org/opendaylight/graph/impl/ConnectedEdgeImpl.java b/graph/graph-impl/src/main/java/org/opendaylight/graph/impl/ConnectedEdgeImpl.java new file mode 100644 index 0000000000..682a4bef0b --- /dev/null +++ b/graph/graph-impl/src/main/java/org/opendaylight/graph/impl/ConnectedEdgeImpl.java @@ -0,0 +1,157 @@ +/* + * Copyright (c) 2019 Orange. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.graph.impl; + +import static com.google.common.base.Preconditions.checkArgument; + +import org.eclipse.jdt.annotation.NonNull; +import org.opendaylight.graph.ConnectedEdge; +import org.opendaylight.graph.ConnectedVertex; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.graph.rev191125.graph.topology.graph.Edge; + +/** + * This Class implements the Connected Edge used by the Connected Graph for path computation algorithms. + * + * @author Olivier Dugeon + * @author Philippe Niger + */ + +public class ConnectedEdgeImpl implements ConnectedEdge { + + /* Reference to Source and Destination Connected Vertex within the Connected Graph */ + private ConnectedVertexImpl source; + private ConnectedVertexImpl destination; + + /* Reference to the Edge within the Graph associated to the Connected Graph */ + private Edge edge; + + /* Edge key in the Connected Graph */ + private Long ceid; + + public ConnectedEdgeImpl(@NonNull Long key) { + checkArgument(key != 0, "Edge Key must not be equal to 0"); + this.ceid = key; + this.edge = null; + } + + public ConnectedEdgeImpl(@NonNull Edge edge) { + checkArgument(edge.getEdgeId().longValue() != 0, "Edge Key must not be equal to 0"); + this.edge = edge; + this.ceid = edge.getEdgeId().longValue(); + } + + /** + * When edge is removed, we must disconnect source and destination Connected Vertices. + */ + void close() { + this.disconnect(); + } + + /** + * Set Connected Vertex as source. + * + * @param vertex Vertex + */ + public ConnectedEdgeImpl setSource(ConnectedVertexImpl vertex) { + source = vertex; + return this; + } + + /** + * Set Connected Vertex as destination. + * + * @param vertex Vertex + */ + public ConnectedEdgeImpl setDestination(ConnectedVertexImpl vertex) { + destination = vertex; + return this; + } + + /** + * Disconnect source Connected Vertex. + */ + public void disconnectSource() { + if (source != null) { + source.removeOutput(this); + source = null; + } + } + + /** + * Disconnect destination Connected Vertex. + */ + public void disconnectDestination() { + if (destination != null) { + destination.removeInput(this); + destination = null; + } + } + + /** + * Disconnect both source and destination Connected Vertices. + */ + public void disconnect() { + disconnectSource(); + disconnectDestination(); + } + + /** + * Set associated Edge to this Connected Edge. + * + * @param edge Edge + */ + public ConnectedEdgeImpl setEdge(Edge edge) { + this.edge = edge; + return this; + } + + @Override + public @NonNull Long getKey() { + return this.ceid; + } + + @Override + public ConnectedVertex getSource() { + return this.source; + } + + @Override + public ConnectedVertex getDestination() { + return this.destination; + } + + @Override + public Edge getEdge() { + return this.edge; + } + + /** + * Returns the name of the associated Edge if set or the interface address otherwise. + * + * @return Edge name or interface address + */ + @Override + public String toString() { + if (edge == null) { + return "Null"; + } + if (edge.getName() != null) { + return edge.getName(); + } + if (edge.getEdgeAttributes() != null) { + if (edge.getEdgeAttributes().getLocalAddress() != null) { + return edge.getEdgeAttributes().getLocalAddress().toString(); + } + if (edge.getEdgeAttributes().getLocalIdentifier() != null) { + return edge.getEdgeAttributes().getLocalIdentifier().toString(); + } + } + return "Unknown Edge"; + } +} diff --git a/graph/graph-impl/src/main/java/org/opendaylight/graph/impl/ConnectedGraphImpl.java b/graph/graph-impl/src/main/java/org/opendaylight/graph/impl/ConnectedGraphImpl.java new file mode 100644 index 0000000000..8e356b5c5d --- /dev/null +++ b/graph/graph-impl/src/main/java/org/opendaylight/graph/impl/ConnectedGraphImpl.java @@ -0,0 +1,330 @@ +/* + * Copyright (c) 2019 Orange. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.graph.impl; + +import static com.google.common.base.Preconditions.checkArgument; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import org.eclipse.jdt.annotation.NonNull; +import org.opendaylight.graph.ConnectedEdge; +import org.opendaylight.graph.ConnectedGraph; +import org.opendaylight.graph.ConnectedVertex; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpPrefix; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv4Prefix; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv6Prefix; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.graph.rev191125.graph.topology.Graph; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.graph.rev191125.graph.topology.graph.Edge; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.graph.rev191125.graph.topology.graph.EdgeKey; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.graph.rev191125.graph.topology.graph.Prefix; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.graph.rev191125.graph.topology.graph.Vertex; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.graph.rev191125.graph.topology.graph.VertexKey; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * This Class implements the Connected Graph for path computation algorithms. + * + * @author Olivier Dugeon + * @author Philippe Niger + */ + + +public class ConnectedGraphImpl implements ConnectedGraph { + + private static final Logger LOG = LoggerFactory.getLogger(ConnectedGraphImpl.class); + + /* List of Connected Vertics that composed this Connected Graph */ + private final HashMap vertices = new HashMap<>(); + + /* List of Connected Edges that composed this Connected Graph */ + private final HashMap edges = new HashMap<>(); + + /* List of IP prefix attached to Vertices */ + private final HashMap prefixes = new HashMap<>(); + + /* Reference to the non connected Graph stored in DataStore */ + private Graph graph; + + /* Reference to Graph Model Server to store corresponding graph in DataStore */ + private final ConnectedGraphServer connectedGraphServer; + + public ConnectedGraphImpl(Graph newGraph, ConnectedGraphServer server) { + this.graph = newGraph; + createConnectedGraph(); + this.connectedGraphServer = server; + } + + /** + * Transform the associated Graph in a Connected Graph. This method will automatically create associated Connected + * Vertices, from the Graph Vertices, Connected Edges, from the Graph Edges and Prefix from the Graph Prefix. + * + */ + private void createConnectedGraph() { + if (this.graph == null) { + return; + } + /* Add all vertices */ + if (this.graph.getVertex() != null) { + for (Vertex vertex : this.graph.getVertex()) { + ConnectedVertexImpl cvertex = new ConnectedVertexImpl(vertex); + vertices.put(cvertex.getKey(), cvertex); + } + } + /* Add all edges */ + if (this.graph.getEdge() != null) { + for (Edge edge : this.graph.getEdge()) { + ConnectedEdgeImpl cedge = new ConnectedEdgeImpl(edge); + edges.put(cedge.getKey(), cedge); + } + } + /* Add all prefixes */ + if (this.graph.getPrefix() != null) { + for (Prefix prefix : this.graph.getPrefix()) { + ConnectedVertexImpl cvertex = vertices.get(prefix.getVertexId().longValue()); + if (cvertex != null) { + cvertex.addPrefix(prefix); + } + prefixes.putIfAbsent(prefix.getPrefix(), prefix); + } + } + } + + /** + * Return Connected Vertex if it exists or create a new one. + * + * @param key Unique Vertex Key identifier + * @return new or existing Connected Vertex + */ + private ConnectedVertexImpl updateConnectedVertex(@NonNull Long key) { + checkArgument(key != 0, "Provided Vertex Key must not be equal to 0"); + ConnectedVertexImpl vertex = vertices.get(key); + if (vertex == null) { + vertex = new ConnectedVertexImpl(key); + vertices.put(key, vertex); + } + return vertex; + } + + /** + * Return Connected Edge if it exist or create a new one. + * + * @param key Unique Edge Key identifier + * @return new or existing Connected Edge + */ + private ConnectedEdgeImpl updateConnectedEdge(@NonNull Long key) { + checkArgument(key != 0, "Provided Edge Key must not be equal to 0"); + ConnectedEdgeImpl edge = edges.get(key); + if (edge == null) { + edge = new ConnectedEdgeImpl(key); + edges.put(edge.getKey(), edge); + } + return edge; + } + + /** + * Connect source and destination Connected Vertices with the given Connected Edge. + * + * @param srcVertex Source Connected Vertex + * @param dstVertex Destination Connected Vertex + * @param edge Connected Edge + */ + private void connectVertices(ConnectedVertexImpl srcVertex, ConnectedVertexImpl dstVertex, ConnectedEdgeImpl edge) { + if (edge != null) { + edge.setSource(srcVertex); + edge.setDestination(dstVertex); + } + if (srcVertex != null) { + srcVertex.addOutput(edge); + } + if (dstVertex != null) { + dstVertex.addInput(edge); + } + } + + @Override + public Graph getGraph() { + return this.graph; + } + + @Override + public List getVertices() { + return new ArrayList(this.vertices.values()); + } + + @Override + public ConnectedVertex getConnectedVertex(@NonNull Long key) { + return vertices.get(key); + } + + @Override + public ConnectedVertex getConnectedVertex(IpAddress address) { + IpPrefix prefix = null; + if (address.getIpv4Address() != null) { + prefix = new IpPrefix(new Ipv4Prefix(address.getIpv4Address().getValue() + "/32")); + } + if (address.getIpv6Address() != null) { + prefix = new IpPrefix(new Ipv6Prefix(address.getIpv6Address().getValue() + "/128")); + } + if (prefix != null && prefixes.containsKey(prefix)) { + long key = prefixes.get(prefix).getVertexId().longValue(); + return vertices.get(key); + } else { + return null; + } + } + + @Override + public int getVerticesSize() { + return vertices.size(); + } + + @Override + public List getEdges() { + return new ArrayList(this.edges.values()); + } + + @Override + public ConnectedEdge getConnectedEdge(@NonNull Long key) { + return edges.get(key); + } + + @Override + public ConnectedEdge getConnectedEdge(IpAddress address) { + for (ConnectedEdge cedge : edges.values()) { + if (cedge.getEdge() == null) { + continue; + } + if (cedge.getEdge().getEdgeAttributes().getLocalAddress().equals(address)) { + return cedge; + } + } + return null; + } + + @Override + public int getEdgesSize() { + return edges.size(); + } + + @Override + public List getPrefixes() { + return new ArrayList(this.prefixes.values()); + } + + @Override + public Prefix getPrefix(IpPrefix prefix) { + return this.prefixes.get(prefix); + } + + @Override + public ConnectedVertex addVertex(Vertex vertex) { + checkArgument(vertex != null, "Provided Vertex is a null object"); + ConnectedVertexImpl cvertex = updateConnectedVertex(vertex.getVertexId().longValue()); + Vertex old = cvertex.getVertex(); + this.connectedGraphServer.addVertex(this.graph, vertex, old); + cvertex.setVertex(vertex); + return cvertex; + } + + @Override + public void deleteVertex(VertexKey key) { + checkArgument(key != null, "Provided Vertex Key is a null object"); + ConnectedVertexImpl cvertex = vertices.get(key.getVertexId().longValue()); + if (cvertex != null) { + cvertex.disconnect(); + vertices.remove(cvertex.getKey()); + this.connectedGraphServer.deleteVertex(this.graph, cvertex.getVertex()); + cvertex.setVertex(null); + } + } + + @Override + public ConnectedEdge addEdge(Edge edge) { + checkArgument(edge != null, "Provided Edge is a null object"); + ConnectedEdgeImpl cedge = updateConnectedEdge(edge.getEdgeId().longValue()); + Edge old = cedge.getEdge(); + if (old == null) { + ConnectedVertexImpl source = null; + ConnectedVertexImpl destination = null; + if (edge.getLocalVertexId() != null) { + source = updateConnectedVertex(edge.getLocalVertexId().longValue()); + } + if (edge.getRemoteVertexId() != null) { + destination = updateConnectedVertex(edge.getRemoteVertexId().longValue()); + } + connectVertices(source, destination, cedge); + } + this.connectedGraphServer.addEdge(this.graph, edge, old); + cedge.setEdge(edge); + return cedge; + } + + @Override + public void deleteEdge(EdgeKey key) { + checkArgument(key != null, "Provided Edge Key is a null object"); + ConnectedEdgeImpl cedge = edges.get(key.getEdgeId().longValue()); + if (cedge != null) { + this.connectedGraphServer.deleteEdge(this.graph, cedge.getEdge()); + cedge.disconnect(); + edges.remove(cedge.getKey()); + cedge.setEdge(null); + } + } + + @Override + public void addPrefix(Prefix prefix) { + checkArgument(prefix != null, "Provided Prefix is a null object"); + ConnectedVertexImpl cvertex = updateConnectedVertex(prefix.getVertexId().longValue()); + cvertex.addPrefix(prefix); + prefixes.putIfAbsent(prefix.getPrefix(), prefix); + this.connectedGraphServer.addPrefix(this.graph, prefix); + } + + @Override + public void deletePrefix(IpPrefix ippfx) { + checkArgument(ippfx != null, "Provided Prefix is a null object"); + Prefix prefix = prefixes.get(ippfx); + if (prefix != null) { + ConnectedVertexImpl cvertex = vertices.get(prefix.getVertexId().longValue()); + if (cvertex != null) { + cvertex.removePrefix(prefix); + } + prefixes.remove(prefix.getPrefix()); + this.connectedGraphServer.deletePrefix(this.graph, prefix); + } + } + + @Override + public void clear() { + LOG.info("Reset Connected Graph({})", graph.getName()); + this.vertices.clear(); + this.edges.clear(); + this.prefixes.clear(); + this.connectedGraphServer.clearGraph(this.graph); + this.graph = null; + } + + @Override + public String getSummary() { + return vertices.size() + "/" + edges.size() + "/" + prefixes.size(); + } + + /** + * Returns the name of the associated Graph. + * + * @return Graph name + */ + @Override + public String toString() { + return this.graph.getName(); + } +} diff --git a/graph/graph-impl/src/main/java/org/opendaylight/graph/impl/ConnectedGraphServer.java b/graph/graph-impl/src/main/java/org/opendaylight/graph/impl/ConnectedGraphServer.java new file mode 100644 index 0000000000..0ae34caca9 --- /dev/null +++ b/graph/graph-impl/src/main/java/org/opendaylight/graph/impl/ConnectedGraphServer.java @@ -0,0 +1,450 @@ +/* + * Copyright (c) 2019 Orange. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.graph.impl; + +import static java.util.Objects.requireNonNull; + +import com.google.common.base.Preconditions; +import com.google.common.util.concurrent.FluentFuture; +import com.google.common.util.concurrent.FutureCallback; +import com.google.common.util.concurrent.MoreExecutors; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import org.opendaylight.graph.ConnectedGraph; +import org.opendaylight.graph.ConnectedGraphProvider; +import org.opendaylight.mdsal.binding.api.DataBroker; +import org.opendaylight.mdsal.binding.api.ReadWriteTransaction; +import org.opendaylight.mdsal.binding.api.Transaction; +import org.opendaylight.mdsal.binding.api.TransactionChain; +import org.opendaylight.mdsal.binding.api.TransactionChainListener; +import org.opendaylight.mdsal.binding.api.WriteTransaction; +import org.opendaylight.mdsal.common.api.CommitInfo; +import org.opendaylight.mdsal.common.api.LogicalDatastoreType; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.graph.rev191125.GraphTopology; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.graph.rev191125.GraphTopologyBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.graph.rev191125.graph.topology.Graph; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.graph.rev191125.graph.topology.Graph.DomainScope; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.graph.rev191125.graph.topology.GraphBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.graph.rev191125.graph.topology.GraphKey; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.graph.rev191125.graph.topology.graph.Edge; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.graph.rev191125.graph.topology.graph.Prefix; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.graph.rev191125.graph.topology.graph.Vertex; +import org.opendaylight.yangtools.yang.binding.DataObject; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * This Class Implements the DataStoreService interface providing the methods + * required to manage the network representation elements in the datastore. + * + * + * @author Olivier Dugeon + * @author Philippe Niger + */ + +public class ConnectedGraphServer implements ConnectedGraphProvider, TransactionChainListener { + + private static final Logger LOG = LoggerFactory.getLogger(ConnectedGraphServer.class); + private final DataBroker dataBroker; + private final InstanceIdentifier graphTopologyIdentifier; + private TransactionChain chain = null; + + private final HashMap graphs = new HashMap<>(); + + public ConnectedGraphServer(final DataBroker dataBroker) { + LOG.info("Create Graph Model Server"); + this.dataBroker = dataBroker; + this.graphTopologyIdentifier = InstanceIdentifier.builder(GraphTopology.class).build(); + } + + /** + * Initialization of the Graph Model Server. This method is called through the blueprint. + */ + public void init() { + initTransactionChain(); + initOperationalGraphModel(); + } + + /** + * Reset a transaction chain by closing the current chain and starting a new one. + */ + private synchronized void initTransactionChain() { + LOG.debug("Initializing transaction chain for Graph Model Server {}", this); + Preconditions.checkState(this.chain == null, "Transaction chain has to be closed before being initialized"); + this.chain = this.dataBroker.createMergingTransactionChain(this); + } + + /** + * Initialize GraphModel tree at Data Store top-level. + */ + private synchronized void initOperationalGraphModel() { + requireNonNull(this.chain, "A valid transaction chain must be provided."); + final WriteTransaction trans = this.chain.newWriteOnlyTransaction(); + LOG.info("Create Graph Model at top level in Operational DataStore: {}", this.graphTopologyIdentifier); + trans.put(LogicalDatastoreType.OPERATIONAL, this.graphTopologyIdentifier, + new GraphTopologyBuilder().setGraph(Collections.emptyList()).build()); + trans.put(LogicalDatastoreType.CONFIGURATION, this.graphTopologyIdentifier, + new GraphTopologyBuilder().setGraph(Collections.emptyList()).build()); + LOG.info("Create Graph Model at top level in Configuration DataStore: {}", this.graphTopologyIdentifier); + trans.commit().addCallback(new FutureCallback() { + @Override + public void onSuccess(final CommitInfo result) { + LOG.trace("Transaction {} committed successfully", trans.getIdentifier()); + } + + @Override + public void onFailure(final Throwable throwable) { + LOG.error("Failed to initialize GraphModel {} (transaction {}) by listener {}", + ConnectedGraphServer.this.graphTopologyIdentifier, trans.getIdentifier(), + ConnectedGraphServer.this, throwable); + } + }, MoreExecutors.directExecutor()); + } + + /** + * Destroy the current operational topology data. Note a valid transaction must be provided. + */ + private synchronized FluentFuture destroyOperationalGraphModel() { + requireNonNull(this.chain, "A valid transaction chain must be provided."); + final WriteTransaction trans = this.chain.newWriteOnlyTransaction(); + trans.delete(LogicalDatastoreType.OPERATIONAL, this.graphTopologyIdentifier); + trans.delete(LogicalDatastoreType.CONFIGURATION, this.graphTopologyIdentifier); + final FluentFuture future = trans.commit(); + future.addCallback(new FutureCallback() { + @Override + public void onSuccess(final CommitInfo result) { + LOG.trace("Operational GraphModel removed {}", ConnectedGraphServer.this.graphTopologyIdentifier); + } + + @Override + public void onFailure(final Throwable throwable) { + LOG.error("Unable to reset operational GraphModel {} (transaction {})", + ConnectedGraphServer.this.graphTopologyIdentifier, trans.getIdentifier(), throwable); + } + }, MoreExecutors.directExecutor()); + + /* Clear Connected Graph */ + for (ConnectedGraph graph : graphs.values()) { + ((ConnectedGraphImpl) graph).clear(); + } + return future; + } + + /** + * Destroy the current transaction chain. + */ + private synchronized void destroyTransactionChain() { + if (this.chain != null) { + LOG.debug("Destroy transaction chain for GraphModel {}", this); + this.chain = null; + } + } + + /** + * Reset the transaction chain only so that the PingPong transaction chain + * will become usable again. However, there will be data loss if we do not + * apply the previous failed transaction again + */ + protected synchronized void resetTransactionChain() { + LOG.debug("Resetting transaction chain for Graph builder"); + destroyTransactionChain(); + initTransactionChain(); + } + + /** + * Remove the Operation Graph Model and destroy the transaction chain. + */ + public void close() { + destroyOperationalGraphModel(); + destroyTransactionChain(); + } + + /** + * DataStore Instance Identifier creation for the various Graph components. + */ + private InstanceIdentifier getGraphInstanceIdentifier(String name) { + GraphKey graphKey = new GraphKey(name); + return this.graphTopologyIdentifier.child(Graph.class, graphKey); + } + + private InstanceIdentifier getVertexInstanceIdentifier(Graph graph, final Vertex vertex) { + return this.graphTopologyIdentifier.child(Graph.class, graph.key()).child(Vertex.class, vertex.key()); + } + + private InstanceIdentifier getEdgeInstanceIdentifier(Graph graph, final Edge edge) { + return this.graphTopologyIdentifier.child(Graph.class, graph.key()).child(Edge.class, edge.key()); + } + + private InstanceIdentifier getPrefixInstanceIdentifier(Graph graph, final Prefix prefix) { + return this.graphTopologyIdentifier.child(Graph.class, graph.key()).child(Prefix.class, prefix.key()); + } + + /** + * Add Graph or Graph components to the Data Store. + * + * @param As a generic method, T must be a Graph, Vertex, Edge or Prefix. + * @param id Instance Identifier of the Data Object + * @param data Data Object (Graph, Vertex, Edge or Prefix) + * @param info Information to be logged + */ + private synchronized void addToDataStore(final InstanceIdentifier id, final T data, + final String info) { + final ReadWriteTransaction trans = this.chain.newReadWriteTransaction(); + trans.put(LogicalDatastoreType.OPERATIONAL, id, data); + trans.commit().addCallback(new FutureCallback() { + @Override + public void onSuccess(final CommitInfo result) { + LOG.info("GraphModel: {} has been published in operational datastore ", info); + } + + @Override + public void onFailure(final Throwable throwable) { + LOG.error("GrahModel: Cannot write {} to the operational datastore (transaction: {})", info, + trans.getIdentifier()); + } + }, MoreExecutors.directExecutor()); + } + + /** + * Update Graph components (Vertex, Edge or Prefix ) to the Data Store. Old value identified by its Instance ID + * will be remove first before adding the new value. + * + * @param As a generic method, T must be a Vertex, Edge or Prefix. + * @param id Instance Identifier of the Data Object + * @param data Data Object (Vertex, Edge or Prefix) + * @param old Instance Identifier of the previous version of the Data Object + * @param info Information to be logged + */ + private synchronized void updateToDataStore(final InstanceIdentifier id, final T data, + final InstanceIdentifier old, final String info) { + final ReadWriteTransaction trans = this.chain.newReadWriteTransaction(); + if (old != null) { + trans.delete(LogicalDatastoreType.OPERATIONAL, old); + } + trans.put(LogicalDatastoreType.OPERATIONAL, id, data); + trans.commit().addCallback(new FutureCallback() { + @Override + public void onSuccess(final CommitInfo result) { + LOG.info("GraphModel: {} has been published in operational datastore ", info); + } + + @Override + public void onFailure(final Throwable throwable) { + LOG.error("GrahModel: Cannot write {} to the operational datastore (transaction: {})", info, + trans.getIdentifier()); + } + }, MoreExecutors.directExecutor()); + } + + /** + * Remove Graph or Graph components to the Data Store. + * + * @param As a generic method, T must be a Graph, Vertex, Edge or Prefix. + * @param id Instance Identifier of the Data Object + * @param info Information to be logged + */ + private synchronized void removeFromDataStore(final InstanceIdentifier id, + final String info) { + final ReadWriteTransaction trans = this.chain.newReadWriteTransaction(); + trans.delete(LogicalDatastoreType.OPERATIONAL, id); + trans.commit().addCallback(new FutureCallback() { + @Override + public void onSuccess(final CommitInfo result) { + LOG.info("GraphModel: {} has been deleted in operational datastore ", info); + } + + @Override + public void onFailure(final Throwable throwable) { + LOG.error("GraphModel: Cannot delete {} to the operational datastore (transaction: {})", info, + trans.getIdentifier()); + } + }, MoreExecutors.directExecutor()); + } + + /** + * Clear Graph. This method is used by the Connected Graph to clear associated Graph. + * + * @param graph Graph associated to the Connected Graph + */ + public void clearGraph(Graph graph) { + Preconditions.checkArgument(graph != null, "Provided Graph is a null object"); + removeFromDataStore(getGraphInstanceIdentifier(graph.getName()), "Graph(" + graph.getName() + ")"); + } + + /** + * Add Vertex to existing Graph. Old vertex is remove first. This method is called when a Connected Vertex is + * created (See addVertex() method from ConnectedGraph Interface). + * + * @param graph Graph where the vertex will be stored + * @param vertex Vertex to be inserted in the graph + * @param old Old vertex when performing an update. Must be null for a simple addition + */ + public void addVertex(Graph graph, Vertex vertex, Vertex old) { + Preconditions.checkArgument(graph != null, "Provided Graph is a null object"); + Preconditions.checkArgument(vertex != null, "Provided Vertex is a null object"); + InstanceIdentifier oldId = null; + /* Remove old Vertex if it exists before storing the new Vertex */ + if (old != null) { + oldId = getVertexInstanceIdentifier(graph, old); + } + updateToDataStore(getVertexInstanceIdentifier(graph, vertex), vertex, oldId, + "Vertex(" + vertex.getName() + ")"); + } + + /** + * Remove Vertex to existing Graph. This method is called when a Connected Vertex is removed (See deleteVertex() + * method from ConnectedGraph Interface). + * + * @param graph Graph where the vertex is stored + * @param vertex Vertex to be removed + */ + public void deleteVertex(Graph graph, Vertex vertex) { + Preconditions.checkArgument(graph != null, "Provided Graph is a null object"); + Preconditions.checkArgument(vertex != null, "Provided Vertex is a null object"); + removeFromDataStore(getVertexInstanceIdentifier(graph, vertex), "Vertex(" + vertex.getName() + ")"); + } + + /** + * Add Edge to existing Graph. Old edge is remove first. This method is called when a Connected Edge is + * created (See addEdge() method from ConnectedGraph Interface). + * + * @param graph Graph where the edge will be stored + * @param edge Edge to be inserted in the graph + * @param old Old edge when performing an update. Must be null for a simple addition + */ + public void addEdge(Graph graph, Edge edge, Edge old) { + Preconditions.checkArgument(graph != null, "Provided Graph is a null object"); + Preconditions.checkArgument(edge != null, "Provided Edge is a null object"); + InstanceIdentifier oldId = null; + /* Remove old Edge if it exists before storing the new Edge */ + if (old != null) { + oldId = getEdgeInstanceIdentifier(graph, old); + } + updateToDataStore(getEdgeInstanceIdentifier(graph, edge), edge, oldId, "Edge(" + edge.getName() + ")"); + } + + /** + * Remove Edge to existing Graph. This method is called when a Connected Edge is removed (See deleteEdge() + * method from ConnectedGraph Interface). + * + * @param graph Graph where the edge is stored + * @param edge Edge to be removed + */ + public void deleteEdge(Graph graph, Edge edge) { + Preconditions.checkArgument(graph != null, "Provided Graph is a null object"); + Preconditions.checkArgument(edge != null, "Provided Edge is a null object"); + removeFromDataStore(getEdgeInstanceIdentifier(graph, edge), "Edge(" + edge.getName() + ")"); + } + + /** + * Add Prefix to existing Graph. This method is called when a Prefix is added to a Connected Vertex + * (See addPrefix() method from ConnectedGraph Interface). + * + * @param graph Graph where the prefix will be stored + * @param prefix Prefix to be interted in the graph + */ + public void addPrefix(Graph graph, Prefix prefix) { + Preconditions.checkArgument(graph != null, "Provided Graph is a null object"); + Preconditions.checkArgument(prefix != null, "Provided Prefix is a null object"); + addToDataStore(getPrefixInstanceIdentifier(graph, prefix), prefix, "Prefix(" + prefix.getPrefix() + ")"); + } + + /** + * Remove Prefix to existing Graph. This method is called when a Prefix is removed from a Connected Vertex + * (See deletePrefix() method from ConnectedGraph Interface). + * + * @param graph Graph where the prefix is stored + * @param prefix Prefix to be removed + */ + public void deletePrefix(Graph graph, Prefix prefix) { + Preconditions.checkArgument(graph != null, "Provided Graph is a null object"); + Preconditions.checkArgument(prefix != null, "Provided Prefix is a null object"); + removeFromDataStore(getPrefixInstanceIdentifier(graph, prefix), "Prefix(" + prefix.getPrefix() + ")"); + } + + @Override + public final synchronized void onTransactionChainFailed(final TransactionChain transactionChain, + final Transaction transaction, final Throwable cause) { + LOG.error("GraphModel builder for {} failed in transaction: {} ", this.graphTopologyIdentifier, + transaction != null ? transaction.getIdentifier() : null, cause); + } + + @Override + public final void onTransactionChainSuccessful(final TransactionChain transactionChain) { + LOG.info("GraphModel builder for {} shut down", this.graphTopologyIdentifier); + } + + @Override + public ArrayList getConnectedGraphs() { + return new ArrayList(this.graphs.values()); + } + + @Override + public ConnectedGraph getConnectedGraph(GraphKey key) { + return graphs.get(key); + } + + @Override + public ConnectedGraph getConnectedGraph(String name) { + return graphs.get(new GraphKey(name)); + } + + @Override + public Graph getGraph(GraphKey key) { + if (graphs.containsKey(key)) { + return graphs.get(key).getGraph(); + } else { + return null; + } + } + + @Override + public Graph getGraph(String name) { + return getGraph(new GraphKey(name)); + } + + @Override + public ConnectedGraph createConnectedGraph(String name, DomainScope scope) { + Graph graph = new GraphBuilder() + .setName(name) + .setDomainScope(scope) + .setEdge(Collections.emptyList()) + .setVertex(Collections.emptyList()) + .setPrefix(Collections.emptyList()) + .build(); + addToDataStore(getGraphInstanceIdentifier(name), graph, "Graph(" + name + ")"); + ConnectedGraphImpl cgraph = new ConnectedGraphImpl(graph, this); + graphs.put(graph.key(), cgraph); + return cgraph; + } + + @Override + public ConnectedGraph addGraph(Graph graph) { + Preconditions.checkArgument(graph != null, "Provided Graph is a null object"); + addToDataStore(getGraphInstanceIdentifier(graph.getName()), graph, "Graph(" + graph.getName() + ")"); + ConnectedGraphImpl cgraph = new ConnectedGraphImpl(graph, this); + graphs.put(graph.key(), cgraph); + return cgraph; + } + + @Override + public void deleteGraph(GraphKey key) { + Preconditions.checkArgument(key != null, "Provided Graph Key is a null object"); + ConnectedGraphImpl cgraph = graphs.remove(key); + /* + * Remove the corresponding Connected Graph which will delete the graph + * by calling clearGraph() method (see above) + */ + if (cgraph != null) { + cgraph.clear(); + } + } +} diff --git a/graph/graph-impl/src/main/java/org/opendaylight/graph/impl/ConnectedVertexImpl.java b/graph/graph-impl/src/main/java/org/opendaylight/graph/impl/ConnectedVertexImpl.java new file mode 100644 index 0000000000..49a36aad23 --- /dev/null +++ b/graph/graph-impl/src/main/java/org/opendaylight/graph/impl/ConnectedVertexImpl.java @@ -0,0 +1,222 @@ +/* + * Copyright (c) 2019 Orange. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.graph.impl; + +import static com.google.common.base.Preconditions.checkArgument; + +import java.util.ArrayList; +import java.util.List; +import org.eclipse.jdt.annotation.NonNull; +import org.opendaylight.graph.ConnectedEdge; +import org.opendaylight.graph.ConnectedVertex; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.graph.rev191125.graph.topology.graph.Edge; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.graph.rev191125.graph.topology.graph.Prefix; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.graph.rev191125.graph.topology.graph.Vertex; + +/** + * This Class implements the Connected Vertex used by the Connected Graph for path computation algorithms. + * + * @author Olivier Dugeon + * @author Philippe Niger + */ + +public class ConnectedVertexImpl implements ConnectedVertex { + + /* Reference to input and output Connected Edge within the Connected Graph */ + private ArrayList input = new ArrayList<>(); + private ArrayList output = new ArrayList<>(); + + /* List of Prefixes announced by this Vertex */ + private ArrayList prefixes = new ArrayList<>(); + + /* Reference to the Vertex of the standard Graph associated to the Connected Graph */ + private Vertex vertex = null; + + /* Connected Vertex Identifier */ + private Long cvid; + + public ConnectedVertexImpl(@NonNull Long key) { + checkArgument(key != 0, "Vertex Key must not be equal to 0"); + this.cvid = key; + this.vertex = null; + } + + public ConnectedVertexImpl(@NonNull Vertex vertex) { + checkArgument(vertex.getVertexId().longValue() != 0, "Vertex Key must not be equal to 0"); + this.cvid = vertex.getVertexId().longValue(); + this.vertex = vertex; + } + + /** + * When vertex is removed, we must disconnect all Connected Edges. + */ + void close() { + this.disconnect(); + } + + /** + * Set associated Vertex to this Connected Vertex. + * + * @param vertex Vertex + */ + public ConnectedVertexImpl setVertex(Vertex vertex) { + this.vertex = vertex; + return this; + } + + /** + * Add Connected Edge as input edge. + * + * @param edge Connected Edge + */ + public ConnectedVertexImpl addInput(ConnectedEdgeImpl edge) { + if (!input.contains(edge)) { + input.add(edge); + } + return this; + } + + /** + * Add Connected Edge as output edge. + * + * @param edge Connected Edge + */ + public ConnectedVertexImpl addOutput(ConnectedEdgeImpl edge) { + if (!output.contains(edge)) { + output.add(edge); + } + return this; + } + + /** + * Remove input Connected Edge. + * + * @param edge Connected Edge + */ + public ConnectedVertexImpl removeInput(ConnectedEdgeImpl edge) { + input.remove(edge); + return this; + } + + /** + * Remove output Connected Edge. + * + * @param edge Connected Edge + */ + public ConnectedVertexImpl removeOutput(ConnectedEdgeImpl edge) { + output.remove(edge); + return this; + } + + /** + * Disconnect all input and output Connected Edge. + */ + public void disconnect() { + for (ConnectedEdgeImpl edge : input) { + edge.disconnectDestination(); + } + for (ConnectedEdgeImpl edge : output) { + edge.disconnectSource(); + } + } + + /** + * Add Prefix to this Connected Vertex. + * + * @param prefix Prefix + */ + public ConnectedVertexImpl addPrefix(Prefix prefix) { + if (!prefixes.contains(prefix)) { + prefixes.add(prefix); + } + return this; + } + + /** + * Remove Prefix. + * + * @param prefix Prefix + */ + public void removePrefix(Prefix prefix) { + if (prefixes.contains(prefix)) { + prefixes.remove(prefix); + } + } + + @Override + public Long getKey() { + return this.cvid; + } + + @Override + public Vertex getVertex() { + return this.vertex; + } + + @Override + public List getEdgeTo(Long dstRid) { + ArrayList edgeList = new ArrayList(); + for (ConnectedEdge edge : output) { + if (edge.getDestination().getKey().equals(dstRid)) { + edgeList.add(edge); + } + } + return edgeList; + } + + @Override + public List getInputEdges() { + ArrayList edgeList = new ArrayList(); + for (ConnectedEdge edge : input) { + edgeList.add(edge.getEdge()); + } + return edgeList; + } + + @Override + public List getInputConnectedEdges() { + return new ArrayList(this.input); + } + + @Override + public List getOutputEdges() { + ArrayList edgeList = new ArrayList(); + for (ConnectedEdge edge : output) { + edgeList.add(edge.getEdge()); + } + return edgeList; + } + + @Override + public List getOutputConnectedEdges() { + return new ArrayList(this.output); + } + + @Override + public List getPrefixes() { + return this.prefixes; + } + + /** + * Return the name of the associated Vertex if set or the router-id otherwise. + * + * @return Vertex name or router-id + */ + @Override + public String toString() { + if (vertex == null) { + return "Null"; + } + if (vertex.getName() != null) { + return vertex.getName(); + } else { + return vertex.getRouterId().toString(); + } + } +} diff --git a/graph/graph-impl/src/main/java/org/opendaylight/graph/impl/GraphListener.java b/graph/graph-impl/src/main/java/org/opendaylight/graph/impl/GraphListener.java new file mode 100644 index 0000000000..5e66e29fdf --- /dev/null +++ b/graph/graph-impl/src/main/java/org/opendaylight/graph/impl/GraphListener.java @@ -0,0 +1,156 @@ +/* + * Copyright (c) 2019 Orange. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.graph.impl; + +import static com.google.common.base.Preconditions.checkState; + +import java.util.Collection; +import org.opendaylight.graph.ConnectedGraph; +import org.opendaylight.graph.ConnectedGraphProvider; +import org.opendaylight.mdsal.binding.api.DataBroker; +import org.opendaylight.mdsal.binding.api.DataObjectModification; +import org.opendaylight.mdsal.binding.api.DataTreeChangeListener; +import org.opendaylight.mdsal.binding.api.DataTreeIdentifier; +import org.opendaylight.mdsal.binding.api.DataTreeModification; +import org.opendaylight.mdsal.common.api.LogicalDatastoreType; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.graph.rev191125.GraphTopology; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.graph.rev191125.graph.topology.Graph; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.graph.rev191125.graph.topology.GraphKey; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.graph.rev191125.graph.topology.graph.Edge; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.graph.rev191125.graph.topology.graph.Prefix; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.graph.rev191125.graph.topology.graph.Vertex; +import org.opendaylight.yangtools.concepts.ListenerRegistration; +import org.opendaylight.yangtools.yang.binding.DataObject; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * This Class Implements the DataStoreService interface providing the methods required to manage the network + * representation elements in the Data Store. + * + * + * @author Olivier Dugeon + * @author Philippe Niger + */ + +public class GraphListener implements DataTreeChangeListener { + + private static final Logger LOG = LoggerFactory.getLogger(GraphListener.class); + private final DataBroker dataBroker; + private final InstanceIdentifier graphIdentifier; + private final ConnectedGraphProvider graphProvider; + private ListenerRegistration listenerRegistration = null; + + public GraphListener(final DataBroker dataBroker, final ConnectedGraphProvider provider) { + this.dataBroker = dataBroker; + this.graphIdentifier = InstanceIdentifier.builder(GraphTopology.class).child(Graph.class).build(); + this.graphProvider = provider; + LOG.info("Graph Model Listener started"); + } + + /** + * Initialization of the Graph Topology Listener. This method is called through the blueprint. + */ + public void init() { + checkState(this.listenerRegistration == null, "Graph Listener has been registered before"); + final DataTreeIdentifier treeId = DataTreeIdentifier.create(LogicalDatastoreType.CONFIGURATION, + graphIdentifier); + this.listenerRegistration = this.dataBroker.registerDataTreeChangeListener(treeId, this); + LOG.info("Registered listener {} on Graph Model at {}", this, this.graphIdentifier); + } + + /** + * Close this Listener. + */ + public void close() { + if (this.listenerRegistration != null) { + LOG.debug("Unregistered listener {} on Graph", this); + this.listenerRegistration.close(); + this.listenerRegistration = null; + } + } + + /** + * Parse Sub Tree modification. This method is called with the Modified Children from + * the Data Tree Modification root.This method is necessary as the getModificationType() method returns + * SUBTREE_MODIFIED only when Data Object is already present in the Data Store. Thus, this is indication is only + * relevant for deletion not for insertion where WRITE modification type is return even if it concerns a child. + * + * @param cgraph Connected Graph where children Data Object must insert or remove + * @param children List of children (Vertex, Edge or Prefix) + */ + private void parseSubTree(ConnectedGraph cgraph, + Collection> children) { + for (DataObjectModification child : children) { + DataObject value; + switch (child.getModificationType()) { + case DELETE: + value = child.getDataBefore(); + if (value instanceof Vertex) { + cgraph.deleteVertex(((Vertex )value).key()); + } + if (value instanceof Edge) { + cgraph.deleteEdge(((Edge )value).key()); + } + if (value instanceof Prefix) { + cgraph.deletePrefix(((Prefix )value).getPrefix()); + } + break; + case SUBTREE_MODIFIED: + case WRITE: + value = child.getDataAfter(); + if (value instanceof Vertex) { + cgraph.addVertex((Vertex )value); + } + if (value instanceof Edge) { + cgraph.addEdge((Edge )value); + } + if (value instanceof Prefix) { + cgraph.addPrefix((Prefix )value); + } + break; + default: + break; + } + } + } + + @Override + public void onDataTreeChanged(final Collection> changes) { + for (DataTreeModification change : changes) { + DataObjectModification root = change.getRootNode(); + GraphKey key = change.getRootPath().getRootIdentifier().firstKeyOf(Graph.class); + switch (root.getModificationType()) { + case DELETE: + graphProvider.deleteGraph(key); + break; + case SUBTREE_MODIFIED: + /* getModificationType() returns SUBTREE_MODIFIED only when Data Object is already present in the + * Data Store, thus, only for deletion. Thus, to insert children, we must used parseSubTree() + * method (See above). This method is called only when the graph already exists. + */ + case WRITE: + /* First look if the Graph was not already configured */ + ConnectedGraph cgraph = this.graphProvider.getConnectedGraph(key); + if (cgraph == null) { + graphProvider.addGraph(root.getDataAfter()); + } else { + /* Graph exist, process Children */ + parseSubTree(cgraph, root.getModifiedChildren()); + } + break; + default: + break; + } + } + } + +} + diff --git a/graph/graph-impl/src/main/resources/OSGI-INF/blueprint/graph-server.xml b/graph/graph-impl/src/main/resources/OSGI-INF/blueprint/graph-server.xml new file mode 100644 index 0000000000..beee762522 --- /dev/null +++ b/graph/graph-impl/src/main/resources/OSGI-INF/blueprint/graph-server.xml @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + + + diff --git a/graph/pom.xml b/graph/pom.xml new file mode 100644 index 0000000000..d9fc15e1d1 --- /dev/null +++ b/graph/pom.xml @@ -0,0 +1,37 @@ + + + + 4.0.0 + + org.opendaylight.odlparent + odlparent-lite + 6.0.4 + + + + org.opendaylight.bgpcep + graph + 0.14.0-SNAPSHOT + pom + Graph Network Topology components + ${project.artifactId} + + + graph-artifacts + graph-api + graph-impl + + + + true + true + + diff --git a/pom.xml b/pom.xml index 7ec3698c3b..729e847c74 100644 --- a/pom.xml +++ b/pom.xml @@ -42,6 +42,7 @@ bgp bmp + graph pcep programming rsvp