Perform NED version upgrades and migration.
Many services in NSO rely on NEDs to perform network provisioning. These services map service-specific configuration to the device data models, provided by the NEDs. As the NED packages can be upgraded independently, they can introduce changes in the device YANG models that cause issues for the services using them.
NSO provides tools to migrate between backward incompatible NED versions. The tools are designed to give you a structured analysis of which paths will change between two NED versions and visibility into the scope of the potential impact that a change in the NED will drive in the service code.
The tools allow for a usage-based analysis of which parts of the NED data model (and instance tree) a particular service has written to. This will give you an (at least opportunistic) sense of which paths must change in the service code.
These features aim to lower the barrier of upgrading NEDs and significantly reduce the amount of uncertainty and side effects that NED upgrades were historically associated with.
migrate
ActionBy using the /ncs:devices/device/migrate
action, you can change the NED major/minor version of a device. The action migrates all configuration and service meta-data. The action can also be executed in parallel on a device group or on all devices matching a NED identity. The procedure for migrating devices is further described in NED Migration.
Additionally, the example examples.ncs/getting-started/developing-with-ncs/26-ned-migration
in the NSO examples collection illustrates how to migrate devices between different NED versions using the migrate
action.
What makes it particularly useful to a service developer is that the action reports what paths have been modified and the service instances affected by those changes. This information can then be used to prepare the service code to handle the new NED version. If the verbose
option is used, all service instances are reported instead of just the service points. If the dry-run
option is used, the action simply reports what it would do. This gives you the chance to analyze before any actual change is performed.
Create generic NEDs.
As described in previous sections, the CLI NEDs are almost programming-free. The NSO CLI engine takes care of parsing the stream of characters that come from "show running-config [toptag]" and also automatically produces the sequence of CLI commands required to take the system from one state to another.
A generic NED is required when we want to manage a device that neither speaks NETCONF or SNMP nor can be modeled so that ConfD - loaded with those models - gets a CLI that looks almost/exactly like the CLI of the managed device. For example, devices that have other proprietary CLIs, devices that can only be configured over other protocols such as REST, Corba, XML-RPC, SOAP, other proprietary XML solutions, etc.
In a manner similar to the CLI NED, the Generic NED needs to be able to connect to the device, return the capabilities, perform changes to the device, and finally, grab the entire configuration of the device.
The interface that a Generic NED has to implement is very similar to the interface of a CLI NED. The main differences are:
When NSO has calculated a diff for a specific managed device, it will for CLI NEDS also calculate the exact set of CLI commands to send to the device, according to the YANG models loaded for the device. In the case of a generic NED, NSO will instead send an array of operations to perform towards the device in the form of DOM manipulations. The generic NED class will receive an array of NedEditOp
objects. Each NedEditOp
object contains:
The operation to perform, i.e. CREATED, DELETED, VALUE_SET, etc.
The keypath to the object in case.
An optional value
When NSO wants to sync the configuration from the device to NSO, the CLI NED only has to issue a series of show running-config [toptag]
commands and reply with the output received from the device. A generic NED has to do more work. It is given a transaction handler, which it must attach to over the Maapi interface. Then the NED code must - by some means - retrieve the entire configuration and write into the supplied transaction, again using the Maapi interface.
Once the generic NED is implemented, all other functions in NSO work precisely in the same manner as with NETCONF and CLI NED devices. NSO still has the capability to run network-wide transactions. The caveat is that to abort a transaction towards a device that doesn't support transactions, we calculate the reverse diff and send it to the device, i.e. we automatically calculate the undo operations.
Another complication with generic NEDs is how the NED class shall authenticate towards the managed device. This depends entirely on the protocol between the NED class and the managed device. If SSH is used to a proprietary CLI, the existing authgroup structure in NSO can be used as is. However, if some other authentication data is needed, it is up to the generic NED implementer to augment the authgroups in tailf-ncs.yang
accordingly.
We must also configure a managed device, indicating that its configuration is handled by a specific generic NED. Below we see that the NED with identity xmlrpc
is handling this device.
The example examples.ncs/generic-ned/xmlrpc-device
in the NSO examples collection implements a generic NED that speaks XML-RPC to 3 HTTP servers. The HTTP servers run the Apache XML-RPC server code and the NED code manipulates the 3 HTTP servers using a number of predefined XML RPC calls.
A good starting point when we wish to implement a new generic NED is the ncs-make-package --generic-ned-skeleton ...
command, which is used to generate a skeleton package for a generic NED.
A generic NED always requires more work than a CLI NED. The generic NED needs to know how to map arrays of NedEditOp
objects into the equivalent reconfiguration operations on the device. Depending on the protocol and configuration capabilities of the device, this may be arbitrarily difficult.
Regardless of the device, we must always write a YANG model that describes the device. The array of NedEditOp
objects that the generic NED code gets exposed to is relative the YANG model that we have written for the device. Again, this model doesn't necessarily have to cover all aspects of the device.
Often a useful technique with generic NEDs can be to write a pyang plugin to generate code for the generic NED. Again, depending on the device it may be possible to generate Java code from a pyang plugin that covers most or all aspects of mapping an array of NedEditOp
objects into the equivalent reconfiguration commands for the device.
Pyang is an extensible and open-source YANG parser (written by Tail-f) available at http://www.yang-central.org
. pyang is also part of the NSO release. A number of plugins are shipped in the NSO release, for example $NCS_DIR/lib/pyang/pyang/plugins/tree.py
is a good plugin to start with if we wish to write our own plugin.
$NCS_DIR/examples.ncs/generic-ned/xmlrpc-device
is a good example to start with if we wish to write a generic NED. It manages a set of devices over the XML-RPC protocol. In this example, we have:
Defined a fictitious YANG model for the device.
Implemented an XML-RPC server exporting a set of RPCs to manipulate that fictitious data model. The XML-RPC server runs the Apache org.apache.xmlrpc.server.XmlRpcServer
Java package.
Implemented a Generic NED which acts as an XML-RPC client speaking HTTP to the XML-RPC servers.
The example is self-contained, and we can, using the NED code, manipulate these XML-RPC servers in a manner similar to all other managed devices.
NedEditOp
ObjectsAs it was mentioned earlier the NedEditOp
objects are relative to the YANG model of the device, and they are to be translated into the equivalent reconfiguration operations on the device. Applying reconfiguration operations may only be valid in a certain order.
For Generic NEDs, NSO provides a feature to ensure dependency rules are being obeyed when generating a diff to commit. It controls the order of operations delivered in the NedEditOp
array. The feature is activated by adding the following option to package-meta-data.xml
:
When the ordered-diff
flag is set, the NedEditOp
objects follow YANG schema order and consider dependencies between leaf nodes. Dependencies can be defined using leafrefs and the tailf:cli-diff-after
, tailf:cli-diff-create-after
, tailf:cli-diff-modify-after
, tailf:cli-diff-set-after
, tailf:cli-diff-delete-after
YANG extensions. Read more about the above YANG extensions in the Tail-f CLI YANG extensions man page.
A device we wish to manage using a NED usually has not just configuration data that we wish to manipulate from NSO, but the device usually has a set of commands that do not relate to configuration.
The commands on the device we wish to be able to invoke from NSO must be modeled as actions. We model this as actions and compile it using a special ncsc
command to compile NED data models that do not directly relate to configuration data on the device.
The NSO example $NCS_DIR/examples.ncs/generic-ned/xmlrpc-device
contains an example where the managed device, a fictitious XML-RPC device contains a YANG snippet :
When that action YANG is imported into NSO it ends up under the managed device. We can invoke the action on the device as :
The NED code is obviously involved here. All NEDs must always implement:
The command()
method gets invoked in the NED, the code must then execute the command. The input parameters in the params
parameter correspond to the data provided in the action. The command()
method must reply with another array of ConfXMLParam
objects.
The above code is fake, on a real device, the job of the command()
method is to establish a connection to the device, invoke the command, parse the output, and finally reply with an ConfXMLParam
array.
The purpose of implementing NED commands is usually that we want to expose device commands to the programmatic APIs in the NSO DOM tree.
Description of SNMP NED.
NSO can use SNMP to configure a managed device, under certain circumstances. SNMP in general is not suitable for configuration, and it is important to understand why:
In SNMP, the size of a SET request, which is used to write to a device, is limited to what fits into one UDP packet. This means that a large configuration change must be split into many packets. Each such packet contains some parameters to set, and each such packet is applied on its own by the device. If one SET request out of many fails, there is no abort command to undo the already applied changes, meaning that rollback is very difficult.
The data modeling language used in SNMP, SMIv2, does not distinguish between configuration objects and other writable objects. This means that it is not possible to retrieve only the configuration from a device without explicit, exact knowledge of all objects in all MIBs supported by the device.
SNMP supports only two basic operations, read and write. There is no protocol support for creating or deleting data. Such operations must be modeled in the MIBs, explicitly.
SMIv2 has limited support for semantic constraints in the data model. This means that it is difficult to know if a certain configuration will apply cleanly on a device. If it doesn't, rollback is tricky, as explained above.
Because of all of the above, ordering of SET requests becomes very important. If a device refuses to create some object A before another B, an SNMP manager must make sure to create B before creating A. It is also common that objects cannot be modified without first making them disabled or inactive. There is no standard way to do this, so again, different data models do this in different ways.
Despite all this, if a device can be configured over SNMP, NSO can use its built-in multilingual SNMP manager to communicate with the device. However, to solve the problems mentioned above, the MIBs supported by the device need to be carefully annotated with some additional information that instructs NSO on how to write configuration data to the device. This additional information is described in detail below.
To add a device, the following steps need to be followed. They are described in more detail in the following sections.
(See the Makefile snmp-ned/basic/packages/ex-snmp-ned/src/Makefile
, for an example of the below description.) Make sure that you have all MIBs available, including import dependencies, and that they contain no errors.
The ncsc --ncs-compile-mib-bundle
compiler is used to compile MIBs and MIB annotation files into NSO load files. Assuming a directory with input MIB files (and optional MIB annotation files) exist, the following command compiles all the MIBs in device-models
and writes the output to ncs-device-model-dir
.
The compilation steps performed by the ncsc --ncs-compile-mib-bundle
are elaborated below:
Transform the MIBs into YANG according to the IETF standardized mapping (https://www.ietf.org/rfc/rfc6643.txt). The IETF-defined mapping makes all MIB objects read-only over NETCONF.
Generate YANG deviations from the MIB, this makes SMIv2 read-write
objects YANG config true
as a YANG deviation.
Include the optional MIB annotations.
Merge the read-only YANG from step 1 with the read-write deviation from step 2.
Compile the merged YANG files into NSO load format.
These steps are illustrated in the figure below:
Finally make sure that the NSO configuration file points to the correct device model directory:
Each managed device is configured with a name, IP address, and port (161 by default), and the SNMP version to use (v1, v2c, or v3).
To minimize the necessary configuration, the authentication group concept (see Authentication Groups) is used also for SNMP. A configured managed device of the type snmp
refers to an SNMP authgroup. An SNMP authgroup contains community strings for SNMP v1 and v2c and USM parameters for SNMP v3.
In the example above, when NSO needs to speak to the device r3
, it sees that the device is of type snmp
, and that SNMP v3 should be used with authentication parameters from the SNMP authgroup my-authgroup
. This authgroup maps the local NSO user admin
to the USM user admin
, with explicit remote passwords given. These passwords will be localized for each SNMP engine that NSO communicates with. While the passwords above are shown encrypted, when you enter them in the CLI you write them in clear text. Note also that the remote engine ID is not configured; NSO performs a discovery process to find it automatically.
No NSO user other than admin
is mapped by the authgroup my-authgroup
for SNMP v3.
With SNMP, there is no standardized, generic way for an SNMP manager to learn which MIBs an SNMP agent implements. By default, NSO assumes that an SNMP device implements all MIBs known to NSO, i.e., all MIBs that have been compiled with the ncsc --ncs-compile-mib-bundle
command. This works just fine if all SNMP devices NSO manages are of the same type, and implement the same set of MIBs. But if NSO is configured to manage many different SNMP devices, some other mechanism is needed.
In NSO, this problem is solved by using MIB groups. MIB group is a named collection of MIB module names. A managed SNMP device can refer to one or more MIB groups. For example, below two MIB groups are defined:
The wildcard *
can be used only at the end of a string; it is thus used to define a prefix of the MIB module name. So the string SNMP*
matches all loaded standard SNMP modules, such as SNMPv2-MIB, SNMP-TARGET-MIB, etc.
An SNMP device can then be configured to refer to one or more of the MIB groups:
Most annotations for MIB objects are used to instruct NSO on how to split a large transaction into suitable SNMP SET requests. This step is not necessary for a default integration. But when for example ordering dependencies in the MIB is discovered it is better to add this as annotations and let NSO handle the ordering rather than leaving it to the CLI user or Java programmer.
In some cases, NSO can automatically understand when rows in a table must be created or deleted before rows in some other table. Specifically, NSO understands that if table B has an INDEX object in table A (i.e., B sparsely augments A), then rows in table B must be created after rows in table B, and vice versa for deletions. NSO also understands that if table B AUGMENTS table A, then a row in table A must be created before any column in B is modified.
However, in some MIBs, table dependencies cannot be detected automatically. In this case, these tables must be annotated with a sort-priority
. By default, all rows have sort-priority 0. If table A has a lower sort priority than table B, then rows in table A are created before rows in table B.
In some tables, existing rows cannot be modified unless the row is inactivated. Once inactive, the row can be modified and then activated again. Unfortunately, there is no formal way to declare this is SMIv2, so these tables must be annotated with two statements; ned-set-before-row-modification
and ned-modification-dependent
. The former is used to instruct NSO which column and which value is used to inactivate a row, and the latter is used on each column that requires the row to be inactivated before modification. ned-modification-dependent
can be used in the same table as ned-set-before-row-modification
, or in a table that augments or sparsely augments the table with ned-set-before-row-modification
.
By default, NSO treats a writable SMIv2 object as configuration, except if the object is of type RowStatus. Any writable object that does not represent configuration must be listed in a MIB annotation file when the MIB is compiled, with the "operational" modifier.
When NSO retrieves data from an SNMP device, e.g., when doing a sync from-device
, it uses the GET-NEXT request to scan the table for available rows. When doing the GET-NEXT, NSO must ask for an accessible column. If the row has a column of type RowStatus, NSO uses this column. Otherwise, if one of the INDEX objects is accessible, it uses this object. Otherwise, if the table has been annotated with ned-accessible-column
, this column is used. And, as a last resort, NSO does not indicate any column in the first GET-NEXT request, and uses the column returned from the device in subsequent requests. If the table has "holes" for this column, i.e., the column is not instantiated in all rows, NSO will not detect those rows.
NSO can automatically create and delete table rows for tables that use the RowStatus TEXTUAL-CONVENTION, defined in RFC 2580.
It is pretty common to mix configuration objects with non-configuration objects in MIBs. Specifically, it is quite common that rows are created automatically by the device, but then some columns in the row are treated as configuration data. In this case, the application programmer must tell NSO to sync from the device before attempting to modify the configuration columns, to let NSO learn which rows exist on the device.
Some SNMP agents require a certain order of row deletions and creations. By default, the SNMP NED sends all creates before deletes. The annotation ned-delete-before-create
can be used on a table entry to send row deletions before row creations, for that table.
Sometimes rows in some SNMP agents cannot be modified once created. Such rows can be marked with the annotation ned-recreate-when-modified
. This makes the SNMP NED to first delete the row, and then immediately recreate it with the new values.
A good starting point for understanding annotations is to look at the example in examples.ncs/snmp-ned
directory. The BASIC-CONFIG-MIB mib has a table where rows can be modified if the bscActAdminState
is set to locked. To have NSO do this automatically when modifying entries rather than leaving it to users an annotation file can be created. See the BASIC-CONFIG-MIB.miba
which contains the following:
This tells NSO that before modifying the bscActFlow
column set the bscActAdminState
to locked and restore the previous value after committing the set operation.
All MIB annotations for a particular MIB are written to a file with the file suffix .miba
. See mib_annotations(5) in manual pages for details.
Make sure that the MIB annotation file is put into the directory where all the MIB files are which is given as input to the ncsc --ncs-compile-mib-bundle
command
NSO can manage SNMP devices within transactions, a transaction can span Cisco devices, NETCONF devices, and SNMP devices. If a transaction fails NSO will generate the reverse operation to the SNMP device.
The basic features of the SNMP will be illustrated below by using the examples.ncs/snmp-ned
example. First, try to connect to all SNMP devices:
When NSO executes the connect request for SNMP devices it performs a get-next request with 1.1 as var-bind. When working with the SNMP NED it is helpful to turn on the NED tracing:
This creates a trace file named ned-devicename.trace
. The trace for the NCS connect
action looks like:
When looking at SNMP trace files it is useful to have the OBJECT-DESCRIPTOR rather than the OBJECT-IDENTIFIER. To do this, pipe the trace file to the smixlate
tool:
You can access the data in the SNMP systems directly (read-only and read-write objects):
NSO can synchronize all writable objects into CDB:
All the standard features of NSO with transactions and roll-backs will work with SNMP devices. The sequence below shows how to enable authentication traps for all devices as one transaction. If any device fails, NSO will automatically roll back the others. At the end of the CLI sequence a manual rollback is shown:
Create CLI NEDs.
The CLI NED is a model-driven way to CLI script towards all Cisco-like devices. Some Java code is necessary for handling the corner cases a human-to-machine interface presents. The NSO CLI NED southbound of NSO shares a Cisco-style CLI engine with the northbound NSO CLI interface, and the CLI engine can thus run in both directions, producing CLI southbound and interpreting CLI data coming from southbound while presenting a CLI interface northbound. It is helpful to keep this in mind when learning and working with CLI NEDs.
A sequence of Cisco CLI commands can be turned into the equivalent manipulation of the internal XML tree that represents the configuration inside NSO.
A YANG model, annotated appropriately, will produce a Cisco CLI. The user can enter Cisco commands, and NSO will parse the Cisco CLI commands using the annotated YANG model and change the internal XML tree accordingly. Thus, this is the CLI parser and interpreter. Model-driven.
The reverse operation is also possible. Given two different XML trees, each representing a configuration state, in the netsim/ConfD case and NSO's northbound CLI interface, it represents the configuration of a single device, i.e., the device using ConfD as a management framework. In contrast, the NSO case represents the entire network configuration and can generate the list of Cisco commands going from one XML tree to another.
NSO uses this technology to generate CLI commands southbound when we manage Cisco-like devices.
It will become clear later in the examples how the CLI engine runs in forward and reverse mode. The key point though, is that the Cisco CLI NED Java programmer doesn't have to understand and parse the structure of the CLI; this is entirely done by the NSO CLI engine.
To implement a CLI NED, the following components are required:
A YANG data model that describes the CLI. An important development tool here is netsim (ConfD), the Tail-f on-device management toolkit. For NSO to manage a CLI device, it needs a YANG file with exactly the right annotations to produce precisely the managed device's CLI. A few examples exist in the NSO NED evaluation collection with annotated YANG models that render different Cisco CLI variants.
See, for example, $NCS_DIR/packages/neds/dell-ftos
and $NCS_DIR/packages/neds/cisco-nx
. Look for tailf:cli-*
extensions in the NED src/yang
directory YANG models.
Thus, to create annotated YANG files for a device with a Cisco-like CLI, the work procedure is to run netsim (ConfD) and write a YANG file that renders the correct CLI.
Furthermore, this YANG model must declare an identity with ned:cli-ned-id
as a base.
It is important to note that a NED only needs to cover certain aspects of the device. To have NSO manage a device with a Cisco-like CLI you do not have to model the entire device, only the commands intended to be used need to be covered. When the show()
callback issues its show running-config [toptag]
command and the device replies with data that is fed to NSO, NSO will ignore all command dump output that the loaded YANG models do not cover.
Thus, whichever Cisco-like device we wish to manage, we must first have YANG models from NSO that cover all aspects of the device we want to use. Once we have a YANG model, we load it into NSO and modify the example CLI NED class to return the NedCapability list of the device.
The NED code gets to see all data from and to the device. If it's impossible or too hard to get the YANG model exactly right for all commands, a last resort is to let the NED code modify the data inline.
The next thing required is a Java class that implements the NED. This is typically not a lot of code, and the existing example NED Java classes are easily extended and modified to fit other needs. The most important point of the Java NED class code is that the code can be oblivious to the CLI commands sent and received.
Java CLI NED code must implement the CliNed
interface.
NedConnectionBase.java
. See $NCS_DIR/java/jar/ncs-src.jar
. Use jar xf ncs-src.jar to extract the JAR file. Look for src/com/tailf/ned/NedConnectionBase.java
.
NedCliBase.java
. See $NCS_DIR/java/jar/ncs-src.jar
. Use jar xf ncs-src.jar to extract the JAR file. Look for src/com/tailf/ned/NedCliBase.java
.
Thus, the Java NED class has the following responsibilities.
It must implement the identification callbacks, i.e modules()
, type()
, and identity()
It must implement the connection-related callback methods newConnection()
, isConnection()
and reconnect()
NSO will invoke the newConnection()
when it requires a connection to a managed device. The newConnection()
method is responsible for connecting to the device, figuring out exactly what type of device it is, and returning an array of NedCapability
objects.\
This is very much in line with how a NETCONF connect works and how the NETCONF client and server exchange hello messages.
Finally, the NED code must implement a series of data methods. For example, the method void prepare(NedWorker w, String data)
get a String
object which is the set of Cisco CLI commands it shall send to the device.
In the other direction, when NSO wants to collect data from the device, it will invoke void show(NedWorker w, String toptag)
for each tag found at the top of the data model(s) loaded for that device. For example, if the NED gets invoked with show(w, "interface")
it's responsibility is to invoke the relevant show configuration command for "interface", i.e. show running-config interface
over the connection to the device, and then dumbly reply with all the data the device replies with. NSO will parse the output data and feed it into its internal XML trees.
NSO can order the showPartial()
to collect part of the data if the NED announces the capability http://tail-f.com/ns/ncs-ned/show-partial?path-format=FORMAT
in which FORMAT is of the following:
key-path: support regular instance keypath format.
top-tag: support top tags under the /devices/device/config
tree.
cmd-path-full: support Cisco's CLI edit path with instances.
path-modes-only: support Cisco CLI mode path.
cmd-path-modes-only-existing: same as path-mode-only
but NSO only supplies the path mode of existing nodes.
The idea is to write a YANG data model and feed that into the NSO CLI engine such that the resulting CLI mimics that of the device to manage. This is fairly straightforward once you have understood how the different constructs in YANG are mapped into CLI commands. The data model usually needs to be annotated with a specific Tail-f CLI extension to tailor exactly how the CLI is rendered.
This section will describe how the general principles work and give a number of cookbook-style examples of how certain CLI constructs are modeled.
The CLI NED is primarily designed to be used with devices that has a CLI that is similar to the CLIs on a typical Cisco box (i.e. IOS, XR, NX-OS, etc). However, if the CLI follows the same principles but with a slightly different syntax, it may still be possible to use a CLI NED if some of the differences are handled by the Java part of the CLI NED. This section will describe how this can be done.
Let's start with the basic data model for CLI mapping. YANG consists of three major elements: containers, lists, and leaves. For example:
The basic rendering of the constructs is as follows. Containers are rendered as command prefixes which can be stacked at any depth. Leaves are rendered as commands that take one parameter. Lists are rendered as submodes, where the key of the list is rendered as a submode parameter. The example above would result in the command:
For entering the interface ethernet submode. The interface is a container and is rendered as a prefix, ethernet is a list and is rendered as a submode. Two additional commands would be available in the submode:
A typical configuration with two interfaces could look like this:
Note that it makes sense to add help texts to the data model since these texts will be visible in the NSO and help the user see the mapping between the J-style CLI in the NSO and the CLI on the target device. The data model above may look like the following with proper help texts.
I will generally not include the help texts in the examples below to save some space but they should be present in a production data model.
The basic rendering suffice in many cases but is also not enough in many situations. What follows is a list of ways to annotate the data model in order to make the CLI engine mimic a device.
Sometimes you want a number of instances (a list) but do not want a submode. For example:
The above would result in the following commands:
A typical show-config
output may look like:
Sometimes you want a submode to be created without having a list instance, for example, a submode called aaa
where all AAA configuration is located.
This is done by using the tailf:cli-add-mode
extension. For example:
This would result in the command aaa for entering the container. However, sometimes the CLI requires that a certain set of elements are also set when entering the submode, but without being a list. For example, the police rules inside a policy map in the Cisco 7200.
Here, the leaves with the annotation tailf:cli-hide-in-submode
is not present as commands once the submode has been entered, but are instead only available as options the police command when entering the police submode.
Often a command is defined as taking multiple parameters in a typical Cisco CLI. This is achieved in the data model by using the annotations tailf:cli-sequence-commands
, tailf:cli-compact-syntax
, tailf:cli-drop-node-name
, and possibly tailf:cli-reset-siblings
.
For example:
This results in the command:
The tailf:cli-sequence-commands
annotation tells the CLI engine to process the leaves in sequence. The tailf:cli-reset-siblings
tells the CLI to reset all leaves in the container if one is set. This is necessary in order to ensure that no lingering config remains from a previous invocation of the command where more parameters were configured. The tailf:cli-drop-node-name
tells the CLI that the leaf name shouldn't be specified. The tailf:cli-compact-syntax
annotation tells the CLI that the leaves should be formatted on one line, i.e. as:
As opposed to without the annotation:
When constructs are used to control if the numerical value should be the milli
or the secs
leaf.
This command could also be written using a choice construct as:
Sometimes the tailf:cli-incomplete-command
is used to ensure that all parameters are configured. The cli-incomplete-command
only applies to the C- and I-style CLI. To ensure that prior leaves in a container are also configured when the configuration is written using J-style or Netconf proper 'must' declarations should be used.
Another example is this, where tailf:cli-optional-in-sequence
is used:
The tailf:cli-optional-in-sequence
means that the parameters should be processed in sequence but a parameter can be skipped. However, if a parameter is specified then only parameters later in the container can follow it.
It is also possible to have some parameters in sequence initially in the container, and then the rest in any order. This is indicated by the tailf:cli-break-sequence command
. For example:
Where it is possible to write:
As well as:
Sometimes a command for entering a submode has parameters that are not really key values, i.e. not part of the instance identifier, but still need to be given when entering the submode. For example
In this case, the tcpudp
is a non-key leaf that needs to be specified as a parameter when entering the service-group
submode. Once in the submode the commands backup-server-event-log and extended-stats are present. Leaves with the tailf:cli-hide-in-submode
attribute are given after the last key, in the sequence they appear in the list.
It is also possible to allow leaf values to be entered in between key elements. For example:
Here we have a list that is not mapped to a submode. It has two keys, read and remote, and an optional oid that can be specified before the remote key. Finally, after the last key, an optional mask parameter can be specified. The use of the tailf:cli-expose-key-name
means that the key names should be part of the command, which they are not by default. The above construct results in the commands:
The tailf:cli-reset-container
attribute means that all leaves in the container will be reset if any leaf is given.
Some devices require that a setting be removed before it can be changed, for example, the service-group list above. This is indicated with the tailf:cli-remove-before-change
annotation. It can be used both on lists and on leaves. A leaf example:
This means that the diff sent to the device will contain first a no source-ip
command, followed by a new source-ip
command to set the new value.
The data model also use the tailf:cli-no-value-on-delete annotation which means that the leaf value should not be present in the no command. With the annotation, a diff to modify the source IP from 1.1.1.1 to 2.2.2.2 would look like:
And, without the annotation as:
By default, a diff for an ordered-by-user list contains information about where a new item should be inserted. This is typically not supported by the device. Instead, the commands (diff) to send the device needs to remove all items following the new item, and then reinsert the items in the proper order. This behavior is controlled using the tailf:cli-long-obu-diff
annotation. For example
Suppose we have the access list:
And we want to change this to:
We would generate the diff with the tailf:cli-long-obu-diff
:
Without the annotation, the diff would be:
Often in a config when a leaf is set to its default value it is not displayed by the show running-config
command, but we still need to set it explicitly. Suppose we have the leaf state
. By default, the value is active
.
If the device state is block
and we want to set it to active
, i.e. the default value. The default behavior is to send to the device:
This will not work. The correct command sequence should be:
The way to achieve this is to do the following:
This way a value for 'state' will always be generated. This may seem unintuitive but the reason this works comes from how the diff is calculated. When generating the diff the target configuration and the desired configuration is compared (per line). The target config will be:
And the desired config will be:
This will be interpreted as a leaf value change and the resulting diff will be to set the new value, i.e. active.
However, without the cli-show-with-default
option, the desired config will be an empty line, i.e. no value set. When we compare the two lines we get:
(current config)
(desired config)
This will result in the command to remove the configured leaf, i.e.
Which does not work.
What you see in the C-style CLI when you do 'show configuration' is the commands needed to go from the running config to the configuration you have in your current session. It usually corresponds to the command you have just issued in your CLI session, but not always.
The output is actually generated by comparing the two configurations, i.e. the running config and your current uncommitted configuration. It is done by running 'show running-config' on both the running config and your uncommitted config, and then comparing the output line by line. Each line is complemented by some meta information which makes it possible to generate a better diff.
For example, if you modify a leaf value, say set the MTU to 1400 and the previous value was 1500. The two configs will then be
When we compare these configs, the first lines are the same -> no action but we remember that we have entered the FastEthernet0/0/1 submode. The second line differs in value (the meta-information associated with the lines has the path and the value). When we analyze the two lines we determine that a value_set has occurred. The default action when the value has been changed is to output the command for setting the new value, i.e. MTU 1500. However, we also need to reposition to the current submode. If this is the first line we are outputting in the submode we need to issue the command before issuing the MTU 1500 command.
Similarly, suppose a value has been removed, i.e. mtu used to be set but it is no longer present
As before, the first lines are equivalent, but the second line has a !
in the new config, and MTU 1400 in the running config. This is analyzed as being a delete and the commands are generated:
There are tweaks to this behavior. For example, some machines do not like the no
command to include the old value but want instead the command:
We can instruct the CLI diff engine to behave in this way by using the YANG annotation tailf:cli-no-value-on-delete;
:
It is also possible to tell the CLI engine to not include the element name in the delete operation. For example the command:
But the command to delete the password is:
The data model for this would be:
It is often necessary to do some minor modifications to the Java part of a CLI NED. There are mainly four functions that needs to be modified: connect, show, applyConfig, and enter/exit config mode.
The CLI NED code should do a few things when the connect callback is invoked.
Set up a connection to the device (usually SSH).
If necessary send a secondary password to enter exec mode. Typically a Cisco IOS-like CLI requires the user to give the enable
command followed by a password.
Verify that it is the right kind of device and respond to NSO with a list of capabilities. This is usually done by running the show version
command, or equivalent, and parsing the output.
Configure the CLI session on the device to not use pagination. This is normally done by setting the screen length to 0 (or infinity or disable). Optionally it may also fiddle with the idle time.
Some modifications may be needed in this section if the commands for the above differ from the Cisco IOS style.
The NSO will invoke the show()
callback multiple times, one time for each top-level tag in the data model. Some devices have support for displaying just parts of the configuration, others do not.
For a device that cannot display only parts of a config the recommended strategy is to wait for a show() invocation with a well known top tag and send the entire config at that point. If, if you know that the data model has a top tag called interface then you can use code like:
From the point of NSO, it is perfectly ok to send the entire config as a response to one of the requested toptags and to send an empty response otherwise.
Often some filtering is required of the output from the device. For example, perhaps part of the configuration should not be sent to NSO, or some keywords replaced with others. Here are some examples:
Some devices start the output from show running-config
with a short header, and some add a footer. Common headers are Current configuration:
and a footer may be end
or return
. In the example below we strip out a header and remove a footer.
Also, you may choose to only model part of a device configuration in which case you can strip out the parts that you have not modelled. For example, stripping out the SNMP configuration:
Sometimes a device generates non-parsable commands in the output from show running-config
. For example, some A10 devices add a keyword cpu-process
at the end of the ip route
command, i.e.:
However, it does not accept this keyword when a route is configured. The solution is to simply strip the keyword before sending the config to NSO and to not include the keyword in the data model for the device. The code to do this may look like this:
Sometimes a device has some other names for delete than the standard no command found in a typical Cisco CLI. NSO will only generate no commands when, for example, an element does not exist (i.e. no shutdown
for an interface), but the device may need undo
instead. This can be dealt with as a simple transformation of the configuration before sending it to NSO. For example:
Another example is the following situation. A device has a configuration for port trunk permit vlan 1-3
and may at the same time have disallowed some VLANs using the command no port trunk permit vlan 4-6
. Since we cannot use a no container in the config, we instead add a disallow
container, and then rely on the Java code to do some processing, e.g.:
And, in the Java show()
code:
A similar transformation needs to take place when the NSO sends a configuration change to the device. A more detailed discussion about apply config modifications follows later but the corresponding code would in this case be:
If the way a device quotes strings differ from the way it can be modeled in NSO, it can be handled in the Java code. For example, one device does not quote encrypted password strings which may contain odd characters like the command character !
. Java code to deal with this may look like:
And similarly de-quoting when applying a configuration.
NSO will send the configuration to the device in three different callbacks: prepare()
, abort()
, and revert()
. The Java code should issue these commands to the device but some processing of the commands may be necessary. Also, the ongoing CLI session needs to enter configure mode, issue the commands, and then exit configure mode. Some processing may be needed if the device has different keywords, or different quoting, as described under the "Displaying the configuration of a device" section above.
For example, if a device uses undo
in place of no
then the code may look like this, where data
is the string of commands received from NSO:
This relies on the fact that NSO will not have any indentation in the commands sent to the device (as opposed to the indentation usually present in the output from show running-config
).
The typical Cisco CLI has two major modes, operational mode and configure mode. In addition, the configure mode has submodes. For example, interfaces are configured in a submode that is entered by giving the command interface <InterfaceType> <Number>
. Exiting a submode, i.e. giving the exit command, leaves you in the parent mode. Submodes can also be embedded in other submodes.
In a typical Cisco CLI, you do not necessary have to exit a submode to execute a command in a parent mode. In fact, the output of the command show running-config
hardly contains any exit commands. Instead, there is an exclamation mark, !
, to indicate that a submode is done, which is only a comment. The config is formatted to rely on the fact that if a command isn't found in the current submode, the CLI engine searches for the command in its parent mode.
Another interesting mapping problem is how to interpret the no command when multiple leaves are given on a command line. Consider the model:
It corresponds to the command syntax foo [a <word> [b <word> [c <word>]]]
, i.e. the following commands are valid:
Now what does it mean to write no foo a <word> b <word> c <word>
? . It could mean that only the c
leaf should be removed, or it could mean that all leaves should be removed, and it may also mean that the foo
container should be removed.
There is no clear principle here and no one right solution. The annotations are therefore necessary to help the diff engine figure out what to actually send to the device.
The full set of annotations can be found in the tailf_yang_cli_extensions
Manual Page. All annotation YANG extensions are not applicable in an NSO context, but most are. The most commonly used annotations are (in alphabetical order):
Develop your own NEDs to integrate unsupported devices in your network.
A Network Element Driver (NED) represents a key NSO component that allows NSO to communicate southbound with network devices. The device YANG models contained in the Network Element Drivers (NEDs) enable NSO to store device configurations in the CDB and expose a uniform API to the network for automation. The YANG models can cover only a tiny subset of the device or all of the device. Typically, the YANG models contained in a NED represent the subset of the device's configuration data, state data, Remote Procedure Calls, and notifications to be managed using NSO.
This guide provides information on NED development, focusing on building your own NED package. For a general introduction to NEDs, Cisco-provided NEDs, and NED administration, refer to the in Administration.
A NED package allows NSO to manage a network device of a specific type. NEDs typically contain YANG models and the code, specifying how NSO should configure and retrieve status. When developing your own NED, there are four categories supported by NSO.
A NETCONF NED is used with the NSO's built-in NETCONF client and requires no code. Only YANG models. This NED is suitable for devices that strictly follow the specification for the NETCONF protocol and YANG mappings to NETCONF targeting a standardized machine-to-machine interface.
CLI NED targeted devices that use a Cisco-style CLI as a human-to-machine configuration interface. Various YANG extensions are used to annotate the YANG model representation of the device together with code-converting data between NSO and device formats.
A generic NED is typically used to communicate with non-CLI devices, such as devices using protocols like REST, TL1, Corba, SOAP, RESTCONF, or gNMI as a configuration interface. Even NETCONF-enabled devices often require a generic NED to function properly with NSO.
NSO's built-in SNMP client can manage SNMP devices by supplying NSO with the MIBs, with some additional declarative annotations and code to handle the communication to the device. Usually, this legacy protocol is used to read state data. Albeit limited, NSO has support for configuring devices using SNMP.
In summary, the NETCONF and SNMP NEDs use built-in NSO clients; the CLI NED is model-driven, whereas the generic NED requires a Java program to translate operations toward the device.
NSO differentiates between managed devices that can handle transactions and devices that can not. This discussion applies regardless of NED type, i.e., NETCONF, SNMP, CLI, or Generic.
NEDs for devices that cannot handle abort must indicate so in the reply of the newConnection()
method indicating that the NED wants a reverse diff in case of an abort. Thus, NSO has two different ways to abort a transaction towards a NED, invoke the abort()
method with or without a generated reverse diff.
For non-transactional devices, we have no other way of trying out a proposed configuration change than to send the change to the device and see what happens.
The table below shows the seven different data-related callbacks that could or must be implemented by all NEDs. It also differentiates between 4 different types of devices and what the NED must do in each callback for the different types of devices.
The table below displays the device types:
INITIALIZE: The initialize phase is used to initialize a transaction. For instance, if locking or other transaction preparations are necessary, they should be performed here. This callback is not mandatory to implement if no NED-specific transaction preparations are needed.
UNINITIALIZE: If the transaction is not completed and the NED has done INITIALIZE, this method is called to undo the transaction preparations, that is restoring the NED to the state before INITIALIZE. This callback is not mandatory to implement if no NED-specific preparations were performed in INITIALIZE.
PREPARE: In the prepare phase, the NEDs get exposed to all the changes that are destined for each managed device handled by each NED. It is the responsibility of the NED to determine the outcome here. If the NED replies successfully from the prepare phase, NSO assumes the device will be able to go through with the proposed configuration change.
ABORT: If any participants in the transaction reject the proposed changes, all NEDs will be invoked in the abort()
method for each managed device the NED handles. It is the responsibility of the NED to make sure that whatever was done in the PREPARE phase is undone. For NEDs that indicate as a reply in newConnection()
that they want the reverse diff, they will get the reverse data as a parameter here.
COMMIT: Once all NEDs that get invoked in commit(Timeout)
reply OK, the transaction is permanently committed to the system. The NED may still reject the change in COMMIT. If any NED rejects the COMMIT, all participants will be invoked in REVERT, NEDs that support confirmed commit with a timeout, Cisco XR may choose to use the provided timeout to make REVERT easy to implement.
REVERT: This state is reached if any NED reports failure in the COMMIT phase. Similar to the ABORT state, the reverse diff is supplied to the NED if the NED has asked for that.
PERSIST: This state is reached at the end of a successful transaction. Here it's the responsibility of the NED to make sure that if the device reboots, the changes are still there.
The following state diagram depicts the different states the NED code goes through in the life of a transaction.
NED devices have runtime data and statistics. The first part of being able to collect non-configuration data from a NED device is to model the statistics data we wish to gather. In normal YANG files, it is common to have the runtime data nested inside the configuration data. In gathering runtime data for NED devices we have chosen to separate configuration data and runtime data. In the case of the archetypical CLI device, the show running-config ...
and friends are used to display the running configuration of the device whereas other different show ...
commands are used to display runtime data, for example show interfaces
, show routes
. Different commands for different types of routers/switches and in particular, different tabular output format for different device types.
To expose runtime data from a NED controlled device, regardless of whether it's a CLI NED or a Generic NED, we need to do two things:
Write YANG models for the aspects of runtime data we wish to expose northbound in NSO.
Write Java NED code that is responsible for collecting that data.
The NSO NED for the Avaya 4k device contains a data model for some real statistics for the Avaya router and also the accompanying Java NED code. Let's start to take a look at the YANG model for the stats portion, we have:
It's a config false;
list of counters per interface. We compile the NED stats module with the --ncs-compile-module
flag or with the --ncs-compile-bundle
flag. It's the same non-config
module that contains both runtime data as well as commands and rpcs.
The config false;
data from a module that has been compiled with the --ncs-compile-module
flag will end up mounted under /devices/device/live-status
tree. Thus running the NED towards a real router we have:
It is the responsibility of the NED code to populate the data in the live device tree. Whenever a northbound agent tries to read any data in the live device tree for a NED device, the NED code is invoked.
The NED code implements an interface called, NedConnection
This interface contains:
This interface method is invoked by NSO in the NED. The Java code must return what is requested, but it may also return more. The Java code always needs to signal errors by invoking NedWorker.error()
and success by invoking NedWorker.showStatsPathResponse()
. The latter function indicates what is returned, and also how long it shall be cached inside NSO.
The reason for this design is that it is common for many show
commands to work on for example an entire interface, or some other item in the managed device. Say that the NSO operator (or MAAPI code) invokes:
requesting a single leaf, the NED Java code can decide to execute any arbitrary show
command towards the managed device, parse the output, and populate as much data as it wants. The Java code also decides how long time the NSO shall cache the data.
When the showStatsPath()
is invoked, the NED should indicate the state/value of the node indicated by the path (i.e. if a leaf was requested, the NED should write the value of this leaf to the provided transaction handler (th) using MAAPI, or indicate its absence as described below; if a list entry or a presence container was requested then the NED should indicate presence or absence of the element, if the whole list is requested then the NED should populate the keys for this list). Often requesting such data from the actual device will give the NED more data than specifically requested, in which case the worker is free to write other values as well. The NED is not limited to populating the subtree indicated by the path, it may also write values outside this subtree. NSO will then not request those paths but read them directly from the transaction. Different timeouts can be provided for different paths.
If a leaf does not have a value or does not exist, the NED can indicate this by returning a TTL for the path to the leaf, without setting the value in the provided transaction. This has changed from earlier versions of NSO. The same applies to optional containers and list entries. If the NED populates the keys for a certain list (both when it is requested to do so or when it decided to do so because it has received this data from the device), it should set the TTL value for the list itself to indicate the time the set of keys should be considered up to date. It may choose to provide different TTL values for some or all list entries, but it is not required to do so.
One important task when implementing a NED of any type is to make it mimic the devices handling of default values as close as possible. Network equipment can typically deal with default values in many different ways.
Some devices display default values on leafs even if they have not been explicitly set. Others use trimming, meaning that if a leaf is set to its default value it will be 'unset' and disappear from the devices configuration dump.
It is the responsibility of the NED to make the NSO aware of how the device handles default values. This is done by registering a special NED Capability entry with the NSO. Two modes are currently supported by the NSO: trim
and report-all
.
Example 129. A device trimming default values
This is the typical behavior of a Cisco IOS device. The simple YANG code snippet below illustrates the behavior. A container with a boolean leaf. Its default value is true.
Try setting the leaf to true in NSO and commit. Then compare the configuration:
The result shows that the configurations differ. The reason is that the device does not display the value of the leaf 'enabled'. It has been trimmed since it has its default value. The NSO is now out of sync with the device.
To solve this issue, make the NED tell the NSO that the device is trimming default values. Register an extra NED Capability entry in the Java code.
Now, try the same operation again:
The NSO is now in sync with the device.
Example: A Device Displaying All Default Values
Some devices display default values for leafs even if they have not been explicitly set. The simple YANG code below will be used to illustrate this behavior. A list containing a key and a leaf with a default value.
Try creating a new list entry in NSO and commit. Then compare the configuration:
The result shows that the configurations differ. The NSO is out of sync. This is because the device displays the default value of the 'threshold' leaf even if it has not been explicitly set through the NSO.
To solve this issue, make the NED tell the NSO that the device is reporting all default values. Register an extra NED Capability entry in the Java code.
Now, try the same operation again:
The NSO is now in sync with the device.
The possibility to do a dry-run on a transaction is a feature in NSO that allows to examine the changes to be pushed out to the managed devices in the network. The output can be produced in different formats, namely cli
, xml
, and native
. In order to produce a dry run in the native output format NSO needs to know the exact syntax used by the device, and the task of converting the commands or operations produced by the NSO into the device-specific output belongs the corresponding NED. This is the purpose of the prepareDry()
callback in the NED interface.
In order to be able to invoke a callback an instance of the NED object needs to be created first. There are two ways to instantiate a NED:
newConnection()
callback that tells the NED to establish a connection to the device which can later be used to perform any action such as show configuration, apply changes, or view operational data as well as produce dry-run output.
Optional initNoConnect()
callback that tells the NED to create an instance that would not need to communicate with the device, and hence must not establish a connection or otherwise communicate with the device. This instance will only be used to calculate dry-run output. It is possible for a NED to reject the initNoConnect()
request if it is not able to calculate the dry-run output without establishing a connection to the device, for example, if a NED is capable of managing devices with different flavors of syntax and it is not known at the moment which syntax is used by this particular device.
The following state diagram displays NED states specific to the dry-run scenario.
Each managed device in NSO has a device type, which informs NSO how to communicate with the device. The device type is one of netconf
, snmp
, cli
, or generic
. In addition, a special ned-id
identifier is needed.
NSO uses a technique called YANG Schema Mount, where all the data models from a device are mounted into the /devices
tree in NSO. Each set of mounted data models is completely separated from the others (they are confined to a "mount jail"). This makes it possible to load different versions of the same YANG module for different devices. The functionality is called Common Data Models (CDM).
In most cases, there are many devices running the same software version in the network managed by NSO, thus using the exact same set of YANG modules. With CDM, all YANG modules for a certain device (or family of devices) are contained in a NED package (or just NED for short). If the YANG modules on the device are updated in a backward-compatible way, the NED is also updated.
However, if the YANG modules on the device are updated in an incompatible way in a new version of the device's software, it might be necessary to create a new NED package for the new set of modules. Without CDM, this would not be possible, since there would be two different packages that contained different versions of the same YANG module.
When a NED is being built, its YANG modules are compiled to be mounted into the NSO YANG model. This is done by device compilation of the device's YANG modules and is performed via the ncsc
tool provided by NSO.
The ned-id identifier is a YANG identity, which must be derived from one of the pre-defined identities in $NCS_DIR/src/ned/yang/tailf-ncs-ned.yang
.
A YANG model for devices handled by NED code needs to extend the base identity and provide a new identity that can be configured.
The Java NED code registers the identity it handles with NSO.
Similar to how we import device models for NETCONF-based devices, we use the ncsc --ncs-compile-bundle
command to import YANG models for NED-handled devices.
Once we have imported such a YANG model into NSO, we can configure the managed device in NSO to be handled by the appropriate NED handler (which is user Java code, more on that later)
When NSO needs to communicate southbound towards a managed device which is not of type NETCONF, it will look for a NED that has registered with the name of the identity, in the case above, the string "ios".
Thus before the NSO attempts to connect to a NED device before it tries to sync, or manipulate the configuration of the device, a user-based Java NED code must have registered with the NSO service manager indicating which Java class is responsible for the NED with the string of the identity, in this case, the string "ios". This happens automatically when the NSO Java VM gets a instantiate-component
request for an NSO package component of type ned
.
The component Java class myNed
needs to implement either of the interfaces NedGeneric
or NedCli
. Both interfaces require the NED class to implement the following:
The above three callbacks are used by the NSO Java VM to connect the NED Java class with NSO. They are called at when the NSO Java VM receives the instantiate-component request
.
The underlying NedMux will start a number of threads, and invoke the registered class with other data callbacks as transactions execute.
juniper-junos_nc
NEDNSO has supported Junos devices from early on. The legacy Junos NED is NETCONF-based, but as Junos devices did not provide YANG modules in the past, complex NSO machinery translated Juniper's XML Schema Description (XSD) files into a single YANG module. This was an attempt to aggregate several Juniper device modules/versions.
Juniper nowadays provides YANG modules for Junos devices. Junos YANG modules can be downloaded from the device and used directly in NSO with the new juniper-junos_nc
NED.
By downloading the YANG modules using juniper-junos_nc
NED tools and rebuilding the NED, the NED can provide full coverage immediately when the device is updated instead of waiting for a new legacy NED release.
This guide describes how to replace the legacy juniper-junos
NED and migrate NSO applications to the juniper-junos_nc
NED using the NSO MPLS VPN example from the NSO examples collection as a reference.
Prepare the example:
Add the juniper-junos
and juniper-junos_nc
NED packages to the example.
Configure the connection to the Junos device.
Add the MPLS VPN service configuration to the simulated network, including the Junos device using the legacy juniper-junos
NED.
Adapting the service to the juniper-junos_nc
NED:
Un-deploy MPLS VPN service instances with no-networking
.
Delete Junos device config with no-networking
.
Set the Junos device to NETCONF/YANG compliant mode.
Switch the ned-id for the Junos device to the juniper-junos_nc
NED package.
Download the compliant YANG models, build, and reload the juniper-junos_nc
NED package.
Sync from the Junos device to get the compliant Junos device config.
Update the MPLS VPN service to handle the difference between the non-compliant and compliant configurations belonging to the service.
Re-deploy the MPLS VPN service instances with no-networking
to make the MPLS VPN service instances own the device configuration again.
If applying the steps for this example on a production system, you should first take a backup using the ncs-backup
tool before proceeding.
This guide uses the MPLS VPN example in Python from the NSO example set under $NCS_DIR/examples.ncs/getting-started/developing-with-ncs/17-mpls-vpn-python
to demonstrate porting an existing application to use the juniper-junos_nc
NED. The simulated Junos device is replaced with a Junos vMX 21.1R1.11 container, but other NETCONF/YANG-compliant Junos versions also work.
juniper-junos
and juniper-junos_nc
NED PackagesBuild and start the example:
Replace the netsim device connection configuration in NSO with the configuration for connecting to the Junos device. Adjust the USER_NAME
, PASSWORD
, and HOST_NAME/IP_ADDR
variables and the timeouts as required for the Junos device you are using with this example:
Open a CLI terminal or use NETCONF on the Junos device to verify that the rfc-compliant
and yang-compliant
modes are not yet enabled. Examples:
Or:
The rfc-compliant
and yang-compliant
nodes must not be enabled yet for the legacy Junos NED to work. If enabled, delete in the Junos CLI or using NETCONF. A netconf-console example:
Back to the NSO CLI to upgrade the legacy juniper-junos
NED to the latest version:
Turn off autowizard
and complete-on-space
to make it possible to paste configs:
The example service config for two MPLS VPNs where the endpoints have been selected to pass through the PE
node PE2
, which is a Junos device:
To verify that the traffic passes through PE2
:
Toward the end of this lengthy output, observe that some config changes are going to the PE2
device using the http://xml.juniper.net/xnm/1.1/xnm
legacy namespace:
Looks good. Commit to the network:
juniper-junos_nc
NEDNow that the service's configuration is in place using the legacy juniper-junos
NED to configure the PE2
Junos device, proceed and switch to using the juniper-junos_nc
NED with PE2
instead. The service template and Python code will need a few adaptations.
no-networking
To keep the NSO service meta-data information intact when bringing up the service with the new juniper-junos_nc
NED, first un-deploy
the service instances in NSO, only keeping the configuration on the devices:
no-networking
First, save the legacy Junos non-compliant mode device configuration to later diff against the compliant mode config:
Delete the PE2
configuration in NSO to prepare for retrieving it from the device in a NETCONF/YANG compliant format using the new NED:
Using the Junos CLI:
Or, using the NSO netconf-console
tool:
juniper-junos_nc
NED Packagejuniper-junos_nc
NED PackageThe juniper-junos_nc
NED is delivered without YANG modules, enabling populating it with device-specific YANG modules. The YANG modules are retrieved directly from the Junos device:
See the juniper-junos_nc
README
for more options and details.
Build the YANG modules retrieved from the Junos device with the juniper-junos_nc
NED:
Reload the packages to load the juniper-junos_nc
NED with the added YANG modules:
The service must be updated to handle the difference between the Junos device's non-compliant and compliant configuration. The NSO service uses Python code to configure the Junos device using a service template. One way to find the required updates to the template and code is to check the difference between the non-compliant and compliant configurations for the parts covered by the template.
Checking the packages/l3vpn/templates/l3vpn-pe.xml
service template Junos device part under the legacy http://xml.juniper.net/xnm/1.1/xnm
namespace, you can observe that it configures interfaces
, routing-instances
, policy-options
, and class-of-service
.
You can save the NETCONF/YANG compliant Junos device configuration and diff it against the non-compliant configuration from the previously stored legacy.xml
file:
Examining the difference between the configuration in the legacy.xml
and new.xml
files for the parts covered by the service template:
There is no longer a single namespace covering all configurations. The configuration is now divided into multiple YANG modules with a namespace for each.
The /configuration/policy-options/policy-statement/then/community
node choice identity is no longer provided with a leaf named key1
. Instead, the leaf name is choice-ident
, and a choice-value
leaf is set.
The /configuration/class-of-service/interfaces/interface/unit/shaping-rate/rate
leaf format has changed from using an int32
value to a string with either no suffix or a "k", "m" or "g" suffix. This differs from the other devices controlled by the template, so a new template BW_SUFFIX
variable set from the Python code is needed.
To enable the template to handle a Junos device in NETCONF/YANG compliant mode, add the following to the packages/l3vpn/templates/l3vpn-pe.xml
service template:
The Python file changes to handle the new BW_SUFFIX
variable to generate a string with a suffix instead of an int32
:
Code that uses the function and set the string to the service template:
After making the changes to the service template and Python code, reload the updated package(s):
The service instances need to be re-deployed to own the device configuration again:
The service is now in sync with the device configuration stored in NSO CDB:
When re-deploying the service instances, any issues with the added service template section for the compliant Junos device configuration, such as the added namespaces and nodes, are discovered.
As there is no validation for the rate leaf string with a suffix in the Junos device model, no errors are discovered if it is provided in the wrong format until updating the Junos device. Comparing the device configuration in NSO with the configuration on the device shows such inconsistencies without having to test the configuration with the device:
If there are issues, correct them and redo the re-deploy no-networking
for the service instances.
When all issues have been resolved, the service configuration is in sync with the device configuration, and the NSO CDB device configuration matches to the configuration on the Junos device:
The NSO service instances are now in sync with the configuration on the Junos device using the juniper-junos_nc
NED.
Create NETCONF NEDs.
Creating and installing a NETCONF NED consists of the following steps:
Make the device YANG data models available to NSO
Build the NED package from the YANG data models using NSO tools
Install the NED with NSO
Configure the device connection and notification events in NSO
Creating a NETCONF NED that uses the built-in NSO NETCONF client can be a pleasant experience with devices and nodes that strictly follow the specification for the NETCONF protocol and YANG mappings to NETCONF. If the device does not, the smooth sailing will quickly come to a halt, and you are recommended to visit the in Administration and get help from the Cisco NSO NED team who can diagnose, develop and maintain NEDs that bypass misbehaving devices special quirks.
Before NSO can manage a NETCONF-capable device, a corresponding NETCONF NED needs to be loaded. While no code needs to be written for such NED, it must contain YANG data models for this kind of device. While in some cases, the YANG models may be provided by the device's vendor, devices that implement RFC 6022 YANG Module for NETCONF Monitoring can provide their YANG models using the functionality described in this RFC.
The NSO example under $NCS_DIR/examples.ncs/development-guide/ned-development/netconf-ned
implements two shell scripts that use different tools to build a NETCONF NED from a simulated hardware chassis system controller device.
netconf-console
and ncs-make-package
ToolsThe netconf-console
NETCONF client tool is a Python script that can be used for testing, debugging, and simple client duties. For example, making the device YANG models available to NSO using the NETCONF IETF RFC 6022 get-schema
operation to download YANG modules and the RFC 6241get
operation, where the device implements the RFC 7895 YANG module library to provide information about all the YANG modules used by the NETCONF server. Type netconf-console -h
for documentation.
Once the required YANG models are downloaded or copied from the device, the ncs-make-package
bash script tool can be used to create and build, for example, the NETCONF NED package. See in Manual Pages and ncs-make-package -h
for documentation.
The demo.sh
script in the netconf-ned
example uses the netconf-console
and ncs-make-package
combination to create, build, and install the NETCONF NED. When you know beforehand which models you need from the device, you often begin with this approach when encountering a new NETCONF device.
The NETCONF NED builder uses the functionality of the two previous tools to assist the NSO developer onboard NETCONF devices by fetching the YANG models from a device and building a NETCONF NED using CLI commands as a frontend.
The demo_nb.sh
script in the netconf-ned
example uses the NSO CLI NETCONF NED builder commands to create, build, and install the NETCONF NED. This tool can be beneficial for a device where the YANG models are required to cover the dependencies of the must-have models. Also, devices known to have behaved well with previous versions can benefit from using this tool and its selection profile and production packaging features.
netconf-console
and ncs-make-package
CombinationFor a demo of the steps below, see README in the $NCS_DIR/examples.ncs/development-guide/ned-development/netconf-ned
example and run the demo.sh script.
List the YANG version 1.0 models the device supports using NETCONF hello
message.
List the YANG version 1.1 models supported by the device from the device yang-library.
The ietf-hardware.yang
model is of interest to manage the device hardware. Use the netconf-console
NETCONF get-schema
operation to get the ietf-hardware.yang
model.
The ietf-hardware.yang
import a few YANG models.
Two of the imported YANG models are shipped with NSO.
Use the netconf-console
NETCONF get-schema
operation to get the iana-hardware.yang
module.
The timestamp-hardware.yang
module augments a node onto the ietf-hardware.yang
model. This is not visible in the YANG library. Therefore, information on the augment dependency must be available, or all YANG models must be downloaded and checked for imports and augments of the ietf-hardware.yang model
to make use of the augmented node(s).
Create and build the NETCONF NED package from the device YANG models using the ncs-make-package
script.
If you make any changes to, for example, the YANG models after creating the package above, you can rebuild the package using make -C nso-rundir/packages/devsim all
.
As communication with the devices being managed by NSO requires authentication, a custom authentication group will likely need to be created with mapping between the NSO user and the remote device username and password, SSH public-key authentication, or external authentication. The example used here has a 1-1 mapping between the NSO admin user and the ConfD-enabled simulated device admin user for both username and password.
In the example below, the device name is set to hw0
, and as the device here runs on the same host as NSO, the NETCONF interface IP address is 127.0.0.1 while the port is set to 12022 to not collide with the NSO northbound NETCONF port. The standard NETCONF port, 830, is used for production.
The default
authentication group, as shown above, is used.
Fetch the public SSH host key from the device and sync the configuration covered by the ietf-hardware.yang
from the device.
NSO can now configure the device, state data can be read, actions can be executed, and notifications can be received. See the $NCS_DIR/examples.ncs/development-guide/ned-development/netconf-ned/demo.sh
example script for a demo.
For a demo of the steps below, see README in the $NCS_DIR/examples.ncs/development-guide/ned-development/netconf-ned
example and run the demo_nb.sh script.
As communication with the devices being managed by NSO requires authentication, a custom authentication group will likely need to be created with mapping between the NSO user and the remote device username and password, SSH public-key authentication, or external authentication.
The example used here has a 1-1 mapping between the NSO admin user and the ConfD-enabled simulated device admin user for both username and password.
In the example below, the device name is set to hw0
, and as the device here runs on the same host as NSO, the NETCONF interface IP address is 127.0.0.1 while the port is set to 12022 to not collide with the NSO northbound NETCONF port. The standard NETCONF port, 830, is used for production.
The default
authentication group, as shown above, is used.
A temporary NED identity is configured to netconf
as the NED package has not yet been built. It will be changed to match the NETCONF NED package NED ID once the package is installed. The generic netconf
ned-id allows NSO to connect to the device for basic NETCONF operations, such as get
and get-schema
for listing and downloading YANG models from the device.
Create a NETCONF NED Builder project called hardware
for the device, here named hw0
.
The NETCONF NED Builder is a developer tool that must be enabled first through the devtools true
command. The NETCONF NED Builder feature is not expected to be used by the end users of NSO.
The cache directory above is where additional YANG and YANG annotation files can be added in addition to the ones downloaded from the device. Files added need to be configured with the NED builder to be included with the project, as described below.
The project argument for the netconf-ned-builder
command requires both the project name and a version number for the NED being built. A version number often picked is the version number of the device software version to match the NED to the device software it is tested with. NSO uses the project name and version number to create the NED name, here hardware-nc-1.0
. The device's name is linked to the device name configured for the device connection.
Copying Manually to the Cache Directory:
This step is not required if the device supports the NETCONF get-schema
operation and all YANG modules can be retrieved from the device.. Otherwise, you copy the YANG models to the state/netconf-ned-builder/cache/hardware-nc-1.0
directory for use with the device.
After downloading the YANG data models and before building the NED with the NED builder, you need to register the YANG module with the NSO NED builder. For example, if you want to include a dummy.yang
module with the NED, you first copy it to the cache directory and then, for example, create an XML file for use with the ncs_load
command to update the NSO CDB operational datastore:
In some situations, you want to annotate the YANG data models that were downloaded from the device. For example, when an encrypted string is stored on the device, the encrypted value that is stored on the device will differ from the value stored in NSO if the two initialization vectors differ.
Say you have a YANG data model:
And create a YANG annotation module:
After downloading the YANG data models and before building the NED with the NED builder, you need to register the dummy-ann.yang
annotation module, as was done above with the XML file for the dummy.yang
module.
get-schema
with the NED BuilderIf the device supports get-schema
requests, the device can be contacted directly to download the YANG data models. The hardware system example returns the below YANG source files when the NETCONF get-schema
operation is issued to the device from NSO. Only a subset of the list is shown.
The fetch-ssh-host-key
command fetches the public SSH host key from the device to set up NETCONF over SSH. The fetch-module-list
command will look for existing YANG modules in the download-cache-path folder, YANG version 1.0 models in the device NETCONF hello
message, and issue a get
operation to look for YANG version 1.1 models in the device yang-library
. The get-schema
operation fetches the YANG modules over NETCONF and puts them in the download-cache-path folder.
After the list of YANG modules is fetched, the retrieved list of modules can be shown. Select the ones you want to download and include in the NETCONF NED.
When you select a module with dependencies on other modules, the modules dependent on are automatically selected, such as those listed below for the ietf-hardware
module including iana-hardware
ietf-inet-types
and ietf-yang-types
. To select all available modules, use the wild card for both fields. Use the deselect
command to exclude modules previously included from the build.
Before diving into more details, the principles of selecting the modules for inclusion in the NED are crucial steps in building the NED and deserve to be highlighted.
The best practice recommendation is to select only the modules necessary to perform the tasks for the given NSO deployment to reduce memory consumption, for example, for the sync-from
command, and improve upgrade wall-clock performance.
For example, suppose the aim of the NSO installation is exclusively to manage BGP on the device, and the necessary configuration is defined in a separate module. In that case, only this module and its dependencies need to be selected. If several services are running within the NSO deployment, it will be necessary to include more data models in the single NED that may serve one or many devices. However, if the NSO installation is used to, for example, take a full backup of the device's configuration, all device modules need to be included with the NED.
Selecting a module will also require selecting the module's dependencies, namely, modules imported by the selected modules, modules that augment the selected modules with the required functionality, and modules known to deviate from the selected module in the device's implementation.
Avoid selecting YANG modules that overlap where, for example, configuring one leaf will update another. Including both will cause NSO to get out of sync with the device after a NETCONF edit-config
operation, forcing time-consuming sync operations.
An NSO NED is a package containing the device YANG data models. The NED package must first be built, then installed with NSO, and finally, the package must be loaded for NSO to communicate with the device via NETCONF using the device YANG data models as the schema for what to configure, state to read, etc.
After the files have been downloaded from the device, they must be built before being used. The following example shows how to build a NED for the hw0
device.
Build errors can be found in the build-error
leaf under the module list entry. If there are errors in the build, resolve the issues in the YANG models, update them and their revision on the device, and download them from the device or place the YANG models in the cache as described earlier.
Warnings after building the NED can be found in the build-warning
leaf under the module list entry. It is good practice to clean up build warnings in your YANG models.
A build error example:
The full compiler output for debugging purposes can be found in the compiler-output
leaf under the project list entry. The compiler-output
leaf is hidden by hide-group debug
and may be accessed in the CLI using the unhide debug
command if the hide-group
is configured in ncs.conf
. Example ncs.conf
config:
For the ncs.conf
configuration change to take effect, it must be either reloaded or NSO restarted. A reload using the ncs_cmd
tool:
As the compilation will halt if an error is found in a YANG data model, it can be helpful to first check all YANG data models at once using a shell script plus the NSO yanger tool.
As an alternative to debugging the NED building issues inside an NSO CLI session, the make-development-ned
action creates a development version of NED, which can be used to debug and fix the issue in the YANG module.
YANG data models that do not compile due to YANG RFC compliance issues can either be updated in the cache folder directly or in the device and re-uploaded again through get-schema
operation by removing them from the cache folder and repeating the previous process to rebuild the NED. The YANG modules can be deselected from the build if they are not needed for your use case.
A successfully built NED may be exported as a tar
file using the export-ned action
. The tar
file name is constructed according to the naming convention below.
The user chooses the directory the file needs to be created in. The user must have write access to the directory. I.e., configure the NSO user with the same uid (id -u) as the non-root user:
When the NED package has been copied to the NSO run-time packages directory, the NED package can be loaded by NSO.
ned-id
for the hw0
DeviceWhen the NETCONF NED has been built for the hw0
device, the ned-id
for hw0
needs to be updated before the NED can be used to manage the device.
NSO can now configure the device, state data can be read, actions can be executed, and notifications can be received. See the $NCS_DIR/examples.ncs/development-guide/ned-development/netconf-ned/demo-nb.sh
example script for a demo.
Installed NED packages can be removed from NSO by deleting them from the NSO project's packages folder and then deleting the device and the NETCONF NED project through the NSO CLI. To uninstall a NED built for the device hw0
:
Non transactional devices | Transactional devices | Transactional devices with confirmed commit | Fully capable NETCONF server |
---|
Non transactional devices | Transactional devices | Transactional devices with confirmed commit | Fully capable NETCONF server |
---|
Non transactional devices | Transactional devices | Transactional devices with confirmed commit | Fully capable NETCONF server |
---|
Non transactional devices | Transactional devices | Transactional devices with confirmed commit | Fully capable NETCONF server |
---|
Non transactional devices | Transactional devices | Transactional devices with confirmed commit | Fully capable NETCONF server |
---|
Non transactional devices | Transactional devices | Transactional devices with confirmed commit | Fully capable NETCONF server |
---|
Non transactional devices | Transactional devices | Transactional devices with confirmed commit | Fully capable NETCONF server |
---|
Non transactional devices | Transactional devices | Transactional devices with confirmed commit | Fully capable NETCONF server |
---|
The first step is to add the latest juniper-junos
and juniper-junos_nc
NED packages to the example's package directory. The NED tar-balls must be available and downloaded from your account to the 17-mpls-vpn-python
example directory. Replace the NSO_VERSION
and NED_VERSION
variables with the versions you use:
Start NSO. NSO will load the new package. If the package was loaded previously, use the --with-package-reload
option. See in Manual Pages for details. If NSO is already running, use the packages reload
CLI command.
Having device vendors update their YANG models to comply with the NETCONF and YANG standards can be time-consuming. Visit the and get help from the Cisco NSO NED team, who can diagnose, develop and maintain NEDs that bypass misbehaving device's special quirks.
SNMP, Cisco IOS, NETCONF devices with startup+running. | Devices that can abort, NETCONF devices without confirmed commit. | Cisco XR type of devices. | ConfD, Junos. |
|
|
| Built in, NSO will lock. |
|
|
| Built in, NSO will unlock. |
|
|
| Built in, NSO will edit-config towards the candidate, validate and commit confirmed with a timeout. |
|
|
| Built in, discard-changes and close. |
|
|
| Built in, commit confirmed with the timeout. |
|
|
| Built in, discard-changes and close. |
|
|
| Built in, commit confirm. |