pyATS/Genie: ネットワーク機器を NETCONF で操作する(yang.connector)

(この記事はネットワーク自動化 Advent Calendar 2019 22日目として書いています)

ネットワーク自動化に関するものなら何でもOKです!

今回はネットワークの自動化で CLI ではなく、YANG モデルを使った NETCONFRESTCONF という手法があり、pyATS/Genie ではどちらも使う事ができますが、今回は pyATS/Genie を使い NETCONF で機器を操作する方法を紹介します。

NETCONFとは?

NETCONF とはなんぞや?という点についての詳細は説明しませんので他の方が書いている情報を参照ください。ざっくり言うと NETCONF というプロトコルを用いて YANG モデルを基に設定の取得/変更や機器情報が確認出来るもので、XML な構造体になっているので、IETF や OpenConfig の共通モデルを使ってベンダ間でも同じ手法で機器が操作/確認が出来ると言う便利なもの(のはず)です。

IOSXE での NETCONF 設定

まずは NETCONF を使う為の設定をネットワーク機器に行います。下記は IOSXE の設定例です。ポイントとしては使用するユーザの privilege を 15 に設定しておきます。

username netconf privilege 15 password netconf123
!
netconf-yang 

pyATS/Genie と yang.connector のインストール

python の virtualenv を有効化した後で、pyATS/Genie と NETCONF コネクタの yang.connector をインストールします。

(genie)$ pip install genie yang.connector

NETCONF 用の testbed.yaml

NETCONF 用のコネクション netconftestbed.yaml へ追加しました。port は NETCONF デフォルトの 830 で、class  に yang.connector.Netconf と yang.connector を使うことを指定します。

他のコネクション(SSH)とユーザ名/パスワードが同じであれば、credentials は必須ではありませんが、今回は NETCONF 用に異なるユーザ/パスワードを使う為、コネクション netconf の配下に credentials を記載しています。
(また、connections 配下の netconf はコネクション名のため、特に何でも構いません)

devices:
  R1_xe:
    alias: R1_xe
    connections:
      defaults:
        via: netconf
      netconf:
        ip: 172.16.1.228
        port: 830
        class: yang.connector.Netconf
        credentials:
          netconf:
            username: netconf
            password: netconf123
      ssh:
        ip: 172.16.1.228
        protocol: ssh
    credentials:
      default:
        password: Cisc0123
        username: cisco
      enable:
        password: Cisc0123
    os: iosxe
    platform: iosxe
    type: CSR1000v

NETCONF で ネットワーク機器へ接続

それでは早速ネットワーク機器(IOSXE)へ NETCONF で接続します。Telnet/SSH の場合と同じように genie shell から試してみます。

SSH の場合と同じように connect() で接続します。alias (別名) を指定していますが、無くても問題ないです。(ただ、この後に続く手順は alias を使った場合のものになります)

>>> dev.connect(alias='nc')
Connected (version 2.0, client OpenSSH_6.1)
Authentication (password) successful!
[host 172.16.1.228 session 0x7efd9eeb5f60] Sending:
<?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><nc:capability>urn:ietf:params:netconf:capability:writable-running:1.0</nc:capability><nc:capability>urn:ietf:params:netconf:capability:candidate:1.0</nc:capability><nc:capability>urn:ietf:params:netconf:capability:confirmed-commit:1.0</nc:capability><nc:capability>urn:ietf:params:netconf:capability:rollback-on-error:1.0</nc:capability><nc:capability>urn:ietf:params:netconf:capability:startup:1.0</nc:capability><nc:capability>urn:ietf:params:netconf:capability:url:1.0?scheme=http,ftp,file,https,sftp</nc:capability><nc:capability>urn:ietf:params:netconf:capability:validate:1.0</nc:capability><nc:capability>urn:ietf:params:netconf:capability:xpath:1.0</nc:capability><nc:capability>urn:ietf:params:netconf:capability:notification:1.0</nc:capability><nc:capability>urn:liberouter:params:netconf:capability:power-control:1.0</nc:capability><nc:capability>urn:ietf:params:netconf:capability:interleave:1.0</nc:capability><nc:capability>urn:ietf:params:netconf:capability:with-defaults:1.0</nc:capability></nc:capabilities></nc:hello>]]>]]>
[host 172.16.1.228 session 0x7efd9eeb5f60] Received:
<?xml version="1.0" encoding="UTF-8"?>
<hello xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
<capabilities>
<capability>urn:ietf:params:netconf:base:1.0</capability>
<capability>urn:ietf:params:netconf:base:1.1</capability>
<capability>urn:ietf:params:netconf:capability:writable-running:1.0</capability>
(snip)
<capability>urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults?module=ietf-netconf-with-defaults&revision=2011-06-01</capability>
<capability>
        urn:ietf:params:netconf:capability:notification:1.1
      </capability>
</capabilities>
<session-id>66</session-id></hello>
[host 172.16.1.228 session-id 66] initialized: session-id=66 | server_capabilities=<dict_keyiterator object at 0x7efd9eea4b38>>>>

上記ログの最後に session-id が表示されます。

接続しているかどうかを確認するには connected アトリビュートで確認できます。dev の後の nc は connect() で設定した alias になります。

>>> dev.nc.connected
True
>>>

前述の session-id は接続するとデバイスオブジェクトからも確認できます。

>>> dev.nc.session_id
'66'

また、ネットワーク機器上では show netconf-yang sessions コマンドでsession-id が確認できます。

R1_xe#show netconf-yang sessions 
R: Global-lock on running datastore
C: Global-lock on candidate datastore
S: Global-lock on startup datastore

Number of sessions : 1

session-id  transport    username             source-host           global-lock  
-------------------------------------------------------------------------------
68          netconf-ssh  netconf              172.16.1.250          None         

NETCONF で接続した際に Hello メッセージで Capabilities が交換されますが、device オブジェクトの server_capabilities に格納されているため、下記のように確認する事が可能です。

