CWM XDK for Cisco NSO
The CWM XDK for NSO (cwm-nsox
) is an application that helps you generate interfaces and logic for custom adapters intended to interact with the Cisco Network Services Orchestrator(NSO).
The primary purpose of cwm-nsox
is to reduce the time-consuming and error-prone manual process of constructing paths and payloads required for CWM to communicate with NSO.
The tool complements the Adapter SDK and is able to automatically define interfaces in .proto files and implementation of logic in the adapter go module. This is achieved by parsing yang files and points of interest provided by the Adapter Developer.
Prerequisites
- Installed CWM SDK and dependencies.
- The NSO
src/ncs/yang
folder for yang module imports. If you don't have it, you may install Cisco NSO to get it as part of a full installation.
Get cwm-nsox
The cwm-nsox
is a binary that comes with the Crosswork Workflow Manager Software Package.
Go to Cisco Software Download page to download the .tar file with the CWM Software Package, where the cwm-nsox
resides. Unpack the .tar and move the contents of the adapters
folder to a desired location

TipIt's recommended that you put the binary in a common folder, with the
cwm-sdk
and other extension binaries like cwm-oasx
.
Remember to include the location of the cwm-nsox
binary by setting the environment variable path:
export PATH=/path/to/adapter-dev-binaries:$PATH
Use cwm-nsox
for creating custom NSO adapter
The cwm-nsox
works with yang models of NSO services and NEDs files to identfiy yang paths that you'd like to address using the adapter.
Step 1: Create an adapter stub with Adapter SDK
Run the following command to create a new adapter using SDK:
cwm-sdk create-adapter \
-vendor cisco \
-product nsox \
-feature services \
-ignore-template

