Add Path Manager to PCE Server Provider 76/97076/18
authorOlivier Dugeon <olivier.dugeon@orange.com>
Tue, 3 Aug 2021 17:24:46 +0000 (19:24 +0200)
committerRobert Varga <nite@hq.sk>
Sun, 20 Mar 2022 17:30:27 +0000 (17:30 +0000)
The goal of the new Path Manager is to improve the PCE Server.
Its main objectives are as follow:
- Ease the management of LSPs, in particular to update them without
  the need to manually compute a path
- Allow the possibility to provide an ERO to reported LSPs without
  a valid path
- Provide persistency of Initiated and Updated LSPs accross PCC and or
  PCE reboot

JIRA: BGPCEP-979
Change-Id: I0ea42958c5e46511c83d5b4c0c23bdc0a5321ed4
Co-authored-by: Guillaume Lambert <guillaume.lambert@orange.com>
Signed-off-by: Olivier Dugeon <olivier.dugeon@orange.com>
18 files changed:
docs/pcep/index.rst
docs/pcep/pcep-user-guide-path-computation.rst [deleted file]
docs/pcep/pcep-user-guide-pce-server.rst [new file with mode: 0644]
features/pcep/odl-bgpcep-pcep-server/pom.xml
pcep/server/server-api/pom.xml
pcep/server/server-api/src/main/java/org/opendaylight/bgpcep/pcep/server/PathComputation.java
pcep/server/server-api/src/main/java/org/opendaylight/bgpcep/pcep/server/PceServerProvider.java
pcep/server/server-api/src/main/yang/pcep-server.yang [new file with mode: 0644]
pcep/server/server-provider/pom.xml
pcep/server/server-provider/src/main/java/org/opendaylight/bgpcep/pcep/server/provider/DefaultPceServerProvider.java
pcep/server/server-provider/src/main/java/org/opendaylight/bgpcep/pcep/server/provider/ManagedTeNode.java [new file with mode: 0644]
pcep/server/server-provider/src/main/java/org/opendaylight/bgpcep/pcep/server/provider/ManagedTePath.java [new file with mode: 0644]
pcep/server/server-provider/src/main/java/org/opendaylight/bgpcep/pcep/server/provider/PathComputationImpl.java
pcep/server/server-provider/src/main/java/org/opendaylight/bgpcep/pcep/server/provider/PathManagerListener.java [new file with mode: 0644]
pcep/server/server-provider/src/main/java/org/opendaylight/bgpcep/pcep/server/provider/PathManagerProvider.java [new file with mode: 0644]
pcep/server/server-provider/src/main/java/org/opendaylight/bgpcep/pcep/server/provider/PcepTopologyListener.java [new file with mode: 0644]
pcep/topology/topology-provider/src/main/java/org/opendaylight/bgpcep/pcep/topology/provider/PCEPTopologySessionListener.java
pcep/topology/topology-provider/src/main/java/org/opendaylight/bgpcep/pcep/topology/provider/ServerSessionManager.java

index 109ce2e5f7d8cda962b796c1db59c7ca0277aaee..19237351bd3911549e8a4a57d21f91babc9a3489 100644 (file)
@@ -13,7 +13,7 @@ The user should learn about PCEP basic concepts, supported capabilities, configu
    pcep-user-guide-supported-capabilities
    pcep-user-guide-running-pcep
    pcep-user-guide-active-stateful-pce
-   pcep-user-guide-path-computation
+   pcep-user-guide-pce-server
    pcep-user-guide-session-statistics
    pcep-user-guide-cli
    pcep-user-guide-test-tools
diff --git a/docs/pcep/pcep-user-guide-path-computation.rst b/docs/pcep/pcep-user-guide-path-computation.rst
deleted file mode 100644 (file)
index 610f149..0000000
+++ /dev/null
@@ -1,134 +0,0 @@
-.. _pcep-user-guide-path-computation:
-
-Path Computation Server
-=======================
-
-This section describes how to use Path Computation Server bundle in
-conjunction with the Path Computation Algorithm and Graph plugins. This Server
-provides a full PCE component that fully supports RFC5440 including PcRequest
-PcResponse messages exchanges from a PCC requesting a valid path to the PCE.
-
-.. contents:: Contents
-   :depth: 2
-   :local:
-
-Installation
-^^^^^^^^^^^^
-
-Check that the feature ``features-algo`` is installed. Normally it will be
-installed with the pcep feature ``features-pcep``. Otherwise, install it
-with the following command:
-
-.. code-block:: console
-
-    feature:install features-algo
-
-Graph Setup
-^^^^^^^^^^^
-
-The Path Computation Server uses the Path Computation Algorithm plugin which
-needs a graph to be able to compute constrained paths. Thus, a valid graph must
-be provided manually or automatically.
-
-Manual activation
-'''''''''''''''''
-
-Create a new graph with the Rest API ``Create Graph``:
-
-.. code-block:: console
-
-    PUT: restconf/config/graph:graph-topology
-
-Refer to Graph documentation for details.
-
-There is a restriction on the name of the graph due to the Path Computation
-Algorithm integration. It must be started by **"ted://"** string in order to
-be learn automatically by the Path Computation Server bundle.
-
-Note that this kind of graph remains static. Thus, resources, mostly bandwidth,
-are not updated after deploying some RSVP-TE tunnels which consume bandwidth.
-
-BGP-LS activation
-'''''''''''''''''
-
-To achieve better experience, notably in conjunction with RSVP-TE and in order
-to work on an up-to-date graph, an integration is provided with the BGP Link
-State protocol. This allows to automatically fulfil a graph with network
-traffic engineering information conveyed by the BGP-LS protocol. The resources,
-mostly bandwidth, are automatically updated in the graph after deploying
-an RSVP-TE tunnel. Note that this is not the case with Segment Routing.
-
-For that purpose, just setup a BGP peering with a router that is BGP-LS
-speaker and report traffic engineering network topology from IS-IS-TE or
-OSPF-TE routing protocol. Refer to BGP documentation for the detail about
-how to setup a BGP peering with Link-State family.
-
-Once done, verify that the graph is correctly fulfil with the Rest API:
-
-.. code-block:: console
-
-    GET: restconf/operational/graph:graph-topology
-
-Usage
-^^^^^
-
-There is two ways to use the Path Computation Server: through PcRequest and
-with PcInitiate.
-
-With PcRequest, just create a new tunnel on the router with an external PCE
-for path computation. Once PcRequest received, the Path Computation Server
-launches the path computation algorithm with requested parameters and in turn
-sends back to the PCC a PcResponse message with the computed path in the ERO.
-A NO-PATH object is returned in case of failure with the reason (e.g. source
-or destination unknown, constraints not met ...). Check on the router that
-the tunnel is up and running. Wireshark capture will help to determine
-if the exchanges between the PCC and the PCE went well. Setting log debug for
-algo and pcep plugins and looking to the log will also ease debugging.
-
-With PcInitiate message, just use the PCEP Rest API to setup an LSP
-
-.. code-block:: console
-
-    POST: /restconf/operations/network-topology-pcep:add-lsp
-
-by omitting the ERO Object. Indeed, an automatic call to the Path Computation
-Algorithm will be triggered when the ERO is absent or empty with the given
-end-points and metrics objects as input paramters. Address family is
-automatically deduced from the IP address family of the end-points object.
-The same behaviour applies for Segment Routing: just add the *PST=1* indication
-in the json or xml payload will force the address family of path computation
-to Segment Routing.
-
-To verify the result, just check the LSP-Database. The new LSP must have an
-ERO automatically computed as well as an RRO. Again, setting log debug for algo
-and pcep plugins and looking to the log will also help to verify that all is
-conform as expected.
-
-Known limitations
-^^^^^^^^^^^^^^^^^
-
-As the Path Computation Server is in its initial release, there are some
-limitations mentioned hereinafter:
-
-* Following PCEP Objects that may be present in the PcRequest message are not
-  yet supported, and right now, ignored:
-
-  * Include Route Object (IRO)
-  * Exclude Route Object (XRO)
-  * Objective Function (OF)
-
-* LSP-Update Rest API with an empty ERO will not trigger Path Computation
-  Algorithm. Use Path Computation Algorithm Rest API to get a new path, and
-  then use the LSP-Update Rest API as usual with the computed ERO.
-
-* For Segment Routing, ERO is provided with Node SID for NAI and SID index.
-
-* Due to the integration with BGP-LS, the graph name must start with *ted://*
-  tag in order to be automatically used by the pcep plugin.
-
-* For Segment Routing, as network resources are not updated due to the lack
-  of signaling, the resources consumed by the new segment path are not updated
-  in the graph.
-
-All these limitations will be solved in future releases.
-
diff --git a/docs/pcep/pcep-user-guide-pce-server.rst b/docs/pcep/pcep-user-guide-pce-server.rst
new file mode 100644 (file)
index 0000000..281b62a
--- /dev/null
@@ -0,0 +1,395 @@
+.. _pcep-user-guide-pce-server:
+
+Path Computation Element Server
+===============================
+
+This section describes how to use Path Computation Element (PCE) Server bundle
+in conjunction with the Path Computation Algorithm and Graph plugins. This
+provides a full PCE component that fully supports RFC5440 including PcRequest
+PcResponse messages exchanges from a PCC requesting a valid path to the PCE.
+
+.. contents:: Contents
+   :depth: 2
+   :local:
+
+Installation
+^^^^^^^^^^^^
+
+Check that the feature ``features-algo`` is installed. Normally it will be
+installed with the pcep feature ``features-pcep``. Otherwise, install it
+with the following command:
+
+.. code-block:: console
+
+    feature:install features-algo
+
+Graph Setup
+^^^^^^^^^^^
+
+The PCE Server uses the Path Computation Algorithm plugin which needs a graph
+to be able to compute constrained paths. Thus, a valid graph must be provided
+manually or automatically.
+
+Manual activation
+'''''''''''''''''
+
+Create a new graph with the Rest API ``Create Graph``:
+
+.. code-block:: console
+
+    PUT: restconf/config/graph:graph-topology
+
+Refer to Graph documentation for details.
+
+There is a restriction on the name of the graph due to the Path Computation
+Algorithm integration. It must be started by **"ted://"** string in order to
+be learn automatically by the Path Computation Server bundle.
+
+Note that this kind of graph remains static. Thus, resources, mostly bandwidth,
+are not updated after deploying some RSVP-TE tunnels which consume bandwidth.
+
+BGP-LS activation
+'''''''''''''''''
+
+To achieve better experience, notably in conjunction with RSVP-TE and in order
+to work on an up-to-date graph, an integration is provided with the BGP Link
+State protocol. This allows to automatically fulfil a graph with network
+traffic engineering information conveyed by the BGP-LS protocol. The resources,
+mostly bandwidth, are automatically updated in the graph after deploying
+an RSVP-TE tunnel. Note that this is not the case with Segment Routing.
+
+For that purpose, just setup a BGP peering with a router that is BGP-LS
+speaker and report traffic engineering network topology from IS-IS-TE or
+OSPF-TE routing protocol. Refer to BGP documentation for the detail about
+how to setup a BGP peering with Link-State family.
+
+Once done, verify that the graph is correctly fulfil with the Rest API:
+
+.. code-block:: console
+
+    GET: restconf/operational/graph:graph-topology
+
+Basic Usage
+^^^^^^^^^^^
+
+There is two ways to use the PCE Server: through PcRequest and with PcInitiate.
+
+With PcRequest, just create a new tunnel on the router with an external PCE
+for path computation. Once PcRequest received, the PCE Server launches the path
+computation algorithm with requested parameters and in turn sends back to the
+PCC a PcResponse message with the computed path in the ERO.
+A NO-PATH object is returned in case of failure with the reason (e.g. source
+or destination unknown, constraints not met ...). Check on the router that
+the tunnel is up and running. Wireshark capture will help to determine
+if the exchanges between the PCC and the PCE went well. Setting log debug for
+algo and pcep plugins and looking to the log will also ease debugging.
+
+With PcInitiate message, just use the PCEP Rest API to setup an LSP
+
+.. code-block:: console
+
+    POST: /restconf/operations/network-topology-pcep:add-lsp
+
+by omitting the ERO Object. Indeed, an automatic call to the Path Computation
+Algorithm will be triggered when the ERO is absent or empty with the given
+end-points and metrics objects as input paramters. Address family is
+automatically deduced from the IP address family of the end-points object.
+The same behaviour applies for Segment Routing: just add the *PST=1* indication
+in the json or xml payload will force the address family of path computation
+to Segment Routing.
+
+To verify the result, just check the LSP-Database. The new LSP must have an
+ERO automatically computed as well as an RRO. Again, setting log debug for algo
+and pcep plugins and looking to the log will also help to verify that all is
+conform as expected.
+
+Advance Usage
+^^^^^^^^^^^^^
+
+A new Path Manager service has been added withing the PCE Server. This Path
+Manager allows:
+
+* The management of LSPs, in particular to update them without the need to
+  manually compute a path
+* The possibility to provide an ERO to reported LSPs without a valid path
+* The Persistency of Initiated and Updated LSPs accross PCC and or PCE reboot
+* The update of reported LSP from PCC with an empty ERO. For such reported LSP,
+  a path computation based on the LSP constraints is automatically triggered.
+  If a path is found, it is automatically enforced through a PcUpdate message.
+
+In order to be able to manage tunnels (RSVP-TE or Segment Routing) a new
+yang model has been added within the pcep configuration with the following
+schema:
+
+.. code-block:: console
+
+  module: pcep-server
+
+  augment /nt:network-topology/nt:topology/nt:node/topo:path-computation-client:
+    +--ro configured-lsp* [name]
+       +--ro name             string
+       +--ro path-status?     path-status
+       +--ro intended-path
+       |  +--ro source?           inet:ip-address
+       |  +--ro destination?      inet:ip-address
+       |  +--ro routing-method?   routing-type
+       |  +--ro constraints
+       |     +--ro metric?           uint32
+       |     +--ro te-metric?        uint32
+       |     +--ro delay?            gr:delay
+       |     +--ro jitter?           gr:delay
+       |     +--ro loss?             gr:loss
+       |     +--ro admin-group?      uint32
+       |     +--ro address-family?   enumeration
+       |     +--ro class-type?       uint8
+       |     +--ro bandwidth?        gr:decimal-bandwidth
+       +--ro computed-path
+          +--ro path-description* []
+          |  +--ro ipv4?          inet:ipv4-address
+          |  +--ro ipv6?          inet:ipv6-address
+          |  +--ro sid?           uint32
+          |  +--ro local-ipv4?    inet:ipv4-address
+          |  +--ro remote-ipv4?   inet:ipv4-address
+          |  +--ro local-ipv6?    inet:ipv6-address
+          |  +--ro remote-ipv6?   inet:ipv6-address
+          +--ro computation-status?   algo:computation-status
+  augment /nt:network-topology/nt:topology/nt:node:
+    +--rw configured-lsp* [name]
+       +--rw name             string
+       +--ro path-status?     path-status
+       +--rw intended-path
+       |  +--rw source?           inet:ip-address
+       |  +--rw destination?      inet:ip-address
+       |  +--rw routing-method?   routing-type
+       |  +--rw constraints
+       |     +--rw metric?           uint32
+       |     +--rw te-metric?        uint32
+       |     +--rw delay?            gr:delay
+       |     +--rw jitter?           gr:delay
+       |     +--rw loss?             gr:loss
+       |     +--rw admin-group?      uint32
+       |     +--rw address-family?   enumeration
+       |     +--rw class-type?       uint8
+       |     +--rw bandwidth?        gr:decimal-bandwidth
+       +--ro computed-path
+          +--ro path-description* []
+          |  +--ro ipv4?          inet:ipv4-address
+          |  +--ro ipv6?          inet:ipv6-address
+          |  +--ro sid?           uint32
+          |  +--ro local-ipv4?    inet:ipv4-address
+          |  +--ro remote-ipv4?   inet:ipv4-address
+          |  +--ro local-ipv6?    inet:ipv6-address
+          |  +--ro remote-ipv6?   inet:ipv6-address
+          +--ro computation-status?   algo:computation-status
+
+Usual REST API could be used against the pcep network topology config schema
+of the Data Store to create, update and remove new tunnels.
+
+REST API
+^^^^^^^^
+
+Get PCE tunnels
+'''''''''''''''
+
+Tunnels are stored in configuration Data Store and are accesible through the
+``network-topology:network-topology/topology=pcep-topology`` namespace in both
+operational (with ``?content=nonconfig``) and onfiguration (with
+``?content=config``) as follow:
+
+-----
+
+**RFC8040:** ``restconf/data/network-topology:network-topology/topology=pcep-topology``
+
+**Method:** ``GET``
+
+**Response Body:**
+
+.. code-block:: json
+   :linenos:
+
+    {
+        "network-topology:topology": [
+            {
+                "node": [
+                    {
+                       "node-id": "10.1.1.1",
+                        "pcep-server:configured-lsp": [
+                            {
+                                "name": "test-sr",
+                                "intended-path": {
+                                    "destination": "10.2.2.2",
+                                    "source": "10.1.1.1",
+                                    "routing-method": "te-metric",
+                                    "constraints": {
+                                        "bandwidth": "100000",
+                                        "class-type": 1,
+                                        "metric": 500,
+                                        "address-family": "sr-ipv4"
+                                    }
+                                }
+                            }
+                        ]
+                    }
+                ]
+            }
+        ]
+    }
+
+Once Tunnels enforced on a PCC, there are available in the operational Data
+Store under the same namespace within the ``pcep-server:configuredi-lsp`` table
+for each PCC.
+
+When getting the tunnel from the operational Data Store, state and computed
+path are also reported:
+
+.. code-block:: json
+   :linenos:
+
+    {
+        "network-topology:topology": [
+            {
+                "node": [
+                    {
+                        "node-id": "10.1.1.1",
+                        "pcep-server:configured-lsp": [
+                            {
+                                "name": "test-sr",
+                                "intended-path": {
+                                    "destination": "10.1.1.1",
+                                    "source": "10.2.2.2",
+                                    "routing-method": "te-metric",
+                                    "constraints": {
+                                        "bandwidth": "100000",
+                                        "class-type": 1,
+                                        "metric": 500,
+                                        "address-family": "sr-ipv4"
+                                    }
+                                },
+                                "computed-path": {
+                                    "path-description": [
+                                        {
+                                            "remote-ipv4": "10.0.1.3",
+                                            "local-ipv4": "10.0.1.1",
+                                            "sid": 113
+                                        },
+                                        {
+                                            "remote-ipv4": "10.0.2.2",
+                                            "local-ipv4": "10.0.3.2",
+                                            "sid": 112
+                                        }
+                                    ],
+                                    "computation-status": "completed"
+                                },
+                                "path-status": "sync"
+                            }
+                        ]
+                    }
+                ]
+            }
+        ]
+    }
+
+The ``path-status`` indicate if the status of the configured tunnel, in
+particular if it is in failure, or correctly configured (sync).
+
+Note that tunnels that are only reported by a PCC and for which
+no particular configuration has been setup are not provided the model
+``pcep-server:configured-lsp`` within the node-id schema.
+
+Create a tunnel:
+''''''''''''''''
+
+To add a tunnel or a set of tunnels on a given PCC, just create new entry in
+the configuration as follow:
+
+-----
+
+**RFC8040:** ``restconf/data/network-topology:network-topology/topology=pcep-topology/node=10.1.1.1``
+
+**Method:** ``POST``
+
+**Content-Type:** ``application/json``
+
+**Request Body:**
+
+.. code-block:: json
+   :linenos:
+   :emphasize-lines: 4,8,9,13
+
+    {
+        "pcep-server:configured-lsp": [
+            {
+                "name": "test",
+                "intended-path": {
+                    "destination": "10.2.2.2",
+                    "source": "10.1.1.1",
+                    "routing-method": "te-metric",
+                    "constraints": {
+                        "bandwidth": "100000",
+                        "class-type": 1,
+                        "metric": 500,
+                        "address-family": "ipv4"
+                    }
+                }
+            }
+        ]
+    }
+
+@line 5: **name** The tunnel identifier. Must be unique.
+
+@line 8: **routing-method** Specify which type of metric is used to compute
+the path: ``metric`` (standard), ``te-metric`` (TE metric) or ``delay``
+
+@line 9: **constraints** Constraints that the path compputation algorithm
+should respect to determine the path of the tunnel. Note that if no path
+is found, the tunnel is not enforced in the PCC and ``computation-status``
+within the ``computed-path`` is set to failed.
+
+@line 13: **address-family** Indicate the IP family of the tunnel: ``ipv4`` or
+``ipv6`` for IPv4 respectively IPv6 RSVP-TE tunnel, ``sr-ipv4`` or ``sr-ipv6``
+for IPv4 respectively IPv6 Segment Routing tunnel.
+
+Update a tunnel
+'''''''''''''''
+
+The procedure is the same as for the creation. Just used the ``PUT`` method
+instead of the ``POST`` mest for the REST API. The json body follows the same
+yang model. Note that it is not allowed to change end points of the tunnel i.e.
+the source and destination. If such modification is required, you must first
+remove the tunnel and then create a new one with the new end points.
+
+Remove a tunnel
+'''''''''''''''
+
+This simply done by removing the corresponding entry in the configuration by
+using the ``DELETE`` method as follow:
+
+**URL:** ``restconf/data/network-topology:network-topology/topology=pcep-topology/node=10.1.1.1/pcep-server:configured-lsp=test``
+
+**Method:** ``DELETE``
+
+
+Known limitations
+^^^^^^^^^^^^^^^^^
+
+As the PCE Server is in its initial release, there are some limitations
+mentioned hereinafter:
+
+* Following PCEP Objects that may be present in the PcRequest message are not
+  yet supported, and right now, ignored:
+
+  * Include Route Object (IRO)
+  * Exclude Route Object (XRO)
+  * Objective Function (OF)
+
+* For Segment Routing, ERO is provided with Node SID for NAI and SID index.
+
+* Due to the integration with BGP-LS, the graph name must start with *ted://*
+  tag in order to be automatically used by the pcep plugin.
+
+* For Segment Routing, as network resources are not updated due to the lack
+  of signaling, the resources consumed by the new segment path are not updated
+  in the graph.
+
+All these limitations will be solved in future releases.
+
index 8ba4b4bd7690d057d5947b6e70cc7905c483e409..ad013fd1e05095b828e32f8cc751e10a7e371ae9 100644 (file)
             <type>xml</type>
             <classifier>features</classifier>
         </dependency>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>odl-bgpcep-pcep-stateful</artifactId>