>>> for cap in dev.nc.server_capabilities:
...   print(cap)
...
urn:ietf:params:netconf:base:1.0
urn:ietf:params:netconf:base:1.1
urn:ietf:params:netconf:capability:writable-running:1.0
urn:ietf:params:netconf:capability:xpath:1.0
urn:ietf:params:netconf:capability:validate:1.0
(snip)

大量の情報をやり取りする場合には timeout が設定できます。default では 30秒 になっています。

>>> dev.nc.timeout
30
>>> dev.nc.timeout = 300
>>> dev.nc.timeout
300

それでは早速 NETCONF での操作をやっていきましょう。基本的に下記の内容は yang.connector のドキュメントをベースにし、追記したような内容になります。

get()

get() ではコンフィグや機器の状態を取得する事ができます。但し、パラメータ filter へ etree オブジェクトを渡す必要があり、少し面倒です。ただ、ncclient を使っても同じです。(yang.connector は ncclient を使っているため)

下記に subtree と XPATH を使った場合の例を載せています。詳細は割愛しますが、こんな感じで使うのかぁと感じてもらえれば幸いです。

subtree filter

>>> from lxml import etree
>>> ele_filter = etree.Element("{urn:ietf:params:xml:ns:netconf:base:1.0}filter", type="subtree")
>>> ele_routing = etree.SubElement(ele_filter, "routing", nsmap = {None: 'urn:ietf:params:xml:ns:yang:ietf-routing'})
>>> ele_routing_instance = etree.SubElement(ele_routing, "routing-instance")
>>> ele_name = etree.SubElement(ele_routing_instance, "name").text = 'default'
>>> res = dev.nc.get(filter=ele_filter).data_xml
[host 172.16.1.228 session-id 68] Requesting 'Get'
[host 172.16.1.228 session-id 68] Sending:

#345
<?xml version="1.0" encoding="UTF-8"?><nc:rpc xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="urn:uuid:bd4b130b-2844-4a93-92e7-735c805a68fd"><nc:get><nc:filter type="subtree"><routing xmlns="urn:ietf:params:xml:ns:yang:ietf-routing"><routing-instance><name>default</name></routing-instance></routing></nc:filter></nc:get></nc:rpc>
##

[host 172.16.1.228 session-id 68] Received:
<?xml version="1.0" encoding="UTF-8"?>
<rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="urn:uuid:bd4b130b-2844-4a93-92e7-735c805a68fd" xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0"><data><routing xmlns="urn:ietf:params:xml:ns:yang:ietf-routing"><routing-instance><name xmlns:nc='urn:ietf:params:xml:ns:netconf:base:1.0'>default</name><description>default-vrf [read-only]</description><routing-protocols><routing-protocol><type xmlns:ospf="urn:ietf:params:xml:ns:yang:ietf-ospf">ospf:ospfv2</type><name>1</name><ospf xmlns="urn:ietf:params:xml:ns:yang:ietf-ospf"><instance><af xmlns:rt="urn:ietf:params:xml:ns:yang:ietf-routing">rt:ipv4</af><nsr><enable>false</enable></nsr><auto-cost><enable>false</enable></auto-cost><redistribution xmlns="urn:ietf:params:xml:ns:yang:cisco-ospf"><rip></rip></redistribution></instance></ospf></routing-protocol><routing-protocol><type>static</type><name>1</name></routing-protocol></routing-protocols></routing-instance></routing></data></rpc-reply>
>>>

res には XML データが入りますが、XML は内容が非常に読みづらいです。。

>>> res
'<?xml version="1.0" encoding="UTF-8"?><data xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0"><routing xmlns="urn:ietf:params:xml:ns:yang:ietf-routing"><routing-instance><name xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0">default</name><description>default-vrf [read-only]</description><routing-protocols><routing-protocol><type xmlns:ospf="urn:ietf:params:xml:ns:yang:ietf-ospf">ospf:ospfv2</type><name>1</name><ospf xmlns="urn:ietf:params:xml:ns:yang:ietf-ospf"><instance><af xmlns:rt="urn:ietf:params:xml:ns:yang:ietf-routing">rt:ipv4</af><nsr><enable>false</enable></nsr><auto-cost><enable>false</enable></auto-cost><redistribution xmlns="urn:ietf:params:xml:ns:yang:cisco-ospf"><rip/></redistribution></instance></ospf></routing-protocol><routing-protocol><type>static</type><name>1</name></routing-protocol></routing-protocols></routing-instance></routing></data>'

なので xmltodict と json を使って、XML -> python dict -> JSON と変換して見ると内容が読みやすいです。

>>> import json, xmltodict
>>> print(json.dumps(xmltodict.parse(res), indent=2, sort_keys=True))
{
  "data": {
    "@xmlns": "urn:ietf:params:xml:ns:netconf:base:1.0",
    "@xmlns:nc": "urn:ietf:params:xml:ns:netconf:base:1.0",
    "routing": {
      "@xmlns": "urn:ietf:params:xml:ns:yang:ietf-routing",
      "routing-instance": {
        "description": "default-vrf [read-only]",
        "name": {
          "#text": "default",
          "@xmlns:nc": "urn:ietf:params:xml:ns:netconf:base:1.0"
        },
        "routing-protocols": {
          "routing-protocol": [
            {
              "name": "1",
              "ospf": {
                "@xmlns": "urn:ietf:params:xml:ns:yang:ietf-ospf",
                "instance": {
                  "af": {
                    "#text": "rt:ipv4",
                    "@xmlns:rt": "urn:ietf:params:xml:ns:yang:ietf-routing"
                  },
                  "auto-cost": {
                    "enable": "false"
                  },
                  "nsr": {
                    "enable": "false"
                  },
                  "redistribution": {
                    "@xmlns": "urn:ietf:params:xml:ns:yang:cisco-ospf",
                    "rip": null
                  }
                }
              },
              "type": {
                "#text": "ospf:ospfv2",
                "@xmlns:ospf": "urn:ietf:params:xml:ns:yang:ietf-ospf"
              }
            },
            {
              "name": "1",
              "type": "static"
            }
          ]
        }
      }
    }
  }
}