NoteThe
ignore-template
option eliminates tips and descriptions from the generated .proto files.
Step 2: Display yang paths and adapter code
display-paths
Use the cwm-nsox display-paths
command to extract paths for activities from a source yang file. With the -src
option, you specify the desired yang configuration file:
cwm-nsox display-paths -src ../path/to/source/file.yang
You will see a list of yang paths based on which you can generate adapter activities.
display-proto
Optionally, use the display-proto
command with the -poi
option to display the output for the activity based on your chosen point of interest:
cwm-nsox display-proto \
-src ../path/to/source/file.yang \
-poi your-nsoservice:your-nsoservice-list=%s/example-leaf
The output will look similar to this:
service Activities {
/*
* Description for activity NsoActivity
*/
rpc NsoActivity (NsoActivityRequest) returns (cisco.cwmlib.nso.Response);
}
/*
* Description for NsoActivityRequest
*/
message NsoActivityRequest {
string deviceName = 1; // devices/device={deviceName}
optional string dummyLeaf = 2; // tailf-ned-cwm-dmycli:dummy-leaf
cisco.cwmlib.nso.PutQuery queries = 3;
}
The following options are available:
Option | Data type | Description | Status |
---|---|---|---|
-deps | string | Define paths to yang imports (comma separated list). | optional |
-poi | string | Point to desired yang path (point of interest) | required |
-src | string | Point to path of yang configuration file. | required |
-verbose | string | Show command progress info. Options are: off, on, or very. | optional |
display-json
Optionally, use the display-json
command with the required -poi
and -src
options to display the data payload for the activity based on your chosen point of interest (path):
cwm-nsox display-json \
-src ../path/to/source/file.yang \
-poi your-nsoservice:your-nsoservice-list=%s/example-leaf
Option | Data type | Description | Status |
---|---|---|---|
-deps | string | Define paths to yang imports (comma separated list). | optional |
-poi | string | Point to desired yang path (point of interest) | required |
-src | string | Point to path of yang configuration file. | required |
-verbose | string | Show command progress info. Options are: off, on, or very. | optional |
Step 3: Generate activity
Using the path defined in the previous section, you can now run the generate-activity
command.
Go to the main directory of your adapter and execute the following command (adjust the path, activity name, operation type and point-of-interest name accordingly):
cwm-nsox generate-activity \
-src ../path/to/source/file.yang \
-feature services \
-activity NsoActivity \
-oper PUT \
-poi your-nsoservice:your-nsoservice-list=%s/example-leaf
This will generate a new adapter activity with a predefined rpc and I/O messages in the .proto files (see example in the section above), as well as a ready-to-execute implementation in the .go files.
Here's an example of the output generated by the cwm-nsox
and inserted in the activities.go file:
package services
import (
"context"
"www.cisco.com/cwm/adapters/cisco/nsox/common"
"www.cisco.com/cwm/sdk/adapters/logger"
"www.cisco.com/cwm/sdk/adapters/nso"
)
func (adp *Adapter) NsoActivity(ctx context.Context,
req *NsoActivityRequest, cfg *common.Config) (*nso.Response, error) {
logger.GetLogger(ctx).Info("Activity cisco.nsox.services.NsoActivity called...")
return nso.SendCustomRequest(ctx, req, cfg)
}
Create activity (optional)
Optionally, you can create a new activity for a selected feature but without indicating the source file. This will create an activity implementation in the .go files and a stub for you to fill in the logic inside the .proto file:
cwm-nsox create-activity \
-feature device \
-activity TestActivity \
-oper GET \
Here's an example of the activity stub generated by the cwm-nsox
inserted in the .proto file:
service Activities {
...
/*
* Description for activity Testactivity
*/
rpc Testactivity (TestactivityRequest) returns (TestactivityResponse);
}
...
/*
* Description for TestactivityRequest
*/
message TestactivityRequest {
// NOTE: Developer needs to set vars
}
message TestactivityResponse {
// NOTE: Developer needs to set vars
}
Step 4: Test your adapter
To test your adapter, generate an installable file and install the adapter in CWM.
Generate installable
Go to the main directory of your adapter and run the following command:
cwm-sdk create-installable
Test adapter activity
This will create a .tar file that can be then uploaded into CWM. Follow the detailed instructions in the Install NSO adapter section to install and deploy the adapter, then run a workflow that uses the newly added adapter activity.
CWM XDK for OpenAPI
Use CWM XDK for OpenAPI (cwm-oasx
) to automatically build interfaces and message logic for custom adapters that require communicating with OpenAPI-based systems. With the cwm-oasx
tool, you point to an OpenAPI operation defined in JSON, which cwm-oasx
will then use to generate a new adapter activity with a predefined rpc and I/O messages in the .proto files, as well as a ready-to-execute implementation in the adapter .go files.
Prerequisites
- Installed CWM SDK and dependencies.
- A JSON or YAML schema file of an OpenAPI or Swagger-enabled API.
Get cwm-oasx
The cwm-oasx
is a binary that comes with the Crosswork Workflow Manager Software Package.
Go to Cisco Software Download page to download the .tar file with the CWM Software Package, where the cwm-oasx
resides. Unpack the .tar and move the contents of the adapters
folder to a desired location.

TipIt's recommended that you put the binary in a common folder, with the
cwm-sdk
and other extension binaries like cwm-nsox
.
Remember to include the location of the cwm-oasx binary by setting the environment variable path:
export PATH=/path/to/adapter-dev-binaries:$PATH
Use cwm-oasx
for implementing adapter activities
The cwm-oasx
works with OpenAPI JSON/YAML schemas to identify endpoint paths and methods that you'd like to call using the adapter. Follow this instruction to create a single adapter activity based on a single API path and method.

NoteAs an example, we'll use the NetBox REST API schema in YAML format downloaded from the NetBox Swagger API. Using a JSON schema is also supported.
Step 1: Create an adapter stub with CWM SDK
Run the following command to create a new adapter using SDK:
cwm-sdk create-adapter \
-vendor cisco \
-product oasx \
-feature services \
-ignore-template

