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>
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
+++ /dev/null
-.. _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.
-
--- /dev/null
+.. _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.
+
<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>
<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>
Ero computeEro(EndpointsObj endpoints, Bandwidth bandwidth, ClassType classType, List<Metrics> metrics,
boolean segmentRouting);
-
}
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);
}
--- /dev/null
+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;
+ }
+}
+
<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>
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();
.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);
+ }
}
--- /dev/null
+/*
+ * 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();
+ }
+}
--- /dev/null
+/*
+ * 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();
+ }
+}
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;
/* 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) {
}
}
+ 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;
}
/* 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) {
*/
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) {
--- /dev/null
+/*
+ * 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);
+ }
+ }
+ }
+}
+
--- /dev/null
+/*
+ * 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();
+ }
+
+}
--- /dev/null
+/*
+ * 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;
+ }
+
+}
+
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);
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;
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;
}
}, MoreExecutors.directExecutor());
+ // Register this new topology to PCE Server
+ final PceServerProvider server = dependencies.getPceServerProvider();
+ if (server != null) {
+ server.registerPcepTopology(topology);
+ }
return future;
}
}
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();