XPATH filter

>>> from lxml import etree
>>> ele_filter = etree.Element("{urn:ietf:params:xml:ns:netconf:base:1.0}filter",
...                            type="xpath",
...                            nsmap = {None: 'urn:ietf:params:xml:ns:yang:ietf-routing'},
...                            select="/routing/routing-instance[name='default']")
>>> dev.nc.get(filter=ele_filter).data_xml
[host 172.16.1.228 session-id 66] Requesting 'Get'
[host 172.16.1.228 session-id 66] Sending:

#307
<?xml version="1.0" encoding="UTF-8"?><nc:rpc xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="urn:uuid:cac034cd-0f2b-457f-9456-411a0fff192d"><nc:get><nc:filter xmlns="urn:ietf:params:xml:ns:yang:ietf-routing" type="xpath" select="/routing/routing-instance[name='default']"/></nc:get></nc:rpc>
##

[host 172.16.1.228 session-id 66] Received:
<?xml version="1.0" encoding="UTF-8"?>
<rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="urn:uuid:cac034cd-0f2b-457f-9456-411a0fff192d" xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0"><data><routing xmlns="urn:ietf:params:xml:ns:yang:ietf-routing"><routing-instance><name>default</name><description>default-vrf [read-only]</description><routing-protocols><routing-protocol><type xmlns:ospf="urn:ietf:params:xml:ns:yang:ietf-ospf">ospf:ospfv2</type><name>1</name><ospf xmlns="urn:ietf:params:xml:ns:yang:ietf-ospf"><instance><af xmlns:rt="urn:ietf:params:xml:ns:yang:ietf-routing">rt:ipv4</af><nsr><enable>false</enable></nsr><auto-cost><enable>false</enable></auto-cost><redistribution xmlns="urn:ietf:params:xml:ns:yang:cisco-ospf"><rip></rip></redistribution></instance></ospf></routing-protocol><routing-protocol><type>static</type><name>1</name></routing-protocol></routing-protocols></routing-instance></routing></data></rpc-reply>
'<?xml version="1.0" encoding="UTF-8"?><data xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0"><routing xmlns="urn:ietf:params:xml:ns:yang:ietf-routing"><routing-instance><name>default</name><description>default-vrf [read-only]</description><routing-protocols><routing-protocol><type xmlns:ospf="urn:ietf:params:xml:ns:yang:ietf-ospf">ospf:ospfv2</type><name>1</name><ospf xmlns="urn:ietf:params:xml:ns:yang:ietf-ospf"><instance><af xmlns:rt="urn:ietf:params:xml:ns:yang:ietf-routing">rt:ipv4</af><nsr><enable>false</enable></nsr><auto-cost><enable>false</enable></auto-cost><redistribution xmlns="urn:ietf:params:xml:ns:yang:cisco-ospf"><rip/></redistribution></instance></ospf></routing-protocol><routing-protocol><type>static</type><name>1</name></routing-protocol></routing-protocols></routing-instance></routing></data>'
>>>

get_config()

get_config() ではコンフィグ情報の全部、または一部を取得することができます。get() と同じくetree のオブジェクトを filter へ渡して取得します。

今回も詳細は割愛しますが、下記が subtree 及び XPATH を使った場合の例です。

subtree filter

>>> from lxml import etree
>>> ele_filter = etree.Element("{urn:ietf:params:xml:ns:netconf:base:1.0}filter", type="subtree")
>>> ele_native = etree.SubElement(ele_filter, "native",
...                               nsmap = {None: 'http://cisco.com/ns/yang/Cisco-IOS-XE-native'})
>>> dev.nc.get_config(source='running', filter=ele_filter).data_xml
[host 172.16.1.228 session-id 66] Requesting 'GetConfig'
[host 172.16.1.228 session-id 66] Sending:

#332
<?xml version="1.0" encoding="UTF-8"?><nc:rpc xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="urn:uuid:9649779f-3ef5-4081-b14b-ee4814326214"><nc:get-config><nc:source><nc:running/></nc:source><nc:filter type="subtree"><native xmlns="http://cisco.com/ns/yang/Cisco-IOS-XE-native"/></nc:filter></nc:get-config></nc:rpc>
##