+            <type>xml</type>
+            <classifier>features</classifier>
+        </dependency>
         <dependency>
             <groupId>${project.groupId}</groupId>
             <artifactId>pcep-server-api</artifactId>
index 3886605ab76705320d465fcc7ca7bfd3f34ae006..641273dcd38b8e8c7810c1f0462f8f34e685caab 100644 (file)
             <groupId>${project.groupId}</groupId>
             <artifactId>graph-api</artifactId>
         </dependency>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>algo-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>pcep-ietf-stateful</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>pcep-topology-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-common</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>concepts</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.mdsal.binding.model.ietf</groupId>
+            <artifactId>rfc6991-ietf-inet-types</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.mdsal.model</groupId>
+            <artifactId>ietf-topology</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.google.guava</groupId>
+            <artifactId>guava</artifactId>
+        </dependency>
     </dependencies>
 </project>
index ad8e761180aa0d875570ba0e6d6b0306fe7d21d2..f4f9cfd662fe54bbc21137d07447f26b84eaf947 100644 (file)
@@ -23,5 +23,4 @@ public interface PathComputation {
 
     Ero computeEro(EndpointsObj endpoints, Bandwidth bandwidth, ClassType classType, List<Metrics> metrics,
             boolean segmentRouting);
-
 }
index ee856c63480ec7e80db38151f9278f659a3ebf56..496b91b2f578176957e090986edb762acb892b2e 100644 (file)
@@ -8,11 +8,30 @@
 
 package org.opendaylight.bgpcep.pcep.server;
 
-import org.opendaylight.graph.ConnectedGraph;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.Topology;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.TopologyKey;
+import org.opendaylight.yangtools.yang.binding.KeyedInstanceIdentifier;
 
 public interface PceServerProvider {
 
+    /**
+     * Return the instance of the Path Computation server.
+     *
+     * @return  Path Computation Object
+     */
     PathComputation getPathComputation();
 
-    ConnectedGraph getTedGraph();
+    /**
+     * Register PCEP Topology into PCE Server to manage LSP.
+     *
+     * @param topology    Configured PCEP Topology
+     */
+    void registerPcepTopology(KeyedInstanceIdentifier<Topology, TopologyKey> topology);
+
+    /**
+     * Un Register current PCEP Topology into PCE Server.
+     *
+     * @param topology    Configured PCEP Topology
+     */
+    void unRegisterPcepTopology(KeyedInstanceIdentifier<Topology, TopologyKey> topology);
 }
diff --git a/pcep/server/server-api/src/main/yang/pcep-server.yang b/pcep/server/server-api/src/main/yang/pcep-server.yang
new file mode 100644 (file)
index 0000000..d36610b
--- /dev/null
@@ -0,0 +1,160 @@
+module pcep-server {
+    yang-version 1;
+    namespace "urn:opendaylight:params:xml:ns:yang:pcep:server";
+    prefix "server";
+
+    import network-topology { prefix nt; revision-date 2013-10-21; }
+    import ietf-inet-types { prefix inet; revision-date 2013-07-15; }
+    import path-computation { prefix algo; revision-date 2020-01-20; }
+    import network-topology-pcep { prefix topo; revision-date 2020-01-20; }
+    import yang-ext { prefix ext; revision-date 2013-07-09; }
+
+    organization "Orange";
+    contact "Olivier Dugeon <olivier.dugeon@orange.com>";
+
+    description
+        "This module contains the model of the Managed Path which
+         allows the tunnels management by the PCE server.
+
+        Copyright (c)2021 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 "2021-07-20" {
+        description
+             "Initial revision.";
+        reference "";
+    }
+
+    typedef path-type {
+        description "Type of Path";
+        type enumeration {
+            enum stateless {
+                description "Path is not managed";
+                value 0;
+            }
+            enum pcc {
+                description "Path is managed by the PCC";
+                value 1;
+            }
+            enum delegated {
+                description "Path management is delegated to PCE";
+                value 2;
+            }
+            enum initiated {
+                description "Path is fully managed by the PCE";
+                value 3;
+            }
+        }
+    }
+
+    typedef routing-type {
+        description "Various objectives for the Path Computation Algorithm";
+        type enumeration {
+            enum none {
+                value 0;
+            }
+            enum metric {
+                value 1;
+            }
+            enum te-metric {
+                value 2;
+            }
+            enum delay {
+                value 3;
+            }
+        }
+        default "none";
+    }
+
+    typedef path-status {
+        description "Status of the TE Path";
+        type enumeration {
+            enum reported {
+                description "TE Path is reported by the PCC";
+                value 0;
+            }
+            enum configured {
+                description "TE Path is configured but not setup";
+                value 1;
+            }
+            enum updated {
+                description "TE Path has been updated and need to be synchronized";
+                value 2;
+            }
+            enum sync {
+                description "TE Path is setup and synchronized";
+                value 3;
+            }
+            enum failed {
+                description "Attempt to configure TE Path on PCC failed";
+                value 4;
+            }
+        }
+        default "reported";
+    }
+
+    grouping pcc-configured-lsp {
+        description "Configured LSP per PCC node";
+        list configured-lsp {
+            description "List of Configured LSP per PCC";
+            key "name";
+            leaf "name" {
+                type string;
+                mandatory true;
+            }
+            /*
+            leaf path-type {
+                description "Type of the TE Path";
+                type path-type;
+                config false;
+            }
+            */
+            leaf path-status {
+                description "Status of TE Path";
+                type path-status;
+                config false;
+            }
+            container intended-path {
+                description "Intended Path constraints";
+                leaf source {
+                    type inet:ip-address;
+                }
+                leaf destination {
+                    type inet:ip-address;
+                }
+                leaf routing-method {
+                     type routing-type;
+                }
+                container constraints {
+                    uses algo:path-constraints;
+                }
+            }
+            container computed-path {
+                uses algo:path-descriptions;
+                leaf computation-status {
+                    type algo:computation-status;
+                }
+                config false;
+            }
+        }
+    }
+
+    /* Add Configured LSP to the Operational PCEP Network Topology */
+    augment "/nt:network-topology/nt:topology/nt:node/topo:path-computation-client" {
+        uses pcc-configured-lsp;
+    }
+
+    /* Add Configured LSP to the Configuration PCEP Network Topology */
+    augment "/nt:network-topology/nt:topology/nt:node" {
+        when "../nt:topology-types/topo:topology-pcep";
+        ext:augment-identifier pcep-node-config;
+        description "Augment Pcep topology node with Configured LSP config";
+
+        uses pcc-configured-lsp;
+    }
+}
+
index 53d2287521fc4624194ac200dcd7d5ba7e53b3d1..9c87a17f86806dda6307f5a7bdc4ec40df24fdf0 100644 (file)
             <groupId>${project.groupId}</groupId>
             <artifactId>algo-api</artifactId>
         </dependency>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>pcep-topology-api</artifactId>
+        </dependency>
         <dependency>
             <groupId>${project.groupId}</groupId>
             <artifactId>concepts</artifactId>
             <groupId>${project.groupId}</groupId>
             <artifactId>pcep-spi</artifactId>
         </dependency>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>pcep-ietf-stateful</artifactId>
+        </dependency>
         <dependency>
             <groupId>${project.groupId}</groupId>
             <artifactId>pcep-segment-routing</artifactId>
             <artifactId>rsvp-api</artifactId>
         </dependency>
         <dependency>
-            <groupId>org.opendaylight.yangtools</groupId>
-            <artifactId>yang-common</artifactId>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>topology-api</artifactId>
         </dependency>
         <dependency>
-            <groupId>com.google.guava</groupId>
-            <artifactId>guava</artifactId>
+            <groupId>org.opendaylight.mdsal</groupId>
+            <artifactId>mdsal-common-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.mdsal</groupId>
+            <artifactId>mdsal-binding-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.mdsal</groupId>
+            <artifactId>yang-binding</artifactId>
         </dependency>
         <dependency>
             <groupId>org.opendaylight.mdsal.binding.model.ietf</groupId>
             <artifactId>rfc6991-ietf-inet-types</artifactId>
         </dependency>
         <dependency>
-            <groupId>org.osgi</groupId>
-            <artifactId>osgi.cmpn</artifactId>
+            <groupId>org.opendaylight.mdsal.model</groupId>
+            <artifactId>ietf-topology</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>concepts</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-common</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.google.guava</groupId>
+            <artifactId>guava</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>javax.annotation</groupId>
+            <artifactId>javax.annotation-api</artifactId>
+            <scope>provided</scope>
+            <optional>true</optional>
         </dependency>
         <dependency>
             <groupId>com.guicedee.services</groupId>
             <artifactId>javax.inject</artifactId>
             <optional>true</optional>
         </dependency>
+        <dependency>
+            <groupId>org.osgi</groupId>
+            <artifactId>osgi.cmpn</artifactId>
+        </dependency>
     </dependencies>
 </project>
index 80565d6dd539a80f3c817f32ae9c0620519b3c6a..7b642ce284cfa04b614a1a31710ee4c6563f6ba8 100644 (file)
@@ -9,43 +9,102 @@ package org.opendaylight.bgpcep.pcep.server.provider;
 
 import static java.util.Objects.requireNonNull;
 
+import java.util.HashMap;
+import java.util.Map;
+import javax.annotation.PreDestroy;
 import javax.inject.Inject;
 import javax.inject.Singleton;
 import org.eclipse.jdt.annotation.Nullable;
 import org.opendaylight.algo.PathComputationProvider;
+import org.opendaylight.bgpcep.pcep.server.PathComputation;
 import org.opendaylight.bgpcep.pcep.server.PceServerProvider;
 import org.opendaylight.graph.ConnectedGraph;
 import org.opendaylight.graph.ConnectedGraphProvider;
