Labspg 2442 LG
Labspg 2442 LG
Labspg 2442 LG
Lab Guide
Connecting to POD
Task 1: Installing NSO
Log in to NSO either directly or launch putty from your remote desktop
Execute the installer to begin installation
[root@nso ~]# ls
1 anaconda-ks.cfg holding nso-4.6.1.3 python_examples tmp-lab
[root@nso ~]#
[root@nso ~]#
Start NSO
Go to project directory
admin@ncs#
admin@ncs# exit
root@nso ~/nso-lab$
Using Netsim
Go to project directory
c1> enable
c1#
c1# exit
root@nso ~/nso-lab$
Task 2: NSO Device Manager
Go to project directory
admin@ncs# conf
Entering configuration mode terminal
admin@ncs(config)#
admin@ncs(config)# commit
Commit complete.
admin@ncs(config)#
Perform sync-from
Perform check-sync
admin@ncs(config-if)# top
admin@ncs(config)#
admin@ncs(config-if)# top
admin@ncs(config)#
admin@ncs(config)# commit
Commit complete.
admin@ncs(config)#
Exit NSO
admin@ncs(config)# exit
admin@ncs#
admin@ncs# exit
root@nso ~/nso-lab$
Rollbacks
root@nso ~/nso-lab$ ls logs/
audit.log ncserr.log.siz rollback10001 rollback10006
devel.log ncs-java-vm.log rollback10002 snmp.log
localhost:8080.access ncs.log rollback10003 webui-browser.log
ncserr.log.1 ncs-python-vm.log rollback10004 xpath.trace
ncserr.log.idx netconf.log rollback10005
root@nso ~/nso-lab$
snmp:snmp {
snmp:agent {
...
}
delete:
snmp:community public;
snmp:vacm {
...
}
delete:
snmp:notify foo;
snmp:usm {
...
}
delete:
snmp:target monitor;
}
root@nso ~/nso-lab$
admin@ncs# conf
Entering configuration mode terminal
admin@ncs(config)#
admin@ncs(config)# revert
All configuration changes will be lost. Proceed? [yes, NO] yes
admin@ncs(config)#
admin@ncs# conf
Entering configuration mode terminal
admin@ncs(config)#
admin@ncs(config-device-group-C)# commit
Commit complete.
admin@ncs(config-device-group-C)#
admin@ncs(config)# commit
Commit complete.
admin@ncs(config)#
admin@ncs(config)# commit
Commit complete.
admin@ncs(config)#
admin@ncs(config)# exit
admin@ncs#
admin@ncs# exit
root@nso ~/nso-lab$
Task 3 – NSO Service Manager
Service Design
Go to project directory
root@nso ~/nso-lab/packages$ ls
cisco-ios dell-ftos
root@nso ~/nso-lab/packages$
root@nso ~/nso-lab/packages$ cd ..
root@nso ~/nso-lab$
admin@ncs# config
Entering configuration mode terminal
admin@ncs(config)#
admin@ncs(config-if)# top
admin@ncs(config)#
admin@ncs(config)# commit dry-run outformat native
native {
device {
name c0
data interface GigabitEthernet0/23
switchport mode trunk
switchport trunk allowed vlan 11
exit
}
device {
name dell0
data interface Vlan 10
no ip address
tagged GigabitEthernet0/11
!
}
}
admin@ncs(config)#
admin@ncs(config)#
admin@ncs(config)# commit
Commit complete.
admin@ncs(config)#
admin@ncs(config)# show full-configuration devices device dell0 config force10:interface Vlan | display
xml
<config xmlns="http://tail-f.com/ns/config/1.0">
<devices xmlns="http://tail-f.com/ns/ncs">
<device>
<name>dell0</name>
<config>
<interface xmlns="http://tail-f.com/ned/dell-ftos">
<Vlan>
<id>10</id>
<tagged>
<id>GigabitEthernet0/11</id>
</tagged>
</Vlan>
</interface>
</config>
</device>
</devices>
</config>
admin@ncs(config)#
admin@ncs(config)# show full-configuration devices device c0 config ios:interface GigabitEthernet 0/23
| display xml
<config xmlns="http://tail-f.com/ns/config/1.0">
<devices xmlns="http://tail-f.com/ns/ncs">
<device>
<name>c0</name>
<config>
<interface xmlns="urn:ios">
<GigabitEthernet>
<name>0/23</name>
<switchport>
<mode>
<trunk/>
</mode>
<trunk>
<allowed>
<vlan>
<vlans>11</vlans>
</vlan>
</allowed>
</trunk>
</switchport>
</GigabitEthernet>
</interface>
</config>
</device>
</devices>
</config>
admin@ncs(config)#
o Resultant XML file
o Parameterise the XML file, so it looks like below, parameters highlighted
in yellow
module trunk {
namespace "http://com/example/trunk";
prefix trunk;
import ietf-inet-types {
prefix inet;
}
import tailf-ncs {
prefix ncs;
}
list trunk {
key name;
uses ncs:service-data;
ncs:servicepoint "trunk";
leaf name {
type string;
}
list endpoint {
key device;
leaf device {
type leafref {
path "/ncs:devices/ncs:device/ncs:name";
}
}
leaf interface {
type string;
}
}
leaf vlan{
type uint16;
}
}
}
admin@ncs# conf
Entering configuration mode terminal
admin@ncs(config)#
admin@ncs(config-endpoint-del0)# vlan 12
admin@ncs(config-trunk-myservice)#
admin@ncs(config-trunk-myservice)# top
admin@ncs(config)#
admin@ncs(config)# commit
Commit complete.
admin@ncs(config)#
Extra tasks
list endpoint {
key device;
max-elements 2;
min-elements 2;
leaf device {
type leafref {
path "/ncs:devices/ncs:device/ncs:name";
}
}
leaf interface {
type string;
}
}
leaf vlan {
type uint16 {
range "0..4095";
}
}
So lets go a head:
Update the service YANG model, add a choice just under the interface leaf (you can
remove the interface leaf later of course) and add two cases, one for each interface
type available. The leafs cant have the same name (one disadvantage of the choice
type). You can see in the leafref path that we are taking advantage of a Tail-f YANG
addition called deref, that method takes a leaf of type leafref as input and returns the
XPATH for that instance. The pure YANG equvivalent would be
type leafref {
path
/ncs:devices:ncs:device[ncs:name=../device]/ncs:config/ios:interface/ios:GigabitEther
net/ios:name;
}
The deref returns the full XPATH for the instance i.e:
/ncs:devices/ncs:device[ncs:name=’c0’]
That is why we have to add the /../ after the deref to get to the device config container
which is in the same level as the name leaf.
choice xvendor {
case cisco-GigabitEthernet {
leaf CiscoGigabitEthernet {
when
/ncs:devices/ncs:device[ncs:name=current()/../device]/ncs:config/ios:interface/ios:Gig
abitEthernet/ios:name;
type leafref {
path
deref(../device)/../ncs:config/ios:interface/ios:GigabitEthernet/ios:name;
}
}
}
case dell-GigabitEthernet {
leaf DellGigabitEthernet {
when
/ncs:devices/ncs:device[ncs:name=current()/../device]/ncs:config/force10:interface/for
ce10:GigabitEthernet/force10:id;
type leafref {
path
deref(../device)/../ncs:config/force10:interface/force10:GigabitEthernet/force10:id;
}
}
}
}
Update the service template.
<config-template xmlns="http://tail-f.com/ns/config/1.0">
<devices xmlns="http://tail-f.com/ns/ncs">
<device>
<name>{/endpoint/device}</name>
<config>
<interface xmlns="http://tail-f.com/ned/dell-ftos">
<Vlan>
<id>{string(/vlan)}</id>
<tagged>
<id>GigabitEthernet{DellGigabitEthernet}</id>
</tagged>
</Vlan>
</interface>
<interface xmlns="urn:ios">
<GigabitEthernet>
<name>{CiscoGigabitEthernet}</name>
<switchport>
<mode>
<trunk/>
</mode>
<trunk>
<allowed>
<vlan>
<vlans>{string(/vlan)}</vlans>
</vlan>
</allowed>
</trunk>
</switchport>
</GigabitEthernet>
</interface>
</config>
</device>
</devices>
</config-template>
admin@ncs(config)# exit
admin@ncs# packages reload
admin@ncs# config
admin@ncs(config)# trunk myservice2 endpoint c0 CiscoGigabitEthernet 0/
Possible completions:
1 2 3
admin@ncs(config)# trunk myservice2 endpoint dell0 DellGigabitEthernet 0/
Possible completions:
1 2 3
admin@ncs(config)# trunk myservice2 endpoint d0 DellGigabitEthernet 0/1
admin@ncs(config-endpoint-d0)# trunk myservice2 endpoint c0
CiscoGigabitEthernet 0/2
admin@ncs(config-endpoint-d0)# top
admin@ncs(config)# commit dry-run
cli {
local-node {
data devices {
device c0 {
config {
ios:interface {
GigabitEthernet 0/2 {
switchport {
mode {
+ trunk {
+ }
}
trunk {
allowed {
vlan {
+ vlans 55;
}
}
}
}
}
}
}
}
device dell0 {
config {
force10:interface {
+ Vlan 55 {
+ tagged GigabitEthernet0/2;
+ }
}
}
}
}
+trunk mys2 {
+ endpoint c0;
+ endpoint d1;
+ vlan 55;
+}
}
}
As you can see this can quickly become quite complex if you have multiple device
types with multiple interface types. This way of designing your service YANG
models is only recommended if the services instances will be created directly on NSO
by users. If it will be mostly northbound systems like order managers etc that will be
interacting with NSO this kind of design just makes that integration harder and the
recommended way is to use a single leaf for all interface types (as the initial design).
Interface verification can and should still be done but at a lower level. Remember a
northbound system can still check if the interface exists by looking under devices
device in NSO.
You can find more information about these instructions in the NSO development
guide. It around page 270 under the chapter “Processing instructions”.
Syntax Description
Allows to assign new variables or manipulate existing variable
value. If
used to create a new variable, then the scope of visibility of
<?set variable = value?> this variable is limited to the parent tag of the processing
instruction or the current processing instruction block.
Specifically, if a new variable is defined inside a loop, then it
is discarded at the end of each iteration.
Processing instruction block that allows conditional execution
<?if {expression}?
>...<?else?>...<? end?> based on the boolean result of the expression. For the detailed
description see the section called “Conditional Statements”
The expression must evaluate to a (possibly empty) XPath
node-set. The template engine will then iterate over each node
<?foreach {expression}? in the node-set by changing the XPath current context node to
>...<?end?> this node and evaluating all children tags within this context.
For the detailed description see the section called “Loop
Statements”
<?for [variable = initial This processing instruction allows to iterate over the same set
value]; {progress of template tags by changing a variable value. The variable
condition}; [variable = visibility scope obeys the same rules as in the case of set
next value]?>...<?end? > processing instruction, except the variable value is carried over
to the next iteration instead of being discarded at the end of
each iteration. The square brackets indicate optional clauses,
so only the condition expression is mandatory. For the detailed
description see the section called “Loop Statements”
This instruction is analogous to copy_tree function available in
the MAAPI API. The parameter is an XPath expression which
must evaluate to exactly one node in the data tree and indicates
<?copy-tree {source}?>
the source path to copy from. The target path is defined by the
position of the copy-tree instruction in the template
within the current context.
Allows to manipulate the current context node used to
<?set- context-node evaluate XPath expressions in the template. The expression is
{expression}?> evaluated within the current XPath context and must evaluate
to exactly one node in the data tree.
Allows to manipulate the root node of the XPath accessible tree. This
<?set-root-node expression is evaluated in an XPath context where the accessible tree is the
{expression}?> entire datastore, which means that it is possible to select a root node outside
the current accessible tree. The current context node remains unchanged. Just
like with the set-context-node instruction the expression must
evaluate to exactly one node in the data tree.
Store both the current context node and the root node of the XPath accessible
tree with name being the key to access it later. It is possible to switch to this
<?save-context context later using switch-context with the name. Multiple contexts
name?> can be stored simultaneously under different names. Using save-context with
the same name multiple times will result in the stored context being
overwritten.
Used to switch to a context stored using save-context with the specified
name. This means that both the current context node and the root node of
<?switch-context the XPath accessible tree will be changed to the stored values. switch-
name?> context does not remove the context from the storage and can be used as
many times as needed, however using it with a name that does not exist in the
storage would cause an error.
Python
The python API together with IPython can be really handy when you want to do some
rapid prototyping and test out your ideas. There is a small script already in your bash
PATH of your Linux box called ncs_pycli that will setup a transaction toward you
running NSO instance so you are ready to play around. If you like to download the
script to your own system it can be found in the NSO Developer Hub Github page:
https://github.com/NSO-developer/ncs_pycli
Just type ncs_pycli and it will start a python prompt and give you a root object
pointing to the NSO root. The beauty of IPython is that it gives you some handy
features like tab completion, pretty much like you have in the normal NSO cli.
bash$ ncs_pycli
Your maagic object 'root -> (root)' is now prepared... go have some fun!
trans.compare() to see your current transaction
trans.apply() to commit
trans.revert() to revert changes
Maapi object can be found at m
You can restart the transaction and create a fresh root object by invoking new_trans:
In [1]: new_trans
new transaction created
Set device to c0
In [8]: type(root.ncs__devices)
Out[8]: ncs.maagic.Container
Help about the objects
In [7]: help(root.ncs__devices)
Help on Container in module ncs.maagic object:
class Container(Node)
| Represents a yang container.
|
| A (non-presence) container node or a list element, contains other nodes.
|
| Method resolution order:
| Container
| Node
| __builtin__.object
|
| Methods defined here:
|
| __init__(self, backend, cs_node, parent=None)
| Initialize Container node. Should not be called explicitly.
|
| __repr__(self)
| Get internal representation.
|
| delete(self)
| Delete the container.
|
| Deletes all nodes inside the container. The container itself is not
| affected as it carries no state of its own.
…..
…..
In [11]: trans.compare()
Diff set:
kp=/ncs:devices/device{d1}, op=MOP_MODIFIED, oldv=None, newv=None
kp=/ncs:devices/device{d1}/config/ios:hostname, op=MOP_VALUE_SET,
oldv=None, newv=C0
In [12]: trans.apply()
In [13]:
On your own, now try to create a new trunk service instance via python.
And as a last advanced task, make the service automatically set a random vlan id. I’ll
help you with parts of the code, think about why it is written as it is? How does
FASTMAP work? Extra extra task can be do make sure that the vlan id hasn’t already
been allocated by any other service instance.
# ------------------------
# SERVICE CALLBACK EXAMPLE
# ------------------------
class ServiceCallbacks(Service):
vars = ncs.template.Variables()
vars.add('DUMMY', '127.0.0.1')
template = ncs.template.Template(service)
template.apply('trunk-template', vars)
Add the missing lines to add the vlan variable to the template, both the missing pieces
in the python code (the variable always needs to exist for the template enginte to
work, it can sometimes be empty but has to exist) and add the missing piece to the
template. Remember to reload packages. If you want to see whats going on you can
always tail the log files:
bash$ cd logs
bash$ tail -f ncs-python*