[host 172.16.1.228 session-id 66] Received:
<?xml version="1.0" encoding="UTF-8"?>
<rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="urn:uuid:9649779f-3ef5-4081-b14b-ee4814326214" xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0"><data><native xmlns="http://cisco.com/ns/yang/Cisco-IOS-XE-native"><version>16.9</version><boot-start-marker/><boot-end-marker/><service><timestamps><debug><datetime><msec></msec></datetime></debug><log><datetime><msec/></datetime></log></timestamps><config/></service><platform><console xmlns="http://cisco.com/ns/yang/Cisco-IOS-XE-platform"><output>serial</output></console></platform><hostname>R1_xe</hostname><enable><secret><type>5</type><secret>$1$WcuL$mKlgNkpnwDJv06YOeHlb11</secret></secret></enable><username><name>admin</name><privilege>15</privilege><password><encryption>0</encryption><password>Cisc0123</password></password></username><username><name>cisco</name><password><encryption>0</encryption><password>Cisc0123</password></password></username><username><name>netconf</name><privilege>15</privilege><password><password>netconf123</password></password></username><username><name>rest</name><privilege>15</privilege><secret><encryption>5</encryption><secret>$1$bJ5k$dJs3xxmg4MlsktBgdm9FC/</secret></secret></username><vrf><definition><name>Mgmt-intf</name><address-family><ipv4></ipv4><ipv6></ipv6></address-family></definition></vrf><ip><admission><watch-list><expiry-time>0</expiry-time></watch-list></admission><domain><name>cisco.com</name></domain><forward-protocol><protocol>nd</protocol></forward-protocol><ssh><version>2</version></ssh><http xmlns="http://cisco.com/ns/yang/Cisco-IOS-XE-http"><server>false</server><secure-server>true</secure-server></http></ip><interface><GigabitEthernet><name>1</name><ip><address><dhcp></dhcp></address></ip><mop><enabled>false</enabled><sysid>false</sysid></mop><negotiation xmlns="http://cisco.com/ns/yang/Cisco-IOS-XE-ethernet"><auto>true</auto></negotiation></GigabitEthernet><GigabitEthernet><name>2</name><ip><address><primary><address>172.16.12.1</address><mask>255.255.255.0</mask></primary></address><ospf xmlns="http://cisco.com/ns/yang/Cisco-IOS-XE-ospf"><process-id><id>1</id><area>0</area></process-id></ospf></ip><mop><enabled>false</enabled><sysid>false</sysid></mop><negotiation xmlns="http://cisco.com/ns/yang/Cisco-IOS-XE-ethernet"><auto>true</auto></negotiation></GigabitEthernet><Loopback><name>0</name><ip><address><primary><address>10.1.1.1</address><mask>255.255.255.255</mask></primary></address><ospf xmlns="http://cisco.com/ns/yang/Cisco-IOS-XE-ospf"><process-id><id>1</id><area>0</area></process-id></ospf></ip></Loopback></interface><control-plane></control-plane><aaa><new-model xmlns="http://cisco.com/ns/yang/Cisco-IOS-XE-aaa"/><authentication xmlns="http://cisco.com/ns/yang/Cisco-IOS-XE-aaa"><login><name>default</name><a1><local/></a1></login></authentication><authorization xmlns="http://cisco.com/ns/yang/Cisco-IOS-XE-aaa"><exec><name>default</name><a1><local/></a1></exec></authorization><session-id xmlns="http://cisco.com/ns/yang/Cisco-IOS-XE-aaa">common</session-id></aaa><multilink><bundle-name xmlns="http://cisco.com/ns/yang/Cisco-IOS-XE-ppp">authenticated</bundle-name></multilink><redundancy></redundancy><spanning-tree><extend xmlns="http://cisco.com/ns/yang/Cisco-IOS-XE-spanning-tree"><system-id/></extend></spanning-tree><subscriber><templating/></subscriber><crypto><pki xmlns="http://cisco.com/ns/yang/Cisco-IOS-XE-crypto"><certificate><chain><name>TP-self-signed-1855185042</name><certificate><serial>01</serial><certtype>self-signed</certtype></certificate></chain></certificate><trustpoint><id>TP-self-signed-1855185042</id><enrollment><selfsigned/></enrollment><revocation-check>none</revocation-check><rsakeypair><key-label>TP-self-signed-1855185042</key-label></rsakeypair><subject-name>cn=IOS-Self-Signed-Certificate-1855185042</subject-name></trustpoint></pki></crypto><router><ospf xmlns="http://cisco.com/ns/yang/Cisco-IOS-XE-ospf"><id>1</id></ospf></router><license><udi><pid>CSR1000V</pid><sn>9ZFZVAFKW4L</sn></udi></license><line><console><first>0</first><exec-timeout><minutes>0</minutes><seconds>0</seconds></exec-timeout><stopbits>1</stopbits></console><vty><first>0</first><last>4</last><transport><input><input>telnet</input><input>ssh</input></input></transport></vty></line><ntp><server xmlns="http://cisco.com/ns/yang/Cisco-IOS-XE-ntp"><server-list><ip-address>172.16.1.250</ip-address></server-list><vrf><name>Mgmt-intf</name><server-list><ip-address>172.16.1.254</ip-address></server-list></vrf></server></ntp><diagnostic xmlns="http://cisco.com/ns/yang/Cisco-IOS-XE-diagnostics"><bootup><level>minimal</level></bootup></diagnostic></native></data></rpc-reply>
'<?xml version="1.0" encoding="UTF-8"?><data xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0"><native xmlns="http://cisco.com/ns/yang/Cisco-IOS-XE-native"><version>16.9</version><boot-start-marker/><boot-end-marker/><service><timestamps><debug><datetime><msec/></datetime></debug><log><datetime><msec/></datetime></log></timestamps><config/></service><platform><console xmlns="http://cisco.com/ns/yang/Cisco-IOS-XE-platform"><output>serial</output></console></platform><hostname>R1_xe</hostname><enable><secret><type>5</type><secret>$1$WcuL$mKlgNkpnwDJv06YOeHlb11</secret></secret></enable><username><name>admin</name><privilege>15</privilege><password><encryption>0</encryption><password>Cisc0123</password></password></username><username><name>cisco</name><password><encryption>0</encryption><password>Cisc0123</password></password></username><username><name>netconf</name><privilege>15</privilege><password><password>netconf123</password></password></username><username><name>rest</name><privilege>15</privilege><secret><encryption>5</encryption><secret>$1$bJ5k$dJs3xxmg4MlsktBgdm9FC/</secret></secret></username><vrf><definition><name>Mgmt-intf</name><address-family><ipv4/><ipv6/></address-family></definition></vrf><ip><admission><watch-list><expiry-time>0</expiry-time></watch-list></admission><domain><name>cisco.com</name></domain><forward-protocol><protocol>nd</protocol></forward-protocol><ssh><version>2</version></ssh><http xmlns="http://cisco.com/ns/yang/Cisco-IOS-XE-http"><server>false</server><secure-server>true</secure-server></http></ip><interface><GigabitEthernet><name>1</name><ip><address><dhcp/></address></ip><mop><enabled>false</enabled><sysid>false</sysid></mop><negotiation xmlns="http://cisco.com/ns/yang/Cisco-IOS-XE-ethernet"><auto>true</auto></negotiation></GigabitEthernet><GigabitEthernet><name>2</name><ip><address><primary><address>172.16.12.1</address><mask>255.255.255.0</mask></primary></address><ospf xmlns="http://cisco.com/ns/yang/Cisco-IOS-XE-ospf"><process-id><id>1</id><area>0</area></process-id></ospf></ip><mop><enabled>false</enabled><sysid>false</sysid></mop><negotiation xmlns="http://cisco.com/ns/yang/Cisco-IOS-XE-ethernet"><auto>true</auto></negotiation></GigabitEthernet><Loopback><name>0</name><ip><address><primary><address>10.1.1.1</address><mask>255.255.255.255</mask></primary></address><ospf xmlns="http://cisco.com/ns/yang/Cisco-IOS-XE-ospf"><process-id><id>1</id><area>0</area></process-id></ospf></ip></Loopback></interface><control-plane/><aaa><new-model xmlns="http://cisco.com/ns/yang/Cisco-IOS-XE-aaa"/><authentication xmlns="http://cisco.com/ns/yang/Cisco-IOS-XE-aaa"><login><name>default</name><a1><local/></a1></login></authentication><authorization xmlns="http://cisco.com/ns/yang/Cisco-IOS-XE-aaa"><exec><name>default</name><a1><local/></a1></exec></authorization><session-id xmlns="http://cisco.com/ns/yang/Cisco-IOS-XE-aaa">common</session-id></aaa><multilink><bundle-name xmlns="http://cisco.com/ns/yang/Cisco-IOS-XE-ppp">authenticated</bundle-name></multilink><redundancy/><spanning-tree><extend xmlns="http://cisco.com/ns/yang/Cisco-IOS-XE-spanning-tree"><system-id/></extend></spanning-tree><subscriber><templating/></subscriber><crypto><pki xmlns="http://cisco.com/ns/yang/Cisco-IOS-XE-crypto"><certificate><chain><name>TP-self-signed-1855185042</name><certificate><serial>01</serial><certtype>self-signed</certtype></certificate></chain></certificate><trustpoint><id>TP-self-signed-1855185042</id><enrollment><selfsigned/></enrollment><revocation-check>none</revocation-check><rsakeypair><key-label>TP-self-signed-1855185042</key-label></rsakeypair><subject-name>cn=IOS-Self-Signed-Certificate-1855185042</subject-name></trustpoint></pki></crypto><router><ospf xmlns="http://cisco.com/ns/yang/Cisco-IOS-XE-ospf"><id>1</id></ospf></router><license><udi><pid>CSR1000V</pid><sn>9ZFZVAFKW4L</sn></udi></license><line><console><first>0</first><exec-timeout><minutes>0</minutes><seconds>0</seconds></exec-timeout><stopbits>1</stopbits></console><vty><first>0</first><last>4</last><transport><input><input>telnet</input><input>ssh</input></input></transport></vty></line><ntp><server xmlns="http://cisco.com/ns/yang/Cisco-IOS-XE-ntp"><server-list><ip-address>172.16.1.250</ip-address></server-list><vrf><name>Mgmt-intf</name><server-list><ip-address>172.16.1.254</ip-address></server-list></vrf></server></ntp><diagnostic xmlns="http://cisco.com/ns/yang/Cisco-IOS-XE-diagnostics"><bootup><level>minimal</level></bootup></diagnostic></native></data>'
>>>
>>> from lxml import etree
>>> ele_filter = etree.Element("{urn:ietf:params:xml:ns:netconf:base:1.0}filter", type="subtree")
>>> ele_native = etree.SubElement(ele_filter, "native",
...                               nsmap = {None: 'http://cisco.com/ns/yang/Cisco-IOS-XE-native'})
>>> ele_aaa = etree.SubElement(ele_native, "aaa")
>>> dev.nc.get_config(source='running', filter=ele_filter).data_xml
[host 172.16.1.228 session-id 66] Requesting 'GetConfig'
[host 172.16.1.228 session-id 66] Sending:

