- About
- NSO 5.7 Getting Started Guide
- NSO 5.7 User Guide
- NSO Installation Guide
- NSO 5.7 Administration Guide
- NSO 5.7 Northbound APIs
- NSO 5.7 Development Guide
- Preface
- The Configuration Database and YANG
- Basic Automation with Python
- Creating a Service
- Applications in NSO
- The NSO Java VM
- The NSO Python VM
- Embedded Erlang applications
- The YANG Data Modeling Language
- Using CDB
- Java API Overview
- Python API Overview
- NSO Packages
- Package Development
- Developing NSO Services
- Templates
- NED Upgrades and Migration
- Developing Alarm Applications
- SNMP Notification Receiver
- The web server
- Kicker
- Scheduler
- Progress Trace
- Nano Services for Staged Provisioning
- Encryption Keys
- External Logging
- NSO 5.7 Web UI
- NSO CDM Migration Guide
- NSO Layered Service Architecture
- NSO 5.7 NED Development
- NSO 5.7 Manual Pages
- SDK API Reference
- NSO on DevNet
- Get Support
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 in order 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:
container foo { tailf:cli-compact-syntax; tailf:cli-sequence-commands; presence true; leaf a { type string; } leaf b { type string; } leaf c { type string; } }
It corresponds to the command syntax foo [a <word> [b <word> [c <word>]]], i.e. the following commands are valid:
foo foo a <word> foo a <word> b <word> foo a <word> b <word> c <word>
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 man page. All are not applicable in a NSO context, but most are. The most commonly used annotations are (in alphabetical order):
Used for adding a submode in a container. The default rendering engine maps a container as a command prefix, and a list node as a submode. However, sometimes entering a submode does not require the user to give a specific instance. In these cases you can use the tailf:cli-add-mode on a container:
container system { tailf:info "For system events."; container "default" { tailf:cli-add-mode; tailf:cli-mode-name "cfg-acct-mlist"; tailf:cli-delete-when-empty; presence true; container start-stop { tailf:info "Record start and stop without waiting"; leaf group { tailf:info "Use Server-group"; type aaa-group-type; } } } }
In this example, the tailf:cli-add-mode annotations tells the CLI engine to render the default container as a submode, in other words there will be a command system default for entering the default container as a submode. All further commands will use that context as base. In the example above, the default container will only contain one command start-stop group, rendered from the start-stop container (rendered as a prefix) and the group leaf.
Tells the parser that the list name is allowed to be joined together with the first key, i.e. written without space in between. This is used to render, for example, the interface FastEthernet command where the list is FastEthernet and the key is the interface name. In a typical Cisco CLI they are allowed to be written both as interface FastEthernet 1 and as interface FastEthernet1.
list FastEthernet { tailf:info "FastEthernet IEEE 802.3"; tailf:cli-allow-join-with-key { tailf:cli-display-joined; } tailf:cli-mode-name "config-if"; key name; leaf name { type string { pattern "[0-9]+.*"; tailf:info "<0-66>/<0-128>;;FastEthernet interface number"; } }
In the above example, the tailf:cli-display-joined substatement is used to tell the command renderer that it should display a list item using the format without space.
This tells the parser that a leaf value is allowed to be written without space between the leaf name and the value. This is typically the case when referring to an interface. For example:
leaf FastEthernet { tailf:info "FastEthernet IEEE 802.3"; tailf:cli-allow-join-with-value { tailf:cli-display-joined; } type string; tailf:non-strict-leafref { path "/ios:interface/ios:FastEthernet/ios:name"; } }
In the example above, a leaf FastEthernet is used to point to an existing interface. The command is allowed to be written both as FastEthernet 1 and as FastEthernet1, when referring to FastEthernet interface 1. The substatements say which is the preferred format when rendering the command.
Normally, keys come before other leaves when a list command is used, and this is required in YANG. However, this is not always the case in Cisco style CLIs. For example the route-map command where the name and sequence numbers are the keys, but the leaf operation (permit or deny) is given in between the first and the second key. The tailf:cli-prefix-key annotation tells the parser to expect a given leaf before the keys, but the substatement tailf:cli-before-key <N> can be used to specify that the leaf should occur in between two keys. For example:
list route-map { tailf:info "Route map tag"; tailf:cli-mode-name "config-route-map"; tailf:cli-compact-syntax; tailf:cli-full-command; key "name sequence"; leaf name { type string { tailf:info "WORD;;Route map tag"; } } // route-map * # leaf sequence { tailf:cli-drop-node-name; type uint16 { tailf:info "<0-65535>;;Sequence to insert to/delete from " +"existing route-map entry"; range "0..65535"; } } // route-map * permit // route-map * deny leaf operation { tailf:cli-drop-node-name; tailf:cli-prefix-key { tailf:cli-before-key 2; } type enumeration { enum deny { tailf:code-name "op_deny"; tailf:info "Route map denies set operations"; } enum permit { tailf:code-name "op_internet"; tailf:info "Route map permits set operations"; } } default permit; } }
A lot of things are going on in the example above, in addition to the tailf:cli-prefix-key and tailf:cli-before-key annotations. The tailf:cli-drop-node-name annotation tells the parser to ignore the name of the leaf (to not accept that as input, or render it when displaying the configuration).
This tells the parser to render a leaf of type boolean as no <leaf> and <leaf> instead of the default <leaf> false and <leaf> true. The other alternative to this is to use a leaf of type empty and the tailf:cli-show-no annotation. The difference is subtle. A leaf with tailf:cli-boolean-no would not be displayed unless explicitly configured to either true or false, whereas a type empty leaf with tailf:cli-show-no would always be displayed if not set. For example:
leaf keepalive { tailf:info "Enable keepalive"; tailf:cli-boolean-no; type boolean; }
In the above example the keepalive leaf is set to true when the command keepalive is given, and to false when no keepalive is given. The well known shutdown command, on the other hand, is modeled as a type empty leaf with the tailf:cli-show-no annotation:
leaf shutdown { // Note: default to "no shutdown" in order to be able to bring if up. tailf:info "Shutdown the selected interface"; tailf:cli-full-command; tailf:cli-show-no; type empty; }
These annotations are used to tell the CLI to only accept leaves in a container in the same order as they appears in the data model. This is typically required when the leaf names are hidden using the tailf:cli-drop-node-name annotation. It is very common in the Cisco CLI that commands accept multiple parameters, and such commands must be mapped to setting of multiple leaves in the data model. For example the aggregate-address command in the router bgp submode:
// router bgp * / aggregate-address container aggregate-address { tailf:info "Configure BGP aggregate entries"; tailf:cli-compact-syntax; tailf:cli-sequence-commands { tailf:cli-reset-all-siblings; } leaf address { tailf:cli-drop-node-name; type inet:ipv4-address { tailf:info "A.B.C.D;;Aggregate address"; } } leaf mask { tailf:cli-drop-node-name; type inet:ipv4-address { tailf:info "A.B.C.D;;Aggregate mask"; } } leaf advertise-map { tailf:cli-break-sequence-commands; tailf:info "Set condition to advertise attribute"; type string { tailf:info "WORD;;Route map to control attribute " +"advertisement"; } } leaf as-set { tailf:info "Generate AS set path information"; type empty; } leaf attribute-map { type string { tailf:info "WORD;;Route map for parameter control"; } } leaf as-override { tailf:info "Override matching AS-number while sending update"; type empty; } leaf route-map { type string { tailf:info "WORD;;Route map for parameter control"; } } leaf summary-only { tailf:info "Filter more specific routes from updates"; type empty; } leaf suppress-map { tailf:info "Conditionally filter more specific routes from " +"updates"; type string { tailf:info "WORD;;Route map for suppression"; } } }
In the above example the tailf:cli-sequence-commands annotation tells the parser to require the leaves in the aggregate-address container to be entered in the same order as in the data model, i.e. first address then mask. Since these leaves also have the tailf:cli-drop-node-name annotation, it would be impossible for the parser to know which leaf to map the values to, unless the order of appearance was used. The tailf:cli-break-sequence-commands annotation on the advertise-map leaf tells the parser that from that leaf and onward the ordering is no longer important and the leaves can be entered in any order (and leaves can be skipped).
Two other annotations are often used in combination with tailf:cli-sequence-commands; tailf:cli-reset-all-siblings and tailf:cli-compact-syntax. The first tells the parser that all leaves should be reset when any leaf is entered, i.e. if the user first gives the command:
aggregate-address 1.1.1.1 255.255.255.0 as-set summary-only
This would result in the leaves address, mask, as-set, and summary-only being set in the configuration. However, if the user then entered:
aggregate-address 1.1.1.1 255.255.255.0 as-set
The assumed result of this is that summary-only is no longer configured, ie that all leaves in the container is zeroed out when the command is entered again. The tailf:cli-compact-syntax annotation tells the CLI engine to render all leaves in the rendered on a separate line.
aggregate-address 1.1.1.1 aggregate-address 255.255.255.0 aggregate-address as-set aggregate-address summary-only
The above will be rendered on one line (compact-syntax) as:
aggregate-address 1.1.1.1 255.255.255.0 as-set summary-only
Tells the parser that this particular leaf should be allowed to be entered in case insensitive format. The reason this is needed is that some devices display a command in one case, and other display the same command in a different case. Normally command parsing is case sensitive. For example:
leaf dhcp { tailf:info "Default Gateway obtained from DHCP"; tailf:cli-case-insensitive; type empty; }
This annotation tells the CLI engine to render all leaves in the container on one command line, i.e. instead of the default rendering where each leaf is rendered on a separate line
aggregate-address 1.1.1.1 aggregate-address 255.255.255.0 aggregate-address as-set aggregate-address summary-only
it should be rendered on one line (compact-syntax) as
aggregate-address 1.1.1.1 255.255.255.0 as-set summary-only
Deleting items in the data base is tricky when using the Cisco CLI syntax. The reason is that no <command> is open to multiple interpretations in many cases, for example when multiple leaves are set in one command, or a presence container is set in addition to a leaf. For example:
container dampening { tailf:info "Enable event dampening"; presence "true"; leaf dampening-time { tailf:cli-drop-node-name; tailf:cli-delete-container-on-delete; tailf:info "<1-30>;;Half-life time for penalty"; type uint16 { range 1..30; } } }
This data model allows both the dampening command and the command dampening 10. When the command no dampening 10 is issued, should both the dampening container and the leaf be removed, or only the leaf? The tailf:cli-delete-container-on-delete tells the CLI engine to also delete the container when the leaf is removed.
This annotation tells the CLI engine to remove a list entry or a presence container when all content of the container or list instance has been removed. For example:
container access-class { tailf:info "Filter connections based on an IP access list"; tailf:cli-compact-syntax; tailf:cli-sequence-commands; tailf:cli-reset-container; tailf:cli-flatten-container; list access-list { tailf:cli-drop-node-name; tailf:cli-compact-syntax; tailf:cli-reset-container; tailf:cli-suppress-mode; tailf:cli-delete-when-empty; key direction; leaf direction { type enumeration { enum "in" { tailf:info "Filter incoming connections"; } enum "out" { tailf:info "Filter outgoing connections"; } } } leaf access-list { tailf:cli-drop-node-name; tailf:cli-prefix-key; type exp-ip-acl-type; mandatory true; } leaf vrf-also { tailf:info "Same access list is applied for all VRFs"; type empty; } } }
In this case, the tailf:cli-delete-when-empty annotation tells the CLI engine to remove an access-list instance when it doesn't have neither an access-list nor a vrf-also child.
This annotations tells the CLI engine that there is a dependency between the current account when generating diff commands to send to the device, or when rendering the show configuration command output. It can have two different substatements: tailf:cli-trigger-on-set and tailf:cli-trigger-on-all.
Without substatements, it should be thought of as similar to a leaf-ref, i.e. if the dependency target is delete, first perform any modifications to this leaf. For example the redistribute ospf submode in router bgp:
// router bgp * / redistribute ospf * list ospf { tailf:info "Open Shortest Path First (OSPF)"; tailf:cli-suppress-mode; tailf:cli-delete-when-empty; tailf:cli-compact-syntax; key id; leaf id { type uint16 { tailf:info "<1-65535>;;Process ID"; range "1..65535"; } } list vrf { tailf:info "VPN Routing/Forwarding Instance"; tailf:cli-suppress-mode; tailf:cli-delete-when-empty; tailf:cli-compact-syntax; tailf:cli-diff-dependency "/ios:ip/ios:vrf"; tailf:cli-diff-dependency "/ios:vrf/ios:definition"; key name; leaf name { type string { tailf:info "WORD;;VPN Routing/Forwarding Instance (VRF) name"; } } } }
The tailf:cli-diff-dependency "/ios:ip/ios:vrf" tells the engine that if the ip vrf part of the configuration is deleted, then first display any changes to this part. This can be used when the device requires a certain ordering of the commands.
If the tailf:cli-trigger-on-all substatement is used, then it means that the target will always be displayed before the current node. Normally the order in the YANG file is used, but and it might not even be possible if they are embedded in a container.
The tailf:cli-trigger-on-set tells the engine that the ordering should be taken into account when this leaf is set and some other leaf is deleted. The other leaf should then be deleted before this is set. Suppose you have this data model:
list b { key "id"; leaf id { type string; } leaf name { type string; } leaf y { type string; } } list a { key id; leaf id { tailf:cli-diff-dependency "/c[id=current()/../id]" { tailf:cli-trigger-on-set; } tailf:cli-diff-dependency "/b[id=current()/../id]"; type string; } } list c { key id; leaf id { tailf:cli-diff-dependency "/a[id=current()/../id]" { tailf:cli-trigger-on-set; } tailf:cli-diff-dependency "/b[id=current()/../id]"; type string; } }
Then the tailf:cli-diff-dependency "/b[id=current()/../id]" tells the CLI that before b list instance is delete, the c instance with the same name needs to be changed.
tailf:cli-diff-dependency "/a[id=current()/../id]" { tailf:cli-trigger-on-set; }
This annotation, on the other hand, says that before this instance is created any changes to the a instance with the same name needs to be displayed.
Suppose you have the configuration:
b foo ! a foo !
Then created c foo and deleted a foo, it should be displayed as:
no a foo c foo
If you then deleted c foo and created a foo, it should be rendered as:
no c foo a foo
That is, in the reverse order.
This annotation is used to disambiguate parsing. This is sometimes necessary when tailf:cli-drop-node-name is used. For example:
container authentication { tailf:info "Authentication"; choice auth { leaf word { tailf:cli-drop-node-name; tailf:cli-disallow-value "md5|text"; type string { tailf:info "WORD;;Plain text authentication string " +"(8 chars max)"; } } container md5 { tailf:info "Use MD5 authentication"; leaf key-chain { tailf:info "Set key chain"; type string { tailf:info "WORD;;Name of key-chain"; } } } } }
when the command authentication md5... is entered the CLI parser cannot determine if the leaf word should be set to the value "md5" of if the leaf md5 should be set. By adding the tailf:cli-disallow-value annotation you can tell the CLI parser that certain regular expressions are not valid values. An alternative would be to add a restriction to the string type of word but this is much more difficult since restrictions can only be used to specify allowed values, not disallowed values.
See the description of tailf:cli-allow-join-with-value and tailf:cli-allow-join-with-key.
This annotation can be used on a presence container and tells the CLI engine that the container should be displayed as a separate command, even when a leaf in the container is set. The default rendering does not do this. For example:
container ntp { tailf:info "Configure NTP"; // interface * / ntp broadcast container broadcast { tailf:info "Configure NTP broadcast service"; //tailf:cli-display-separated; presence true; container client { tailf:info "Listen to NTP broadcasts"; tailf:cli-full-command; presence true; } } }
If both broadcast and client are created in the configuration then this will be displayed as:
ntp broadcast ntp broadcast client
When the tailf:cli-display-separated annotation is used. If the annotation isn't present then it would only be displayed as:
ntp broadcast client
The creation of the broadcast container would be implied.
This might be the most used annotation of them all. It can be used for multiple purposes. Primarily it tells the CLI engine that the node name should be ignored, which is typically needed when there is no corresponding leaf name in the command, typically when a command requires multiple parameters:
container exec-timeout { tailf:info "Set the EXEC timeout"; tailf:cli-sequence-commands; tailf:cli-compact-syntax; leaf minutes { tailf:info "<0-35791>;;Timeout in minutes"; tailf:cli-drop-node-name; type uint32; } leaf seconds { tailf:info "<0-2147483>;;Timeout in seconds"; tailf:cli-drop-node-name; type uint32; } }
However, it can also be used to introduce ambiguity, or a choice in the parse tree if you like. Suppose you need to support these commands:
// interface * / vrf forwarding // interface * / ip vrf forwarding choice vrf-choice { container ip-vrf { tailf:cli-no-keyword; tailf:cli-drop-node-name; container ip { container vrf { leaf forwarding { tailf:info "Configure forwarding table"; type string { tailf:info "WORD;;VRF name"; } tailf:non-strict-leafref { path "/ios:ip/ios:vrf/ios:name"; } } } } } container vrf { tailf:info "VPN Routing/Forwarding parameters on the interface"; // interface * / vrf forwarding leaf forwarding { tailf:info "Configure forwarding table"; type string { tailf:info "WORD;;VRF name"; } tailf:non-strict-leafref { path "/ios:vrf/ios:definition/ios:name"; } } } // interface * / ip container ip { tailf:info "Interface Internet Protocol config commands"; }
In the above case when the parser see the beginning of the command ip, it can interpret it as either entering the interface */vrf-choice/ip-vrf/ip/vrf config tree, or the interface */ip tree since the tokens consumed are the same in both branches. When the parser sees a tailf:cli-drop-node-name in the parse tree, it will try to match the current token stream to that parse tree, and if that fails backtrack and try other paths.
Tells the CLI engine to add an explicit exit command in the current submode. Normally, a submode does not have exit commands for leaving a submode, instead it is implied by the following command residing in a parent mode. However, to avoid ambiguity it is sometimes necessary. For example, in the address-family submode:
container address-family { tailf:info "Enter Address Family command mode"; container ipv6 { tailf:info "Address family"; container unicast { tailf:cli-add-mode; tailf:cli-mode-name "config-router-af"; tailf:info "Address Family Modifier"; tailf:cli-full-command; tailf:cli-exit-command "exit-address-family" { tailf:info "Exit from Address Family configuration " +"mode"; } } } }
This tells the CLI engine to render explicit exit commands instead of the default ! when leaving a submode. The annotation is inherited by all submodes. For example:
container interface { tailf:info "Configure interfaces"; tailf:cli-diff-dependency "/ios:vrf"; tailf:cli-explicit-exit; // interface Loopback list Loopback { tailf:info "Loopback interface"; tailf:cli-allow-join-with-key { tailf:cli-display-joined; } tailf:cli-mode-name "config-if"; tailf:cli-suppress-key-abbreviation; // tailf:cli-full-command; key name; leaf name { type string { pattern "([0-9\.])+"; tailf:info "<0-2147483647>;;Loopback interface number"; } } uses interface-common-grouping; } }
Without the tailf:cli-explicit-exit annotation, the edit sequences sent to the NED device will contain ! at the end of a mode, and rely on the next command to move from one submode to some other place in the CLI. This is the way the Cisco CLI usually works. However, it may cause problems if the next edit command is also a valid command in the current submode. Using tailf:cli-explicit-exit gets around this problem.
By default the key leaf names are not shown in the CLI, but sometimes you want them to be visible, for example:
// ip explicit-path name * list explicit-path { tailf:info "Configure explicit-path"; tailf:cli-mode-name "cfg-ip-expl-path"; key name; leaf name { tailf:info "Specify explicit path by name"; tailf:cli-expose-key-name; type string { tailf:info "WORD;;Enter name"; } } }
By default a leaf-list is rendered as a single line with the elements enclosed by [ and ]. If you want the values to be listed on one line this is the annotation to use. For example:
// class-map * / match cos leaf-list cos { tailf:info "IEEE 802.1Q/ISL class of service/user priority values"; tailf:cli-flat-list-syntax; type uint16 { range "0..7"; tailf:info "<0-7>;;Enter up to 4 class-of-service values"+ " separated by white-spaces"; } }
This annotation is a bit tricky. It tells the CLI engine that the container should be allowed to co-exist with leaves on the same command line, i.e. flattened. Normally, once the parser has entered a container it will not exit. However, if the container is flattened, the container will be exited once all leaves in the container have been entered. Also, a flattened container will be displayed together with sibling leaves on the same command line (provided the surrounding container has tailf:cli-compact-syntax).
Suppose you want to model the command limit [inbound <int16> <int16>] [outbound <int16> <int16>] mtu <uint16>. In other word the inbound and outbound settings are optional, but if you give inbound you have to specify two 16-bit integers, and you can always specify mtu.
container foo { tailf:cli-compact-syntax; container inbound { tailf:cli-compact-syntax; tailf:cli-sequence-commands; tailf:cli-flatten-container; leaf a { tailf:cli-drop-node-name; type uint16; } leaf b { tailf:cli-drop-node-name; type uint16; } } container outbound { tailf:cli-compact-syntax; tailf:cli-sequence-commands; tailf:cli-flatten-container; leaf a { tailf:cli-drop-node-name; type uint16; } leaf b { tailf:cli-drop-node-name; type uint16; } } leaf mtu { type uint16; } }
In the above example the tailf:cli-flatten-container tells the parser that it should exit the outbound/inbound container once both values have been entered. Without the annotation it would not be possible to exit the container once it has been entered. It would be possible to have the command foo inbound 1 3 or foo outbound 1 2 but not both at the same time, and not the final mtu leaf. The tailf:cli-compact-syntax annotation tells the renderer to display all leaves on the same line. If it wasn't used the line setting foo inbound 1 2 outbound 3 4 mtu 1500 would be displayed as:
foo inbound 1 foo inbound 2 foo outbound 3 foo outbound 4 foo mtu 1500
The annotation tailf:cli-sequence-commands tells the CLI that the user has to enter the leaves inside the container in the specified order. Without this annotation it would not be possible to drop the names of the leaves and still have a deterministic parser. With the annotation, the parser knows that for the command foo inbound 1 2, leaf a should be assigned the value 1 and leaf b the value 2.
Another example:
container htest { tailf:cli-add-mode; container param { tailf:cli-hide-in-submode; tailf:cli-flatten-container; tailf:cli-compact-syntax; leaf a { type uint16; } leaf b { type uint16; } } leaf mtu { type uint16; } }
The above model results in the command htest param a <uint16> b <uint16> for entering the submode. Once the submode has been entered, the command mtu <uint16> is available. Without the tailf:cli-flatten-container annotation it wouldn't be possible to use the tailf:cli-hide-in-submode annotation to attach the leaves to the command for entering the submode.
This annotation tells the parser to not accept any more input beyond this element. By default the parser will allow setting of multiple leaves in the same command, and both enter a submode and set leaf values in the submode. In most cases, it doesn't matter that the parser accepts commands that are not actually generated by the device in the output of show running-config. It is however needed to avoid ambiguity, or just to make the NSO CLI for the device more user friendly.
container transceiver { tailf:info "Select from transceiver configuration commands"; container "type" { tailf:info "type keyword"; // transceiver type all container all { tailf:cli-add-mode; tailf:cli-mode-name "config-xcvr-type"; tailf:cli-full-command; // transceiver type all / monitoring container monitoring { tailf:info "Enable/disable monitoring"; presence true; leaf interval { tailf:info "Set interval for monitoring"; type uint16 { tailf:info "<300-3600>;;Time interval for monitoring "+ "transceiver in seconds"; range "300..3600"; } } } } } }
In the above example it is possible to have the command transceiver type all for entering a submode, and then give the command monitor [interval <300-3600>]. If the tailf:cli-full-command annotation had not been used, the following would also have been a valid command: transceiver type all monitor [interval <300-3600>]. In the above example it doesn't make a difference as far as being able to parse the configuration on a device. The device will never show the oneline command syntax, but always display it as two lines, one for entering the submode and one for setting the monitor interval.
This annotation tells the CLI parser that no further arguments should be accepted for this path when the path is traversed as an argument to the no command.
Example of use:
// event manager applet * / action * info container info { tailf:info "Obtain system specific information"; // event manager applet * / action info type container "type" { tailf:info "Type of information to obtain"; tailf:cli-full-no; container snmp { tailf:info "SNMP information"; // event manager applet * / action info type snmp var container var { tailf:info "Trap variable"; tailf:cli-compact-syntax; tailf:cli-sequence-commands; tailf:cli-reset-container; leaf variable-name { tailf:cli-drop-node-name; tailf:cli-incomplete-command; type string { tailf:info "WORD;;Trap variable name"; } } } } } }
In some cases you need to give some parameters for entering a submode, but the submode cannot be modeled as a list, or the parameters should not be modeled as a key element of the list, but rather behaves as a leaf. In these cases you model the parameter as a leaf and use the tailf:cli-hide-in-submode annotation. It has two purposes, the leaf is displayed as part of the command for entering the submode when rendering the config, and the leaf is not available as a command in the submode.
For example:
// event manager applet * list applet { tailf:info "Register an Event Manager applet"; tailf:cli-mode-name "config-applet"; tailf:cli-exit-command "exit" { tailf:info "Exit from Event Manager applet configuration submode"; } key name; leaf name { type string { tailf:info "WORD;;Name of the Event Manager applet"; } } // event manager applet * authorization leaf authorization { tailf:info "Specify an authorization type for the applet"; tailf:cli-hide-in-submode; type enumeration { enum bypass { tailf:info "EEM aaa authorization type bypass"; } } } // event manager applet * class leaf class { tailf:info "Specify a class for the applet"; tailf:cli-hide-in-submode; type string { tailf:info "Class A-Z | default - default class"; pattern "[A-Z]|default"; } } // event manager applet * trap leaf trap { tailf:info "Generate an SNMP trap when applet is triggered."; tailf:cli-hide-in-submode; type empty; } }
In the example above the key to the list is the name leaf, but in order to enter the submode the user may also give the arguments event manager applet <name> [authorization bypass] [class <word>] [trap]. It is clear that these leaves are not keys to the list since giving the same name but different authorization, class or trap argument does not result in a new applet instance.
Tells the CLI that it should not be possible to hit cr after the current element. This is usually the case when a command takes multiple parameters, for example, given the following data model:
container foo { tailf:cli-compact-syntax; tailf:cli-sequence-commands; presence true; leaf a { type string; } leaf b { type string; } leaf c { type string; } }
The valid commands are foo [a <word> [b <word> [c <word>]]]. If it however should be foo a <word> b <word> [c <word>], i.e. the parameters a and b are mandatory, and c is optional, then the tailf:cli-incomplete-command annotation should be used as follows:
container foo { tailf:cli-compact-syntax; tailf:cli-sequence-commands; tailf:cli-incomplete-command; presence true; leaf a { tailf:cli-incomplete-command; type string; } leaf b { type string; } leaf c { type string; } }
In other words, the command is incomplete after entering just foo, and also after entering foo a <word>, but not after foo a <word> b <word> or foo a <word> b <word> c <word>.
This annotation is similar to the tailf:cli-incomplete-command above, but applies to no commands. Sometimes you want to prevent the user from entering a generic no command. Suppose you have the data model:
container foo { tailf:cli-compact-syntax; tailf:cli-sequence-commands; tailf:cli-incomplete-command; presence true; leaf a { tailf:cli-incomplete-command; type string; } leaf b { type string; } leaf c { type string; } }
Then it would be valid to write any of the following:
no foo no foo a <word> no foo a <word> b <word> no foo a <word> b <word> c <word>
If you only want the last version of this to be a valid command, then you can use tailf:cli-incomplete-no to enforce this. For example:
container foo { tailf:cli-compact-syntax; tailf:cli-sequence-commands; tailf:cli-incomplete-command; tailf:cli-incomplete-no; presence true; leaf a { tailf:cli-incomplete-command; tailf:cli-incomplete-no; type string; } leaf b { tailf:cli-incomplete-no; type string; } leaf c { type string; } }
The default rendering of a leaf-list element is as a command taking a list of values enclosed in square brackets. Given the following element:
// class-map * / source-address container source-address { tailf:info "Source address"; leaf-list mac { tailf:info "MAC address"; type string { tailf:info "H.H.H;;MAC address"; } } }
This would result in the command source-address mac [ H.H.H... H.H.H ], instead of the desired source-address mac H.H.H. Given the configuration:
source-address { mac [ 1410.9fd8.8999 a110.9fd8.8999 bb10.9fd8.8999 ] }
It should be rendered as:
source-address mac 1410.9fd8.8999 source-address mac a110.9fd8.8999 source-address mac bb10.9fd8.8999
This is achieved by adding the tailf:cli-list-syntax annotation. For example:
// class-map * / source-address container source-address { tailf:info "Source address"; leaf-list mac { tailf:info "MAC address"; tailf:cli-list-syntax; type string { tailf:info "H.H.H;;MAC address"; } } }
An alternative would be to model this as a list, i.e.:
// class-map * / source-address container source-address { tailf:info "Source address"; list mac { tailf:info "MAC address"; tailf:cli-suppress-mode; key address; leaf address { type string { tailf:info "H.H.H;;MAC address"; } } } }
In many cases, this may be the better choice. Notice how the tailf:cli-suppress-mode annotation is used to prevent the list from being rendered as a submode.
This annotation is not really needed when writing a NED. It is used to tell the CLI which prompt to use when in the submode. Without specific instructions, the CLI will invent a prompt based on the name of the submode container/list and the list instance. If a specific prompt is desired this annotation can be used. For example:
container transceiver { tailf:info "Select from transceiver configuration commands"; container "type" { tailf:info "type keyword"; // transceiver type all container all { tailf:cli-add-mode; tailf:cli-mode-name "config-xcvr-type"; tailf:cli-full-command; // transceiver type all / monitoring container monitoring { tailf:info "Enable/disable monitoring"; presence true; leaf interval { tailf:info "Set interval for monitoring"; type uint16 { tailf:info "<300-3600>;;Time interval for monitoring "+ "transceiver in seconds"; range "300..3600"; } } } } } }
This annotation is used to indicate that a leaf should accept multiple tokens, and concatenate them. By default, only a single token is accepted as value to a leaf. If spaces are required then the value needs to be quoted. If this isn't desired the tailf:cli-multi-value annotation can be used to tell the parser that a leaf should accept multiple tokens. A common example of this is the description command. It is modeled as:
// event manager applet * / description leaf "description" { tailf:info "Add or modify an applet description"; tailf:cli-full-command; tailf:cli-multi-value; type string { tailf:info "LINE;;description"; } }
In the above example the description command will take all tokens to the end of the line, concatenate them with a space, and use that for leaf value. The tailf:cli-full-command annotation is used to tell the parser that no other command following this can be entered on the same command line. The parser would not be able to determine when the argument to this command ended and the next command commenced anyway.
By default all key values consists of a single parser token, i.e. a string without spaces, or a quoted string. If multiple tokens should be accepted for a single key element, without quotes, then the tailf:cli-multi-word-key annotation can be used. The sub-annotation tailf:cli-max-words can be used to tell the parser that at most a fixed number of words should be allowed for the key. For example:
container permit { tailf:info "Specify community to accept"; presence "Specify community to accept"; list permit-list { tailf:cli-suppress-mode; tailf:cli-delete-when-empty; tailf:cli-drop-node-name; key expr; leaf expr { tailf:cli-multi-word-key { tailf:cli-max-words 10; } type string { tailf:info "LINE;;An ordered list as a regular-expression"; } } } }
The tailf:cli-max-words annotation can be used to allow more things to be entered on the same command line.
When generating delete commands towards the device, the default behaviour is to simply add "no" in front of the line you are trying to remove. However, this is not always allowed. In some cases only parts of the command is allowed. For example, suppose you have the data model:
container ospf { tailf:info "OSPF routes Administrative distance"; leaf external { tailf:info "External routes"; type uint32 { range "1.. 255"; tailf:info "<1-255>;;Distance for external routes"; } tailf:cli-suppress-no; tailf:cli-no-value-on-delete; tailf:cli-no-name-on-delete; } leaf inter-area { tailf:info "Inter-area routes"; type uint32 { range "1.. 255"; tailf:info "<1-255>;;Distance for inter-area routes"; } tailf:cli-suppress-no; tailf:cli-no-name-on-delete; tailf:cli-no-value-on-delete; } leaf intra-area { tailf:info "Intra-area routes"; type uint32 { range "1.. 255"; tailf:info "<1-255>;;Distance for intra-area routes"; } tailf:cli-suppress-no; tailf:cli-no-name-on-delete; tailf:cli-no-value-on-delete; } }
If the old configuration has the configuration ospf external 3 inter-area 4 intra-area 1 then the default behaviour would be to send no ospf external 3 inter-area 4 intra-area 1 but this would generate an error. Instead, the device simply wants no ospf. This is then achieved by adding tailf:cli-no-name-on-delete (telling the CLI engine to remove the element name from the no line), and tailf:cli-no-value-on-delete (telling the CLI engine to strip the leaf value from the command line to be sent).
This annotation is used in combination with tailf:cli-sequence-commands. It tells the parser that a leaf in the sequence isn't mandatory. Suppose you have the data model:
container foo { tailf:cli-compact-syntax; tailf:cli-sequence-commands; presence true; leaf a { tailf:cli-incomplete-command; type string; } leaf b { tailf:cli-incomplete-command; type string; } leaf c { type string; } }
If you want the command to behave as foo a <word> [b <word>] c <word>, it means that the leaves a and c are required and b is optional. If b is to be entered it must be entered after a and before c. This would be achieved by adding tailf:cli-optional-in-sequence in b.
container foo { tailf:cli-compact-syntax; tailf:cli-sequence-commands; presence true; leaf a { tailf:cli-incomplete-command; type string; } leaf b { tailf:cli-incomplete-command; tailf:cli-optional-in-sequence; type string; } leaf c { type string; } }
A live example of this from the cisco-ios data model is:
// voice translation-rule * / rule * list rule { tailf:info "Translation rule"; tailf:cli-suppress-mode; tailf:cli-delete-when-empty; tailf:cli-incomplete-command; tailf:cli-compact-syntax; tailf:cli-sequence-commands { tailf:cli-reset-all-siblings; } ordered-by "user"; key tag; leaf tag { type uint8 { tailf:info "<1-15>;;Translation rule tag"; range "1..15"; } } leaf reject { tailf:info "Call block rule"; tailf:cli-optional-in-sequence; type empty; } leaf "pattern" { tailf:cli-drop-node-name; tailf:cli-full-command; tailf:cli-multi-value; type string { tailf:info "WORD;;Matching pattern"; } } }
This annotation is used when the key element of a list isn't the first value that you give when setting a list element (for example when entering a submode). This is similar to tailf:cli-hide-in-submode, except it allows the leaf values to be entered in between key elements. In the example below the match leaf is entered before giving the filter id.
container radius { tailf:info "RADIUS server configuration command"; // radius filter * list filter { tailf:info "Packet filter configuration"; key id; leaf id { type string { tailf:info "WORD;;Name of the filter (max 31 characters, longer will " +"be rejected"; } } leaf match { tailf:cli-drop-node-name; tailf:cli-prefix-key; type enumeration { enum match-all { tailf:info "Filter if all of the attributes matches"; } enum match-any { tailf:info "Filter if any of the attributes matches"; } } } }
It is also possible to have a sub-annotation to tailf:cli-prefix-key that specifies that the leaf should occur before a certain key position. For example:
list route-map { tailf:info "Route map tag"; tailf:cli-mode-name "config-route-map"; tailf:cli-compact-syntax; tailf:cli-full-command; key "name sequence"; leaf name { type string { tailf:info "WORD;;Route map tag"; } } // route-map * # leaf sequence { tailf:cli-drop-node-name; type uint16 { tailf:info "<0-65535>;;Sequence to insert to/delete from " +"existing route-map entry"; range "0..65535"; } } // route-map * permit // route-map * deny leaf operation { tailf:cli-drop-node-name; tailf:cli-prefix-key { tailf:cli-before-key 2; } type enumeration { enum deny { tailf:code-name "op_deny"; tailf:info "Route map denies set operations"; } enum permit { tailf:code-name "op_internet"; tailf:info "Route map permits set operations"; } } default permit; } // route-map * / description leaf "description" { tailf:info "Route-map comment"; tailf:cli-multi-value; type string { tailf:info "LINE;;Comment up to 100 characters"; length "0..100"; } } }
The keys for this list are name and sequence, but in between you need to specify deny or permit. This is not a key since you cannot have two different list instances with the same name and sequence number, but differ in deny and permit.
This annotation is used to group together list instances, or values in a leaf-list into ranges. The type of the value are not restricted to integer only. It works with a string also, and it is possible to have a value like this: 1-5, t1, t2.
// spanning-tree vlans-root container vlans-root { tailf:cli-drop-node-name; list vlan { tailf:info "VLAN Switch Spanning Tree"; tailf:cli-range-list-syntax; tailf:cli-suppress-mode; tailf:cli-delete-when-empty; key id; leaf id { type uint16 { tailf:info "WORD;;vlan range, example: 1,3-5,7,9-11"; range "1..4096"; } } } }
What will exist in the database is separate instances, i.e. if the configuration is vlan 1,3-5,7,9-11 this will result in the database having the instances 1,3,4,5,7,9,10, and 11. Similarly, to create these instances on the device, the command generated by NSO will be vlan 1,3-5,7,9-11. Without this annotation NSO would generate unique commands for each instance, i.e.:
vlan 1 vlan 2 vlan 3 vlan 5 vlan 7 ...
Same thing for leaf-lists:
leaf-list vlan { tailf:info "Range of vlans to add to the instance mapping"; tailf:cli-range-list-syntax; type uint16 { tailf:info "LINE;;vlan range ex: 1-65, 72, 300 -200"; } }
Some settings needs to be unset before they can be set. This can be accommodated by using the tailf:cli-remove-before-change annotation. An example of such a leaf is:
// ip vrf * / rd leaf rd { tailf:info "Specify Route Distinguisher"; tailf:cli-full-command; tailf:cli-remove-before-change; type rd-type; }
You are not allowed to define a new route distinguisher before removing the old.
This annotation is used on leaf-lists to tell the CLI engine that the entire list should be written and not just the additions or subtractions, which is the default behaviour for leaf-lists. For example:
// controller * / channel-group list channel-group { tailf:info "Specify the timeslots to channel-group "+ "mapping for an interface"; tailf:cli-suppress-mode; tailf:cli-delete-when-empty; key number; leaf number { type uint8 { range "0..30"; } } leaf-list timeslots { tailf:cli-replace-all; tailf:cli-range-list-syntax; type uint16; } }
The timeslots leaf is changed by writing the entire range value. The default would be to generate commands for adding and deleting values from the range.
This annotation is a sub-annotation to tailf:cli-sequence-commands. The problem it addresses is what should happen when a command that takes multiple parameters is run a second time. Consider the data model:
container foo { tailf:cli-compact-syntax; tailf:cli-sequence-commands { tailf:cli-reset-siblings; } presence true; leaf a { type string; } leaf b { type string; } leaf c { type string; } }
You are allowed to enter any of the below commands:
foo foo a <word> foo a <word> b <word> foo a <word> b <word> c <word>
If you first enter the command foo a 1 b 2 c 3, what will be stored in the database is foo being present, the leaf a having the value 1, the leaf b having the value 2 and the leaf c having the value 3.
Now, if the command foo a 3 is executed, it will set the value of leaf a to 3, but will leave leaf b and c as they were before. This is probably not the way the device works. In most cases it expects the leaves b and c to be unset. The annotation tailf:cli-reset-siblings tells the CLI engine that all siblings covered by the tailf:cli-sequence-commands should be reset.
Another similar case is when you have some leaves covered by the command sequencing, and some not. For example:
container foo { tailf:cli-compact-syntax; tailf:cli-sequence-commands { tailf:cli-reset-all-siblings; } presence true; leaf a { type string; } leaf b { tailf:cli-break-sequence-commands; type string; } leaf c { type string; } }
The above model will allow the user to enter the b and c leaves in any order, as long as leaf a is entered first. The annotation tailf:cli-reset-siblings will reset the leaves up to the tailf:cli-break-sequence-commands. The tailf:cli-reset-all-siblings tells the CLI engine to reset all siblings, also those outside the command sequencing.
This annotation can be used on both containers/lists and on leaves, but has slightly different meaning. When used on a container it means that whenever the container is entered, all leaves in it are reset.
If used on a leaf it should be understood as whenever that leaf is set all other leaves in the container are reset. For example:
// license udi container udi { tailf:cli-compact-syntax; tailf:cli-sequence-commands; tailf:cli-reset-container; leaf pid { type string; } leaf sn { type string; } } container ietf { tailf:info "IETF graceful restart"; container helper { tailf:info "helper support"; presence "helper support"; leaf disable { tailf:cli-reset-container; tailf:cli-delete-container-on-delete; tailf:info "disable helper support"; type empty; } leaf strict-lsa-checking { tailf:info "enable helper strict LSA checking"; type empty; } }
Changes to lists that has the ordered-by "user" annotation are shown as insert, delete, and move operations. However, most devices do not support such operations on the lists. In these cases, if you want to insert an element in the middle of a list, you need to first delete all elements following the insertion point, add the new element, and then add all the elements you deleted. The tailf:cli-show-long-obu-diffs tells the CLI engine to do exactly this. For example:
list foo { ordered-by user; tailf:cli-show-long-obu-diffs; tailf:cli-suppress-mode; key id; leaf id { type string; } }
If the old configuration is:
foo a foo b foo c foo d
The desired configuration is:
foo a foo b foo e foo c foo d
NSO will send the following to the device:
no foo c no foo d foo e foo c foo d
An example from the cisco-ios model is:
// ip access-list extended * container extended { tailf:info "Extended Access List"; tailf:cli-incomplete-command; list ext-named-acl { tailf:cli-drop-node-name; tailf:cli-full-command; tailf:cli-mode-name "config-ext-nacl"; key name; leaf name { type ext-acl-type; } list ext-access-list-rule { tailf:cli-suppress-mode; tailf:cli-delete-when-empty; tailf:cli-drop-node-name; tailf:cli-compact-syntax; tailf:cli-show-long-obu-diffs; ordered-by user; key rule; leaf rule { tailf:cli-drop-node-name; tailf:cli-multi-word-key; type string { tailf:info "deny;;Specify packets to reject\n"+ "permit;;Specify packets to forwards\n"+ "remark;;Access list entry comment"; pattern "(permit.*)|(deny.*)|(no.*)|(remark.*)|([0-9]+.*)"; } } } } }
One common CLI behaviour is to not only show when something is configured, but also when it isn't configured by displaying it as no <command>. You can tell the CLI engine that you want this behaviour by using the tailf:cli-show-no annotation. It can be used both on leaves and on presence containers. For example:
// ipv6 cef container cef { tailf:info "Cisco Express Forwarding"; tailf:cli-display-separated; tailf:cli-show-no; presence true; }
and
// interface * / shutdown leaf shutdown { // Note: default to "no shutdown" in order to be able to bring if up. tailf:info "Shutdown the selected interface"; tailf:cli-full-command; tailf:cli-show-no; type empty; }
However, this is a much more subtle behaviour than one may think and it is not obvious when the tailf:cli-show-no and the tailf:cli-boolean-no should be used. For example, it would also be possible to model the shutdown leaf a boolean value, i.e.:
// interface * / shutdown leaf shutdown { tailf:cli-boolean-no; type boolean; }
The problem with the above is that when a new interface is created, say a vlan interface, the shutdown leaf would not be set to anything and you would not send anything to the device. With the cli-show-no definition you would send no shutdown since the shutdown leaf would not be defined when a new interface vlan instance is created.
The boolean version can be tweaked to behave in a similar way using the default annotation and tailf:cli-show-with-default, i.e.:
// interface * / shutdown leaf shutdown { tailf:cli-show-with-default; tailf:cli-boolean-no; type boolean; default "false"; }
The problem with this is that if you explicitly configure the leaf to false in NSO, you will send no shutdown to the device (which is fine), but if you then read the config from the device it will not display no shutdown since it now has its default setting. This will lead to an out-of-sync situation in NSO. NSO thinks the value should be set to false (which is different from the leaf not being set), whereas the device report the value as being unset.
The whole situation comes from the fact that NSO and the device treat default values differently. NSO considers a leaf as either being set or not set. If a leaf is set to it's default value, it is still considered as set. A leaf must be explicitly deleted in order for it to become unset. Whereas a typical Cisco device considers a leaf unset if you set it to its default value.
This tells the CLI engine to render a leaf not only when it is actually set, but also when it has its default value. For example:
leaf "input" { tailf:cli-boolean-no; tailf:cli-show-with-default; tailf:cli-full-command; type boolean; default true; }
Tells the CLI that it should not be possible to delete all lists instances, i.e. the command no foo is not allowed, it needs to be no foo <instance>. For example:
list class-map { tailf:info "Configure QoS Class Map"; tailf:cli-mode-name "config-cmap"; tailf:cli-suppress-list-no; tailf:cli-delete-when-empty; tailf:cli-no-key-completion; tailf:cli-sequence-commands; tailf:cli-full-command; // class-map * key name; leaf name { tailf:cli-disallow-value "type|match-any|match-all"; type string { tailf:info "WORD;;class-map name"; } } }
By default all lists are rendered as submodes. This can be suppressed using the tailf:cli-suppress-mode annotation. For example, the data model:
list foo { key id; leaf id { type string; } leaf mtu { type uint16; } }
If you have the configuration:
foo a { mtu 1400; } foo b { mtu 1500; }
It would be rendered as:
foo a mtu 1400 ! foo b mtu 1500 !
However, if you add tailf:cli-suppress-mode:
list foo { tailf:cli-suppress-mode; key id; leaf id { type string; } leaf mtu { type uint16; } }
It will be rendered as:
foo a mtu 1400 foo b mtu 1500
The format string is used when parsing a key value and when generating a key value for an existing configuration. The key items are numbered from 1-N and the format string should indicate how they are related by using $(X) (where X is the key number). For example:
list interface { tailf:cli-key-format "$(1)/$(2)/$(3):$(4)"; key "chassis slot subslot number"; leaf chassis { type uint8 { range "1 .. 4"; } } leaf slot { type uint8 { range "1 .. 16"; } } leaf subslot { type uint8 { range "1 .. 48"; } } leaf number { type uint8 { range "1 .. 255"; } } }
It will be rendered as:
interface 1/2/3:4
When generating configuration diffs delete all contents of a container or list before deleting the node. For example:
list foo { tailf:cli-recursive-delete; key "id""; leaf id { type string; } leaf a { type uint8; } leaf b { type uint8; } leaf c { type uint8; } }
It will be rendered as:
# show full foo bar a 1 b 2 c 3 ! # ex # no foo bar # show configuration foo bar no a 1 no b 2 no c 3 ! no foo bar #
Specifies that the CLI should not auto-render 'no' commands for this element. An element with this annotation will not appear in the completion list to the 'no' command. For example:
list foo { tailf:cli-recursive-delete; key "id""; leaf id { type string; } leaf a { type uint8; } leaf b { tailf:cli-suppress-no; type uint8; } leaf c { type uint8; } }
It will be rendered as:
(config-foo-bar)# no ? Possible completions: a c ---
The problem with the above is that the diff will still generate the no. To avoid it, you must use the tailf:cli-no-value-on-delete and tailf:cli-no-name-on-delete.
(config-foo-bar)# no ? Possible completions: a c --- service Modify use of network based services (config-foo-bar)# ex (config)# no foo bar (config)# show config foo bar no a 1 no b 2 no c 3 ! no foo bar (config)#
Do not display value if it is same as default. Please note that this annotation works only in case of with-defaults basic-mode capability set to 'explicit' and the value is explicitly set by the user to the default value. For example:
list foo { key "id""; leaf id { type string; } leaf a { type uint8; default 1; } leaf b { tailf:cli-trim-default; type uint8; default 2; } }
It will be rendered as:
(config)# foo bar (config-foo-bar)# a ? Possible completions: <unsignedByte>[1] (config-foo-bar)# a 2 b ? Possible completions: <unsignedByte>[2] (config-foo-bar)# a 2 b 3 (config-foo-bar)# commit Commit complete. (config-foo-bar)# show full foo bar a 2 b 3 ! (config-foo-bar)# a 1 b 2 (config-foo-bar)# commit Commit complete. (config-foo-bar)# show full foo bar a 1 !
Embed no in front of the element name instead of at the beginning of the line. For example:
list foo { key "id"; leaf id { type string; } leaf a { type uint8; } container x { leaf b { type uint8; tailf:cli-embed-no-on-delete; } } }
It will be rendered as:
(config-foo-bar)# show full foo bar a 1 x b 3 ! (config-foo-bar)# no x (config-foo-bar)# show conf foo bar x no b 3 !
Means that the non-integer key should allow range expressions. Can be used in key leafs only. The key must support a range format. The range applies only for matching existing instances. For example:
list interface { key name; leaf name { type string; tailf:cli-allow-range; } leaf number { type uint32; } }
It will be rendered as:
(config)# interface eth0-100 number 90 Error: no matching instances found (config)# interface Possible completions: <name:string> eth0 eth1 eth2 eth3 eth4 eth5 range (config)# interface eth0-3 number 100 (config-interface-eth0-3)# ex (config)# interface eth4-5 number 200 (config-interface-eth4-5)# commit Commit complete. (config-interface-eth4-5)# ex (config)# do show running-config interface interface eth0 number 100 ! interface eth1 number 100 ! interface eth2 number 100 ! interface eth3 number 100 ! interface eth4 number 200 ! interface eth5 number 200 !
Specifies that this node is case-sensitive. If applied to a container or a list, any nodes below will also be case-sensitive. For example:
list foo { tailf:cli-case-sensitive; key "id"; leaf id { type string; } leaf a { type string; } }
It will be rendered as:
(config)# foo bar a test (config-foo-bar)# ex (config)# commit Commit complete. (config)# do show running-config foo foo bar a test ! (config)# foo bar a Test (config-foo-bar)# ex (config)# foo Bar a TEST (config-foo-Bar)# commit Commit complete. (config-foo-Bar)# ex (config)# do show running-config foo foo Bar a TEST ! foo bar a Test !
When used force the CLI to display namespace prefix of all children. For example:
list foo { tailf:cli-expose-ns-prefix; key "id""; leaf id { type string; } leaf a { type uint8; } leaf b { type uint8; } leaf c { type uint8; } }
It will be rendered as:
(config)# foo bar (config-foo-bar)# ? Possible completions: example:a example:b example:c ---
Enforces the CLI engine to generate 'insert' comments when displaying configuration changes of ordered-by user lists. Should not be used together with tailf:cli-show-long-obu-diffs. For example:
container policy { list policy-list { tailf:cli-drop-node-name; tailf:cli-show-obu-comments; ordered-by user; key policyid; leaf policyid { type uint32 { tailf:info "policyid;;Policy ID."; } } leaf-list srcintf { tailf:cli-flat-list-syntax { tailf:cli-replace-all; } type string; } leaf-list srcaddr { tailf:cli-flat-list-syntax { tailf:cli-replace-all; } type string; } leaf-list dstaddr { tailf:cli-flat-list-syntax { tailf:cli-replace-all; } type string; } leaf action { type enumeration { enum accept { tailf:info "Action accept."; } enum deny { tailf:info "Action deny."; } }
It will be rendered as:
admin@ncs(config-policy-4)# commit dry-run outformat cli ... policy { policy-list 1 { - action accept; + action deny; } + # after policy-list 3 + policy-list 4 { + srcintf aaa; + srcaddr bbb; + dstaddr ccc; + } } } } } }
Tells the CLI to automatically enter multi-line mode when prompting the user for a value to this leaf. The user must type <CR> to enter in the multiline mode. For example:
leaf message { tailf:cli-multi-line-prompt; type string; }
If configured on the same line, no prompt will appear and it will be rendered as:
(config)# message aaa
If <CR> typed, it will be rendered as:
(config)# message (<string>) (aaa): [Multiline mode, exit with ctrl-D.] > Lorem ipsum dolor sit amet, consectetuer adipiscing elit. > Aenean commodo ligula eget dolor. Aenean massa. > Cum sociis natoque penatibus et magnis dis parturient montes, > nascetur ridiculus mus. Donec quam felis, ultricies nec, > pellentesque eu, pretium quis, sem. > (config)# commit Commit complete. ubuntu(config)# do show running-config message message "Lorem ipsum dolor sit amet, consectetuer adipiscing elit. \nAenean commodo ligula eget dolor. Aenean massa. \nCum sociis natoque penatibus et magnis dis parturient montes, \nnascetur ridiculus mus. Donec quam felis, ultricies nec,\n pellentesque eu, pretium quis, sem. \n" (config)#
This statement specifies that the data node should be implemented as a link to another data node, called the target data node. This means that whenever the node is modified, the system modifies the target data node instead, and whenever the data node is read, the system returns the value of target data node. Note that if the data node is a leaf, the target node MUST also be a leaf, and if the data node is a leaf-list,the target node MUST also be a leaf-list. The argument is an XPath absolute location path. If the target lies within lists, all keys must be specified. A key either has a value, or is a reference to a key in the path of the source node, using the function current() as starting point for an XPath location path. For example:
container foo { list bar { key id; leaf id { type uint32; } leaf a { type uint32; } leaf b { tailf:link "/example:foo/example:bar[id=current()/../id]/example:a"; type uint32; } } }
It will be rendered as:
(config)# foo bar 1 ubuntu(config-bar-1)# ? Possible completions: a b --- commit Commit current set of changes describe Display transparent command information exit Exit from current mode help Provide help information no Negate a command or set its defaults pwd Display current mode path top Exit to top level and optionally run command (config-bar-1)# b 100 (config-bar-1)# show config foo bar 1 b 100 ! (config-bar-1)# commit Commit complete. (config-bar-1)# show full foo bar 1 a 100 b 100 ! (config-bar-1)# a 20 (config-bar-1)# commit Commit complete. (config-bar-1)# show full foo bar 1 a 20 b 20 !