Package: nornir-netconf
Nornir NETCONFCollection of NETCONF tasks and connection plugin forNornirInstallationpipinstallnornir_netconfPluginsConnectionsnetconf- Connect to network devices usingncclientTasksnetconf_capabilities- Return server capabilities from target ->Result.result -> RpcResultnetconf_commit- Commits a change ->Result.result -> RpcResultnetconf_edit_config- Edits configuration on specified datastore (default="running") ->Result.result -> RpcResultnetconf_get- Returns state data based on the supplied xpath ->Result.result -> RpcResultnetconf_get_config- Returns configuration from specified configuration store (default="running") ->Result.result -> RpcResultnetconf_get_schemas- Retrieves schemas and saves aggregates content into a directory with schema output ->Result.result -> SchemaResultnetconf_lock- Locks or Unlocks a specified datastore (default="lock") ->Result.result -> RpcResultnetconf_validate- Validates configuration datastore. Requires thevalidatecapability. ->Result.result -> RpcResultResponse ResultThe goal of the task results is to put the NETCONF RPC-reply back in your hands. In most cases, the NornirResult.resultattribute will return back adataclassdepending on the task operation. It's important that you understand the object you will be working with. Please see thedataclassessection below and review the code if you want to see what attributes to expect.DataclassesDefined innornir_netconf/plugins/helpers/models.pyRpcResult-> This will return an attribute ofrpcandmanager. You will encounter this object in most NornirResultsas the return value to theresultattribute. NETCONF / XML payloads can be overwhelming, especially with large configurations and it's just not efficient or useful to display thousands of lines of code in any result.SchemaResult-> An aggregation of interesting information when grabbing schemas from NETCONF servers.Global LockThenetconf_locktask will always return the Manager object, which is the established (and locked) agent used to send RPC's back and forth. The idea of retrieving the Manager is to carry this established locked session from task to task and only lock and unlock once during a run of tasks. Please review the examples below to see how to extract the manager and store it under thetask.hostdictionary as a variable that can be used across multiple tasks. The Manager is passed into other tasks and re-used to send RPCs to the remote server.ExamplesHead over to theExamples directoryif you'd like to review the files.Directory Structure├──example-project
│├──config.yml
│├──inventory
││├──groups.yml
││├──hosts-local.yml
││└──ssh_config
│├──logs
││└──nornir.log
│└──nr-get-config.py
└──README.mdNetconf Connection PluginBelow is the snippet of a host inside the host-local.yml file and its associated group,sros.nokia_rtr:hostname:"192.168.1.205"port:830groups:-"sros"sros:username:"netconf"password:"NCadmin123"port:830platform:"sros"connection_options:netconf:extras:hostkey_verify:falsetimeout:300allow_agent:falselook_for_keys:falseTask: Get Config"""Nornir NETCONF Example Task: 'get-config'."""fromnornirimportInitNornirfromnornir.core.taskimportTaskfromnornir_utils.plugins.functionsimportprint_resultfromnornir_netconf.plugins.tasksimportnetconf_get_config__author__="Hugo Tinoco"__email__="hugotinoco@icloud.com"nr=InitNornir("config.yml")# Filter the hosts by 'west-region' assignmentwest_region=nr.filter(region="west-region")defexample_netconf_get_config(task:Task)->str:"""Test get config."""config=task.run(netconf_get_config,source="running",path="""<configure xmlns="urn:nokia.com:sros:ns:yang:sr:conf"><router><router-name>Base</router-name></router></configure>""",filter_type="subtree",)returnconfig.result.rpc.data_xmldefmain():"""Execute Nornir Script."""print_result(west_region.run(task=example_netconf_get_config))if__name__=="__main__":main()This returns the followingvvvvexample_netconf_get_config**changed:FalsevvvvvvvvvvvvvvvvvvvvvvvvvvvvvINFO<?xmlversion="1.0"encoding="UTF-8"?><rpc-replyxmlns="urn:ietf:params:xml:ns:netconf:base:1.0"message-id="urn:uuid:918bf169-523f-4bb0-b00c-c97c01a48ecd"><data><configurexmlns="urn:nokia.com:sros:ns:yang:sr:conf"><router><router-name>Base</router-name><interface><interface-name>L3-OAM-eNodeB069420-X1</interface-name><admin-state>disable</admin-state><ingress-stats>false</ingress-stats></interface></router></configure></data></rpc-reply>----netconf_get_config**changed:False-------------------------------------INFORpcResult(rpc=<ncclient.xml_.NCElementobjectat0x7f4b1e08a440>)^^^^ENDexample_netconf_get_config^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^(nornir-netconf-Ky5gYI2O-py3.10)➜example-projectgit:(feature/validate-tasks)✗Task: Get Capabilities"""Nornir NETCONF Example Task: 'capabilities'."""fromnornirimportInitNornirfromnornir.core.taskimportTaskfromnornir_utils.plugins.functionsimportprint_resultfromnornir_netconf.plugins.tasksimportnetconf_capabilities__author__="Hugo Tinoco"__email__="hugotinoco@icloud.com"nr=InitNornir("config.yml")# Filter the hosts by 'west-region' assignmentwest_region=nr.filter(region="west-region")defexample_netconf_get_capabilities(task:Task)->str:"""Test get capabilities."""capabilities=task.run(netconf_capabilities)# This may be a lot, so for example we'll just print the first onereturn[capforcapincapabilities.result.rpc][0]defmain():"""Execute Nornir Script."""print_result(west_region.run(task=example_netconf_get_capabilities))if__name__=="__main__":main()This returns the following(nornir-netconf-Ky5gYI2O-py3.10)➜example-projectgit:(feature/validate-tasks)✗python3nr_get_capabilities.pyexample_netconf_get_capabilities*************************************************nokia_rtr**changed:False*************************************************vvvvexample_netconf_get_capabilities**changed:FalsevvvvvvvvvvvvvvvvvvvvvvvINFOurn:ietf:params:netconf:base:1.0----netconf_capabilities**changed:False-----------------------------------INFORpcResult(rpc=<dict_keyiteratorobjectat0x7f7111328c70>)^^^^ENDexample_netconf_get_capabilities^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^(nornir-netconf-Ky5gYI2O-py3.10)➜example-projectgit:(feature/validate-tasks)✗Task: Edit-Config with Global Lock"""Nornir NETCONF Example Task: 'edit-config', 'netconf_lock'."""fromnornirimportInitNornirfromnornir_utils.plugins.functionsimportprint_resultfromnornir_netconf.plugins.tasksimportnetconf_edit_config,netconf_lock,netconf_commit__author__="Hugo Tinoco"__email__="hugotinoco@icloud.com"nr=InitNornir("config.yml")# Filter the hosts by 'west-region' assignmentwest_region=nr.filter(region="west-region")defexample_global_lock(task):"""Test global lock operation of 'candidate' datastore."""lock=task.run(netconf_lock,datastore="candidate",operation="lock")# Retrieve the Manager(agent) from lock operation and store for further# operations.task.host["manager"]=lock.result.managerdefexample_edit_config(task):"""Test edit-config with global lock using manager agent."""config_payload="""<config><configure xmlns="urn:nokia.com:sros:ns:yang:sr:conf"><router><router-name>Base</router-name><interface><interface-name>L3-OAM-eNodeB069420-X1</interface-name><admin-state>disable</admin-state><ingress-stats>false</ingress-stats></interface></router></configure></config>"""result=task.run(netconf_edit_config,config=config_payload,target="candidate",manager=task.host["manager"])# Validate configurationtask.run(netconf_validate)# Committask.run(netconf_commit,manager=task.host["manager"])defexample_unlock(task):"""Unlock candidate datastore."""task.run(netconf_lock,datastore="candidate",operation="unlock",manager=task.host["manager"])defmain():"""Execute Nornir Script."""print_result(west_region.run(task=example_global_lock))print_result(west_region.run(task=example_edit_config))print_result(west_region.run(task=example_unlock))if__name__=="__main__":main()Task: Get Schemas"""Get Schemas from NETCONF device."""fromnornirimportInitNornirfromnornir.coreimportTaskfromnornir.core.taskimportResultfromnornir_utils.plugins.functionsimportprint_resultfromnornir_netconf.plugins.tasksimportnetconf_get,netconf_get_schemasfromtests.conftestimportxml_dict__author__="Hugo Tinoco"__email__="hugotinoco@icloud.com"nr=InitNornir("config.yml")# Filter the hosts by 'west-region' assignmentwest_region=nr.filter(region="west-region")SCHEMA_FILTER="""<netconf-state xmlns="urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring"><schemas></schemas></netconf-state>"""defexample_task_get_schemas(task:Task)->Result:"""Get Schemas from NETCONF device."""result=task.run(netconf_get,path=SCHEMA_FILTER,filter_type="subtree")# xml_dict is a custom function to convert XML to Python dictionary. Not part of Nornir Plugin.# See the code example if you want to use it.parsed=xml_dict(result.result.rpc.data_xml)first_schema=parsed["rpc-reply"]["data"]["netconf-state"]["schemas"]["schema"][0]returntask.run(netconf_get_schemas,schemas=[first_schema["identifier"]],schema_path="./output/schemas")defmain():"""Execute Nornir Script."""print_result(west_region.run(task=example_task_get_schemas))if__name__=="__main__":main()This returns the following(nornir-netconf-Ky5gYI2O-py3.10)➜example-projectgit:(feature/validate-tasks)✗python3nr_get_schemas.pyexample_task_get_schemas*********************************************************nokia_rtr**changed:False*************************************************vvvvexample_task_get_schemas**changed:FalsevvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvINFO----netconf_get**changed:False--------------------------------------------INFORpcResult(rpc=<ncclient.xml_.NCElementobjectat0x7f36391540d0>)----netconf_get_schemas**changed:False------------------------------------INFOSchemaResult(directory='./output/schemas')^^^^ENDexample_task_get_schemas^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^Additional DocumentationNCClientContributionsGithub actions spins up a Containerlab instance to do full integration tests once linting has been satisfied.No line of code shall go untested! Any contribution will need to be accounted for by the coverage report and satisfy all linting.Linters:Ruff (Flake8/Pydocstyle)BlackYamllintPylintBanditMyPyTestingTo test within a local docker environmentgitclonehttps://github.com/h4ndzdatm0ld/nornir_netconfdocker-composebuild&&docker-composeruntestTo test locally with pytestIf you'd like to run integration tests with ContainerLabexportSKIP_INTEGRATION_TESTS=Falsedocker-composeup-dpoetryinstall&&poetryshellpytest--cov=nornir_netconf--color=yes--disable-pytest-warnings-vvvIntegration TestsDevices with full integration tests with ContainerLabNokia SROS - TiMOS-B-21.2.R1Cisco IOSxR - Cisco IOS XR Software, Version 6.1.3Cisco IOSXE - Cisco IOS XE Software, Version 17.03.02Arista CEOS - 4.28.0F-26924507.4280F (engineering build)DocumentationDocumentation is generated with Sphinx and hosted with Github Pages.Documentation