#346
<?xml version="1.0" encoding="UTF-8"?><nc:rpc xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="urn:uuid:e05e257b-ff35-4f19-b4df-61dbd521bf68"><nc:get-config><nc:source><nc:running/></nc:source><nc:filter type="subtree"><native xmlns="http://cisco.com/ns/yang/Cisco-IOS-XE-native"><aaa/></native></nc:filter></nc:get-config></nc:rpc>
##

[host 172.16.1.228 session-id 66] Received:
<?xml version="1.0" encoding="UTF-8"?>
<rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="urn:uuid:e05e257b-ff35-4f19-b4df-61dbd521bf68" xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0"><data><native xmlns="http://cisco.com/ns/yang/Cisco-IOS-XE-native"><aaa><new-model xmlns="http://cisco.com/ns/yang/Cisco-IOS-XE-aaa"/><authentication xmlns="http://cisco.com/ns/yang/Cisco-IOS-XE-aaa"><login><name>default</name><a1><local/></a1></login></authentication><authorization xmlns="http://cisco.com/ns/yang/Cisco-IOS-XE-aaa"><exec><name>default</name><a1><local/></a1></exec></authorization><session-id xmlns="http://cisco.com/ns/yang/Cisco-IOS-XE-aaa">common</session-id></aaa></native></data></rpc-reply>
'<?xml version="1.0" encoding="UTF-8"?><data xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0"><native xmlns="http://cisco.com/ns/yang/Cisco-IOS-XE-native"><aaa><new-model xmlns="http://cisco.com/ns/yang/Cisco-IOS-XE-aaa"/><authentication xmlns="http://cisco.com/ns/yang/Cisco-IOS-XE-aaa"><login><name>default</name><a1><local/></a1></login></authentication><authorization xmlns="http://cisco.com/ns/yang/Cisco-IOS-XE-aaa"><exec><name>default</name><a1><local/></a1></exec></authorization><session-id xmlns="http://cisco.com/ns/yang/Cisco-IOS-XE-aaa">common</session-id></aaa></native></data>'
>>>

XPATH filter