+import org.opendaylight.mdsal.binding.api.DataBroker;
+import org.opendaylight.mdsal.binding.api.RpcConsumerRegistry;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.topology.pcep.rev200120.NetworkTopologyPcepService;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.TopologyId;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.Topology;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.TopologyKey;
+import org.opendaylight.yangtools.yang.binding.KeyedInstanceIdentifier;
 import org.osgi.service.component.annotations.Activate;
 import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.Deactivate;
 import org.osgi.service.component.annotations.Reference;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
 
 @Singleton
 @Component(immediate = true)
-public final class DefaultPceServerProvider implements PceServerProvider {
+public final class DefaultPceServerProvider implements PceServerProvider, AutoCloseable {
+    private static final Logger LOG = LoggerFactory.getLogger(DefaultPceServerProvider.class);
     private final ConnectedGraphProvider graphProvider;
     private final PathComputationProvider algoProvider;
-
+    private final DataBroker dataBroker;
+    private final RpcConsumerRegistry rpcRegistry;
+    private Map<TopologyId, PathManagerProvider> pathManagers = new HashMap<TopologyId, PathManagerProvider>();
+    private Map<TopologyId, PathManagerListener> pathListeners = new HashMap<TopologyId, PathManagerListener>();
+    private Map<TopologyId, PcepTopologyListener> pcepListeners = new HashMap<TopologyId, PcepTopologyListener>();
     private volatile ConnectedGraph tedGraph;
 
     @Inject
     @Activate
     public DefaultPceServerProvider(@Reference final ConnectedGraphProvider graphProvider,
-            @Reference final PathComputationProvider pathComputationProvider) {
+            @Reference final PathComputationProvider pathComputationProvider,
+            @Reference final DataBroker dataBroker,
+            @Reference final RpcConsumerRegistry rpcConsumerRegistry) {
         this.graphProvider = requireNonNull(graphProvider);
         this.algoProvider = requireNonNull(pathComputationProvider);
-        setTedGraph();
+        this.dataBroker = requireNonNull(dataBroker);
+        this.rpcRegistry = requireNonNull(rpcConsumerRegistry);
+    }
+
+    @Override
+    @Deactivate
+    @PreDestroy
+    public void close() {
+        for (PathManagerListener pathListener: pathListeners.values()) {
+            pathListener.close();
+            pathListener = null;
+        }
+        pathListeners.clear();
+        for (PcepTopologyListener pcepListener: pcepListeners.values()) {
+            pcepListener.close();
+            pcepListener = null;
+        }
+        pcepListeners.clear();
+        for (PathManagerProvider pathManager: pathManagers.values()) {
+            pathManager.close();
+            pathManager = null;
+        }
+        pathManagers.clear();
+    }
+
+    private void closeListenerAndManager(TopologyId key) {
+
+        PathManagerListener pathListener = pathListeners.remove(key);
+        if (pathListener != null) {
+            pathListener.close();
+        }
+        PcepTopologyListener pcepListener = pcepListeners.remove(key);
+        if (pcepListener != null) {
+            pcepListener.close();
+        }
+        PathManagerProvider pathManager = pathManagers.remove(key);
+        if (pathManager != null) {
+            pathManager.close();
+        }
     }
 
     @Override
-    public @Nullable PathComputationImpl getPathComputation() {
+    public @Nullable PathComputation getPathComputation() {
         /* Check that we have a valid graph */
         final ConnectedGraph graph = getTedGraph();
         return graph == null ? null : new PathComputationImpl(tedGraph, algoProvider);
     }
 
-    @Override
-    public @Nullable ConnectedGraph getTedGraph() {
+    private ConnectedGraph getTedGraph() {
         /* Leave a chance to get a valid Graph in case of late fulfillment */
         if (tedGraph == null) {
             setTedGraph();
@@ -64,4 +123,35 @@ public final class DefaultPceServerProvider implements PceServerProvider {
             .findFirst()
             .orElse(null);
     }
+
+    @Override
+    public void registerPcepTopology(KeyedInstanceIdentifier<Topology, TopologyKey> topology) {
+        TopologyId key = requireNonNull(topology).getKey().getTopologyId();
+
+        LOG.info("Start PCE Server components for Topology {}", key.getValue());
+
+        /* First close current Listener & Manager if there are active */
+        closeListenerAndManager(key);
+
+        /* Then create Path Manger */
+        final NetworkTopologyPcepService ntps = rpcRegistry.getRpcService(NetworkTopologyPcepService.class);
+        PathManagerProvider pathManager = new PathManagerProvider(dataBroker, topology, ntps, this);
+
+        /* And Listener */
+        PathManagerListener pathListener = new PathManagerListener(dataBroker, topology, pathManager);
+        PcepTopologyListener pcepListener = new PcepTopologyListener(dataBroker, topology, pathManager);
+
+        /* Finally, register all of them for later deletion */
+        pathManagers.put(key, pathManager);
+        pathListeners.put(key, pathListener);
+        pcepListeners.put(key, pcepListener);
+    }
+
+    @Override
+    public void unRegisterPcepTopology(KeyedInstanceIdentifier<Topology, TopologyKey> topology) {
+        TopologyId key = requireNonNull(topology).getKey().getTopologyId();
+
+        LOG.info("Stop PCE Server for Topology {}", key.getValue());
+        closeListenerAndManager(key);
+    }
 }
diff --git a/pcep/server/server-provider/src/main/java/org/opendaylight/bgpcep/pcep/server/provider/ManagedTeNode.java b/pcep/server/server-provider/src/main/java/org/opendaylight/bgpcep/pcep/server/provider/ManagedTeNode.java
new file mode 100644 (file)
index 0000000..5c079c4
--- /dev/null
@@ -0,0 +1,127 @@
+/*
+ * Copyright (c) 2021 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.bgpcep.pcep.server.provider;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+import com.google.common.base.MoreObjects;
+import java.util.HashMap;
+import java.util.Map;
+import org.opendaylight.mdsal.binding.api.TransactionChain;
+import org.opendaylight.mdsal.binding.api.WriteTransaction;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.server.rev210720.PathType;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.server.rev210720.pcc.configured.lsp.ConfiguredLsp;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.server.rev210720.pcc.configured.lsp.ConfiguredLspKey;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId;
+import org.opendaylight.yangtools.yang.binding.CodeHelpers;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class ManagedTeNode {
+
+    private enum NodeState {
+        Disabled,
+        Enabled,
+        Sync
+    }
+
+    private final NodeId id;
+    private NodeState state;
+    private Map<ConfiguredLspKey, ManagedTePath> mngPaths = new HashMap<ConfiguredLspKey, ManagedTePath>();
+    private final TransactionChain chain;
+    private static final Logger LOG = LoggerFactory.getLogger(ManagedTeNode.class);
+
+    public ManagedTeNode(final NodeId id, final TransactionChain chain) {
+        this.id = id;
+        this.chain = chain;
+        this.state = NodeState.Enabled;
+    }
+
+    public ManagedTeNode(final NodeId id, TransactionChain chain, final NodeState state) {
+        this.id = id;
+        this.chain = chain;
+        this.state = state;
+    }
+
+    public NodeState getState() {
+        return state;
+    }
+
+    public NodeId getId() {
+        return id;
+    }
+
+    public Map<ConfiguredLspKey, ManagedTePath> getTePaths() {
+        return mngPaths;
+    }
+
+    public WriteTransaction getTransaction() {
+        return chain.newWriteOnlyTransaction();
+    }
+
+    public boolean isSync() {
+        return state == NodeState.Sync;
+    }
+
+    public void sync() {
+        state = NodeState.Sync;
+    }
+
+    /**
+     * Disable this Managed TE Node and removed associated Managed Node from the Data Store. TE Path which are not
+     * delegated are removed from the path list other ones are mark as disabled.
+     */
+    public void disable() {
+        /* Remove associated TE Paths that are not managed by the PCE i.e. TE Path which are not delegated */
+        for (ManagedTePath mngPath: mngPaths.values()) {
+            final ConfiguredLsp lsp = mngPath.getLsp();
+            if (mngPath.getType() == PathType.Stateless || mngPath.getType() == PathType.Pcc) {
+                mngPaths.remove(lsp.key());
+            } else {
+                mngPath.disabled();
+            }
+        }
+
+        state = NodeState.Disabled;
+        LOG.debug("Managed TE Node {} has been disabled. Keep configuration {}", id, this);
+    }
+
+    public void addManagedTePath(final ManagedTePath mngPath) {
+        mngPaths.put(mngPath.getLsp().key(), mngPath);
+    }
+
+    public ManagedTePath removeManagedTePath(final ConfiguredLspKey key) {
+        checkArgument(key != null, "Provided Configured LSP Key is a null object");
+
+        /* Get corresponding Managed TE Path */
+        final ManagedTePath mngPath = mngPaths.remove(key);
+        if (mngPath == null) {
+            return null;
+        }
+
+        /* Remove corresponding Managed Path from the Data Store */
+        mngPath.removeFromDataStore();
+
+        return mngPath;
+    }
+
+    public ManagedTePath getManagedTePath(final ConfiguredLspKey key) {
+        checkArgument(key != null, "Provided Configured LSP Key is a null object");
+
+        return mngPaths.get(key);
+    }
+
+    @Override
+    public String toString() {
+        final MoreObjects.ToStringHelper helper = MoreObjects.toStringHelper("ManagedTeNode");
+        CodeHelpers.appendValue(helper, "NodeId", id);
+        CodeHelpers.appendValue(helper, "NodeState", state);
+        CodeHelpers.appendValue(helper, "ManagedTePaths", mngPaths);
+        return helper.toString();
+    }
+}
diff --git a/pcep/server/server-provider/src/main/java/org/opendaylight/bgpcep/pcep/server/provider/ManagedTePath.java b/pcep/server/server-provider/src/main/java/org/opendaylight/bgpcep/pcep/server/provider/ManagedTePath.java
new file mode 100644 (file)
index 0000000..49f93c6
--- /dev/null
@@ -0,0 +1,599 @@
+/*
+ * Copyright (c) 2021 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.bgpcep.pcep.server.provider;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.base.MoreObjects;
+import com.google.common.util.concurrent.FutureCallback;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.MoreExecutors;
+import java.nio.ByteBuffer;
+import java.util.Collections;
+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.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv4AddressNoZone;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv6AddressNoZone;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ieee754.rev130819.Float32;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.network.concepts.rev131125.Bandwidth;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.network.topology.rev140113.NetworkTopologyRef;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.ietf.stateful.rev200720.Arguments2Builder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.ietf.stateful.rev200720.Arguments3Builder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.ietf.stateful.rev200720.lsp.object.LspBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.server.rev210720.PathComputationClient1;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.server.rev210720.PathStatus;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.server.rev210720.PathType;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.server.rev210720.pcc.configured.lsp.ConfiguredLsp;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.server.rev210720.pcc.configured.lsp.ConfiguredLspBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.server.rev210720.pcc.configured.lsp.configured.lsp.IntendedPath;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.server.rev210720.pcc.configured.lsp.configured.lsp.intended.path.Constraints;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.types.rev181109.bandwidth.object.BandwidthBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.types.rev181109.endpoints.address.family.Ipv4CaseBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.types.rev181109.endpoints.address.family.Ipv6CaseBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.types.rev181109.endpoints.address.family.ipv4._case.Ipv4Builder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.types.rev181109.endpoints.address.family.ipv6._case.Ipv6Builder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.types.rev181109.endpoints.object.EndpointsObjBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.types.rev181109.lsp.attributes.MetricsBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.types.rev181109.metric.object.MetricBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.types.rev181109.path.setup.type.tlv.PathSetupTypeBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.topology.pcep.rev200120.AddLspInput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.topology.pcep.rev200120.AddLspInputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.topology.pcep.rev200120.AddLspOutput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.topology.pcep.rev200120.NetworkTopologyPcepService;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.topology.pcep.rev200120.Node1;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.topology.pcep.rev200120.RemoveLspInput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.topology.pcep.rev200120.RemoveLspInputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.topology.pcep.rev200120.RemoveLspOutput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.topology.pcep.rev200120.UpdateLspInput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.topology.pcep.rev200120.UpdateLspInputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.topology.pcep.rev200120.UpdateLspOutput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.topology.pcep.rev200120.add.lsp.args.ArgumentsBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.topology.pcep.rev200120.pcep.client.attributes.PathComputationClient;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.Topology;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.NodeKey;
+import org.opendaylight.yangtools.yang.binding.CodeHelpers;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.opendaylight.yangtools.yang.common.Uint8;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class ManagedTePath {
+
+    private ConfiguredLsp cfgLsp = null;
+    private final ManagedTeNode teNode;
+    private boolean sent = false;
+    private PathType type = PathType.Pcc;
+    private final InstanceIdentifier<Topology> pcepTopology;
+    private final InstanceIdentifier<PathComputationClient1> pccIdentifier;
+
+    private static final Logger LOG = LoggerFactory.getLogger(ManagedTePath.class);
+
+    public ManagedTePath(ManagedTeNode teNode, InstanceIdentifier<Topology> topology) {
+        this.teNode = requireNonNull(teNode);
+        this.pcepTopology = requireNonNull(topology);
+        this.pccIdentifier = pcepTopology.child(Node.class, new NodeKey(teNode.getId())).augmentation(Node1.class)
+                .child(PathComputationClient.class).augmentation(PathComputationClient1.class);
+    }
+
+    public ManagedTePath(ManagedTeNode teNode, final ConfiguredLsp lsp, InstanceIdentifier<Topology> topology) {
+        this.cfgLsp = requireNonNull(lsp);
+        this.teNode = requireNonNull(teNode);
+        this.pcepTopology = requireNonNull(topology);
+        this.pccIdentifier = pcepTopology.child(Node.class, new NodeKey(teNode.getId())).augmentation(Node1.class)
+                .child(PathComputationClient.class).augmentation(PathComputationClient1.class);
+    }
+
+    public ManagedTePath(ManagedTeNode teNode, final ManagedTePath mngPath) {
+        checkArgument(mngPath != null, "Managed TE Path is mandatory. Can't be null or empty!");
+        this.cfgLsp = mngPath.getLsp();
+        this.sent = mngPath.isSent();
+        this.type = mngPath.getType();
+        this.teNode = requireNonNull(teNode);
+        this.pcepTopology = mngPath.getTopology();
+        this.pccIdentifier = pcepTopology.child(Node.class, new NodeKey(teNode.getId())).augmentation(Node1.class)
+                .child(PathComputationClient.class).augmentation(PathComputationClient1.class);
+    }
+
+    public ConfiguredLsp getLsp() {
+        return cfgLsp;
+    }
+
+    public ManagedTeNode getManagedTeNode() {
+        return teNode;
+    }
+
+    public PathType getType() {
+        return type;
+    }
+
+    public InstanceIdentifier<Topology> getTopology() {
+        return pcepTopology;
+    }
+
+    public ManagedTePath setConfiguredLsp(final ConfiguredLsp lsp) {
+        this.cfgLsp = lsp;
+        return this;
+    }
+
+    public ManagedTePath setType(PathType type) {
+        this.type = type;
+        return this;
+    }
+
+    /**
+     * Mark this TE Path as synchronized and update the Data Store accordingly.
+     *
+     */
+    public void sync() {
+        this.cfgLsp = new ConfiguredLspBuilder(cfgLsp).setPathStatus(PathStatus.Sync).build();
+        updateToDataStore();
+    }
+
+    /**
+     * Disabling this TE Path by marking it as Configured. Do not update the Data Store.
+     */
+    public void disabled() {
+        this.cfgLsp = new ConfiguredLspBuilder(cfgLsp).setPathStatus(PathStatus.Configured).build();
+    }
+
+    /**
+     * Mark this TE Path as Failed.
+     */
+    public void failed() {
+        this.cfgLsp = new ConfiguredLspBuilder(cfgLsp).setPathStatus(PathStatus.Failed).build();
+        updateToDataStore();
+    }
+
+    public boolean isSent() {
+        return sent;
+    }
+
+    /**
+     * Compare the current TE Path against the reported LSP to determine if there are in Sync, need Update or
+     * considered as in Failure if already updated.
+     *
+     * @param lsp   LSP that corresponds to the reported LSP
+     *
+     * @return      new LSP status
+     */
+    public PathStatus checkReportedPath(final ConfiguredLsp lsp) {
+        /* Check if this path has not been already verified */
+        if (cfgLsp.getPathStatus() == PathStatus.Sync) {
+            return PathStatus.Sync;
+        }
+
+        /* Check Source, Destination and routing method which must be the same */
+        final IntendedPath iPath = lsp.getIntendedPath();
+        final IntendedPath rPath = lsp.getIntendedPath();
+        PathStatus newStatus = sent ? PathStatus.Failed : PathStatus.Updated;
+        if (!iPath.getSource().equals(rPath.getSource()) || !iPath.getDestination().equals(rPath.getDestination())) {
+            return PathStatus.Failed;
+        }
+
+        /* Check constraints ... */
+        final Constraints icts = iPath.getConstraints();
+        final Constraints rcts = rPath.getConstraints();
+        if (!icts.getAddressFamily().equals(rcts.getAddressFamily())) {
+            return newStatus;
+        }
+        if (icts.getBandwidth() != null && !icts.getBandwidth().equals(rcts.getBandwidth())) {
+            return newStatus;
+        }
+        /*
+         * ClassType, TE metric and Delay are not supported by all routers: need
+         * to check them only if there are all present
+         */
+        if (icts.getClassType() != null && rcts.getClassType() != null
+                        && !icts.getClassType().equals(rcts.getClassType())) {
+            return newStatus;
+        }
+        if (icts.getTeMetric() != null && rcts.getTeMetric() != null
+                && !icts.getTeMetric().equals(rcts.getTeMetric())) {
+            return newStatus;
+        }
+        if (icts.getDelay() != null && rcts.getDelay() != null && !icts.getDelay().equals(rcts.getDelay())) {
+            return newStatus;
+        }
+
+        /* ... and Path to determine if an update is required */
+        if (lsp.getComputedPath().getPathDescription() == null) {
+            return PathStatus.Failed;
+        }
+        if (!lsp.getComputedPath().getPathDescription().equals(lsp.getComputedPath().getPathDescription())) {
+            return newStatus;
+        }
+
+        /* All is conform. LSP is in sync with expected result. */
+        return PathStatus.Sync;
+    }
+
+    /**
+     * Convert LSP as Add LSP Input class to enforce it into the PCC through a call to add-lsp RPCs.
+     *
+     * @return  new Add LSP Input
+     */
+    private AddLspInput getAddLspInput() {
+        /* Create EndPoint Object */
+        final IntendedPath iPath = cfgLsp.getIntendedPath();
+        final EndpointsObjBuilder epb = new EndpointsObjBuilder()
+                .setIgnore(false)
+                .setProcessingRule(true);
+        if (iPath.getSource().getIpv4Address() != null) {
+            final Ipv4Builder ipBuilder = new Ipv4Builder()
+                    .setDestinationIpv4Address(new Ipv4AddressNoZone(iPath.getDestination().getIpv4Address()))
+                    .setSourceIpv4Address(new Ipv4AddressNoZone(iPath.getSource().getIpv4Address()));
+            epb.setAddressFamily((new Ipv4CaseBuilder().setIpv4(ipBuilder.build()).build()));
+        } else if (cfgLsp.getIntendedPath().getSource().getIpv6Address() != null) {
+            final Ipv6Builder ipBuilder = new Ipv6Builder()
+                    .setDestinationIpv6Address(new Ipv6AddressNoZone(iPath.getDestination().getIpv6Address()))
+                    .setSourceIpv6Address(new Ipv6AddressNoZone(iPath.getSource().getIpv6Address()));
+            epb.setAddressFamily((new Ipv6CaseBuilder().setIpv6(ipBuilder.build()).build()));
+        } else {
+            // In case of ...
+            return null;
+        }
+
+        /* Create Path Setup Type */
+        final PathSetupTypeBuilder pstBuilder = new PathSetupTypeBuilder();
+        switch (iPath.getConstraints().getAddressFamily()) {
+            case SrIpv4:
+            case SrIpv6:
+                pstBuilder.setPst(Uint8.ONE);
+                break;
+            default:
+                pstBuilder.setPst(Uint8.ZERO);
+                break;
+        }
+
+        /* Create LSP */
+        final LspBuilder lspBuilder = new LspBuilder()
+                .setAdministrative(true)
+                .setDelegate(true);
+
+        /*
+         * Build Arguments.
+         * Note that TE Metric and Delay are not set because, at least, Juniper Routers don't support them.
+         */
+        final ArgumentsBuilder args = new ArgumentsBuilder()
+                .setEndpointsObj(epb.build())
+                .setEro(MessagesUtil.getEro(cfgLsp.getComputedPath().getPathDescription()))
+                .addAugmentation(new Arguments2Builder()
+                        .setLsp(lspBuilder.build())
+                        .setPathSetupType(pstBuilder.build())
+                        .build());
+
+        /* with Bandwidth and Standard Metric */
+        if (iPath.getConstraints().getBandwidth() != null) {
+            final int ftoi = Float.floatToIntBits(iPath.getConstraints().getBandwidth().getValue().floatValue());
+            final byte[] itob = { (byte) (0xFF & ftoi >> 24), (byte) (0xFF & ftoi >> 16), (byte) (0xFF & ftoi >> 8),
+                (byte) (0xFF & ftoi) };
+            args.setBandwidth(new BandwidthBuilder().setBandwidth(new Bandwidth(itob)).build());
+        }
+        if (iPath.getConstraints().getMetric() != null) {
+            final MetricBuilder metricBuilder = new MetricBuilder()
+                    .setComputed(true)
+                    .setMetricType(Uint8.ONE)
+                    .setValue(new Float32(
+                            ByteBuffer.allocate(4).putFloat(iPath.getConstraints().getMetric().floatValue()).array()));
+            args.setMetrics(Collections.singletonList(new MetricsBuilder().setMetric(metricBuilder.build()).build()));
+        }
+
+        /*
+         * NOTE: Seems that ClassType is not supported by some routers. Skip it for the moment.
+         */
+        /* With Class Type if defined
+        if (iPath.getConstraints().getClassType() != null) {
+            args.setClassType(
+                new ClassTypeBuilder()
+                    .setClassType(new ClassType(iPath.getConstraints().getClassType()))
+                    .setIgnore(false)
+                    .setProcessingRule(true)
+                    .build());
+        }
+        */
+
+        /* Finally, build addLSP input */
+        return new AddLspInputBuilder()
+                .setNode(teNode.getId())
+                .setName(cfgLsp.getName())
+                .setArguments(args.build())
+                .setNetworkTopologyRef(new NetworkTopologyRef(pcepTopology))
+                .build();
+    }
+
+
+    /**
+     * Call add-lsp RPC to enforce the LSP into the PCC. This action will trigger a PcInitiate message to the PCC.
+     *
+     * @param ntps  Network Topology PCEP Service
+     *
+     * @return      Add LSP Output to convey the RPC result
+     */
+    public ListenableFuture<RpcResult<AddLspOutput>> addPath(final NetworkTopologyPcepService ntps) {
+        /* Check if we could add this path */
+        if ((type != PathType.Initiated) || !teNode.isSync()) {
+            return null;
+        }
+
+        /* Check if we have a valid Path */
+        if (cfgLsp.getComputedPath().getPathDescription() == null) {
+            return null;
+        }
+
+        sent = true;
+        final ListenableFuture<RpcResult<AddLspOutput>> enforce = ntps.addLsp(getAddLspInput());
+        LOG.info("Call Add LSP to {} with {}", ntps, enforce);
+        Futures.addCallback(enforce, new FutureCallback<RpcResult<AddLspOutput>>() {
+            @Override
+            public void onSuccess(final RpcResult<AddLspOutput> result) {
+                if (result.isSuccessful()) {
+                    LOG.debug("Enforce LSP success {}", result.getResult());
+                } else {
+                    LOG.debug("Unable to enforce LSP {} on Node {}: Got error {}", cfgLsp.getName(), teNode.getId(),
+                            result.getErrors());
+                }
+                sent = false;
+            }
+
+            @Override
+            public void onFailure(final Throwable throwable) {
+                LOG.warn("Failed enforce LSP {} on Node {}", cfgLsp.getName(), teNode.getId());
+                sent = false;
+            }
+        }, MoreExecutors.directExecutor());
+
+        return enforce;
+    }
+
+    /**
+     * Convert LSP as Update LSP Input class to update it into the PCC through a call to update-lsp RPCs.
+     *
+     * @return  new Update LSP Input
+     */
+    private UpdateLspInput getUpdateLspInput() {
+        /* Create Path Setup Type */
+        final IntendedPath iPath = cfgLsp.getIntendedPath();
+        final PathSetupTypeBuilder pstBuilder = new PathSetupTypeBuilder();
+        switch (iPath.getConstraints().getAddressFamily()) {
+            case SrIpv4:
+            case SrIpv6:
+                pstBuilder.setPst(Uint8.ONE);
+                break;
+            default:
+                pstBuilder.setPst(Uint8.ZERO);
+                break;
+        }
+
+        /* Create LSP */
+        final LspBuilder lspBuilder = new LspBuilder()
+                .setAdministrative(true)
+                .setDelegate(true);
+
+        /* Build Arguments */
+        final org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.topology.pcep.rev200120
+            .update.lsp.args.ArgumentsBuilder args;
+        args = new org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.topology.pcep.rev200120
+            .update.lsp.args.ArgumentsBuilder()
+                .addAugmentation(new Arguments3Builder()
+                    .setLsp(lspBuilder.build())
+                    .setPathSetupType(pstBuilder.build())
+                    .build())
+                .setEro(MessagesUtil.getEro(cfgLsp.getComputedPath().getPathDescription()));
+
+        /*  with Bandwidth if defined, but not other Metrics as some routers don't support them */
+        if (iPath.getConstraints().getBandwidth() != null) {
+            final int ftoi = Float.floatToIntBits(iPath.getConstraints().getBandwidth().getValue().floatValue());
+            final byte[] itob = { (byte) (0xFF & ftoi >> 24), (byte) (0xFF & ftoi >> 16), (byte) (0xFF & ftoi >> 8),
+                (byte) (0xFF & ftoi) };
+            args.setBandwidth(new BandwidthBuilder().setBandwidth(new Bandwidth(itob)).build());
+        }
+
+        /*
+         * NOTE: Seems that ClassType is not supported by some routers. Skip it for the moment.
+         */
+        /* With Class Type if defined
+        if (iPath.getConstraints().getClassType() != null) {
+            args.setClassType(
+                new ClassTypeBuilder()
+                    .setClassType(new ClassType(iPath.getConstraints().getClassType()))
+                    .setIgnore(false)
+                    .setProcessingRule(true)
+                    .build());
+        }
+        */
+
+        /* Finally, build updateLSP input */
+        return new UpdateLspInputBuilder()
+                .setNode(teNode.getId())
+                .setName(cfgLsp.getName())
+                .setArguments(args.build())
+                .setNetworkTopologyRef(new NetworkTopologyRef(pcepTopology))
+                .build();
+    }
+
+    /**
+     * Call update-lsp RPC to enforce the LSP into the PCC. This action will trigger a PcUpdate message to the PCC.
+     *
+     * @param ntps  Network Topology PCEP Service
+     *
+     * @return      Update LSP Output to convey the RPC result
+     */
+    public ListenableFuture<RpcResult<UpdateLspOutput>> updatePath(final NetworkTopologyPcepService ntps) {
+
+        /* Check if we could update this path */
+        if ((type != PathType.Initiated && type != PathType.Delegated) || !teNode.isSync()) {
+            return null;
+        }
+
+        /* Check if we have a valid ERO */
+        if (cfgLsp.getComputedPath().getPathDescription() == null) {
+            return null;
+        }
+
+        sent = true;
+        final NodeId id = teNode.getId();
+        final ListenableFuture<RpcResult<UpdateLspOutput>> enforce = ntps.updateLsp(getUpdateLspInput());
+        LOG.info("Call Update LSP to {} with {}", ntps, enforce);
+        Futures.addCallback(enforce, new FutureCallback<RpcResult<UpdateLspOutput>>() {
+            @Override
+            public void onSuccess(final RpcResult<UpdateLspOutput> result) {
+                if (result.isSuccessful()) {
+                    LOG.debug("Update LSP success {}", result.getResult());
+                } else {
+                    LOG.debug("Unable to update LSP {} on Node {}: Got error {}", cfgLsp.getName(), id,
+                            result.getErrors());
+                }
+                sent = false;
+            }
+
+            @Override
+            public void onFailure(final Throwable throwable) {
+                LOG.warn("Failed update LSP {} on Node {}", cfgLsp.getName(), id);
+                sent = false;
+            }
+        }, MoreExecutors.directExecutor());
+
+        return enforce;
+    }
+
+    /**
+     * Call remove-lsp RPC to remove the LSP from the PCC. This action will trigger a PcInitiate message to the PCC
+     * with 'R' bit set.
+     *
+     * @param ntps  Network Topology PCEP Service
+     *
+     * @return      Remove LSP Output to convey the RPC result
+     */
+    public ListenableFuture<RpcResult<RemoveLspOutput>> removePath(final NetworkTopologyPcepService ntps) {
+
+        /* Check if we could remove this path */
+        if ((type != PathType.Initiated) || !teNode.isSync() || cfgLsp.getPathStatus() != PathStatus.Sync) {
+            return null;
+        }
+
+        sent = true;
+        final NodeId id = teNode.getId();
+        final RemoveLspInput rli = new RemoveLspInputBuilder()
+                .setNode(id)
+                .setName(cfgLsp.getName())
+                .setNetworkTopologyRef(new NetworkTopologyRef(pcepTopology))
+                .build();
+        final ListenableFuture<RpcResult<RemoveLspOutput>> enforce = ntps.removeLsp(rli);
+        LOG.info("Call Remove LSP to {} with {}", ntps, enforce);
+        Futures.addCallback(enforce, new FutureCallback<RpcResult<RemoveLspOutput>>() {
+            @Override
+            public void onSuccess(final RpcResult<RemoveLspOutput> result) {
+                if (result.isSuccessful()) {
+                    LOG.debug("Delete LSP success {}", result.getResult());
+                } else {
+                    LOG.debug("Unable to delete LSP {} on Node {}: Got error {}", cfgLsp.getName(), id,
+                            result.getErrors());
+                }
+                sent = false;
+            }
+
+            @Override
+            public void onFailure(final Throwable throwable) {
+                LOG.warn("Failed delete LSP {} on Node {}", cfgLsp.getName(), id);
+                sent = false;
+            }
+        }, MoreExecutors.directExecutor());
+
+        return enforce;
+    }
+
+    /**
+     * Add LSP components to the Operational Data Store.
+     */
+    public synchronized void addToDataStore() {
+        /* Check if we could add this path */
+        if (!teNode.isSync()) {
+            return;
+        }
+
+        final WriteTransaction trans = teNode.getTransaction();
+        trans.put(LogicalDatastoreType.OPERATIONAL, pccIdentifier.child(ConfiguredLsp.class, cfgLsp.key()), cfgLsp);
+        trans.commit().addCallback(new FutureCallback<CommitInfo>() {
+            @Override
+            public void onSuccess(final CommitInfo result) {
+                LOG.debug("Configured LSP {} has been published in operational datastore ", cfgLsp.getName());
+            }
+
+            @Override
+            public void onFailure(final Throwable throwable) {
+                LOG.error("Cannot write Configured LSP {} to the operational datastore (transaction: {})",
+                        cfgLsp.getName(), trans.getIdentifier());
+            }
+        }, MoreExecutors.directExecutor());
+    }
+
+    /**
+     * Update LSP components to the Data Store.
+     */
+    public synchronized void updateToDataStore() {
+        /* Check if we could update this path */
+        if (!teNode.isSync()) {
+            return;
+        }
+
+        final WriteTransaction trans = teNode.getTransaction();
+        trans.merge(LogicalDatastoreType.OPERATIONAL, pccIdentifier.child(ConfiguredLsp.class, cfgLsp.key()), cfgLsp);
+        trans.commit().addCallback(new FutureCallback<CommitInfo>() {
+            @Override
+            public void onSuccess(final CommitInfo result) {
+                LOG.debug("Configured LSP {} has been updated in operational datastore ", cfgLsp.getName());
+            }
+
+            @Override
+            public void onFailure(final Throwable throwable) {
+                LOG.error("Cannot update Configured LSP {} to the operational datastore (transaction: {})",
+                        cfgLsp.getName(), trans.getIdentifier());
+            }
+        }, MoreExecutors.directExecutor());
+    }
+
+    /**
+     * Remove LSP components to the Data Store.
+     */
+    public synchronized void removeFromDataStore() {
+        /* Check if we could remove this path */
+        if (!teNode.isSync()) {
+            return;
+        }
+
+        final WriteTransaction trans = teNode.getTransaction();
+        trans.delete(LogicalDatastoreType.OPERATIONAL, pccIdentifier.child(ConfiguredLsp.class, cfgLsp.key()));
+        trans.commit().addCallback(new FutureCallback<CommitInfo>() {
+            @Override
+            public void onSuccess(final CommitInfo result) {
+                LOG.debug("Configured LSP {} has been deleted in operational datastore ", cfgLsp.getName());
+            }
+
+            @Override
+            public void onFailure(final Throwable throwable) {
+                LOG.error("Cannot delete Configured LSP {} from the operational datastore (transaction: {})",
+                        cfgLsp.getName(), trans.getIdentifier());
+            }
+        }, MoreExecutors.directExecutor());
+    }
+
+    @Override
+    public String toString() {
+        final MoreObjects.ToStringHelper helper = MoreObjects.toStringHelper("ManagedTePath");
+        CodeHelpers.appendValue(helper, "ConfiguredLsp", cfgLsp);
+        CodeHelpers.appendValue(helper, "PathType", type);
+        CodeHelpers.appendValue(helper, "Sent", sent);
+        return helper.toString();
+    }
+}
index 1cd71f7650881493905ceaa873383ed7a7d917ed..9eff7b98693e13711422e1925fd3be085397352e 100644 (file)
@@ -29,6 +29,10 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.path.com
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.path.computation.rev200120.PathConstraints;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.path.computation.rev200120.PathConstraints.AddressFamily;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.path.computation.rev200120.get.constrained.path.input.ConstraintsBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.server.rev210720.RoutingType;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.server.rev210720.pcc.configured.lsp.configured.lsp.ComputedPath;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.server.rev210720.pcc.configured.lsp.configured.lsp.ComputedPathBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.server.rev210720.pcc.configured.lsp.configured.lsp.IntendedPath;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.types.rev181109.Message;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.types.rev181109.bandwidth.object.Bandwidth;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.types.rev181109.classtype.object.ClassType;
@@ -91,12 +95,12 @@ public class PathComputationImpl implements PathComputation {
 
         /* Determine Path Computation Algorithm according to Input choice */
         AlgorithmType algoType;
-        if (cts.getTeMetric() == null && cts.getDelay() == null) {
-            algoType = AlgorithmType.Spf;
-        } else if (cts.getDelay() == null) {
+        if (cts.getDelay() != null) {
+            algoType = AlgorithmType.Samcra;
+        } else if (cts.getTeMetric() != null) {
             algoType = AlgorithmType.Cspf;
         } else {
-            algoType = AlgorithmType.Samcra;
+            algoType = AlgorithmType.Spf;
         }
         PathComputationAlgorithm algo = algoProvider.getPathComputationAlgorithm(tedGraph, algoType);
         if (algo == null) {
@@ -118,14 +122,61 @@ public class PathComputationImpl implements PathComputation {
         }
     }
 
+    public ComputedPath computeTePath(final IntendedPath intend) {
+        final ComputedPathBuilder cpb = new ComputedPathBuilder();
+        ConnectedVertex source = tedGraph.getConnectedVertex(intend.getSource());
+        ConnectedVertex destination = tedGraph.getConnectedVertex(intend.getDestination());
+
+        if (source == null || destination == null) {
+            return cpb.setComputationStatus(ComputationStatus.Failed).build();
+        }
+
+        /* Determine Path Computation Algorithm according to parameters */
+        AlgorithmType algoType;
+        final RoutingType rt = intend.getRoutingMethod();
+        switch (rt) {
+            case Metric:
+                algoType = AlgorithmType.Spf;
+                break;
+            case TeMetric:
+                algoType = AlgorithmType.Cspf;
+                break;
+            case Delay:
+                algoType = AlgorithmType.Samcra;
+                break;
+            default:
+                algoType = AlgorithmType.Spf;
+                break;
+        }
+        PathComputationAlgorithm algo = algoProvider.getPathComputationAlgorithm(tedGraph, algoType);
+        if (algo == null) {
+            return cpb.setComputationStatus(ComputationStatus.Failed).build();
+        }
+
+        /* Request Path Computation for given source, destination and constraints */
+        final ConstrainedPath cpath = algo.computeP2pPath(source.getVertex().key(), destination.getVertex().key(),
+                intend.getConstraints());
+
+        LOG.info("Computed path: {}", cpath.getPathDescription());
+
+        /* Check if we got a valid Path and return appropriate Path Description */
+        if (cpath.getStatus() == ComputationStatus.Completed) {
+            return cpb.setPathDescription(cpath.getPathDescription()).setComputationStatus(ComputationStatus.Completed)
+                    .build();
+        } else {
+            return cpb.setComputationStatus(ComputationStatus.Failed).build();
+        }
+    }
+
     @Override
     public Ero computeEro(final EndpointsObj endpoints, final Bandwidth bandwidth, final ClassType classType,
             final List<Metrics> metrics, final boolean segmentRouting) {
+        /* Get source and destination Vertex and verify there are valid */
         VertexKey source = getSourceVertexKey(endpoints);
-        VertexKey destination = getDestinationVertexKey(endpoints);
         if (source == null) {
             return null;
         }
+        VertexKey destination = getDestinationVertexKey(endpoints);
         if (destination == null) {
             return null;
         }
@@ -134,12 +185,12 @@ public class PathComputationImpl implements PathComputation {
 
         /* Determine Path Computation Algorithm according to parameters */
         AlgorithmType algoType;
-        if (cts.getTeMetric() == null && cts.getDelay() == null && cts.getBandwidth() == null) {
-            algoType = AlgorithmType.Spf;
-        } else if (cts.getDelay() == null) {
+        if (cts.getDelay() != null) {
+            algoType = AlgorithmType.Samcra;
+        } else if (cts.getTeMetric() != null) {
             algoType = AlgorithmType.Cspf;
         } else {
-            algoType = AlgorithmType.Samcra;
+            algoType = AlgorithmType.Spf;
         }
         PathComputationAlgorithm algo = algoProvider.getPathComputationAlgorithm(tedGraph, algoType);
         if (algo == null) {
@@ -152,7 +203,7 @@ public class PathComputationImpl implements PathComputation {
          */
         final ConstrainedPath cpath = algo.computeP2pPath(source, destination, cts);
 
-        LOG.info("Computed ERO: {}", cpath.getPathDescription());
+        LOG.info("Computed path: {}", cpath.getPathDescription());
 
         /* Check if we got a valid Path and return appropriate ERO */
         if (cpath.getStatus() == ComputationStatus.Completed) {
diff --git a/pcep/server/server-provider/src/main/java/org/opendaylight/bgpcep/pcep/server/provider/PathManagerListener.java b/pcep/server/server-provider/src/main/java/org/opendaylight/bgpcep/pcep/server/provider/PathManagerListener.java
new file mode 100644 (file)
index 0000000..313d6c9
--- /dev/null
@@ -0,0 +1,162 @@
+/*
+ * Copyright (c) 2021 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.bgpcep.pcep.server.provider;
+
+import static java.util.Objects.requireNonNull;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.stream.Collectors;
+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.pcep.server.rev210720.PcepNodeConfig;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.server.rev210720.pcc.configured.lsp.ConfiguredLsp;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.Topology;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.TopologyKey;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.binding.KeyedInstanceIdentifier;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * This Class Implements the DataStoreService interface providing the methods required to manage the path
+ * representation elements in the Data Store.
+ *
+ * @author Olivier Dugeon
+ */
+
+public final class PathManagerListener implements DataTreeChangeListener<Node>, AutoCloseable {
+    private static final Logger LOG = LoggerFactory.getLogger(PathManagerListener.class);
+    private ListenerRegistration<PathManagerListener> listenerRegistration;
+
+    private final PathManagerProvider pathManager;
+
+    public PathManagerListener(final DataBroker dataBroker, KeyedInstanceIdentifier<Topology, TopologyKey> topology,
+            final PathManagerProvider pathManager) {
+        requireNonNull(dataBroker);
+        requireNonNull(topology);
+        this.pathManager = requireNonNull(pathManager);
+        final InstanceIdentifier<Node> nodeTopology = topology.child(Node.class);
+        this.listenerRegistration = dataBroker.registerDataTreeChangeListener(
+                DataTreeIdentifier.create(LogicalDatastoreType.CONFIGURATION, nodeTopology), this);
+        LOG.info("Registered listener for Managed TE Path on Topology {}",
+                topology.getKey().getTopologyId().getValue());
+    }
+
+    /**
+     * Close this Listener.
+     */
+    @Override
+    public void close() {
+        if (this.listenerRegistration != null) {
+            LOG.debug("Unregistered listener {} for Managed TE Path", this);
+            this.listenerRegistration.close();
+            this.listenerRegistration = null;
+        }
+    }
+
+    /**
+     * Handle Configured LSP modifications.
+     *
+     * @param nodeId    Node Identifier to which the modified children belongs to.
+     * @param lspMod    List of Configured LSP modifications.
+     */
+    private void handleLspChange(NodeId nodeId, List<? extends DataObjectModification<? extends DataObject>> lspMod) {
+        for (DataObjectModification<? extends DataObject> lsp : lspMod) {
+            ConfiguredLsp cfgLsp;
+            switch (lsp.getModificationType()) {
+                case DELETE:
+                    cfgLsp = (ConfiguredLsp) lsp.getDataBefore();
+                    LOG.debug("Un-Register Managed TE Path: {}", cfgLsp.getName());
+                    pathManager.deleteManagedTePath(nodeId, cfgLsp.key());
+                    break;
+                case SUBTREE_MODIFIED:
+                case WRITE:
+                    cfgLsp = (ConfiguredLsp) lsp.getDataAfter();
+                    LOG.debug("Register Managed TE Path {}", cfgLsp.getName());
+                    pathManager.createManagedTePath(nodeId, cfgLsp);
+                    break;
+                default:
+                    break;
+            }
+
+        }
+    }
+
+    /**
+     * Parse Sub Tree modification. Given list has been filtered to get only Path Computation Client1 modifications.
+     * This function first create, update or delete Managed TE Node that corresponds to the given NodeId. Then, it
+     * filter the children to retain only the Configured LSP modifications.
+     *
+     * @param nodeId    Node Identifier to which the modified children belongs to.
+     * @param pccMod    List of PCEP Node Configuration modifications.
+     */
+    private void handlePccChange(NodeId nodeId, List<? extends DataObjectModification<? extends DataObject>> pccMod) {
+        for (DataObjectModification<? extends DataObject> node : pccMod) {
+            /* First, process PCC modification */
+            switch (node.getModificationType()) {
+                case DELETE:
+                    LOG.debug("Delete Managed TE Node: {}", nodeId);
+                    pathManager.deleteManagedTeNode(nodeId);
+                    break;
+                case SUBTREE_MODIFIED:
+                case WRITE:
+                    /* First look if the Managed TE Node belongs to this PCC was not already created */
+                    final PcepNodeConfig pccNode = (PcepNodeConfig) node.getDataAfter();
+                    if (!pathManager.checkManagedTeNode(nodeId)) {
+                        LOG.info("Create new Managed Node {}", nodeId);
+                        pathManager.createManagedTeNode(nodeId, pccNode);
+                    } else {
+                        /* Then, look to Configured LSP modification */
+                        final List<DataObjectModification<? extends DataObject>> lspMod = node.getModifiedChildren()
+                                .stream().filter(mod -> mod.getDataType().equals(ConfiguredLsp.class))
+                                .collect(Collectors.toList());
+                        if (!lspMod.isEmpty()) {
+                            handleLspChange(nodeId, lspMod);
+                        }
+                    }
+                    break;
+                default:
+                    break;
+            }
+        }
+    }
+
+    @Override
+    public void onDataTreeChanged(final Collection<DataTreeModification<Node>> changes) {
+        for (DataTreeModification<Node> change : changes) {
+            DataObjectModification<Node> root = change.getRootNode();
+
+            final String nodeAddr = root.getModificationType() == DataObjectModification.ModificationType.DELETE
+                    ? root.getDataBefore().getNodeId().getValue()
+                    : root.getDataAfter().getNodeId().getValue();
+            NodeId nodeId;
+            if (nodeAddr.startsWith("pcc://")) {
+                nodeId = new NodeId(nodeAddr);
+            } else {
+                nodeId = new NodeId("pcc://" + nodeAddr);
+            }
+
+            /* Look only to PcepNodeConfig.class modification */
+            final List<DataObjectModification<? extends DataObject>> pccMod = root.getModifiedChildren().stream()
+                    .filter(mod -> mod.getDataType().equals(PcepNodeConfig.class)).collect(Collectors.toList());
+            if (!pccMod.isEmpty()) {
+                handlePccChange(nodeId, pccMod);
+            }
+        }
+    }
+}
+
diff --git a/pcep/server/server-provider/src/main/java/org/opendaylight/bgpcep/pcep/server/provider/PathManagerProvider.java b/pcep/server/server-provider/src/main/java/org/opendaylight/bgpcep/pcep/server/provider/PathManagerProvider.java
new file mode 100644 (file)
index 0000000..4e1da8e
--- /dev/null
@@ -0,0 +1,639 @@
+/*
+ * Copyright (c) 2021 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.bgpcep.pcep.server.provider;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkState;
+import static java.util.Objects.requireNonNull;
+
+import java.util.HashMap;
+import java.util.Map;
+import javax.annotation.PreDestroy;
+import org.opendaylight.bgpcep.pcep.server.PceServerProvider;
+import org.opendaylight.mdsal.binding.api.DataBroker;
+import org.opendaylight.mdsal.binding.api.Transaction;
+import org.opendaylight.mdsal.binding.api.TransactionChain;
+import org.opendaylight.mdsal.binding.api.TransactionChainListener;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.path.computation.rev200120.ComputationStatus;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.server.rev210720.PathStatus;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.server.rev210720.PathType;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.server.rev210720.PcepNodeConfig;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.server.rev210720.pcc.configured.lsp.ConfiguredLsp;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.server.rev210720.pcc.configured.lsp.ConfiguredLspBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.server.rev210720.pcc.configured.lsp.ConfiguredLspKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.server.rev210720.pcc.configured.lsp.configured.lsp.ComputedPathBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.server.rev210720.pcc.configured.lsp.configured.lsp.IntendedPath;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.server.rev210720.pcc.configured.lsp.configured.lsp.IntendedPathBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.topology.pcep.rev200120.NetworkTopologyPcepService;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.Topology;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.TopologyKey;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.binding.KeyedInstanceIdentifier;
+import org.osgi.service.component.annotations.Deactivate;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * This Class implements the Path Manager in charge of Managed TE Node and Managed TE Path.
+ *
+ * @author Olivier Dugeon
+ */
+
+public final class PathManagerProvider implements TransactionChainListener, AutoCloseable {
+    private static final Logger LOG = LoggerFactory.getLogger(PathManagerProvider.class);
+    private final InstanceIdentifier<Topology> pcepTopology;
+    private final DataBroker dataBroker;
+    private final PceServerProvider pceServerProvider;
+    private final NetworkTopologyPcepService ntps;
+    private TransactionChain chain = null;
+
+    private final Map<NodeId, ManagedTeNode> mngNodes = new HashMap<NodeId, ManagedTeNode>();
+
+    public PathManagerProvider(final DataBroker dataBroker, KeyedInstanceIdentifier<Topology, TopologyKey> topology,
+            final NetworkTopologyPcepService ntps, final PceServerProvider pceServerProvider) {
+        this.dataBroker = requireNonNull(dataBroker);
+        this.pceServerProvider = requireNonNull(pceServerProvider);
+        this.ntps = requireNonNull(ntps);
+        this.pcepTopology = requireNonNull(topology);
+        initTransactionChain();
+        LOG.info("Path Manager Server started for topology {}", topology.getKey().getTopologyId().getValue());
+    }
+
+    /**
+     * Remove the Path Manager Server and destroy the transaction chain.
+     */
+    @Override
+    @Deactivate
+    @PreDestroy
+    public void close() {
+        destroyTransactionChain();
+    }
+
+    /**
+     * Reset a transaction chain by closing the current chain and starting a new one.
+     */
+    private synchronized void initTransactionChain() {
+        LOG.debug("Initializing transaction chain for Path Manager Server {}", this);
+        checkState(this.chain == null, "Transaction chain has to be closed before being initialized");
+        this.chain = dataBroker.createMergingTransactionChain(this);
+    }
+
+    /**
+     * Destroy the current transaction chain.
+     */
+    private synchronized void destroyTransactionChain() {
+        if (this.chain != null) {
+            LOG.debug("Destroy transaction chain for Path Manager {}", 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 Path Manager");
+        destroyTransactionChain();
+        initTransactionChain();
+    }
+
+    @Override
+    public synchronized void onTransactionChainFailed(final TransactionChain transactionChain,
+            final Transaction transaction, final Throwable cause) {
+        LOG.error("Path Manager Provider for {} failed in transaction: {} ", pcepTopology,
+                transaction != null ? transaction.getIdentifier() : null, cause);
+    }
+
+    @Override
+    public void onTransactionChainSuccessful(final TransactionChain transactionChain) {
+        LOG.info("Path Manager Provider for {} shut down", pcepTopology);
+    }
+
+    /**
+     * Setup Managed TE Path to existing Managed Node.
+     *
+     * @param id        Managed Node ID where the TE Path will be enforced
+     * @param lsp    TE Path to be inserted in the Managed Node
+     *
+     * @return          Newly created Managed TE Path
+     */
+    private ManagedTePath addManagedTePath(final ManagedTeNode teNode, final ConfiguredLsp lsp) {
+        checkArgument(teNode != null, "Provided Managed TE Node is a null object");
+        checkArgument(lsp != null, "Provided TE Path is a null object");
+
+        LOG.info("Setup TE Path {} for Node {}", lsp.getName(), teNode.getId());
+
+        /* Complete the LSP with the Computed Route */
+        ConfiguredLspBuilder clb = new ConfiguredLspBuilder(lsp)
+                .setPathStatus(PathStatus.Configured);
+        final PathComputationImpl pci = (PathComputationImpl) pceServerProvider.getPathComputation();
+        if (pci != null) {
+            clb.setComputedPath(pci.computeTePath(lsp.getIntendedPath()));
+        } else {
+            clb.setComputedPath(new ComputedPathBuilder().setComputationStatus(ComputationStatus.Failed).build());
+        }
+
+        /* Create Corresponding Managed LSP */
+        final ManagedTePath mngLsp = new ManagedTePath(teNode, clb.build(), pcepTopology).setType(PathType.Initiated);
+
+        /* Store this new Managed TE Node */
+        teNode.addManagedTePath(mngLsp);
+
+        /* Then, setup Path on PCC if it is synchronized */
+        if (teNode.isSync()) {
+            mngLsp.addPath(ntps);
+        }
+
+        LOG.debug("Added new Managed LSP: {}", mngLsp);
+        return mngLsp;
+    }
+
+    /**
+     * Update TE Path to existing Managed Node.
+     *
+     * @param id       Managed Node ID where the TE Path will be updated
+     * @param mngPath  Managed TE Path to be updated
+     * @param tePath   New TE Path to be updated in the Managed Node
+     */
+    private ConfiguredLsp updateManagedTePath(final ManagedTePath mngPath, final ConfiguredLsp tePath) {
+        checkArgument(mngPath != null, "Provided Managed TE Path is a null object");
+        checkArgument(tePath != null, "Provided TE Path is a null object");
+
+        final ManagedTeNode teNode = mngPath.getManagedTeNode();
+        final IntendedPath iPath = tePath.getIntendedPath();
+        final IntendedPath oPath = mngPath.getLsp().getIntendedPath();
+        IntendedPathBuilder ipb = new IntendedPathBuilder(iPath);
+
+        LOG.info("Update TE Path {} for Node {}", mngPath.getLsp().getName(), teNode.getId());
+
+        /* Check that Source and Destination have not been modified and revert to old value instead */
+        if (!iPath.getSource().equals(oPath.getSource())) {
+            LOG.warn("Source IP Address {}/{} of TE Path has been modified. Revert to initial one",
+                    iPath.getSource(), oPath.getSource());
+            ipb.setSource(oPath.getSource());
+        }
+        if (!iPath.getDestination().equals(oPath.getDestination())) {
+            LOG.warn("Destination IP Address {}/{} of TE Path has been modified. Revert to initial one",
+                    iPath.getDestination(), oPath.getDestination());
+            ipb.setDestination(oPath.getDestination());
+        }
+        /*
+         * Same for Routing Method: i.e. refused to change a TE Path from
+         * RSVP-TE to Segment Routing and vice versa
+         */
+        if (!iPath.getRoutingMethod().equals(oPath.getRoutingMethod())) {
+            LOG.warn("Routing Method {}/{} of TE Path has been modified. Revert to initial one",
+                    iPath.getRoutingMethod(), oPath.getRoutingMethod());
+            ipb.setRoutingMethod(oPath.getRoutingMethod());
+        }
+
+        /* Create updated TE Path */
+        ConfiguredLspBuilder clb = new ConfiguredLspBuilder(tePath)
+                .setIntendedPath(ipb.build())
+                .setPathStatus(PathStatus.Updated);
+        /* Complete it with the new Computed Route */
+        final PathComputationImpl pci = (PathComputationImpl) pceServerProvider.getPathComputation();
+        if (pci != null) {
+            clb.setComputedPath(pci.computeTePath(tePath.getIntendedPath()));
+        } else {
+            clb.setComputedPath(new ComputedPathBuilder().setComputationStatus(ComputationStatus.Failed).build());
+        }
+
+        /* Finally, update the new TE Path for this Node ID */
+        mngPath.setConfiguredLsp(clb.build());
+        mngPath.updateToDataStore();
+
+        /* Finally, update Path on PCC if it is synchronized and we computed a valid path */
+        if (teNode.isSync()) {
+            mngPath.updatePath(ntps);
+        }
+
+        LOG.debug("Updated Managed Paths: {}", mngPath);
+        return mngPath.getLsp();
+    }
+
+    /**
+     * Create a new Managed TE Path.
+     *
+     * @param id        Managed TE Node Identifier to which the TE path is attached.
+     * @param cfgLsp    TE Path.
+     *
+     * @return          new or updated TE Path i.e. original TE Path augmented by a valid computed route.
+     */
+    public ConfiguredLsp createManagedTePath(final NodeId id, ConfiguredLsp cfgLsp) {
+        checkArgument(id != null, "Provided Node ID is a null object");
+        checkArgument(cfgLsp != null, "Provided TE Path is a null object");
+
+        /* Check that Managed Node is registered */
+        final ManagedTeNode teNode = mngNodes.get(id);
+        if (teNode == null) {
+            LOG.warn("Managed TE Node {} is not registered. Cancel transaction!", id);
+            return null;
+        }
+
+        /* Check if TE Path already exist or not */
+        ManagedTePath tePath = teNode.getManagedTePath(cfgLsp.key());
+        if (tePath != null) {
+            updateManagedTePath(tePath, cfgLsp);
+            tePath.updateToDataStore();
+        } else {
+            tePath = addManagedTePath(teNode, cfgLsp);
+            tePath.addToDataStore();
+        }
+
+        return tePath.getLsp();
+    }
+
+    /**
+     * Remove TE Path to existing Managed Node. This method is called when a TE Path is deleted.
+     *
+     * @param id   Managed Node ID where the TE Path is stored
+     * @param key  TE Path, as Key, to be removed
+     */
+    private void removeTePath(final NodeId id, final ConfiguredLspKey key) {
+        LOG.info("Remove TE Path {} for Node {}", key, id);
+
+        /* Check that Managed Node is registered */
+        final ManagedTeNode teNode = mngNodes.get(id);
+        if (teNode == null) {
+            LOG.warn("Managed TE Node {} is not registered. Cancel transaction!", id);
+            return;
+        }
+
+        /* Get corresponding TE Path from the TE Node */
+        ManagedTePath mngPath = teNode.getManagedTePath(key);
+        if (mngPath == null) {
+            LOG.warn("Doesn't found Managed TE Path {} for TE Node {}. Abort delete operation", key, id);
+            return;
+        }
+
+        /*
+         * Delete TE Path on PCC node if it is synchronized, TE Path is Initiated and is enforced on the PCC.
+         * TE Path will be removed from Data Store once received the PcReport.
+         */
+        if (teNode.isSync() && mngPath.getType() == PathType.Initiated
+                && mngPath.getLsp().getPathStatus() == PathStatus.Sync) {
+            mngPath.removePath(ntps);
+        }
+
+        /*
+         * If TE Path is not Initiated or there is a failure to remove it on PCC,
+         * remove immediately TE Path from the Data Store.
+         */
+        if (!mngPath.isSent()) {
+            unregisterTePath(id, key);
+        }
+    }
+
+    /**
+     * Remove TE Path to existing Managed Node if TE Path has been initiated by the PCE server.
+     *
+     * @param id   Managed Node ID where the TE Path is stored
+     * @param key  TE Path, as Key, to be removed
+     */
+    public void deleteManagedTePath(final NodeId id, final ConfiguredLspKey key) {
+        checkArgument(id != null, "Provided Node ID is a null object");
+        checkArgument(key != null, "Provided TE Path Key is a null object");
+
+        /* Check that Managed Node is registered */
+        final ManagedTeNode teNode = mngNodes.get(id);
+        if (teNode == null) {
+            LOG.warn("Managed TE Node {} is not registered. Cancel transaction!", id);
+            return;
+        }
+
+        ManagedTePath mngPath = teNode.getManagedTePath(key);
+        if (mngPath == null) {
+            LOG.warn("Managed TE Path {} for TE Node {} doesn't exist", key, id);
+            return;
+        }
+
+        /*
+         * Start by sending corresponding Message to PCC if TE Path is initiated.
+         * TE Path will be removed when PCC confirm the deletion with PcReport.
+         * If TE Path is not initiated, the TE Path should be removed by the PCC
+         * by sending appropriate PcReport which is handle in unregisterTePath.
+         */
+        if (teNode.isSync() && mngPath.getType() == PathType.Initiated) {
+            removeTePath(id, key);
+        } else {
+            LOG.warn("Managed TE Path {} for TE Node {} is not managed by this PCE. Remove only configuration",
+                    key, id);
+        }
+    }
+
+    /**
+     * Register Reported LSP as a TE Path for the PCC identified by its Node ID.
+     *
+     * @param id        Node ID of the Managed Node (PCC) which report this LSP
+     * @param rptPath   Reported TE Path
+     *
+     * @return          Newly created or Updated Managed TE Path
+     */
+    public ManagedTePath registerTePath(NodeId id, final ConfiguredLsp rptPath, final PathType ptype) {
+        checkArgument(id != null, "Provided Node ID is a null object");
+
+        /* Verify we got a valid reported TE Path */
+        if (rptPath == null) {
+            return null;
+        }
+
+        /* Check that Managed Node is registered */
+        final ManagedTeNode teNode = mngNodes.get(id);
+        if (teNode == null) {
+            LOG.warn("Managed TE Node {} is not registered. Cancel transaction!", id);
+            return null;
+        }
+
+        LOG.info("Registered TE Path {} for Node {}", rptPath, id);
+
+        /* Look for existing corresponding Managed TE Path */
+        final ManagedTePath curPath = teNode.getManagedTePath(rptPath.key());
+
+        if (curPath == null) {
+            final ManagedTePath newPath = new ManagedTePath(teNode, pcepTopology).setType(ptype);
+            final ConfiguredLspBuilder clb = new ConfiguredLspBuilder(rptPath);
+
+            /* Check if ERO needs to be updated i.e. Path Description is empty */
+            if (rptPath.getComputedPath().getPathDescription() == null) {
+                clb.setPathStatus(PathStatus.Updated);
+                /* Complete the TE Path with Computed Route */
+                final PathComputationImpl pci = (PathComputationImpl) pceServerProvider.getPathComputation();
+                if (pci != null) {
+                    clb.setComputedPath(pci.computeTePath(rptPath.getIntendedPath()));
+                } else {
+                    clb.setComputedPath(
+                            new ComputedPathBuilder().setComputationStatus(ComputationStatus.Failed).build());
+                }
+
+                /* Finally, update the new TE Path for this Node ID */
+                newPath.setConfiguredLsp(clb.build());
+
+                /* and update Path on PCC if it is synchronized */
+                if (teNode.isSync()) {
+                    newPath.updatePath(ntps);
+                }
+            } else {
+                /* Mark this TE Path as Synchronous and add it to the Managed TE Path */
+                clb.setPathStatus(PathStatus.Sync);
+                newPath.setConfiguredLsp(clb.build());
+            }
+
+            /* Store this new reported TE Path */
+            teNode.addManagedTePath(newPath);
+
+            LOG.debug("Created new Managed TE Path: {}", newPath);
+            return newPath;
+        }
+
+        /* Check this TE Path against current configuration */
+        final PathStatus newStatus = curPath.checkReportedPath(rptPath);
+        LOG.debug("Managed TE Path {} got new status {}", curPath.getLsp().getName(), newStatus);
+
+        /* Check if we should stop here. i.e. the Path is failed */
+        if (newStatus == PathStatus.Failed) {
+            curPath.setConfiguredLsp(new ConfiguredLspBuilder(rptPath).setPathStatus(PathStatus.Failed).build());
+            curPath.updateToDataStore();
+            LOG.debug("Managed TE Path {} is in Failure", curPath);
+            return curPath;
+        }
+
+        /* Check if Current Path has no valid route while Reported Path has one */
+        if ((curPath.getLsp().getComputedPath().getPathDescription() == null)
+                && (rptPath.getComputedPath().getPathDescription() != null)) {
+            curPath.setConfiguredLsp(new ConfiguredLspBuilder(rptPath).setPathStatus(PathStatus.Sync).build());
+            curPath.updateToDataStore();
+            LOG.debug("Updated Managed TE Path with reported LSP: {}", curPath);
+            return curPath;
+        }
+
+        /* Check if we need to update the TE Path */
+        if (teNode.isSync() && newStatus == PathStatus.Updated) {
+            curPath.updatePath(ntps);
+            LOG.debug("Updated Managed TE Path {} on NodeId {}", curPath, id);
+            return curPath;
+        }
+
+        /* Check if TE Path becoming in SYNC */
+        if (newStatus == PathStatus.Sync && curPath.getLsp().getPathStatus() != PathStatus.Sync) {
+            curPath.sync();
+            LOG.debug("Sync Managed TE Path {} on NodeId {}", curPath, id);
+            return curPath;
+        }
+
+        /* Managed Path is already in SYNC, nothing to do */
+        return curPath;
+    }
+
+    /**
+     * Remove TE Path from Operational Data Store and Path Manager.
+     *
+     * @param id    Node ID of the Managed Node which own this TE Path
+     * @param key   TE Path name
+     */
+    public void unregisterTePath(final NodeId id, final ConfiguredLspKey key) {
+        checkArgument(id != null, "Provided Node ID is a null object");
+        checkArgument(key != null, "Provided TE Path Key is a null object");
+
+        /* Verify that Node is managed by the PCE Server */
+        final ManagedTeNode teNode = mngNodes.get(id);
+        if (teNode == null) {
+            LOG.warn("There is no Managed TE Node entry for this PCC {}", id);
+            return;
+        }
+
+        teNode.removeManagedTePath(key);
+    }
+
+    /**
+     * Indicate that the TE Path is failed following reception of a PCE Error message.
+     *
+     * @param id    Node ID of the Managed Node which own this TE Path
+     * @param key   TE Path name
+     */
+    public void setTePathFailed(final NodeId id, final ConfiguredLspKey key) {
+        checkArgument(id != null, "Provided Node ID is a null object");
+        checkArgument(key != null, "Provided TE Path Key is a null object");
+
+        /* Verify that Node is managed by the PCE Server */
+        final ManagedTeNode teNode = mngNodes.get(id);
+        if (teNode == null) {
+            LOG.warn("There is no Managed TE Node entry for this PCC {}", id);
+            return;
+        }
+
+        /* Get Corresponding TE Path */
+        ManagedTePath mngPath = teNode.getManagedTePath(key);
+        if (mngPath != null) {
+            mngPath.failed();
+        } else {
+            LOG.warn("TE Path {} for Node {} doesn't exist", key, id);
+        }
+    }
+
+    /**
+     * Check if a Managed TE Node is controlled by the Path Manager.
+     *
+     * @param id    Node ID of the Managed TE Node
+     *
+     * @return      True if Managed TE Node exist, false otherwise
+     */
+    public boolean checkManagedTeNode(final NodeId id) {
+        return (mngNodes.get(id) != null);
+    }
+
+    /**
+     * Create new Managed TE Node. This method is called by a new Managed Node is created in the Configuration
+     * Data Store. All TE Path associated to this Managed Node are also created. A new Managed Node, with TE Paths
+     * augmented with valid computed routes, is stored in the Operational Data Store.
+     *
+     * @param nodeId  Managed TE Node Identifier
+     * @param pccNode Path Computation Client
+     *
+     * @return        New Managed TE Node.
+     */
+    public synchronized ManagedTeNode createManagedTeNode(final NodeId nodeId, final PcepNodeConfig pccNode) {
+        checkArgument(pccNode != null, "Provided Managed TE Node is a null object");
+
+        /* First, create new Managed TE Node */
+        ManagedTeNode teNode = new ManagedTeNode(nodeId, chain);
+        mngNodes.put(nodeId, teNode);
+
+        /* Then, create all TE Paths for this Managed Node */
+        if (pccNode.getConfiguredLsp() != null) {
+            for (ConfiguredLsp tePath: pccNode.getConfiguredLsp().values()) {
+                addManagedTePath(teNode, tePath);
+            }
+        }
+
+        LOG.info("Created new Managed TE Node {}", nodeId);
+
+        return teNode;
+    }
+
+    /**
+     * Register a PCC as a new Managed TE Node. This method is called by the PCEP Topology Listener when a new PCC
+     * connects to the PCE Server.
+     *
+     * @param id    Node ID of the PCC
+     *
+     * @return      current or new Managed TE Node
+     */
+    public synchronized ManagedTeNode registerManagedTeNode(final NodeId id) {
+        checkArgument(id != null, "Provided Managed Node ID is a null object");
+
+        ManagedTeNode teNode = mngNodes.get(id);
+        /* Create new Managed TE Node if not already exist */
+        if (teNode == null) {
+            teNode = new ManagedTeNode(id, chain);
+            mngNodes.put(id, teNode);
+            LOG.debug("Created new Managed TE Node: {}", teNode);
+        }
+        return teNode;
+    }
+
+    /**
+     * Synchronized Managed TE Node. Once PCC finished initial report of all LSP, its state change to Synchronized.
+     * This function update the Managed TE Node status, and then parse all reported LSPs to determine if:
+     *  - There is missing LSPs that need to be setup
+     *  - There is LSPs that need to be updated
+     *
+     * @param id    Node ID of the Managed TE Node
+     */
+    public void syncManagedTeNode(final NodeId id) {
+        checkArgument(id != null, "Provided Managed Node ID is a null object");
+
+        /* Verify that Node is managed by the PCE Server */
+        final ManagedTeNode teNode = mngNodes.get(id);
+        if (teNode == null) {
+            LOG.warn("There is no Managed TE Node entry for this PCC {}", id);
+            return;
+        }
+
+        if (teNode.isSync()) {
+            LOG.debug("PCC {} is already synchronised", id);
+            return;
+        }
+
+        /* First, mark the Node as Synchronous */
+        teNode.sync();
+
+        /*
+         * PCC is synchronized, browse all TE Path to check if:
+         *  - some are missing i.e. apply previously initiated paths that have been created before the PCC connects
+         *  - some need update i.e. apply previous modifications
+         */
+        for (ManagedTePath mngPath : teNode.getTePaths().values()) {
+            switch (mngPath.getLsp().getPathStatus()) {
+                case Updated:
+                    mngPath.updatePath(ntps);
+                    break;
+                case Configured:
+                    mngPath.addPath(ntps);
+                    break;
+                default:
+                    break;
+            }
+        }
+    }
+
+    /**
+     * Delete Managed TE Node. This method is called when a Managed Node is removed from the Configuration Data Store.
+     * All initiated Managed TE Path own by this PCC are removed and corresponding Managed Node is removed from the
+     * Operational Data Store if it is not connected.
+     *
+     * @param nodeId  Managed Node Identifier
+     */
+    public void deleteManagedTeNode(final NodeId nodeId) {
+        checkArgument(nodeId != null, "Provided Node Identifie is a null object");
+
+        /* Verify that Node is managed by the PCE Server */
+        final ManagedTeNode teNode = mngNodes.get(nodeId);
+        if (teNode == null) {
+            LOG.warn("Unknown Managed TE Node {}. Abort!", nodeId);
+            return;
+        }
+
+        /* Remove all associated TE Paths that are managed by the PCE */
+        for (ManagedTePath mngPath: teNode.getTePaths().values()) {
+            if (mngPath.getType() == PathType.Initiated) {
+                removeTePath(nodeId, mngPath.getLsp().key());
+            }
+        }
+
+        /* Remove Managed Node from PCE Server if it is not connected */
+        if (!teNode.isSync()) {
+            mngNodes.remove(nodeId);
+        } else {
+            LOG.warn("Node {} is still connected. Keep Node in PCE Server.", nodeId);
+        }
+    }
+
+    /**
+     * Call when a PCC disconnect from the PCE to disable the corresponding Managed TE Node.
+     *
+     * @param id    Managed Node ID
+     */
+    public void disableManagedTeNode(final NodeId id) {
+        checkArgument(id != null, "Provided Node ID is a null object");
+
+        /* Verify that Node is managed by the PCE Server */
+        final ManagedTeNode teNode = mngNodes.get(id);
+        if (teNode == null) {
+            LOG.warn("Unknown Managed TE Node {}. Abort!", id);
+            return;
+        }
+
+        /* And mark the Node as disable */
+        teNode.disable();
+    }
+
+}
diff --git a/pcep/server/server-provider/src/main/java/org/opendaylight/bgpcep/pcep/server/provider/PcepTopologyListener.java b/pcep/server/server-provider/src/main/java/org/opendaylight/bgpcep/pcep/server/provider/PcepTopologyListener.java
new file mode 100644 (file)
index 0000000..e00b8dd
--- /dev/null
@@ -0,0 +1,498 @@
+/*
+ * Copyright (c) 2021 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.bgpcep.pcep.server.provider;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.collect.Iterables;
+import java.math.BigDecimal;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.stream.Collectors;
+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.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.Ipv4Address;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv6Address;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.graph.rev191125.DecimalBandwidth;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.graph.rev191125.Delay;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.path.computation.rev200120.PathConstraints.AddressFamily;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.path.computation.rev200120.path.descriptions.PathDescription;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.path.computation.rev200120.path.descriptions.PathDescriptionBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.ietf.initiated.rev200720.Lsp1;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.ietf.stateful.rev200720.Path1;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.ietf.stateful.rev200720.lsp.identifiers.tlv.lsp.identifiers.address.family.Ipv4Case;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.ietf.stateful.rev200720.lsp.identifiers.tlv.lsp.identifiers.address.family.Ipv6Case;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.ietf.stateful.rev200720.lsp.identifiers.tlv.lsp.identifiers.address.family.ipv4._case.Ipv4;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.ietf.stateful.rev200720.lsp.identifiers.tlv.lsp.identifiers.address.family.ipv6._case.Ipv6;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.ietf.stateful.rev200720.lsp.object.Lsp;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.segment.routing.rev200720.SrSubobject;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.segment.routing.rev200720.sr.subobject.nai.IpAdjacency;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.segment.routing.rev200720.sr.subobject.nai.IpNodeId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.server.rev210720.PathStatus;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.server.rev210720.PathType;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.server.rev210720.RoutingType;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.server.rev210720.pcc.configured.lsp.ConfiguredLsp;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.server.rev210720.pcc.configured.lsp.ConfiguredLspBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.server.rev210720.pcc.configured.lsp.ConfiguredLspKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.server.rev210720.pcc.configured.lsp.configured.lsp.ComputedPathBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.server.rev210720.pcc.configured.lsp.configured.lsp.IntendedPathBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.server.rev210720.pcc.configured.lsp.configured.lsp.intended.path.ConstraintsBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.types.rev181109.explicit.route.object.Ero;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.types.rev181109.lsp.attributes.Metrics;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.types.rev181109.reported.route.object.Rro;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.rsvp.rev150820.basic.explicit.route.subobjects.SubobjectType;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.rsvp.rev150820.basic.explicit.route.subobjects.subobject.type.IpPrefixCase;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.topology.pcep.rev200120.Node1;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.topology.pcep.rev200120.PccSyncState;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.topology.pcep.rev200120.pcep.client.attributes.PathComputationClient;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.topology.pcep.rev200120.pcep.client.attributes.path.computation.client.ReportedLsp;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.topology.pcep.rev200120.pcep.client.attributes.path.computation.client.reported.lsp.Path;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.Topology;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.TopologyKey;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.binding.KeyedInstanceIdentifier;
+import org.opendaylight.yangtools.yang.common.Uint32;
+import org.opendaylight.yangtools.yang.common.Uint8;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * This Class listen to the PCEP Topology in order to trigger Path Manager methods to control Managed TE Path.
+ *
+ * @author Olivier Dugeon
+ */
+
+public final class PcepTopologyListener implements DataTreeChangeListener<Node>, AutoCloseable {
+    private static final Logger LOG = LoggerFactory.getLogger(PcepTopologyListener.class);
+    private ListenerRegistration<PcepTopologyListener> listenerRegistration;
+    private final PathManagerProvider pathManager;
+
+    public PcepTopologyListener(final DataBroker dataBroker, KeyedInstanceIdentifier<Topology, TopologyKey> topology,
+            final PathManagerProvider pathManager) {
+        requireNonNull(dataBroker);
+        requireNonNull(topology);
+        this.pathManager = requireNonNull(pathManager);
+        final InstanceIdentifier<Node> nodeTopology = topology.child(Node.class);
+        this.listenerRegistration = dataBroker.registerDataTreeChangeListener(
+                DataTreeIdentifier.create(LogicalDatastoreType.OPERATIONAL, nodeTopology), this);
+        LOG.info("Registered PCE Server listener {} for Operational PCEP Topology {}",
+                listenerRegistration, topology.getKey().getTopologyId().getValue());
+    }
+
+    /**
+     * Close this Listener.
+     */
+    @Override
+    public void close() {
+        if (this.listenerRegistration != null) {
+            LOG.info("Unregistered PCE Server listener {} for Operational PCEP Topology", listenerRegistration);
+            this.listenerRegistration.close();
+            this.listenerRegistration = null;
+        }
+    }
+
+    /**
+     * Handle reported LSP modifications.
+     *
+     * @param nodeId    Node Identifier to which the modified children belongs to.
+     * @param lspMod    List of Reported LSP modifications.
+     */
+    private void handleLspChange(NodeId nodeId, List<? extends DataObjectModification<? extends DataObject>> lspMod) {
+        for (DataObjectModification<? extends DataObject> lsp : lspMod) {
+            ReportedLsp rptLsp;
+
+            switch (lsp.getModificationType()) {
+                case DELETE:
+                    rptLsp = (ReportedLsp) lsp.getDataBefore();
+                    LOG.debug("Un-Register Managed TE Path: {}", rptLsp.getName());
+                    pathManager.unregisterTePath(nodeId, new ConfiguredLspKey(rptLsp.getName()));
+                    break;
+                case SUBTREE_MODIFIED:
+                case WRITE:
+                    rptLsp = (ReportedLsp) lsp.getDataAfter();
+                    LOG.debug("Register Managed TE Path {}", rptLsp.getName());
+                    pathManager.registerTePath(nodeId,  getConfiguredLsp(rptLsp), getPathType(rptLsp));
+                    break;
+                default:
+                    break;
+            }
+
+        }
+    }
+
+    /**
+     * Parse Sub Tree modification. Given list has been filtered to get only Path Computation Client modifications.
+     * This function first create, update or delete Managed TE Node that corresponds to the given NodeId. Then, it
+     * filter the children to retain only the reported LSP modifications.
+     *
+     * @param nodeId    Node Identifier to which the modified children belongs to.
+     * @param pccMod    List of Path Computation Client modifications.
+     */
+    private void handlePccChange(NodeId nodeId, List<? extends DataObjectModification<? extends DataObject>> pccMod) {
+        for (DataObjectModification<? extends DataObject> node : pccMod) {
+            /* First, process PCC modification */
+            switch (node.getModificationType()) {
+                case DELETE:
+                    LOG.debug("Un-Register Managed TE Node: {}", nodeId);
+                    pathManager.disableManagedTeNode(nodeId);
+                    /* Should stop here to avoid deleting later the associated Managed TE Path */
+                    return;
+                case SUBTREE_MODIFIED:
+                case WRITE:
+                    /* First look if the PCC was already created or not yet */
+                    if (pathManager.checkManagedTeNode(nodeId)) {
+                        /* Check if PCC State is Synchronized */
+                        if (node.getModifiedChildren() == null || node.getModifiedChildren().isEmpty()) {
+                            PathComputationClient pcc = (PathComputationClient) node.getDataAfter();
+                            if (pcc.getStateSync() == PccSyncState.Synchronized) {
+                                LOG.debug("Synchronize Managed TE Node {}", nodeId);
+                                pathManager.syncManagedTeNode(nodeId);
+                            }
+                            return;
+                        }
+                    } else {
+                        LOG.debug("Register new Managed TE Node {}", nodeId);
+                        pathManager.registerManagedTeNode(nodeId);
+                    }
+                    break;
+                default:
+                    break;
+            }
+
+            /* Then, look to reported LSP modification */
+            final List<DataObjectModification<? extends DataObject>> lspMod = node.getModifiedChildren()
+                    .stream().filter(mod -> mod.getDataType().equals(ReportedLsp.class))
+                    .collect(Collectors.toList());
+            if (!lspMod.isEmpty()) {
+                handleLspChange(nodeId, lspMod);
+            }
+        }
+    }
+
+    /**
+     * Parse Sub Tree modification. Given children list has been filtered to get only Node1 modifications.
+     * This function filter again this given list to retain only PathComputationClient modifications.
+     *
+     * @param nodeId    Node Identifier to which the modified children belongs to.
+     * @param node1Mod  List of Node1 modifications.
+     */
+    private void handleNode1Change(NodeId nodeId, List<DataObjectModification<? extends DataObject>> node1Mod) {
+        for (DataObjectModification<? extends DataObject> child : node1Mod) {
+            /* Then, look only to PathComputationClient.class modification */
+            final List<DataObjectModification<? extends DataObject>> pccMod = child.getModifiedChildren()
+                    .stream().filter(mod -> mod.getDataType().equals(PathComputationClient.class))
+                    .collect(Collectors.toList());
+            if (!pccMod.isEmpty()) {
+                handlePccChange(nodeId, pccMod);
+            }
+        }
+    }
+
+    @Override
+    public void onDataTreeChanged(final Collection<DataTreeModification<Node>> changes) {
+        for (DataTreeModification<Node> change : changes) {
+            DataObjectModification<Node> root = change.getRootNode();
+
+            final NodeId nodeId =
+                root.getModificationType() == DataObjectModification.ModificationType.DELETE
+                    ? root.getDataBefore().getNodeId()
+                    : root.getDataAfter().getNodeId();
+
+            /* Look only to Node1.class modification */
+            final List<DataObjectModification<? extends DataObject>> node1Mod =
+                root.getModifiedChildren().stream()
+                    .filter(mod -> mod.getDataType().equals(Node1.class))
+                    .collect(Collectors.toList());
+            if (!node1Mod.isEmpty()) {
+                handleNode1Change(nodeId, node1Mod);
+            }
+        }
+    }
+
+    /**
+     * Translate ERO Segment Routing SubOject i.e. NaiType into Path Description.
+     *
+     * @param srObj Segment Routing SubObject.
+     * @param af    Address Family, SR-IPv4 or SR-IPv6.
+     *
+     * @return      Path Description of the corresponding ERO SubObject.
+     */
+    private static PathDescription getSrPath(SrSubobject srObj, final AddressFamily af) {
+        switch (af) {
+            case SrIpv4:
+                switch (srObj.getNaiType()) {
+                    case Ipv4Adjacency:
+                        return new PathDescriptionBuilder()
+                            .setSid(srObj.getSid())
+                            .setLocalIpv4(((IpAdjacency)(srObj).getNai()).getLocalIpAddress().getIpv4AddressNoZone())
+                            .setRemoteIpv4(((IpAdjacency)(srObj).getNai()).getRemoteIpAddress().getIpv4AddressNoZone())
+                            .build();
+                    case Ipv4NodeId:
+                        return new PathDescriptionBuilder()
+                            .setSid(srObj.getSid())
+                            .setRemoteIpv4(((IpNodeId)(srObj).getNai()).getIpAddress().getIpv4AddressNoZone())
+                            .build();
+                    default:
+                        return null;
+                }
+            case SrIpv6:
+                switch (srObj.getNaiType()) {
+                    case Ipv6Adjacency:
+                        return new PathDescriptionBuilder()
+                            .setSid(srObj.getSid())
+                            .setLocalIpv6(((IpAdjacency)(srObj).getNai()).getLocalIpAddress().getIpv6AddressNoZone())
+                            .setRemoteIpv6(((IpAdjacency)(srObj).getNai()).getRemoteIpAddress().getIpv6AddressNoZone())
+                            .build();
+                    case Ipv6NodeId:
+                        return new PathDescriptionBuilder()
+                            .setSid(srObj.getSid())
+                            .setRemoteIpv6(((IpNodeId)(srObj).getNai()).getIpAddress().getIpv6AddressNoZone())
+                            .build();
+                    default:
+                        return null;
+                }
+            default:
+                return null;
+        }
+    }
+
+    /**
+     * Translate ERO RSVP-TE SubObject i.e. IpPrefixCase into Path Description.
+     *
+     * @param srObj Segment Routing SubObject.
+     * @param af    Address Family, SR-IPv4 or SR-IPv6.
+     *
+     * @return      Path Description of the corresponding ERO SubObject.
+     */
+    private static PathDescription getIpPath(IpPrefixCase ipc, final AddressFamily af) {
+        switch (af) {
+            case Ipv4:
+                return new PathDescriptionBuilder().setRemoteIpv4(
+                        new Ipv4Address(ipc.getIpPrefix().getIpPrefix().getIpv4Prefix().getValue().split("/")[0]))
+                        .build();
+            case Ipv6:
+                return new PathDescriptionBuilder().setRemoteIpv6(
+                        new Ipv6Address(ipc.getIpPrefix().getIpPrefix().getIpv6Prefix().getValue().split("/")[0]))
+                        .build();
+            default:
+                return null;
+        }
+    }
+
+    /**
+     * Translate RRO RSVP-TE SubObject i.e. IpPrefixCase into Path Description.
+     *
+     * @param srObj Segment Routing SubObject.
+     * @param af    Address Family, SR-IPv4 or SR-IPv6.
+     *
+     * @return      Path Description of the corresponding RRO SubObject.
+     */
+    private static PathDescription getIpPath(org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang
+            .rsvp.rev150820._record.route.subobjects.subobject.type.IpPrefixCase ipc, final AddressFamily af) {
+        switch (af) {
+            case Ipv4:
+                return new PathDescriptionBuilder().setRemoteIpv4(
+                        new Ipv4Address(ipc.getIpPrefix().getIpPrefix().getIpv4Prefix().getValue().split("/")[0]))
+                        .build();
+            case Ipv6:
+                return new PathDescriptionBuilder().setRemoteIpv6(
+                        new Ipv6Address(ipc.getIpPrefix().getIpPrefix().getIpv6Prefix().getValue().split("/")[0]))
+                        .build();
+            default:
+                return null;
+        }
+    }
+
+    /**
+     * Translate Explicit Route Object (ERO) of the LSP into Path Description.
+     *
+     * @param ero   Explicit Route Object.
+     * @param af    Address Family, IPv4 or IPv6.
+     *
+     * @return      Path Description of the corresponding TE Path.
+     */
+    private static List<PathDescription> getPathDescription(final Ero ero, final AddressFamily af) {
+        final ArrayList<PathDescription> pathDesc = new ArrayList<PathDescription>();
+        for (int i = 0; i < ero.getSubobject().size(); i++) {
+            final SubobjectType sbt = ero.getSubobject().get(i).getSubobjectType();
+            if (sbt instanceof SrSubobject) {
+                pathDesc.add(getSrPath((SrSubobject) sbt, af));
+            } else if (sbt instanceof IpPrefixCase) {
+                pathDesc.add(getIpPath((IpPrefixCase) sbt, af));
+            }
+        }
+        return pathDesc.isEmpty() ? null : pathDesc;
+    }
+
+    /**
+     * Translate Record Route Object (RRO) of the LSP into a Path Description.
+     *
+     * @param rro   Record Route Object of the reported LSP.
+     * @param af    Address Family, IPv4, IPv6, SR-IPv4 or SR-IPv6
+     *
+     * @return      Path Description of the corresponding TE Path.
+     */
+    private static List<PathDescription> getPathDescription(final Rro rro, final AddressFamily af) {
+        final ArrayList<PathDescription> pathDesc = new ArrayList<PathDescription>();
+        for (int i = 0; i < rro.getSubobject().size(); i++) {
+            final org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.rsvp.rev150820
+                ._record.route.subobjects.SubobjectType sbt = rro.getSubobject().get(i).getSubobjectType();
+            if (sbt instanceof SrSubobject) {
+                pathDesc.add(getSrPath((SrSubobject) sbt, af));
+            } else if (sbt instanceof org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.rsvp.rev150820
+                    ._record.route.subobjects.subobject.type.IpPrefixCase) {
+                pathDesc.add(getIpPath((org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.rsvp.rev150820
+                        ._record.route.subobjects.subobject.type.IpPrefixCase)sbt, af));
+            }
+        }
+        return pathDesc.isEmpty() ? null : pathDesc;
+    }
+
+    /**
+     * Build a new TE Path from a reported LSP.
+     *
+     * @param rl    Reported LSP.
+     *
+     * @return      new TE Path.
+     */
+    private static ConfiguredLsp getConfiguredLsp(ReportedLsp rl) {
+        /* New reported LSP is always the last Path in the List i.e. old Paths are place before */
+        Path path = Iterables.getLast(rl.getPath().values());
+        Float convert;
+        ConstraintsBuilder cb = new ConstraintsBuilder();
+
+        /* Set Constraints */
+        if (path.getClassType() != null) {
+            cb.setClassType(path.getClassType().getClassType().getValue());
+        }
+        if (path.getBandwidth() != null) {
+            convert = ByteBuffer.wrap(path.getBandwidth().getBandwidth().getValue()).getFloat();
+            cb.setBandwidth(new DecimalBandwidth(BigDecimal.valueOf(convert.longValue())));
+        }
+        if ((cb.getBandwidth() == null || cb.getBandwidth().getValue() == BigDecimal.ZERO)
+                && path.getReoptimizationBandwidth() != null) {
+            convert = ByteBuffer.wrap(path.getReoptimizationBandwidth().getBandwidth().getValue()).getFloat();
+            cb.setBandwidth(new DecimalBandwidth(BigDecimal.valueOf(convert.longValue())));
+        }
+        RoutingType rtype = RoutingType.None;
+        if (path.getMetrics() != null) {
+            for (Metrics metric: path.getMetrics()) {
+                convert = ByteBuffer.wrap(metric.getMetric().getValue().getValue()).getFloat();
+                switch (metric.getMetric().getMetricType().intValue()) {
+                    case MessagesUtil.IGP_METRIC:
+                        cb.setMetric(Uint32.valueOf(convert.longValue()));
+                        rtype = RoutingType.Metric;
+                        break;
+                    case MessagesUtil.TE_METRIC:
+                        cb.setTeMetric(Uint32.valueOf(convert.longValue()));
+                        rtype = RoutingType.TeMetric;
+                        break;
+                    case MessagesUtil.PATH_DELAY:
+                        cb.setDelay(new Delay(Uint32.valueOf(convert.longValue())));
+                        rtype = RoutingType.Delay;
+                        break;
+                    default:
+                        break;
+                }
+            }
+        }
+
+        /* Get Source and Destination addresses and family */
+        if (path.augmentations() == null) {
+            return null;
+        }
+        final Path1 p1 = path.augmentation(Path1.class);
+        final Uint8 pst = p1.getPathSetupType() != null ? p1.getPathSetupType().getPst() : Uint8.ZERO;
+        final Lsp lsp = p1.getLsp();
+        final org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.ietf.stateful.rev200720.lsp
+            .identifiers.tlv.lsp.identifiers.AddressFamily af = lsp.getTlvs().getLspIdentifiers().getAddressFamily();
+        IpAddress source = null;
+        IpAddress destination = null;
+        if (af instanceof Ipv4Case) {
+            final Ipv4 ipv4 = ((Ipv4Case) af).getIpv4();
+            source = new IpAddress(ipv4.getIpv4TunnelSenderAddress());
+            destination = new IpAddress(ipv4.getIpv4TunnelEndpointAddress());
+            cb.setAddressFamily(pst == Uint8.ZERO ? AddressFamily.Ipv4 : AddressFamily.SrIpv4);
+        } else if (af instanceof Ipv6Case) {
+            final Ipv6 ipv6 = ((Ipv6Case) af).getIpv6();
+            source = new IpAddress(ipv6.getIpv6TunnelSenderAddress());
+            destination = new IpAddress(ipv6.getIpv6TunnelSenderAddress());
+            cb.setAddressFamily(pst == Uint8.ZERO ? AddressFamily.Ipv6 : AddressFamily.SrIpv6);
+        } else {
+            return null;
+        }
+
+        /* Build Intended Path */
+        final IntendedPathBuilder ipb = new IntendedPathBuilder()
+                .setSource(source)
+                .setDestination(destination)
+                .setRoutingMethod(rtype)
+                .setConstraints(cb.build());
+
+        /* Build Actual Path */
+        ComputedPathBuilder cpb = new ComputedPathBuilder();
+
+        /* Get a Valid Path Description for this TePath if any */
+        List<PathDescription> pathDesc = null;
+        if (path.getEro() != null
+                && path.getEro().getSubobject() != null
+                && path.getEro().getSubobject().size() > 0) {
+            pathDesc = getPathDescription(path.getEro(), cb.getAddressFamily());
+        }
+        if (pathDesc == null
+                && path.getRro() != null
+                && path.getRro().getSubobject() != null
+                && path.getRro().getSubobject().size() > 0) {
+            pathDesc = getPathDescription(path.getRro(), cb.getAddressFamily());
+        }
+        if (pathDesc != null) {
+            cpb.setPathDescription(pathDesc);
+        }
+
+        /* Finally build TE Path */
+        return new ConfiguredLspBuilder()
+                .setName(rl.getName())
+                .setPathStatus(PathStatus.Reported)
+                .setIntendedPath(ipb.build())
+                .setComputedPath(cpb.build())
+                .build();
+    }
+
+    /**
+     * get Path Type from a reported LSP.
+     *
+     * @param rl    Reported LSP.
+     *
+     * @return      Path Type.
+     */
+    private static PathType getPathType(ReportedLsp rl) {
+        /* New reported LSP is always the last Path in the List i.e. old Paths are place before */
+        final Path1 p1 = Iterables.getLast(rl.getPath().values()).augmentation(Path1.class);
+        if (!p1.getLsp().getDelegate()) {
+            return PathType.Pcc;
+        }
+        final Lsp1 lspCreateFlag = p1.getLsp().augmentation(Lsp1.class);
+        if (lspCreateFlag == null || !lspCreateFlag.getCreate()) {
+            return PathType.Delegated;
+        }
+        return PathType.Initiated;
+    }
+
+}
+
index 2d0266aee325c7bd2fac50e5ee671d4d36ff5724..fc0da22ab3320f7f4f1d741eb0576e693f65f621 100644 (file)
@@ -388,10 +388,16 @@ class PCEPTopologySessionListener extends AbstractTopologySessionListener<SrpIdN
     private boolean handlePcreqMessage(final PcreqMessage message) {
 
         LOG.info("Start PcRequest Message handler");
+        Message rep = null;
 
         /* Get a Path Computation to compute the Path from the Request */
+        // TODO: Adjust Junit Test to avoid this test
+        if (pceServerProvider == null) {
+            rep = createErrorMsg(PCEPErrors.RESOURCE_LIMIT_EXCEEDED, Uint32.ZERO);
+            sendMessage(rep, new SrpIdNumber(Uint32.ZERO), null);
+            return false;
+        }
         PathComputation pathComputation = pceServerProvider.getPathComputation();
-        Message rep = null;
         /* Reply with Error Message if no valid Path Computation is available */
         if (pathComputation == null) {
             rep = createErrorMsg(PCEPErrors.RESOURCE_LIMIT_EXCEEDED, Uint32.ZERO);
@@ -775,19 +781,24 @@ class PCEPTopologySessionListener extends AbstractTopologySessionListener<SrpIdN
 
             rb.fieldsFrom(input.getArguments());
 
-            /* Call Path Computation if an ERO was not provided */
             boolean segmentRouting = !PSTUtil.isDefaultPST(args2.getPathSetupType());
+
+            /* Call Path Computation if an ERO was not provided */
             if (rb.getEro() == null
                     || rb.getEro().getSubobject() == null
                     || rb.getEro().getSubobject().size() == 0) {
 
                 /* Get a Path Computation to compute the Path from the Arguments */
+                // TODO: Adjust Junit Test to avoid this test
+                if (pceServerProvider == null) {
+                    return OperationResults.createUnsent(PCEPErrors.ERO_MISSING).future();
+                }
                 PathComputation pathComputation = pceServerProvider.getPathComputation();
                 if (pathComputation == null) {
                     return OperationResults.createUnsent(PCEPErrors.ERO_MISSING).future();
                 }
-                rb.setEro(pathComputation.computeEro(args.getEndpointsObj(), args.getBandwidth(), args.getClassType(),
-                        args.getMetrics(), segmentRouting));
+                rb.setEro(pathComputation.computeEro(args.getEndpointsObj(), args.getBandwidth(),
+                        args.getClassType(), args.getMetrics(), segmentRouting));
             }
 
             final TlvsBuilder tlvsBuilder;
index e9b3fe14737c12117dd4649c8f4a96678f5f4e4c..05d7b99b9d69564eda33f528f0b60ebbee25f01a 100644 (file)
@@ -23,6 +23,7 @@ import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicBoolean;
 import org.checkerframework.checker.lock.qual.GuardedBy;
 import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.bgpcep.pcep.server.PceServerProvider;
 import org.opendaylight.bgpcep.pcep.topology.spi.stats.TopologySessionStatsRegistry;
 import org.opendaylight.mdsal.binding.api.WriteTransaction;
 import org.opendaylight.mdsal.common.api.CommitInfo;
@@ -108,6 +109,11 @@ class ServerSessionManager implements PCEPSessionListenerFactory, TopologySessio
             }
         }, MoreExecutors.directExecutor());
 
+        // Register this new topology to PCE Server
+        final PceServerProvider server = dependencies.getPceServerProvider();
+        if (server != null) {
+            server.registerPcepTopology(topology);
+        }
         return future;
     }
 
@@ -125,6 +131,12 @@ class ServerSessionManager implements PCEPSessionListenerFactory, TopologySessio
         }
         state.clear();
 
+        // Un-Register Pcep Topology into PCE Server
+        final PceServerProvider server = dependencies.getPceServerProvider();
+        if (server != null) {
+            server.unRegisterPcepTopology(topology);
+        }
+
         final WriteTransaction t = dependencies.getDataBroker().newWriteOnlyTransaction();
         t.delete(LogicalDatastoreType.OPERATIONAL, topology);
         final FluentFuture<? extends CommitInfo> future = t.commit();