TipThe
ignore-template
option eliminates tips and descriptions from the generated .proto files.
Step 2: Display paths and adapter code
display-paths
Use the cwm-oasx display-paths
to extract paths and methods for activities from a source JSON/YAML file. Use the -src
option to point to the desired JSON/YAML API schema file:
cwm-oasx display-paths -src ../path/to/source/NetBox_REST_API.yaml
A list of paths will be displayed. In the example, we're interested in first of the four that define operations on ipam/prefixes
:
/api/ipam/prefixes/ : [GET POST PUT PATCH DELETE]
/api/ipam/prefixes/{id}/ : [GET PUT PATCH DELETE]
/api/ipam/prefixes/{id}/available-ips/ : [GET POST]
/api/ipam/prefixes/{id}/available-prefixes/ : [GET POST]
display-proto
Optionally, use the display-proto
command with the -oper
, -path
, and -src
options to display the output for the activity based on your chosen point of interest:
cwm-oasx display-proto \
-oper POST \
-path /api/ipam/prefixes/ \
-src ../path/to/source/NetBox_rest.yaml
The output will look similar to this:
Proto messages for activity:
message ProtoRequest {
message Data {
optional string comments = 1;
optional string customFields = 2;
optional string description = 3;
optional bool isPool = 4; // All IP addresses within this prefix are considered usable
optional bool markUtilized = 5; // Treat as 100% utilized
string prefix = 6;
optional int32 role = 7; // The primary function of this prefix
optional int32 site = 8;
optional string status = 9; // Operational status of this prefix\n\n* `container` - Container\n* `active` - Active\n* `reserved` - Reserved\n* `deprecated` - Deprecated
message Tags {
optional string color = 1;
string name = 2;
string slug = 3;
}
repeated Tags tags = 10;
optional int32 tenant = 11;
optional int32 vlan = 12;
optional int32 vrf = 13;
}
Data data = 3;
}
message ProtoResponse {
int32 status = 1;
google.protobuf.Value data = 2;
}
Note that three of four available options are required:
Option | Data type | Description | Status |
---|---|---|---|
-oper | string | Point to specific operation: GET, POST, PUT, PATCH or DELETE. | required |
-path | string | Point to specific API path. | required |
-src | string | Point to desired JSON API schema file. | required |
-verbose | string | Show command progress info. Options are: off, on, or very. | optional |
display-json
Optionally, use the display-json
command with the required -oper
, -path
, and -src
options to display the data payload for the activity based on your chosen point of interest (path):
cwm-oasx display-json \
-oper POST \
-path /api/ipam/prefixes/ \
-src ../path/to/source/NetBox_rest.yaml
The output will look similar to this:
Data payload for activity:
{
"comments": "%s",
"custom_fields": "{{'{'}}%s{{'}'}}",
"description": "%s",
"is_pool": %t,
"mark_utilized": %t,
"prefix": "%s",
"role": %d,
"site": %d,
"status": "%s",
"tags": [
"color": "%s",
"name": "%s",
"slug": "%s"
],
"tenant": %d,
"vlan": %d,
"vrf": %d
}
Note that three of four available options are required:
Option | Data type | Description | Status |
---|---|---|---|
-oper | string | Point to specific operation: GET, POST, PUT, PATCH or DELETE. | required |
-path | string | Point to specific API path. | required |
-src | string | Point to desired JSON API schema file. | required |
-verbose | string | Show command progress info. Options are: off, on, or very. | optional |
Step 3: Generate activity
Using the path defined in the previous section, you can now run the generate-activity
command.
Go to the main directory of your adapter and execute the following command (adjust the feature and activity name, operation, path, and source accordingly):
cwm-oasx generate-activity \
-feature services \
-activity PostPrefix \
-oper POST \
-poi /api/ipam/prefixes/ \
-src ../path/to/source/NetBox_rest.yaml
This will generate a new adapter activity with a predefined rpc and I/O messages in the .proto files (see example in the display-proto
section above), as well as a ready-to-execute implementation in the .go files. Here's an example of the function generated by the cwm-oasx
and inserted in the activities.go file:
func (adp *Adapter) PostPrefix(ctx context.Context, req *PostPrefixRequest, cfg *common.Config) (*PostPrefixResponse, error) {
return oas.SendRequest[*PostPrefixResponse](ctx, req, cfg.GetResource(), cfg.GetSecret())
}
Create activity (optional)
Optionally, you can create a new activity for a selected feature but without indicating the source json/yaml file. This will create an activity implementation in the .go files and a stub for you to fill in the logic inside the .proto file:
cwm-oasx create-activity \
-feature services \
-activity TestActivity \
-oper POST \
Here's an example of the activity stub generated by the cwm-oasx
inserted in the .proto file:
service Activities {
...
/*
* Description for activity Testactivity
*/
rpc Testactivity (TestactivityRequest) returns (TestactivityResponse);
}
...
/*
* Description for TestactivityRequest
*/
message TestactivityRequest {
// NOTE: Developer needs to set vars
}
message TestactivityResponse {
// NOTE: Developer needs to set vars
}
Step 4: Generate feature (optional)
Use this command to bulk create activities for new or existing features. If you point to a path, generate-feature
will pick all the endpoints existing down this path and generate activity code based on each, for all available methods. Set verbose
to on
or very
to see details of command execution.
For example, let's use the CWM JSON API specification and pass /secret
as the path parameter for feature services
.
cwm-oasx generate-feature \
-src ../path/to/source/cwm.json \
-feature services \
-poi /secret \
-verbose on
Expand the sample to see what the generated output will look like in the activities.go file:
??? sample ```go package services
import (
"context"
"cisco.com/cwm/lib/xdk/oas"
"cisco.com/cwm/adapters/cisco/oasx/common"
)
func (adp *Adapter) GetType(ctx context.Context, req *GetTypeRequest, cfg *common.Config) (*GetTypeResponse, error) {
return oas.SendRequest[*GetTypeResponse](ctx, req, cfg.GetResource(), cfg.GetSecret())
}
func (adp *Adapter) Get(ctx context.Context, req *GetRequest, cfg *common.Config) (*GetResponse, error) {
return oas.SendRequest[*GetResponse](ctx, req, cfg.GetResource(), cfg.GetSecret())
}
func (adp *Adapter) Post(ctx context.Context, req *PostRequest, cfg *common.Config) (*PostResponse, error) {
return oas.SendRequest[*PostResponse](ctx, req, cfg.GetResource(), cfg.GetSecret())
}
func (adp *Adapter) GetWithSecretId(ctx context.Context, req *GetWithSecretIdRequest, cfg *common.Config) (*GetWithSecretIdResponse, error) {
return oas.SendRequest[*GetWithSecretIdResponse](ctx, req, cfg.GetResource(), cfg.GetSecret())
}
func (adp *Adapter) PatchWithSecretId(ctx context.Context, req *PatchWithSecretIdRequest, cfg *common.Config) (*PatchWithSecretIdResponse, error) {
return oas.SendRequest[*PatchWithSecretIdResponse](ctx, req, cfg.GetResource(), cfg.GetSecret())
}
func (adp *Adapter) DeleteWithSecretId(ctx context.Context, req *DeleteWithSecretIdRequest, cfg *common.Config) (*DeleteWithSecretIdResponse, error) {
return oas.SendRequest[*DeleteWithSecretIdResponse](ctx, req, cfg.GetResource(), cfg.GetSecret())
}
func (adp *Adapter) GetTypeWithSecretTypeId(ctx context.Context, req *GetTypeWithSecretTypeIdRequest, cfg *common.Config) (*GetTypeWithSecretTypeIdResponse, error) {
return oas.SendRequest[*GetTypeWithSecretTypeIdResponse](ctx, req, cfg.GetResource(), cfg.GetSecret())
}
```
The generate-feature
command comes with the following options:
Option | Data type | Description | Status |
---|---|---|---|
-src | string | Point to desired JSON/YAML API schema file. | required |
-feature | string | Give name of adapter feature to be updated. | required |
-poi | string | Point to specific API path. | required |
-verbose | string | Show command progress info. Options are: off, on, or very. | optional |
Export XDK module to local directory
The cwm-oasx
uses the XDK go module for performing tasks, and some of them can share some of the resources with the NSOX extension. While the XDK module is exported to the directory of your adapter upon executing the generate-activity
command, in certain cases you might want to have the XDK go module created in the adapter directory beforehand. For this purpose, use the export-lib
command.
The export-lib
command comes with the following options:
Option | Data type | Description | Status |
---|---|---|---|
-location | string | Provide location where XDK lib should be created. (default: current directory) | optional |
-verbose | string | Show command progress info. Options are: off, on, or very. | optional |
Generate installable
Go to the main directory of your adapter and run the following command:
cwm-sdk create-installable
Test adapter activity
The command will produce a .tar
file that can be then installed in CWM and tested for proper functioning.