>>> ele_filter = etree.Element("{urn:ietf:params:xml:ns:netconf:base:1.0}filter",
...                            type="xpath",
...                            nsmap = {None: 'urn:ietf:params:xml:ns:yang:ietf-interfaces'},
...                            select="/interfaces/interface[name='GigabitEthernet2']")
>>> dev.nc.get_config(source='running', filter=ele_filter).data_xml
[host 172.16.1.228 session-id 66] Requesting 'GetConfig'
[host 172.16.1.228 session-id 66] Sending:

#365
<?xml version="1.0" encoding="UTF-8"?><nc:rpc xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="urn:uuid:eccffd20-9931-49cc-9e75-11ad760b2e82"><nc:get-config><nc:source><nc:running/></nc:source><nc:filter xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces" type="xpath" select="/interfaces/interface[name='GigabitEthernet2']"/></nc:get-config></nc:rpc>
##

[host 172.16.1.228 session-id 66] Received:
<?xml version="1.0" encoding="UTF-8"?>
<rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="urn:uuid:eccffd20-9931-49cc-9e75-11ad760b2e82" xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0"><data><interfaces xmlns="http://openconfig.net/yang/interfaces"><interface><name>GigabitEthernet2</name><config><name>GigabitEthernet2</name><type xmlns:ianaift="urn:ietf:params:xml:ns:yang:iana-if-type">ianaift:ethernetCsmacd</type><enabled>true</enabled></config><subinterfaces><subinterface><index>0</index><config><index>0</index><enabled>true</enabled></config><ipv4 xmlns="http://openconfig.net/yang/interfaces/ip"><addresses><address><ip>172.16.12.1</ip><config><ip>172.16.12.1</ip><prefix-length>24</prefix-length></config></address></addresses></ipv4><ipv6 xmlns="http://openconfig.net/yang/interfaces/ip"><config><enabled>false</enabled></config></ipv6></subinterface></subinterfaces><ethernet xmlns="http://openconfig.net/yang/interfaces/ethernet"><config><mac-address>fa:16:3e:b3:6f:fe</mac-address><auto-negotiate>true</auto-negotiate></config></ethernet></interface></interfaces><interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces"><interface><name>GigabitEthernet2</name><type xmlns:ianaift="urn:ietf:params:xml:ns:yang:iana-if-type">ianaift:ethernetCsmacd</type><enabled>true</enabled><ipv4 xmlns="urn:ietf:params:xml:ns:yang:ietf-ip"><address><ip>172.16.12.1</ip><netmask>255.255.255.0</netmask></address></ipv4><ipv6 xmlns="urn:ietf:params:xml:ns:yang:ietf-ip"></ipv6></interface></interfaces></data></rpc-reply>
'<?xml version="1.0" encoding="UTF-8"?><data xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0"><interfaces xmlns="http://openconfig.net/yang/interfaces"><interface><name>GigabitEthernet2</name><config><name>GigabitEthernet2</name><type xmlns:ianaift="urn:ietf:params:xml:ns:yang:iana-if-type">ianaift:ethernetCsmacd</type><enabled>true</enabled></config><subinterfaces><subinterface><index>0</index><config><index>0</index><enabled>true</enabled></config><ipv4 xmlns="http://openconfig.net/yang/interfaces/ip"><addresses><address><ip>172.16.12.1</ip><config><ip>172.16.12.1</ip><prefix-length>24</prefix-length></config></address></addresses></ipv4><ipv6 xmlns="http://openconfig.net/yang/interfaces/ip"><config><enabled>false</enabled></config></ipv6></subinterface></subinterfaces><ethernet xmlns="http://openconfig.net/yang/interfaces/ethernet"><config><mac-address>fa:16:3e:b3:6f:fe</mac-address><auto-negotiate>true</auto-negotiate></config></ethernet></interface></interfaces><interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces"><interface><name>GigabitEthernet2</name><type xmlns:ianaift="urn:ietf:params:xml:ns:yang:iana-if-type">ianaift:ethernetCsmacd</type><enabled>true</enabled><ipv4 xmlns="urn:ietf:params:xml:ns:yang:ietf-ip"><address><ip>172.16.12.1</ip><netmask>255.255.255.0</netmask></address></ipv4><ipv6 xmlns="urn:ietf:params:xml:ns:yang:ietf-ip"/></interface></interfaces></data>'
>>>

filter 無し

また、filter を使わない場合はコンフィグ全体を取得する事になります。

>>> dev.get_config(source='running')
[host 172.16.1.228 session-id 70] Requesting 'GetConfig'
[host 172.16.1.228 session-id 70] Sending:

#232
<?xml version="1.0" encoding="UTF-8"?><nc:rpc xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="urn:uuid:b57983d0-52a8-4d95-b987-49353c1c5515"><nc:get-config><nc:source><nc:running/></nc:source></nc:get-config></nc:rpc>
##

[host 172.16.1.228 session-id 70] Received:
<?xml version="1.0" encoding="UTF-8"?>
<rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="urn:uuid:b57983d0-52a8-4d95-b987-49353c1c5515"
(snip)
static</type><name>1</name></routing-protocol></routing-protocols></routing-instance></routing></data></rpc-reply>

edit_config()

eidt_config() を使うとコンフィグの追加や削除ができます。この場合は下記のように YANG モデルに沿った XML を送ることで設定の変更が可能です。

RPC メッセージの <config>…</config> のみを送信するような形になります。

XML で設定追加

GigabitEthernet2 の description を追加する例です。

>>> config = """
...     <config xmlns:xc="urn:ietf:params:xml:ns:netconf:base:1.0">
...       <interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces">
...         <interface>
...           <name>GigabitEthernet2</name>
...           <description>description update by netconf</description>
...         </interface>
...       </interfaces>
...     </config>
...     """
>>> dev.nc.edit_config(target='running', config=config)
[host 172.16.1.228 session-id 66] Requesting 'EditConfig'
[host 172.16.1.228 session-id 66] Sending:

