Skip to content

NETCONF console in a docker container#

Its an engineers core ability to decompose a complex task in a set of a smaller, easy to understand and perform sub-tasks. Be it a feature-rich program that is decomposed to classes, functions and APIs or a huge business operation captured in steps in a Methods Of Procedure document.

In a network automation field where the configuration protocols such as NETCONF or gRPC are emerging, it is always needed to have a quick way to validate an RPC or Notification feature before implementing this in a code or a workflow.

This blog post is about a handy tool called netconf-console which allows you to interface with your network device using NETCONF quick and easy. And, of course, I packed it in a smallish container so you can enjoy it hassle-free on every docker-enabled host.

netconf-console is a tool from Tail-f that basically gives you a NETCONF client for your console. That is exactly the packaging that I appreciate to have when I need to play with NETCONF. Cold-starting a python project with ncclient is much slower and you need ensure that you have all the RPCs coded, meh. With the console client you have almost anything you need to start tinkering with NETCONF enabled device.

# netconf-console --host=example.com --db candidate --lock --edit-config=fragment1.xml \
--rpc=commit-confirmed.xml --unlock --sleep 5 --rpc=confirm.xml

Moreover, you can have an interactive NETCONF console to your device:

# netconf-console --host=example.com -i
netconf> lock
netconf> edit-config fragment1.xml --db candidate
netconf> rpc commit-confirmed.xml
netconf> unlock
netconf> get-config
netconf> rpc confirm.xml

Containerize#

I've been spoiled by Rust/Go tools that are self-contained, dependency-free and almost platform-agnostic. To achieve the same level of hassle-free for python tool I practice containerization.

So I decided to put netconf-console in a whale protected cage by building a multi-stage docker image. Even though there are some images for the netconf-console, they are all outdated, based on an old version of the tool and use python2 under the hood. Its 2020 here, so I wanted to create a fresh, small image based off of the recent netconf-console code and running with python3.

Of course you can install the netconf-console with pip as usual: pip install netconf-console

So here it is, a multi-stage build Dockerfile that builds netconf-console in Alpine linux with python3.7.
The result of this build can be found at the relevant docker hub page.

Tags#

The docker image will be tagged in accordance with the release version numbers of the netconf-console; at the time of this writing, the latest version is 2.2.0, hence you will find the image with the corresponding tag. Also, the latest tag will point to the most recent version.

Installation#

As with any other docker image, all it takes is to make a pull:

docker pull hellt/netconf-console

Usage examples#

The entry point of the docker image is the netconf-console itself, therefore you can run it almost in the same way as you'd do with a standalone installation - by providing the arguments to the callable.

# verify that the tool is properly working,
docker run --rm -it hellt/netconf-console --help

usage: netconf-console [-h] [-s [{plain,noaaa} [{plain,noaaa} ...]]] [--db DB]
                       [--timeout TIMEOUT]
                       [--with-defaults {explicit,trim,report-all,report-all-tagged}]
                       [--with-inactive] [-x XPATH]
                       [-t {test-then-set,set,test-only}]
                       [-o {merge,replace,create}]
                       [--del-operation {remove,delete}] [-v VERSION]
                       [-u USERNAME] [-p [PASSWORD]] [--host HOST]
                       [-r REPLY_TIMEOUT] [--port PORT]
                       [--privKeyFile PRIVKEYFILE] [--raw [RAW]] [--tcp]
                       [-N [NS [NS ...]]] [--debug] [--hello] [--get [GET]]
                       [--get-config [GET_CONFIG]] [--kill-session SESSION_ID]
                       [--discard-changes] [--lock] [--unlock]
                       [--commit [{confirmed}]] [--validate [VALIDATE]]
                       [--copy-running-to-startup]
                       [--copy-config [COPY_CONFIG]]
                       [--edit-config [EDIT_CONFIG [EDIT_CONFIG ...]]]
                       [--set [SET [SET ...]]]
                       [--delete [DELETE [DELETE ...]]]
                       [--create [CREATE [CREATE ...]]]
                       [--get-schema GET_SCHEMA]
                       [--create-subscription [CREATE_SUBSCRIPTION]]
                       [--rpc [RPC]] [--sleep SLEEP] [-e EXPR] [--dry]
                       [--interactive]
                       [filename]

The interactive console mode, of course, also works:

docker run -it --rm hellt/netconf-console --host=10.1.0.11 --port=830 -u admin -p admin -i
netconf> hello
<?xml version='1.0' encoding='UTF-8'?>
<nc:hello xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0">
  <nc:capabilities>
    <nc:capability>urn:ietf:params:netconf:base:1.0</nc:capability>
    <nc:capability>urn:ietf:params:netconf:base:1.1</nc:capability>
<<SNIPPED>>

Now a more real-life example would be to throw a NETCONF RPC to the target device:

$ cat test.xml
<rpc message-id="113" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
  <get>
    <filter>
      <state xmlns="urn:nokia.com:sros:ns:yang:sr:state">
        <system>
          <security>
            <user-params>
              <local-user>
              <user/>
             </local-user>
            </user-params>
          </security>
        </system>
      </state>
    </filter>
  </get>
</rpc>
$ docker run -it --rm -v $(pwd):/rpc hellt/netconf-console --host=10.1.0.11 --port=830 -u admin -p admin test.xml
<?xml version='1.0' encoding='UTF-8'?>
<rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="113">
    <data>
        <state xmlns="urn:nokia.com:sros:ns:yang:sr:state">
            <system>
                <security>
                    <user-params>
                        <local-user>
                            <user>
                                <user-name>admin</user-name>
                                <attempted-logins>13</attempted-logins>
                                <failed-logins>0</failed-logins>
                                <locked-out>false</locked-out>
                                <password-changed-time>2020-01-24T23:27:33.0Z</password-changed-time>
                            </user>
                            <user>
                                <user-name>grpc</user-name>
                                <attempted-logins>0</attempted-logins>
                                <failed-logins>0</failed-logins>
                                <locked-out>false</locked-out>
                                <password-changed-time>2020-01-24T23:27:35.0Z</password-changed-time>
                            </user>
                        </local-user>
                    </user-params>
                </security>
            </system>
        </state>
    </data>
</rpc-reply>

Note that the WORKDIR of the container image is set to /rpc, therefore mounting the directory with your RPCs to that mountpoint will allow to refer to the file names directly.

And you can create pretty complex ad-hoc RPCs with locking the datastore, committing and discarding the changes effortlessly.

Comments