CNC 6.0 Service Extensibility Developer Guide
About the CNC Service Extensibility SDK
Cisco Crosswork Network Controller (CNC) provides a unified UI to provision and visualize network services, and to manage the network service lifecycle (create, read/visualize, update, delete). To accomplish this, CNC uses a CAT function pack (a.k.a. the TSDN function pack). The function pack integrates CNC with Cisco Network Services Orchestrator (NSO) and the NSO Services implementation. This CAT function pack is the CNC-side counterpart to NSO Services.
CNC supports customization and extension of VPN and TE transport services that are built from NSO T-SDN model extension and brownfield custom model. Its support and current limitation are summarized in the table below.
Model | Provisioning & Visualization | User Interface Navigation |
---|---|---|
VPN NSO T-SDN Model | Yes | Provisioning UI from/to VPN UI |
VPN NSO Brownfield Custom Model | Yes | Provisioning UI from/to VPN UI |
TE NSO T-SDN Model | Yes | Provisioning UI from/to TE UI |
TE NSO Brownfield Custom Model | Yes | None |
The SDK for the CAT function pack provides tools and source-code examples you can use to develop, build, package and deploy the CAT function pack on CNC.
You should start by downloading the SDK from the link below. Unzip the file, then follow the README.html in the ~/tsdn-fp-sdk/docs folder. The README explains how to set up the SDK; how to create, build and deploy the CAT function pack; and how to try out the ready-to-use examples the SDK provides.
- Download and unzip the CNC 6.0 Service Extensibility Framework SDK (tsdn-fp-sdk-6.0.0-bin.zip):https://software.cisco.com/download/home/286326174/type/286326550/release/6.0.0
- Follow the README file: ~/tsdn-fp-sdk/docs/README.html
This document assumes you will download and unzip the SDK to your machine's home (~) directory, so it references the root of the unzipped directory as ~/tSDK-fp-SDK throughout.
SDK Content
The unzipped CNC SDK offers the following content under the ~/tsdn-fp-sdk root directory:
- /bin: Executables and scripts for setup, creating projects, building and testing projects, deploying and undeploying function packs
- /docs: README.html**,** API Javadocs, guide documents
- /examples: Ready-to-use examples, including an end-to-end NSO Service package and CAT function pack projects. All are ready to build and deploy.
- /cat-fp: All CAT function packs, including multi-module Apache Maven projects for building and deploying example CAT function packs
- /nso-fp: All NSO Services package projects, including my-l3vpn, my-l2vpn, my-sr-policy service
- /mvn-tools: Maven binaries and configuration setups to build the Maven projects in tsdn-fp-sdk/examples/cat-fp
- /postman: Postman collection providing Northbound requests and examples
- /swagger: Swagger JSON files used in API descriptions.
- /sdkrc: Bash source input file that sets up the environment for the SDK to create projects, build them, and so on. This file exists only after initial setup.
Getting Started With the SDK
You must set up the environment before attempting development or building the examples. Here are the steps to follow to set up the environment:
Step 1: Install Python 3.8 and Java JDK 17 (One-Time Setup)
If you want to use the SDK with Python, Python 3.8 or above is required (you can download Python here).
JDK 11 is the required version, and may already be installed (you can download the JDK here).
After installing the JDK, make sure you have the JAVA_HOME environment variable set and exported, as follows:
echo $JAVA\_HOME`
If the JAVA_HOME variable is not set, set it using the export command. For example:
export JAVA\_HOME=/Library/Java/JavaVirtualMachines/amazon-corretto-17.jdk/Contents/Home`
Step 2: Unzip the SDK
For example:
cd ~
tar -xvzf tsdn-fp-sdk-6.0.0-bin.zip
Step 3: Run SDK Setup (One-Time Setup)
Follow these two steps to set up and generate the sdkrc file. This is a shell script file that sets the SDK environment.
- Go to the SDK root folder which is the home directory for the SDK (that is, TSDN_FP_SDK_HOME).
- Run the setup script from the bin directory, as follows:
cd tsdn-fp-sdk
./bin/setup
Step 4: Run SDK Environment Setup
This step is required to set up the development environment so that it can use the tools provided in the SDK:
source ./sdkrc
This is the first step you perform on any new shell or terminal window. You can add it to your bash profile, so that it will run every time you open a shell or terminal window.
SDK Example Implementations
The examples provided in the SDK are ready-to-use reference implementations. They showcase end-to-end implementation of different integration scenarios. Each example consists of a NSO-side service implementation and a corresponding CAT function pack for the integration of the NSO service with CNC provisioning and visualization.
Here is the directory structure of the SDK Example CAT function packs and the corresponding NSO service implementations:
~/tSDK-fp-SDK/examples
|- cat-fp
| |- *cat-function-pack-project* // Maven multi-module project for CAT FP implementation
|
|- nso-fp
|- *nso-service-project* // NSO project with service implementation
To build NSO service packages, you must have an NSO development environment. See the NSO developer documentation for details on how to install and set up NSO for development.
SDK Commands
The SDK defines a Maven-based project structure to develop, build, test, package, and deploy a CAT function pack. The SDK provides a set of commands to help in various phases of the development (create and update), test (build and unit test) and deployment (deploy and undeploy) lifecycle of the CAT function packs.
The following sections explain the development, test, and deployment commands available in the SDK environment.
create-prj
Use this command to create a Maven multi-module project in the current working directory. The project consists of the inventory plugin, overlay plugin, packaging projects with build configuration and source code required to produce the CAT function pack archive file. The source code generated is based on a sample YANG service model.
The user is expected to modify the source code to customize according to any updates required to support the NSO service integrated with this CAT function pack. This can include service YANG models, service metadata, and the corresponding Java or Python code.
The create-prj command has an option to create a Python project for developing the overlay plugin in the Python language. Before choosing the Python option for overlay plugin development, make sure your development environment is setup for Python and has the required Python packages. For example: jsonpath- rw, requests, google-api-python-client .
The command also has an option to supply parameters while creating the project for the service metadata. The parameters (the YANG model files and property name) will be used to auto-create the service-metadata file needed by the CAT function pack. This file will be placed inside the created fp-package dir under the /src/main/resources folder. Command Usage Examples: create-prj Create a project for 'demo-vpn' service CAT function pack:
create-prj demo-vpn
Create a project for 'demo-vpn' service CAT function pack with Python project for overlay plugin development in the Python language:
create-prj demo-vpn -D python.overlay=true
Create a project for 'demo-vpn' service CAT function pack with metadata parameters for the creation of a service-metadata file:
create-prj demo-vpn -Dservice.yang.file=cisco-flat-L3vpn-fp.yang -Dresource.yang.file=cisco-flat-L3vpn-fp.yang -Dresource.property=l3vpn-route-policy
build-prj
Use the build-prj command to build the CAT function pack project, and to compile, unit test, and package the CAT function pack archive.
The build-prj command runs a validation procedure on the project's service metadata file. If you have created the project using the create-prj command and supplied optional YANG file(s) as parameters to generate the service metadata file during project creation, be sure to copy any dependent YANG modules to the inventory plugin project under the src/main/yang folder. This will allow the validation procedure to run properly. Command Usage Example: build-prje Build, unit test and package the 'demo-vpn' service CAT function pack from a project:
cd demo-vpn-tsdn-fp
build-prj
update-inventory-plugin
After a successful build, use this command to update the generated code in the RESTCONF plugin project that matches with the updated service YANG models and the service metadata descriptor. You can use this command whenever service metadata changes and corresponding changes are needed in the RESTCONF inventory plugin for data reader and tests. ==This command overwrites the RESTCONF plugin code that was generated during project creation.== Command Usage Example: update-inventory-plugin Update the RESTCONF inventory plugin project source after updating the YANG files and service metadata file:
cd demo-vpn-tsdn-fp/demo-vpn-tsdn-fp-inventory-plugin
update-inventory-plugin
deploy-prj
Use this command to deploy a CAT function pack archive built in the packaging project to a target CNC server. Before running the deploy-prj or undeploy-prj commands, be sure to add the CNC CAT API server configuration to the Maven settings, as follows:
- Edit ~/tsdn-fp-sdk/mvn-tools/mvn/mvn-settings.xml
- Change the cat.api.* properties to include the target CNC CAT API server host, port and credentials. For example:
<properties>
<!-- CNC Northbound Interface server settings: Setting these properties here will set globally the tsdn-fp deploy configuration for the plugin. Inidvidual project plugin configs can override this. -->
<cat.api.host>192.0.0.1</cat.api.host>
<cat.api.port>30603</cat.api.port>
<cat.api.username>admin</cat.api.username>
<cat.api.password>changeit</cat.api.password>
</properties>
Command Usage Example: depoly-prj Build, package and deploy 'demo-vpn' service CAT function pack from the project:
cd demo-vpn-tsdn-fp
build-prj
cd demo-vpn-tsdn-fp/demo-vpn-tsdn-fp-package
deploy-prj
undeploy-prj
Use this command to undeploy a previously deployed CAT function pack archive built in the packaging project from a target CNC server. Before using this command, see the deploy-prj command documentation on how to set up the CNC CAT API server configuration. Command Usage Example: undepoly-prj Undeploy 'demo-vpn' service CAT function pack from target CNC server built by the project.
`cd demo-vpn-tsdn-fp/demo-vpn-tsdn-fp-package`
`undeploy-prj`
SDK Commands for Creating Independent Plugins and Packaging Projects
In some cases, you will need to develop inventory and overlay plugin projects independently. You can then package them as a CAT function pack useable in a development pipeline (such as in a CI/CD pipeline) where there is only a binary dependency. For this scenario, use the following create commands to create standalone plugin projects or package projects.
We recommend using the create-prj command wherever possible. It is much easier to organize and maintain CAT function packs if you use create-prj to create a multi-module project.
create-prj-inventory-plugin
Use this command to create a Maven project with build configuration and sample code to build and package an inventory plugin.
create-prj-overlay-plugin
Use this command to create a Maven project with a build configuration and sample code to build and package overlay plugin.
create-prj-packager
Use this command to create a Maven project with a build configuration that will assemble inventory and overlay plugin binaries, together with some default service metadata, to produce the CAT function pack. There is also an option to supply parameters (that is, the YANG model files and property name) while creating the project for the service metadata. The command will use these parameters to auto-create the service metadata file that the CAT function pack needs. The command will put the service metadata file inside the created fp-package dir under the /src/main/resources folder.
create-service-metadata
Use this command to create a service metadata file for the given technology that will be consumed by the CAT function pack. The command generates the metadata from the supplied YANG model file containing the model of the service.
attach-resource-to-service-metadata
Use this command to attach a metadata portion for a given resource to the existing service metadata file. The metadata is generated from the supplied YANG model file containing the model of the resource.
Step-by-Step Instructions for Developing a CAT Function Pack Using the SDK
This section provides step-by-step instructions for:
- Using the build, deploy and test examples
- Developing a new CAT function pack
- Updating an existing CAT function pack
This document references or uses the code from the examples where needed. It assumes you have already installed the SDK and performed initial setup. Where the example involves building NSO service packages, the instructions also assume that you have installed the NSO platform.
Using Build, Deploy, and Test Examples
The examples provided in the SDK are ready to build and can be deployed without changes. Use the following instructions to build, deploy, and test a CAT function pack:
Set up the development environment (the
.bin/setup
command is only required if you have not already done the one-time setup):cd ~/tsdn-fp-sdk` ./bin/setup source ./sdkrc
Set up the NSO service development environment:
source {/path/to/nso/install/dir} /ncsrc
Build the example NSO service packages:
cd ~/tsdn-fp-sdk/examples/nso-fp ./build.sh
Install the NSO service archive files in NSO attched to CNC:
A. Upload NSO service tar.gz files from ~/tsdn-fp-sdk/examples/nso-fp/target to NSO under the runtime packages directory. For example: On an NSO instance installed with the system install option, this directory is under /var/opt/ncs/packages . B. Restart NSO with the package reload option. C. Check that the service packages are loaded without issues (check in the CLI or NSO admin UI for any errors loading the packages).
Build example CAT function packs. This step compiles the plugin code, runs unit tests and packages artifacts into CAT function pack archive.
cd ~/tsdn-fp-sdk/examples/cat-fp; build-prj;
Deploy each CAT function pack to the CNC server:
cd ~/tsdn-fp-sdk/examples/cat-fp/my-l3vpn-tsdn-fp/my-l3vpn-tsdn-fp-package; deploy-prj cd ~/tsdn-fp-sdk/examples/cat-fp/my-l2vpn-tsdn-fp/my-l2vpn-tsdn-fp-package; deploy-prj cd ~/tsdn-fp-sdk/examples/cat-fp/my-sr-policy-tsdn-fp/my-sr-policy-tsdn-fp-package; deploy-prj
Verify that the CAT function packs deployed successfully. You can verify this by logging into the CNC UI and navigating to the Provisioning UI. All the example service types should appear under the CAT-SDK-Examples folder in the left-hand navigation pane.
Test the services using the provided Postman collection: a. Import into Postman the collection ~/tsdn-fp-sdk/postman/CAT-SDK-Examples.postman_collection.json. b. Follow the documented instructions in the Postman collection to create, read, and delete service instances for each service type.
Use the CNC Service Topology Visualization and Provisioning UI to visualize the service instances created in CNC.
Developing a New CAT Function Pack
To provision and visualize a new or existing NSO service in CNC, you must first integrate the NSO service using a corresponding CAT function pack deployed in CNC. The following steps show how to create a new CAT function pack and deploy it to CNC in order to visualize a new NSO service.
In steps, the my-l3vpn NSO service provided in the SDK examples (~/tSDK-fp-SDK/examples/nso-cfp/my-l3vpn) is the NSO service that we integrate with CNC. The my-l3vpn service is an NSO service that creates a simple, pre-defined L3VPN service configuration on one or more devices. The service implementation is for demonstration purposes only.
We create and deploy to the CNC server the new CAT function pack demo-vpn-tsdn-fp to integrate the my-l3vpn NSO service with CNC. If the my-l3vpn NSO service is not already installed in the NSO instance attached to the target CNC server, install it by following the instructions in the preceding "Using Build, Deploy, and Test Examples" topic.
Below are the workflow steps to create, build, deploy, and test the new CAT function pack. These steps assume that the SDK is installed under the user's home directory, so the SDK root directory is ~/taSDK-fp-SDK, and the user development workspace directory is ~/develop.
Set up the development environment (the
./bin/setup
call is required only if you have not already performed the [one-time environment setup](#Getting Started)):cd ~/tsdn-fp-sdk ./bin/setup source ./sdkrc
Create a CAT function pack project:
cd ~/develop create-prj demo-vpn
Build the generated CAT function pack project successfully with all dependencies downloaded:
cd demo-vpn-tsdn-fp build-prj
==A first-time build will take longer than subsequent builds, as it must first download the required Maven and project build artifacts.==
Update the service YANG models in the RESTCONF inventory plugin project (that is, remove existing YANG files and copy new service YANG files):
rm ./demo-vpn-tsdn-fp-inventory-plugin/src/main/yang/\*.yang cp ~/tsdn-fp-sdk/examples/nso-fp/my-l3vpn/src/yang/\*.yang ./demo-vpn-tsdn-fp-inventory-plugin/src /main/yang/
Build the project to ensure that the YANG models compile properly:
build-prj
At this stage, unit tests will fail service metadata validations, as the service model has changed and no longer matches the service metadata. You must update the service metadata file in the package project according to the service description in the new YANG models. To do this, edit the ./demo-vpn-tsdn-fp-package/src/main/resources/service-metadata.json file so that you replace the file's existing content with the content below, which matches with the new service model for service description.
{ "services":{ "service-metadata":[ { "model-version":"2021-07-18", "namespace":"http://cisco.com/ns/nso/cnc/cat/examples/my-l3vpn", "service-path":"my-l3vpn:my-l3vpn-services/my-l3vpn", "plan-path":"my-l3vpn-plan:my-l3vpn-service-plans/my-l3vpn-plan", "service-id-fields" : ["name"], "service-layer":"VPN", "label":"CAT-SDK-Examples", "service-type-label":"MY-L3VPN" } ] } }
Build the project to make sure the metadata file passes the validation unit test. This unit test validates the service metadata against the schema in the new service YANG models to make sure they match:
build-prj
At this stage, the metadata validations should pass, but the RESTCONF data reader tests will fail. You must now run the RESTCONF inventory plugin code generator to update the service inventory plugin and RESTCONF data reader to use the service YANG paths used in the service metadata file:
cd ./demo-vpn-tsdn-fp-inventory-plugin update-inventory-plugin
Build the project to make sure the RESTCONF data reader validations pass:
build-prj
At this stage, service data de/serialization tests will fail. Fix the unit test for the service data de/serialization. This step ensures that the data read from the service instance and service plan data from the cat-inventory service are both validated. Copy/create the test service instance and plan data that is schema-valid with the new service model:
cp ~/tsdn-fp-sdk/examples/cat-fp/my-l3vpn-tsdn-fp/my-l3vpn-tsdn-fp-inventory-plugin/src/test /resources/\*.json ./src/test/resources
Build the project to make sure all the unit tests in the RESTCONF inventory plugin project pass.
build-prj
Update the overlay plugin project code:
cd ../demo-vpn-tsdn-fp-overlay-plugin
- Update the overlay parser to replace the content of ./src/main/java/com/cisco/sp/cw/service/overlay/parsers /Demo_vpnParser.java with the following code:
package com.cisco.sp.cw.service.overlay.parsers; import java.util.Map; import org.json.JSONObject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; import com.cisco.cw.service.overlay.model.proto.CoreEdgeDirection; import com.cisco.cw.service.overlay.model.proto.SEPRole; import com.cisco.cw.service.overlay.model.proto.ServiceEndPoint; import com.cisco.cw.service.overlay.model.proto.ServiceOverlayProto; import com.cisco.sp.cw.service.overlay.java.proto.ParameterizedOverlayProtoParser; import com.cisco.sp.cw.service.overlay.java.proto.SepBuilderFactory; @Component public class Demo_vpnParser extends ParameterizedOverlayProtoParser { private static final Logger LOG = LoggerFactory.getLogger(Demo_vpnParser.class); @Override public String getType() { // yang tag path to the service container return "my-l3vpn:my-l3vpn-services/my-l3vpn"; } @Override public ServiceOverlayProto parseParameterizedOverlayProto(String serviceJsonStr, Map<String, String> inputParams) { // 1. load service instance data and create and initialize the overlay model loadService(serviceJsonStr); String serviceName = evaluateMandatoryField(getServiceJson(), "name"); createOverlayModel(serviceName, getType()); final boolean extendedView = Boolean.parseBoolean(inputParams.get("view_extended")); LOG.info("loaded service instance data, created and init overlay model"); // 2. build the service endpoints of the overlay model ServiceEndPoint.Builder[] sepArr = buildServiceEndPoints("endpoint[*]", new SepBuilderFactory() { @Override public ServiceEndPoint.Builder[] createSEPBuilders(JSONObject endpoint, int endpointSequentialNumber) { String peDevice = evaluateMandatoryField(endpoint, "access-pe"); String ceFacingInterface = "Loopback" + evaluateMandatoryField(endpoint, "vpn-network-access.loopback-if-id"); String ceDeviceLabel = evaluateMandatoryField(endpoint, "vpn-network-access.ce-pe-routing-ebgp.neighbor"); // 2.1 create service endpoint LOG.info("building overlay model service endpoint : peDevice={}", peDevice); ServiceEndPoint.Builder sepBuilder = createServiceEndPoint_UnamangedCE(peDevice, ceFacingInterface, ceDeviceLabel, SEPRole.SEP); if(extendedView) { evalMandatoryObjectCollection(endpoint, "vrf.vpn-target[*]").forEach(vpnTargetJson -> { String rtValue = evaluateMandatoryField(vpnTargetJson, "rt-value"); String rtType = evaluateMandatoryField(vpnTargetJson, "rt-type"); CoreEdgeDirection coreEdgeldirection = rtType.equals("both") ? CoreEdgeDirection.BIDIRECTIONAL : rtType.equals("import") ? CoreEdgeDirection.INCOMING : CoreEdgeDirection.OUTGOING; // 2.2 connect service endpoint to service domain connectServiceEndPointToServiceDomain(sepBuilder, "RT", "IPv4:" + rtValue, coreEdgeldirection); }); }else { connectServiceEndPointToServiceDomain(sepBuilder, "L3VPN", serviceName, CoreEdgeDirection.DIRECTIONLESS); } return new ServiceEndPoint.Builder[] { sepBuilder }; } }); // 3. return the final overlay model build frm service instance data return buildModel(sepArr); } }
Update the overlay parser unit test:
cp ../demo-vpn-tsdn-fp-inventory-plugin/src/test/resources/service-instance-test-data.json ./src/test /resources
Update ./src/test/java/com/cisco/cw/tsdn/fp/demo_vpn/overlay/Demo_vpnTest.java to replace its content with the following code:
package com.cisco.sp.cw.service.overlay.parsers; import static org.junit.Assert.assertEquals; import java.io.IOException; import java.net.URISyntaxException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.stream.Collectors; import java.util.stream.Stream; import org.junit.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.cisco.cw.service.overlay.model.proto.ServiceOverlayProto; import com.cisco.sp.cw.service.overlay.java.proto.ParameterizedOverlayProtoParser; /** *
- Run the unit test to validate the overlay parser:
public class Demo_vpnParserTest {
private static final Logger LOG = LoggerFactory.getLogger(Demo_vpnParser.class);
@Test
public void testMyL3VpnOverlay() throws Exception {
String myL3vpnInstace = readTestServiceContent("service-instance-test-data.json");
ParameterizedOverlayProtoParser parser = new Demo_vpnParser();
Map<String,String> overlayOptions = new HashMap<>();
overlayOptions.put("view_extended","true");
ServiceOverlayProto serviceOverlay = parser.parseParameterizedOverlayProto(myL3vpnInstace, overlayOptions);
LOG.info("Overlay Model: {}", serviceOverlay);
assertEquals("service name", "test-service-1", serviceOverlay.getName());
List<String> hosts = serviceOverlay.getServiceEndPointsList().stream().map(sep -> sep.getHostName())
.collect(Collectors.toList());
LOG.info(String.format("%n======HOSTS=====%n%s%n", hosts));
Set<String> actualHosts = new HashSet<>(hosts);
Set<String> expectedHosts = new HashSet<>(Arrays.asList("PE-A", "PE-B"));
assertEquals("2 hosts expected", expectedHosts, actualHosts);
assertEquals("In extended view expecting two service domains", 3,serviceOverlay.getServiceDomainsCount());
}
private String readTestServiceContent(String resourcePath) throws URISyntaxException, IOException {
Path path = Paths.get(getClass().getClassLoader().getResource(resourcePath).toURI());
Stream<String> lines = Files.lines(path);
String data = lines.collect(Collectors.joining("\n"));
lines.close();
LOG.info(String.format("%n=== %s ===%n%s%n====%n", resourcePath, data));
return data;
}
}
```
15. Service payload example:
```cli
{
"my-l3vpn:my-l3vpn": [
{
"name": "test-service-1",
"endpoint": [
{
"access-pe": "PE-A",
"as-no": 1,
"vpn-network-access": {
"loopback-if-id": "7",
"ip-connection": {
"pe-address": "10.1.1.1"
},
"ce-pe-routing-ebgp": {
"neighbor": "10.1.1.2",
"remote-as": 65002,
"ebgp-multihop": 12
}
},
"vrf": {
"route-distinguisher": "1:1",
"vpn-target": [
{
"rt-value": "100:100",
"rt-type": "both"
},
{
"rt-value": "100:101",
"rt-type": "export"
},
{
"rt-value": "100:102",
"rt-type": "import"
}
]
}
},
{
"access-pe": "PE-B",
"as-no": 1,
"vpn-network-access": {
"loopback-if-id": "5",
"ip-connection": {
"pe-address": "20.1.1.1"
},
"ce-pe-routing-ebgp": {
"neighbor": "20.1.1.2",
"remote-as": 65003,
"ebgp-multihop": 11
}
},
"vrf": {
"route-distinguisher": "2:2",
"vpn-target": [
{
"rt-value": "100:100",
"rt-type": "both"
},
{
"rt-value": "100:101",
"rt-type": "import"
},
{
"rt-value": "100:102",
"rt-type": "export"
}
]
}
}
]
}
]
}
Build the project:
build-prj
Deploy the CAT function pack produced from the build to the CNC server and verify topology visualization and Provisioning UI: A. If the my-l3vpn CAT function pack from the examples is already deployed on the target CNC server, undeploy it first:
cd ~/tsdn-fp-sdk/examples/cat-fp/my-l3vpn-tsdn-fp/my-l3vpn-tsdn-fp-package; undeploy-prj
B. Build and deploy the demo-vpn CAT function pack to the target CNC server:
cd ~/develop/demo-vpn-tsdn-fp; build-prj cd ./demo-vpn-tsdn-fp-package; deploy-prj
Updating the Existing CAT Function Pack
- Edit the service model YANG file in the NSO service, map the implementation to a device CLI, build and install on the NSO instance attached to CNC.
- Copy the edited YANG model to the CAT function pack project.
- If the additional config data can be used in overlay, update the overlay parser as required.
- Build the project.
- Update the test payloads to test the additional data as part of unit tests.
- Build and deploy the CAT function pack to the CNC server.
Upgrading Existing CNC Function Packs to CNC 6.0.0
CNC service function packs that were developed and deployed for older releases of CNC should be re-built for CNC 6.0. Deploy them to the CNC server after you complete upgrading. See the Cisco Crosswork Infrastructure 6.0 and Applications Installation Guide for details on how to upgrade to CNC 6.0.
In order to rebuild the existing function pack projects developed for CNC releases earlier than 6.0, do the following:
Change the library dependencies: In each Maven project, update the pom.xml file for parent pom and dependencies configuration (artifact versions and additional libraries) according to the 6.0 requirements. You can take this pom configuration from 6.0 example projects. You can also create a new project using the 6.0 Create Project tool and copy the pom from the new project's files.
In the inventory plugin pom.xml:
- Change the parent pom version to 11.0.15.
- Change the <tsdn.fp.odl.restconf.version> property value to 5.0.9.
- Change the <tsdn.fp.service.inventory.plugin.libs.version> property value to 1.6.28.
- Change property <maven.compiler.release> value to 17
- Update the artifact versions specified in pom dependencies to match with what is specified in the sdk example (e.g. my-l2vpn-tsdn-fp-inventory-plugin/pom.xml) or inventory plugin project generated using create-prj command.
- Exclude grpc-core library transitive dependency in cat-restconf-db library dependency
<dependency> <groupId>com.cisco.sp.cw.cat.nbi</groupId> <artifactId>cat-restconf-db</artifactId> <exclusions> <exclusion> <groupId>io.grpc</groupId> <artifactId>grpc-core</artifactId> </exclusion> </exclusions> </dependency>
In the overlay plugin pom.xml, change the <tsdn.fp.service.overlay.plugin.libs.version> property value to 6.0.28.
If required, change the service metadata descriptor.
Build with the 6.0.0 SDK environment setup and deploy to CNC 6.0.0.
Deploy the newly built TSDN function pack to CNC 6.0.0.
NSO Service Implementation Guidelines for Integration with CNC
There are many ways to implement a NSO service. Follow the implementation guidelines below for a smooth integration of the NSO service with CNC.
CNC CAT integrates with NSO using NSO's northbound interface (JSON-RPC, RESTCONF) to:
- Collect service data: Learn about the available service types, service instances, and associated resources data.
- Service lifecycle management: Monitor and report the status (success, failure) of provisioning and changes (create, update, delete) to the service instance, perform error handling of service instances, perform service-specific actions in CNC.
- Service Visualization: Represent the service intent (service config data) in a graphical view (topology) and provide additional details about the service.
To collect service data and service lifecycle management, CNC CAT relies on the NSO standard service state change notifications and NSO standard progress monitoring data (plan data), which provides key functionality of status and progress reporting of a service. The plan data is not represented by a service config model. Instead, it is represented using a standard NSO operational model which can be defined outside the service config model using the common NSO operational model.
The recommended way to support this standardized mechanism for progress reporting and notification is by implementing the NSO service using Reactive FASTMAP (RFM) service implementation. Known as a nano-service, RFM provides most of the framework required. See the NSO developer documentation for more details.
For NSO services that are implemented using the FASTMAP technique only: You can still support the plan-data model and notifications by implementing pluggable code. For example, use the data-kicker approach as implemented in the my-l3vpn service in the examples provided in this SDK. See the NSO platform developer guide documentation on data kicker usage or post-modifications service implementation tasks.
In summary, to have a good integration with CNC, the NSO service implementation should support:
- Plan-data model as a progress-reporting NSO operational data. This is outside of service config model definition and does not put any restrictions on how the config model data is structured in the service model.
- Service state change notifications to report the provisioning progress and service changes.
- Service state change notifications for service deletion (delete notifications) for regular service deletion as well as service zombies deletion.
- Service YANG model definitions should follow a few common rules while defining the service models as follows (note that this is only applicable to top-level service container definition; it places no restrictions on how the config model data is structured inside the service container in the service model):
- Service definition should be modeled as a list container with a key to identify the service (the standard way to define a service).
- Path to service definition (service parent path) should not have any list containers except for service or service plan. For example:
- A service instance YANG key paths to a well defined service list container in the service model: /mod:my/path/to/service=foo , /mod:my/path/augment:to/service=bar,baz
- A service instance YANG key path that is not suitable for CNC integration (key in parent path .../type=abc/...): /mod:my/path/to /type=abc/service=bar
- In CNC 3.0, your service config model and service plan model had to be in the same namespace and defined under the same parent (see the example below). This restriction was removed in CNC 4.1. You can add the plan model to the legacy services using external packages, where the corresponding plan model is defined in a different YANG module and added using an external package.
Example Service and Plan Data Definitions
The example YANG module my-service-module.yang, shown below, assumes that:
- The service instance YANG path (YANG key path) is /my-services/my-vpn-service=foo
- The service plan instance YANG path is /my-services/my-vpn-service=foo
The example includes a top-level structure of the YANG module definition, which defines the service configuration model and the service plan model.
my-service-module {
namespace "urn:com:example:my-service";
container my-services {
list my-vpn-service {
// service config data model
key service-identifier;
leaf service-identifier;
container endpoints-common-config-data { }
container endpoints-specific-config-data { }
...
// NSO specific hookup
ncs:servicepoint my-vpn-service-nso-servicepoint;
}
list my-vpn-service-plan {
// NSO service operational data model
key service-identifier; // same key as service
leaf service-identifier;
// NSO specific hookup
uses ncs:nano-plan-data; // or uses ncs:plan-data;
}
}
}
CAT Function Pack Development and Integration Workflow
Service extensibility plugins packaged and deployed in the form of CAT function packs are required to integrate NSO services with CNC. The integration is required to use CNC to provision and visualize network services supported by NSO services.
The illustration below shows the developer workflow to implement a CAT function pack corresponding to an NSO service:
- Use the SDK to create a project to build the CAT function pack for the service type corresponding to the NSO service.
- Use service definition YANG models from NSO service.
- Update the plugins according to the service definition of the NSO service.
- Build, package and deploy the CAT function pack to CNC.
See the previous topic, Developing a New CAT Function Pack, for step-by-step instructions.
CAT Function Pack Archive
The CAT function pack archive is a package with a specific structure, where the plugins and the service metadata artifacts are packaged for deployment to CNC. The following illustration shows the package structure.
Service Metadata Description
Service Metadata describes the service instance, service plan and associated service resources. It describes the CNC components that process that data to learn about the service type that is integrated with this CAT function pack. You can generate service metadata using the scripts create-service-metadata and attach-resource-to-service-metadata.
Below is an example of service metadata derived from the service definition YANG models:
{
"services":{
"service-metadata":[
{
"model-version":"2021-07-18",
"namespace":"http://cisco.com/ns/nso/cnc/cat/examples/my-l3vpn",
"service-path":"my-l3vpn:my-l3vpn-services/my-l3vpn",
"plan-path":"my-l3vpn-plan:my-l3vpn-service-plans/my-l3vpn-plan",
"service-id-fields" : ["name"],
"service-layer":"VPN",
"label":"CAT-SDK-Examples",
"service-type-label":"MY-L3VPN"
}
]
}
}
The following illustration provides an example of how the above service metadata is derived from the service definition YANG models.
Inventory Plugin
The Inventory plugin provides the implementation of the RESTCONF interface required to read the service data store in the CAT datastore (ISTP) for a specific service type. It provides:
- The service definition YANG model schema as Java bindings to build the schema context for the supported service type.
- The data reader implementation that maps the service YANG paths (intent, plan, resources) to read corresponding service data from the CAT data store (istp). It returns the data (as JSON or XML) through the RESTCONF data interface.
- The service inventory plugin implementation to provide service type details to a service inventory RESTCONF RPC operations implementation. This provides a way to retrieve data for all services via iterative batch interface. An OSS/BSS client can use this RESTCONF RPC interface to learn about all the services in CNC, including the type of service currently supported, provisioning status of each service, number of service instances in the CNC, and so on.
The SDK's Create Project commands generate the required project, code (with sample implementation), and configuration artifacts for the inventory plugin. Depending on the code generation tools available to them, developers in most cases will not be required to modify the Java code in this plugin.
The developer workflow to update the project based on the NSO service definition is as follows:
- Bring in the service definition YANG files.
- Update the service metadata file.
- Run the plugin update command to update the code.
- Adjust the unit test payload according to the service definition schema.
See the previous topic, Developing a New CAT Function Pack, for step-by-step instructions. The following illustration shows the code and configuration artifacts in the project.
Overlay Plugin
The overlay plugin provides the required implementation to understand the service intent data and render it in the topology overlay view. See About the CNC Service Overlay for more details.
Provision UI Plugin
The service provisioning UI plugin provides the capability to add custom validation logic to user input inside the provisioning editor form. The plugin also enables configurations to be represented in either a text field or a text field with a convertible dropdown list. See the Provision UI Plugin guide for more details.
SDK Examples
The following SDK examples provide a simple, ready to build, deployable, and testable set of code and configuration artifacts that demonstrate end-to-end integration of NSO services with CNC. You can start with these examples to learn how to create your own CAT function packs to integrate NSO services with CNC.
The focus of NSO service implementation in the SDK examples is to have a minimum required implementation needed to integrate with CNC. The device configs generated from the examples use simple and fixed configurations. They do not demonstrate all the possible validations. The examples are also intended to work with a standalone NSO deployment. NSO LSA cluster deployment is out of scope for these examples. These example packages may need need updates for LSA requirements if you need to install them in a NSO LSA cluster.
Example: my-l3vpn
The NSO service produces a simple L3VPN configuration. It demonstrates:
- Reference service model definition and implementation that is the minimum required for CNC integration.
- Use of a service plan data model external to the service intent model module and namespace.
- How to create the service plan notifications.
- Sources:
- CAT function pack: examples/cat-fp/my-l3vpn-tsdn-fp
- NSO service package: examples/nso-fp/my-l3vpn
- Sample device config CLI implemented by the NSO service.
vrf test-l3
address-family ipv4 unicast
import route-target
100:100
!
export route-target
100:100
!
!
!
interface Loopback 7
description T-SDN Interface
vrf test-l3
ipv4 address 10.2.2.2 255.255.255.255
!
!
route-policy PASS_ALL
pass
end-policy
!
router bgp 1
vrf test-l3
rd 1:1
address-family ipv4 unicast
redistribute connected
!
neighbor 10.1.1.1
remote-as 65003
ebgp-multihop 11
address-family ipv4 unicast
route-policy PASS_ALL in
route-policy PASS_ALL out
!
!
!
!
Example: my-l2vpn
The NSO service produces a simple L2VPN static pseudowire configuration. It demonstrates:
- Reference service model definition and RFM implementation (minimum required for CNC integration).
- Use of transport data association in the service overlay parser.
- Use of the 3.0 overlay API.
- Sources:
- CAT function pack: examples/cat-fp/my-l2vpn-tsdn-fp
- NSO service package: examples/nso-fp/my-l2vpn
- Sample device config CLI implemented by the NSO service.
interface GigabitEthernet 0/0/0/1.101 l2transport
description CAT Interface
mtu 1500
encapsulation dot1q 101
no shutdown
rewrite ingress tag push dot1q 201 symmetric
exit
l2vpn
pw-class foo-127
encapsulation mpls
control-word
preferred-path sr-te policy srte_c_200_ep_100.100.100.6
exit
exit
xconnect group foo-127
p2p foo-127
interface GigabitEthernet0/0/0/1.101
neighbor ipv4 10.10.10.2 pw-id 127
mpls static label local 301 remote 302
pw-class foo-127
exit
exit
exit
exit
Example: my-sr-policy
The NSO service produces a simple segment routing policy configuration. It demonstrates:
- Use of a complex key-based service implementation and integration with CNC.
- Sources:
- CAT function pack: examples/cat-fp/my-sr-policy-tsdn-fp
- NSO service package: examples/nso-fp/my-sr-policy
- Sample device config CLI implemented by the NSO service
IGP (Best Path) Policy
segment-routing
traffic-eng
policy srte_c_200_ep_100.100.100.6
color 200 end-point ipv4 100.100.100.6
candidate-paths
preference 7
dynamic
pcep
!
metric
type igp
!
!
!
!
!
!
!
or TE Metric Policy
segment-routing
traffic-eng
policy srte_c_100_ep_100.100.100.5
color 100 end-point ipv4 100.100.100.5
candidate-paths
preference 9
dynamic
pcep
!
metric
type te
!
!
!
!
!
!
!
Frequently Asked Questions
How can I create a CAT function pack project? Create it using the create-prj command. For example:
cd ~/tsdn-fp-sdk
source ./sdkrc
create-prj demo-vpn
Why is create-prj or build-prj taking a long time to complete?
First time-invocation of these commands will force a download of all of the Maven and build dependencies that the project requires. These downloads can take a long time to complete. Subsequent invocations of either command, on the same or a different project, will finish much faster.
Why is create-prj or build-prj timing out or failing to download dependencies from an external Maven repository?
Your build machine (where you installed the SDK) might be behind an HTTP proxy. If this is the case, configure the proxies in the Maven proxy settings XML and try to build again. For example:
cd ~/tsdn-fp-sdk/mvn-tools/mvn
vi mvn-settings.xml
- Uncomment the
proxies
configuration and update it with your HTTP proxy settings.
Why is there generated code in the inventory plugin main source directory?
Based on the YANG model definitions, the OpenDaylight (ODL) YANG Tools may determine that there is a user customization for the data model bindings. If so, it generates the code in the main source directory (src/main/java) so you can customize it. The package for this ODL YANG tools-generated code starts with the package name "org.opendaylight.yang.gen... " (in src/main/java/org/opendaylight/yang/gen/...).
If there is a YANG model change with a newer revision, delete this generated code in the main source so ODL YANG Tools can re-generate it with the changed revision package.
How can I get the service instance test payload for the unit test?
When a service YANG model changes, the corresponding unit-test service instance test payload should be updated to validate the generated Java bindings against the changed YANG model for reading the service instance data.
To get the valid payload: If the NSO service pack is installed on NSO, and a service instance is created on NSO, use the GET data RESTCONF API for the service. This will get the JSON service instance data and use it in the unit test service instance payload. As an alternative, manually update the test service instance JSON payload according to the schema change.
About the CNC Service Overlay Model and API
The CNC Service Overlay is part of the CNC extensibility framework. It provides users with the ability to overlay their own custom service models onto the built-in CNC topology model.
Different organizations use different service models. For example, one customer may want to present services in a very information-rich manner, as shown in the following illustration:
A different organization may prefer a simpler view:
To meet this need, CNC offers its Overlay Model as an abstraction layer for service visualization, and its Overlay API as a convenient method for populating the Overlay Model. Together, they give every customer the ability to decide exactly how their organization's service model is presented in CNC.
Basic and Extended Overlay
CNC 5.0 introduces a new concept: Basic and Extended overlays. With this new feature, users also have the power to decide the level of service detail to present in each overlay view:
- Basic Overlay - Shows the high-level service topology
- Extended Overlay - Shows both high-level service topology and low-level service wiring
The illustration below shows the difference. In the basic view on the left, we can only see the service type (L2VPN) and service topology (hub and spoke). In the extended overlay, the view shows route targets, core and edge traffic directions (import, export, and both), and EVI IDs.
Service Overlay Model Terminology
The following illustration shows an example of the Overlay Model structure.
For help with the entities shown in the model, see the Legend below.
About the Service Overlay Framework
The Service Overlay framework consists of the Overlay Model, Overlay Parser, and Overlay API. The Overlay Model is an abstraction model used to describe service structure. The Overlay Model is created by an Overlay Parser, which uses the Overlay API to populate it.
The Overlay Model is implemented in Google Protocol Buffers format and compiled for Java and Python languages, as shown in the following illustration. Note that the Service Overlay framework populates the Device object (highlighted in gray in the illustration) from data in the Crosswork Device Lifecycle Manager (DLM). There is no need to populate it in parsers.
Service Overlay Model Example
The following illustration is an example from the L3VPN model, showing:
- An unmanaged CE scenario
- Two SEPs
- One Route Target (Import and Export)
- TE Associations (Bidirectional SRv6 ODN Route Policy)
Code for the Overlay Model in this scenario looks like this:
name: "L3VPN-696-SRv6-PE7-PE6"
yangPath: "my-service:yang/path"
serviceEndPoints {
hostName: "xrv9k-PE6"
CEfacingInterface: "GigabitEthernet0/0/0/3"
CEfacingInterfaceAttributes {
key: "ipv6.neighbor"
value: "2001:192:168::7"
}
CEfacingInterfaceAttributes {
key: "ipv6.peer-autonomous-system"
value: "65000"
}
CEfacingInterfaceAttributes {
key: "ipv6.provider-address"
value: "2001:192:168::6/128"
}
CEfacingInterfaceAttributes {
key: "ipv6.local-autonomous-system"
value: "65000"
}
CEfacingInterfaceAttributes {
key: "ipv6.type"
value: "bgp"
}
customerEdge {
unManagedCEID: "efdf6e4d-4159-319e-8fc8-c270dc103e57"
unManagedCELabel: "ipv6:2001:192:168::7"
}
coreEdges {
serviceDomainID: "549838467"
coreEdgeDirection: BIDIRECTIONAL
}
teConfigs {
headEnds: "xrv9k-PE6"
tailEnds: "fd00::100:100:100:7"
type: SRv6
connectionID: 708
isDynamic: true
}
relatedResources {
resourceName: "SRv6-7082-igp-relative-disjoint-node-PE6-PE7"
yangPath: "cisco-flat-L3vpn-fp:l3vpn-route-policy"
}
relatedResources {
resourceName: "L3VPN_NM-SRTE-ODN-7082"
yangPath: "cisco-sr-te-cfp:sr-te/cisco-sr-te-cfp-sr-odn:odn/odn-template"
}
relatedResources {
resourceName: "SRv6-708-igp-relative-disjoint-node"
yangPath: "cisco-flat-L3vpn-fp:l3vpn-route-policy"
}
relatedResources {
resourceName: "L3VPN_NM-SRTE-ODN-708"
yangPath: "cisco-sr-te-cfp:sr-te/cisco-sr-te-cfp-sr-odn:odn/odn-template"
}
}
serviceEndPoints {
hostName: "xrv9k-PE7"
CEfacingInterface: "GigabitEthernet0/0/0/3"
CEfacingInterfaceAttributes {
key: "ipv6.neighbor"
value: "2001:192:168::6"
}
CEfacingInterfaceAttributes {
key: "ipv6.peer-autonomous-system"
value: "65000"
}
CEfacingInterfaceAttributes {
key: "ipv6.provider-address"
value: "2001:192:168::7/128"
}
CEfacingInterfaceAttributes {
key: "ipv6.local-autonomous-system"
value: "65000"
}
CEfacingInterfaceAttributes {
key: "ipv6.type"
value: "bgp"
}
customerEdge {
unManagedCEID: "177dff6f-0658-3358-b5d8-53e7caa2fff5"
unManagedCELabel: "ipv6:2001:192:168::6"
}
coreEdges {
serviceDomainID: "549838467"
coreEdgeDirection: BIDIRECTIONAL
}
teConfigs {
headEnds: "xrv9k-PE7"
tailEnds: "fd00::100:100:100:6"
type: SRv6
connectionID: 708
isDynamic: true
}
relatedResources {
resourceName: "SRv6-7082-igp-relative-disjoint-node-PE6-PE7"
yangPath: "cisco-flat-L3vpn-fp:l3vpn-route-policy"
}
relatedResources {
resourceName: "L3VPN_NM-SRTE-ODN-7082"
yangPath: "cisco-sr-te-cfp:sr-te/cisco-sr-te-cfp-sr-odn:odn/odn-template"
}
relatedResources {
resourceName: "SRv6-708-igp-relative-disjoint-node"
yangPath: "cisco-flat-L3vpn-fp:l3vpn-route-policy"
}
relatedResources {
resourceName: "L3VPN_NM-SRTE-ODN-708"
yangPath: "cisco-sr-te-cfp:sr-te/cisco-sr-te-cfp-sr-odn:odn/odn-template"
}
}
serviceDomains {
domainID: "549838467"
caption: "RT"
label: "IPv6:0:697:696"
}
participatingDevices {
uuid: "65efa5cf-55b1-4f88-b27f-08ba7b7a5deb"
hostName: "xrv9k-PE6"
teRouterId: "100.100.100.6"
ipv6TeRouterId: "fd00::100:100:100:6"
providerNodeId: "xrv9k-PE7"
}
participatingDevices {
uuid: "39fb6f58-abb4-43db-b478-987074dcd471"
hostName: "xrv9k-PE7"
teRouterId: "100.100.100.7"
ipv6TeRouterId: "fd00::100:100:100:7"
providerNodeId: "xrv9k-PE7"
}
The Service Model Overlay Parser
The parser is the CNC extensibility module (customizable code) that allows you to decide how to overlay a service. You can implement it using either Java or Python, as shown in the figures below. Note that:
- Every Java overlay parser must extend the
ParameterizedOverlayProtoParser
abstract class and implementServiceOverlayProtoparseParameterizedOverlayProto(String serviceJson,MapinputParams)
. - Every Python overlay parser must import the
service_overlay_proto_api.py
base module and implementdef parseParameterizedOverlayProto(serviceJson: str,inputParams: dict) -> ServiceOverlayProto
.
Starting with CNC version 4.0, all Overlay Parsers must have the same structure.
Java Overlay Parser
Python Overlay Parser
In these illustrations:
- Service YANG Path Declaration: This is the service path as it appears in NSO and in the
service-path
field in packageservice-metadata.json
. - Initialization Boilerplate: This section will look the same for all parsers except for two parameters:
A. Service ID Field Name: Unique service identifier (
vpn-id
in these code snippets). B. SEP Path Expression: Under which JSON path service end points are declared (vpn-nodes.vpn-node[*]
in the above code snippets )- ==There is one important thing to remember with regard to this expression: The provided JsonPath must yield an array of objects representing the service endpoints.==
- Make sure you are using legitimate Jayway JsonPath expressions.
- Service End Point Construction: This is where you actually create your service end points:
A. SEP Field Parsing: Using JSON Utilities provided by the Overlay framework, parse out from the service JSON all the required fields for SEP construction.
B. Managed/Unmanaged SEP Creation: Create the Service End Points for Managed or Unmanaged CE scenarios (that is, populate the CE/PE interface details and so on).
C. TE Association: Associate your SEP with the underlying TE configuration. In the code snippets above, we are using the
associateStaticTEService
Overlay framework method to associate the service end point with the underlying RSVP-TE tunnel. If the service payload supports numerous options (such as SR-MPLS, SRv6, Tree SID, and so on) your TE association code will become more and more complicated to support all the scenarios. D. Connection to Service Domain: When you want to connect your SEP to a Service Domain, create the association using theconnectServiceEndPointToServiceDomain
method. E. Related Resource Association: If your service depends on external resources (such as Route Policies, ODN templates, and so on) you might want to add them to the related resources collection so that they will appear in the Service Details > Related Resources section of the CNC topology map. - Operations on Service Level: The purpose of this section is to perform operations on service level such as connecting two SEPs. As shown in the code snippet above, we are creating this core edge between the two service end points based on the overlay request type. If it is a Basic view overlay request, we create the core edge DIRECTIONLESS and with no additional information, as shown below: If it is an Extended view request, we create the core edge BIDIRECTIONAL with Pseudo Wire ID information:
- Final Model Construction: This is where the Overlay Model is built and returned to the Overlay framework for post processing.
Overlay Post Processing
After the Overlay Parser has constructed the Overlay Model, the framwork proceeds to the post-processing stage. This is where the Overlay framework:
- Enhances the model with device information.
- Filters out any Traffic Engineering policies and tunnels that were not discovered by the Crosswork Optimization Engine application.
- Enhances the Traffic Engineering data with Administrative and Operational statuses from Crosswork Optimization Engine.
After post-processing is complete, there may be cases where the parser needs to react to the data generated during post-processing. Starting with CNC 5.0, users with this purpose can implement in the parser the following methods:
ServiceOverlayProto.Builder postProcessing(ServiceOverlayProto.Builder serviceOverlayProto, Map<String, String> inputParams)
postProcessing(serviceOverlayProto: ServiceOverlayProto, inputParams: dict) → ServiceOverlayProto:
The serviceOverlayProto
input parameter in both methods is an enhanced Overlay Model already containing the up-to-date device and Traffic Engineering information.
Overlay Parser Creation
Using TSDN SDK Tools, you can create the parser in Java or Python.
Java:
- For the full function pack creation use:
create-prj-quickstart hello-service
- For overlay project creation only. use:
create-prj-overlay-plugin hello-service
Python:
- Use the
-Dpython.overlay=true
option for project creation: A. For the full function pack creation, use:create-prj-quickstart hello-service -Dpython.overlay=true
B. For overlay project creation only, use:create-prj-overlay-plugin hello-service -Dpython.overlay=true
- Make sure that your development environment contains the following Python packages:
pip3 install jsonpath-rw jsonpickle requests google-api-python-client
CNC Extensibility Project
CNC Extensibility - Compiled Function Package
Overlay API
The Overlay API is a set of utilities (inherited from the base class/module) providing a convenient way to parse the service JSON payload and create an Overlay Model out of it. These Overlay API utilites fall into these main categories:
- JSON Utilities: Allow users to traverse JSON object structures easily. They help the developer avoid complex JSON Path expressions and error handling, and enable simple field-name traversal.
- CAT Utilities: Allow users to easily fetch resources from RESTCONF services and represent them as JSON Objects, while dealing with error handling and wrapper pilling.
- Model Utilities: Allow users to easily construct the service overlay model instanc, by hiding all tedious GPB builder APIs.
The following table summarizes these utilities. Note that, although the table is based on Java, the same APIs exist for Python. Also note that the Overlay Parser Framework uses Jayway JsonPath to evaluate JSON expressions. In the Javadocs included in the SDK, you will find detailed explanations for every API.
JSON Utilities
This category includes:
- Field Lookup and Evaluation: Find and fetch a given field or set of fields based on the provided JSONPath expression. The fields can be at any level of the provided root object.
- Object Lookup and Evaluation: Find and fetch a given object or set of objects based on the provided JSONPath expression. The objects can be at any level of the provided root object.
All the mandatory methods in both subcategories will halt the parsing flow, in case the provided JSONPath does not yield a result.
CAT Utilities
Utilities in this category fetch resources from RESTCONF, loading them into JSONObjects while pilling their wrappers.
"Pilling their wrappers" means to remove excess wrapper objects. RESTCONF services return query results wrapped in two levels of objects. The first level is an object that represents the service or resource type, the second level is an array. We don't need that complexity in parsers, as it results in overly complex JSONPath expressions and confusing deviations between online tools and parser behavior. The load methods in this category strip the object out of the wrappers, and returns to the caller a clean JSONobject that represents the desired service or resource.
Model Utilities
These utilities hide the instrumentation and population of the GBP model. It includes these sub categories:
- Load and construct the model
- Create SEPs and Service Domains
- Connect SEPs and Service Domains
- Associate SEPs with underlying TE services
- Associate SEP's with related resources
- Error Handling
Please note:
- In Java, GPB uses the builder design pattern (all objects are constructed using *.Builder objects). In Python, this practice does not exist.
- Markup means that this method has several overloadings. It was designed in this manner because many fields in the model are optional, and we want clean and readable calls.
Example 1: L2VPN NM Over Static SR-MPLS Overlay
Let's analyze this example line by line:
Lines | Analysis |
---|---|
1-3 | These first three lines are part of every parser boilerplate. This is where you specify your service YANG path, and where your service ID field is located. |
4-5 | Every service is a collection of service endpoints (SEPs), so the first task of every parser is to iterate over the SEPs and parse them one by one. |
6 | This line is optional, and used only when you want to assign roles to your SEP. Those roles will be reflected as balloons on top of the SEP devices. In our example, these are the A and Z balloons shown on top of the devices: |
7 | In this line we identify the PE device on which this service end point was configured. |
8 | This is where we read the Access Interface Information. The access interface is presented as a black dot between the PE and CE devices. We are dealing with an Unmanaged CE scenario in this example, so we have only one black dot on the link close to the PE device, representing the PE access interface. This dot is clickable. When you click on it, the side panel will show all the details that you had parsed out for that interface, as shown in the example illustration below. |
9 | In this unmanaged CE scenario, the square generic-device icons represent CE devices. CNC does not know much about those devices, since CNC is not managing them. This means that if the implementer wants to decorate those icons with labels (HQ-LA,HQ-NK, HQ-NY), the parser has to do it explicitly. |
10 | This command actually creates the service end point for an Unmanaged CE scenario. If this were a Managed CE scenario, the parser would have to call on a variety of createServiceEndPoint_ManagedCE methods exposed by the Overlay API. |
11-12 | These lines instantiate the underlying TE Association. In our example, the SEP can be associated with a static SR-TE policy. Normally, if you wanted to establish this, you would have to fetch that policy from the Inventory service, parse it out, and associate it with the SEP. To make life easier, the Overlay API exposes the associateStaticTEService method, which does all of this coding behind the scenes. All the associated TE services will be shown in the Transport Tab under Service Details. |
13-17 | Core Edge Creation. Outside of the SEP creation block, we connect the SEPs with Core Edges. The API also allows you to specify the edge direction and label. Note that: - If it is basic view overlay request, we create the core edge DIRECTIONLESS and with no additional information - If it is extended view request, we create the core edge BIDIRECTIONAL with EVI ID information |
Here is the Java code for this example:
package com.cisco.sp.cw.service.overlay.parsers;
import java.util.Map;
import org.json.JSONObject;
import com.cisco.cw.service.overlay.model.proto.CoreEdgeDirection;
import com.cisco.cw.service.overlay.model.proto.SEPRole;
import com.cisco.cw.service.overlay.model.proto.ServiceEndPoint;
import com.cisco.cw.service.overlay.model.proto.ServiceOverlayProto;
import com.cisco.cw.service.overlay.model.proto.TeType;
import com.cisco.sp.cw.service.overlay.java.proto.ParameterizedOverlayProtoParser;
import com.cisco.sp.cw.service.overlay.java.proto.SepBuilderFactory;
import org.springframework.stereotype.Component;
import lombok.extern.slf4j.Slf4j;
@Component
@Slf4j
public class MyParser extends ParameterizedOverlayProtoParser {
@Override
public String getType() {
return "l2vpn:l2vpn-ntw/vpn-services/vpn-service";
}
@Override
public ServiceOverlayProto parseParameterizedOverlayProto(String serviceJsonStr, Map<String, String> inputParams) {
loadService(serviceJsonStr);
createOverlayModel(evaluateMandatoryField(getServiceJson(), "vpn-id"), getType());
boolean extendedView = Boolean.parseBoolean(inputParams.get("view_extended"));
ServiceEndPoint.Builder[] sepBuilders = buildServiceEndPoints("vpn-nodes.vpn-node[*]", new SepBuilderFactory() {
@Override
public ServiceEndPoint.Builder[] createSEPBuilders(JSONObject sepJson, int endpointSequentialNumber) {
SEPRole sepRole = endpointSequentialNumber == 0 ? SEPRole.HEAD : SEPRole.TAIL;
String peDevice = evaluateMandatoryField(sepJson, "ne-id");
String ceFacingInterface =
evaluateMandatoryField(sepJson, "vpn-network-accesses.vpn-network-access[0].connection.dot1q-interface.dot1q.physical-inf") + "." +
evaluateMandatoryField(sepJson, "vpn-network-accesses.vpn-network-access[0].connection.dot1q-interface.dot1q.c-vlan-id");
String ceUnmanagedDeviceLabel =
evaluateMandatoryField(sepJson, "vpn-network-accesses.vpn-network-access[0].id");
ServiceEndPoint.Builder sepBuilder = createServiceEndPoint_UnamangedCE(peDevice, ceFacingInterface, ceUnmanagedDeviceLabel,sepRole);
String teServiceName = evaluateField(sepJson, "te-service-mapping.te-mapping.sr-policy.policy");
associateStaticTEService(sepBuilder, teServiceName,
"sr-te-cfp:sr-te/sr-te-cfp-sr-policies:policies/policy", "head-end.[0].name", "tail-end", "color", TeType.SR_MPLS);
return new ServiceEndPoint.Builder[] { sepBuilder };
}
});
if (extendedView) {
String eviID = "EVI: " + evaluateMandatoryField(getServiceJson(), "evi-id");
connectServiceEndPoints(sepBuilders[0], sepBuilders[1], eviID, CoreEdgeDirection.BIDIRECTIONAL);
} else {
connectServiceEndPoints(sepBuilders[0], sepBuilders[1], "EVPN", CoreEdgeDirection.DIRECTIONLESS);
}
return buildModel(sepBuilders);
}
}
Example 2: Flat L3VPN Native Overlay
If you have read through the detailed explanation in Example 1, you should find this example self-explanatory.
Here is the Java code for this example:
package com.cisco.sp.cw.service.overlay.parsers;
import java.util.Map;
import org.json.JSONObject;
import com.cisco.cw.service.overlay.model.proto.CoreEdgeDirection;
import com.cisco.cw.service.overlay.model.proto.ServiceEndPoint;
import com.cisco.cw.service.overlay.model.proto.ServiceOverlayProto;
import com.cisco.sp.cw.service.overlay.java.proto.ParameterizedOverlayProtoParser;
import com.cisco.sp.cw.service.overlay.java.proto.SepBuilderFactory;
import org.springframework.stereotype.Component;
import lombok.extern.slf4j.Slf4j;
@Component
@Slf4j
public class MyL3VPNParser extends ParameterizedOverlayProtoParser {
@Override
public String getType() {
return "L3vpn-fp:L3vpn";
}
@Override
public ServiceOverlayProto parseParameterizedOverlayProto(String serviceJsonStr, Map<String, String> inputParams) {
loadService(serviceJsonStr);
createOverlayModel(evaluateMandatoryField(getServiceJson(), "vpn-id"), getType());
boolean extendedView = Boolean.parseBoolean(inputParams.get("view_extended"));
ServiceEndPoint.Builder[] sepBuilders = buildServiceEndPoints("vpn-nodes.vpn-node[*]", new SepBuilderFactory() {
@Override
public ServiceEndPoint.Builder[] createSEPBuilders(JSONObject sepJsonObj, int endpointSequentialNumber) {
ServiceEndPoint.Builder sepBuilder = createServiceEndPoint_UnamangedCE(
evaluateMandatoryField (sepJsonObj,"access-pe") ,
evaluateMandatoryField(sepJsonObj,"if-type") + evaluateMandatoryField(sepJsonObj,"if-id") ,
evaluateMandatoryField(sepJsonObj,"ce-port.e-bgp.neibghor-ipv4"));
if( extendedView ){
for (JSONObject afJson : evalObjectCollection(sepJsonObj,"vrf.address-family")){
String addressFamily = evaluateMandatoryField(afJson, "address-family");
for (JSONObject vpnTargetJson : evalObjectCollection(afJson,"vpn-target")){
String label = addressFamily + ":" + evaluateMandatoryField (vpnTargetJson,"rt-value");
String rtType = evaluateMandatoryField(vpnTargetJson,"rt-type");
CoreEdgeDirection coreEdgeldirection =
rtType.equals("both") ? CoreEdgeDirection.BIDIRECTIONAL :
rtType.equals("import") ? CoreEdgeDirection.INCOMING :
CoreEdgeDirection.OUTGOING;
connectServiceEndPointToServiceDomain(sepBuilder, "RT" , label, null, coreEdgeldirection) ;
}
}
}else{
String serviceName = evaluateMandatoryField(getServiceJson(),"name");
connectServiceEndPointToServiceDomain( sepBuilder, "L3VPN" , serviceName , null, CoreEdgeDirection.DIRECTIONLESS) ;
}
return new ServiceEndPoint.Builder[] {sepBuilder};
}
});
return buildModel(sepBuilders);
}
}
Provision UI Plugin Guide
Overview
This document provides an overview of two features available in the UI-plugin:
- Change Field Type
- Validate Field
These features enhance user interaction inside the provisioning UI editor panels for both create and edit flow. These features contribute to a more robust and user-friendly provisioning UI by allowing configs UI element conversion and custom validation of user input.
Change Field Type
The "Change Field Type" feature allows users to transform a UI representation of a configuration field, which is initially presented as a text field in the provision UI, into a switchable convertible-dropdown. User can switch between a selectable dropdown and a text field as needed.
Component Diagram
To change a text field representation of a configuration to a convertible-dropdown, the following steps must be followed.
- Add cnc-custom tailf:metadata in the corresponding Yang model inside the NSO Function Pack (FP) of the target configuration and recompile the NSO CFP to produce an updated function pack tar and update NSO with the updated CFP.
tailf:meta-data "cnc-custom" {
tailf:meta-value '
{
"type": "dropdown"
}';
}
Below is a sample for the "interface-id" configuration.
tailf:annotate "/l2vpn-ntw:l2vpn-ntw/l2vpn-ntw:vpn-services/l2vpn-ntw:vpn-service/l2vpn-ntw:vpn-nodes/l2vpn-ntw:vpn-node/l2vpn-ntw:vpn-network-accesses/l2vpn-ntw:vpn-network-access/l2vpn-ntw:interface-id" {
tailf:meta-data "cnc-custom" {
tailf:meta-value '
{
"type": "dropdown"
}';
}
Add a ui-list-plugin in the corresponding CAT Function Pack
CAT Function Pack Changes
Description of the parameters passed to the getListOfConfigValues method
- servicePath: Root Path of the model.
- configField: Name of the configuration.
- dependencyFromMetaData: Data that is added in the cnc-custom metadata.
- path: The instance path of the configuration.
- transactionChange: The transaction data details for the NSO user’s current transaction.
- additionalParam: Used for API pagination support.
Sample payload for the "interface-id" configuration.
{
"input": {
"servicePath": "/l2vpn-ntw:l2vpn-ntw/vpn-services/vpn-service",
"configField": "interface-id",
"dependencyFromMetaData": "[/l2vpn-ntw:l2vpn-ntw/vpn-services/vpn-service/vpn-nodes/vpn-node/vpn-node-id]",
"path": "/l2vpn-ntw:l2vpn-ntw/vpn-services/vpn-service{aa11}/vpn-nodes/vpn-node{xrv9k-22}/vpn-network-accesses/vpn-network-access{1}/interface-id",
"additionalParam": {
"endRow": 50,
"startRow": 0,
"searchTextVal": ""
},
"transactionChange": [
{
"path": "/l2vpn-ntw:l2vpn-ntw/vpn-services/vpn-service{aa11}/vpn-type",
"op": "value_set",
"value": "vpws",
"old": ""
},
{
"path": "/l2vpn-ntw:l2vpn-ntw/vpn-services/vpn-service{aa11}/vpn-nodes/vpn-node{xrv9k-22}/vpn-network-accesses/vpn-network-access{1}",
"op": "created",
"value": "",
"old": ""
},
{
"path": "/l2vpn-ntw:l2vpn-ntw/vpn-services/vpn-service{aa11}/vpn-nodes/vpn-node{xrv9k-22}",
"op": "created",
"value": "",
"old": ""
},
{
"path": "/l2vpn-ntw:l2vpn-ntw/vpn-services/vpn-service{aa11}",
"op": "created",
"value": "",
"old": ""
}
]
}
}
Response Data
The plugin code should return data structured in the following format.
Example response data format
{
"endOfData": true,
"status": "SUCCESS",
"data": [
{
"value": "GigabitEthernet0/0/0/7",
"label": "GigabitEthernet0/0/0/7"
},
{
"value": "GigabitEthernet0/0/0/6",
"label": "GigabitEthernet0/0/0/6"
},
{
"value": "GigabitEthernet0/0/0/2",
"label": "GigabitEthernet0/0/0/2"
},
{
"value": "GigabitEthernet0/0/0/1",
"label": "GigabitEthernet0/0/0/1"
}
]
}
Validate Field
The Validate Field feature enables users to implement custom validation logic for any user input using the UI-plugin. Provisioning-UI has an inline NSO server side validation. However, if this capability is not sufficient, the customer can add custom validation logic that would be invoked before the NSO field level validation.
Component Diagram
To add custom validation logic, the following steps must be followed.
- Add cnc-custom tailf:metadata in the corresponding Yang model inside the NSO Function Pack (FP) of the target configuration and recompile the NSO CFP to produce an updated function pack tar and update NSO with the updated CFP.
tailf:meta-data "cnc-custom" {
tailf:meta-value '
{
"leaf-validation": "true"
}';
}
Below is sample cnc-custom metadata for the "policy" configuration in the Yang model.
tailf:annotate "/l2vpn-ntw:l2vpn-ntw/l2vpn-ntw:vpn-services/l2vpn-ntw:vpn-service/l2vpn-ntw:vpn-nodes/l2vpn-ntw:vpn-node/cisco-l2vpn-ntw:te-service-mapping/cisco-l2vpn-ntw:te-mapping/cisco-l2vpn-ntw:te/cisco-l2vpn-ntw:sr-policy/cisco-l2vpn-ntw:sr-policy/cisco-l2vpn-ntw:policy" {
tailf:meta-data "cnc-custom" {
tailf:meta-value '
{
"field": "policy",
"leaf-validation": "true"
}';
}
}
Add a ui-validation-plugin in the corresponding CAT Function Pack.
CAT Function Pack Changes
Description of the parameters passed to the validateLeaf method
- servicePath: Root Path of the model.
- serviceName: It remains an empty string for now.
- yang-path-to-leaf: The instance path of the configuration.
- changes: The transaction data details for the NSO user’s current transaction.
Sample payload for the "policy" configuration.
{
"input": {
"yang-path-to-leaf": "/l2vpn-ntw:l2vpn-ntw/vpn-services/vpn-service{sw}/vpn-nodes/vpn-node{xrv9k-22}/te-service-mapping/te-mapping/sr-policy/policy",
"servicePath": "/l2vpn-ntw:l2vpn-ntw/vpn-services/vpn-service",
"serviceName": "",
"changes": [
{
"path": "/l2vpn-ntw:l2vpn-ntw/vpn-services/vpn-service{sw}/vpn-type",
"op": "value_set",
"value": "vpws",
"old": ""
},
{
"path": "/l2vpn-ntw:l2vpn-ntw/vpn-services/vpn-service{sw}/vpn-nodes/vpn-node{xrv9k-22}",
"op": "created",
"value": "",
"old": ""
},
{
"path": "/l2vpn-ntw:l2vpn-ntw/vpn-services/vpn-service{sw}",
"op": "created",
"value": "",
"old": ""
}
],
"leaf-value": "aa"
}
}
Response Data
The plugin should return either "status":"failure" or "status":"success" based of the validation result.
Example response data for success
{
"message": null,
"status": "success"
}
Example response data for failure
{
"message": "Policy is not discovered in crosswork",
"status": "failure"
}