#495
<?xml version="1.0" encoding="UTF-8"?><nc:rpc xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="urn:uuid:71cfbb14-1601-4e9f-90fd-718367e86343"><nc:edit-config><nc:target><nc:running/></nc:target><config>
      <interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces">
        <interface>
          <name>GigabitEthernet2</name>
          <description>description update by netconf</description>
        </interface>
      </interfaces>
    </config></nc:edit-config></nc:rpc>
##

[host 172.16.1.228 session-id 66] Received:
<?xml version="1.0" encoding="UTF-8"?>
<rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="urn:uuid:71cfbb14-1601-4e9f-90fd-718367e86343" xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0"><ok/></rpc-reply>
<?xml version="1.0" encoding="UTF-8"?>
<rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="urn:uuid:71cfbb14-1601-4e9f-90fd-718367e86343" xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0"><ok/></rpc-reply>
>>>

subtree で設定追加

get()/get_config() と同じく subtree で etree オブジェクトを生成して同じ事ができます。XML での方が簡単ですね。

>>> from lxml import etree
>>> ele_config = etree.Element("config")
>>> ele_interfaces = etree.SubElement(ele_config, "interfaces",
...                                   nsmap = {None: 'urn:ietf:params:xml:ns:yang:ietf-interfaces'})
>>> ele_interface = etree.SubElement(ele_interfaces, "interface")
>>> ele_name = etree.SubElement(ele_interface, "name").text = 'GigabitEthernet2'
>>> ele_description = etree.SubElement(ele_interface, "description").text = 'description update by netconf'
>>> dev.nc.edit_config(target='running', config=ele_config)
[host 172.16.1.228 session-id 66] Requesting 'EditConfig'
[host 172.16.1.228 session-id 66] Sending:

#436
<?xml version="1.0" encoding="UTF-8"?><nc:rpc xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="urn:uuid:a918d1c2-a132-4876-a0e6-4e3a4a81189e"><nc:edit-config><nc:target><nc:running/></nc:target><config><interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces"><interface><name>GigabitEthernet2</name><description>description update by netconf</description></interface></interfaces></config></nc:edit-config></nc:rpc>
##

[host 172.16.1.228 session-id 66] Received:
<?xml version="1.0" encoding="UTF-8"?>
<rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="urn:uuid:a918d1c2-a132-4876-a0e6-4e3a4a81189e" xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0"><ok/></rpc-reply>
<?xml version="1.0" encoding="UTF-8"?>
<rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="urn:uuid:a918d1c2-a132-4876-a0e6-4e3a4a81189e" xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0"><ok/></rpc-reply>
>>>

XML で設定削除

GigabitEthernet2 のdescription を削除する例です。追加との違いは <description> の中に operation="delete" というのが追加されている点です。

>>> config = """
...     <config xmlns:xc="urn:ietf:params:xml:ns:netconf:base:1.0">
...       <interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces">
...         <interface>
...           <name>GigabitEthernet2</name>
...           <description xc:operation="delete"></description>
...         </interface>
...       </interfaces>
...     </config>
...     """
>>> dev.nc.edit_config(target='running', config=config)
[host 172.16.1.228 session-id 66] Requesting 'EditConfig'
[host 172.16.1.228 session-id 66] Sending:

#475
<?xml version="1.0" encoding="UTF-8"?><nc:rpc xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="urn:uuid:db7e6c91-885c-427f-9aab-53739e2340d4"><nc:edit-config><nc:target><nc:running/></nc:target><config>
      <interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces">
        <interface>
          <name>GigabitEthernet2</name>
          <description nc:operation="delete"/>
        </interface>
      </interfaces>
    </config></nc:edit-config></nc:rpc>
##

[host 172.16.1.228 session-id 66] Received:
<?xml version="1.0" encoding="UTF-8"?>
<rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="urn:uuid:db7e6c91-885c-427f-9aab-53739e2340d4" xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0"><ok/></rpc-reply>
<?xml version="1.0" encoding="UTF-8"?>
<rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="urn:uuid:db7e6c91-885c-427f-9aab-53739e2340d4" xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0"><ok/></rpc-reply>
>>>

request()

request() では RPCリクエストを送ります。この場合は NETCONF の操作 get/get-config/edit-config などは RPCリクエスト に含まれるため、情報の取得から設定の変更まで出来るものになります。汎用的なものということですね。

>>> rpc_request = """
...     <rpc message-id="101" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
...       <get-config>
...         <source>
...           <running/>
...         </source>
...         <filter type="subtree">
...           <interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces">
...             <interface>
...               <name>GigabitEthernet2</name>
...             </interface>
...           </interfaces>
...         </filter>
...       </get-config>
...     </rpc>
...     """
>>> reply = dev.nc.request(rpc_request, timeout=40)
Sending rpc...

    <rpc message-id="101" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
      <get-config>
        <source>
          <running/>
        </source>
        <filter type="subtree">
          <interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces">
            <interface>
              <name>GigabitEthernet2</name>
            </interface>
          </interfaces>
        </filter>
      </get-config>
    </rpc>

[host 172.16.1.228 session-id 66] Sending:

#428

    <rpc message-id="101" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
      <get-config>
        <source>
          <running/>
        </source>
        <filter type="subtree">
          <interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces">
            <interface>
              <name>GigabitEthernet2</name>
            </interface>
          </interfaces>
        </filter>
      </get-config>
    </rpc>

##

[host 172.16.1.228 session-id 66] Received:
<?xml version="1.0" encoding="UTF-8"?>
<rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="101"><data><interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces"><interface><name>GigabitEthernet2</name><description>description update by netconf</description><type xmlns:ianaift="urn:ietf:params:xml:ns:yang:iana-if-type">ianaift:ethernetCsmacd</type><enabled>true</enabled><ipv4 xmlns="urn:ietf:params:xml:ns:yang:ietf-ip"><address><ip>172.16.12.1</ip><netmask>255.255.255.0</netmask></address></ipv4><ipv6 xmlns="urn:ietf:params:xml:ns:yang:ietf-ip"></ipv6></interface></interfaces></data></rpc-reply>
Receiving rpc-reply after 0.159 sec...
<?xml version="1.0" encoding="UTF-8"?>
<rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="101"><data><interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces"><interface><name>GigabitEthernet2</name><description>description update by netconf</description><type xmlns:ianaift="urn:ietf:params:xml:ns:yang:iana-if-type">ianaift:ethernetCsmacd</type><enabled>true</enabled><ipv4 xmlns="urn:ietf:params:xml:ns:yang:ietf-ip"><address><ip>172.16.12.1</ip><netmask>255.255.255.0</netmask></address></ipv4><ipv6 xmlns="urn:ietf:params:xml:ns:yang:ietf-ip"></ipv6></interface></interfaces></data></rpc-reply>
>>> print(reply)
<?xml version="1.0" encoding="UTF-8"?>
<rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="101"><data><interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces"><interface><name>GigabitEthernet2</name><description>description update by netconf</description><type xmlns:ianaift="urn:ietf:params:xml:ns:yang:iana-if-type">ianaift:ethernetCsmacd</type><enabled>true</enabled><ipv4 xmlns="urn:ietf:params:xml:ns:yang:ietf-ip"><address><ip>172.16.12.1</ip><netmask>255.255.255.0</netmask></address></ipv4><ipv6 xmlns="urn:ietf:params:xml:ns:yang:ietf-ip"></ipv6></interface></interfaces></data></rpc-reply>
>>>

get_schema()

機器が RFC6022 に対応している場合、YANG モデルのスキーマを確認する事ができます。

>>> reply = dev.nc.get_schema('ietf-interfaces')
[host 172.16.1.228 session-id 66] Requesting 'GetSchema'
[host 172.16.1.228 session-id 66] Sending:

#310
<?xml version="1.0" encoding="UTF-8"?><nc:rpc xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="urn:uuid:f4e78130-10fc-4b84-8703-2277c978db8a"><ncm:get-schema xmlns:ncm="urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring"><ncm:identifier>ietf-interfaces</ncm:identifier></ncm:get-schema></nc:rpc>
##

[host 172.16.1.228 session-id 66] Received:
<?xml version="1.0" encoding="UTF-8"?>
<rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="urn:uuid:f4e78130-10fc-4b84-8703-2277c978db8a" xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0"><data xmlns='urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring'><![CDATA[module ietf-interfaces {

  namespace "urn:ietf:params:xml:ns:yang:ietf-interfaces";
  prefix if;

  import ietf-yang-types {
    prefix yang;
  }

  organization
    "IETF NETMOD (NETCONF Data Modeling Language) Working Group";

  contact
    "WG Web:   <http://tools.ietf.org/wg/netmod/>
     WG List:  <mailto:netmod@ietf.org>

     WG Chair: Thomas Nadeau
               <mailto:tnadeau@lucidvision.com>

     WG Chair: Juergen Schoenwaelder
               <mailto:j.schoenwaelder@jacobs-university.de>

     Editor:   Martin Bjorklund
               <mailto:mbj@tail-f.com>";

  description
    "This module contains a collection of YANG definitions for
     managing network interfaces.
(snip)
>>> print(reply.data)
(snip)

見ると分かるようにスキーマとは、GitHub で確認できる YANG ファイルの中身と同じですね。

参考:
https://github.com/YangModels/yang/blob/master/vendor/cisco/xe/1691/ietf-interfaces.yang

disconnect()

NETCONF で使っているトランスポートセッションをクローズします。

>>> dev.nc.connected
True
>>> dev.nc.disconnect()
>>> dev.nc.connected

close_session()

RPCメッセージでNETCONFセッションをクローズします。

>>> dev.nc.connected
True
>>> dev.nc.close_session()
[host 172.16.1.228 session-id 67] Requesting 'CloseSession'
[host 172.16.1.228 session-id 67] Sending:

#184
<?xml version="1.0" encoding="UTF-8"?><nc:rpc xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="urn:uuid:43c030c4-f5d0-40d8-9117-ef51619f4618"><nc:close-session/></nc:rpc>
##

[host 172.16.1.228 session-id 67] Received:
<?xml version="1.0" encoding="UTF-8"?>
<rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="urn:uuid:43c030c4-f5d0-40d8-9117-ef51619f4618" xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0"><ok/></rpc-reply>
<?xml version="1.0" encoding="UTF-8"?>
<rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="urn:uuid:43c030c4-f5d0-40d8-9117-ef51619f4618" xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0"><ok/></rpc-reply>
>>> dev.nc.connected
False

まとめ

  • NETCONFを使う場合はネットワーク機器上でNETCONF設定が必要
  • pyATS/Genie で使う場合は yang.connector をインストールする
  • yang.connector は ncclient を使っているので、使い方はほぼ同じ
  • testbed.yaml の NETCONF 用コネクション も SSH/Telnet と同じように書ける。但し、class で yang.connector を指定
  • pyATS/Genie では NETCONF だろうが device オブジェクトをベースに ssh/telnet と同じように操作できる。但し、メソッドは異なる。(device.get_config()など)
  • NETCONF のやり取りを行うには subtree/XPATH/XML表記などで行う事ができる
  • request() は RPCメッセージ をやり取りできる汎用的なメソッド。get/get-config/edit-config ができる

pyATS/Genie は CLI 出力を構造化データで処理できる Parser/Ops を備えており、CLI を使ってネットワークの自動化ができる事は知られていますが、NETCONF を使えるという事も知ってもらえたのではないでしょうか。勿論 REST もできます。Python なので Python でできる事なら、ほぼ何でもできます。w

pyATS/Genie なら古い機器や既存機器は CLI で自動化、新規ネットワーク(NETCONF対応)は NETCONF で自動化というのも可能になり、NETCONF / REST な自動化へ移行する過程も含めて幅広く自動化ができますね。

参考

スポンサーリンク