NETCONF/YANG を使って、Ciscoルータのコンフィグを設定する(IOS-XR)

#何がやりたい？
これまで、**IOS-XE** を対象としてNETCONFを使って設定できることを確認してきましたが、今回は、**IOS-XR** に対してNETCONFでルータを設定します。


##IOS-XR上でNETCONFを有効にする設定

```text:必須の設定
conf t
ssh server v2
ssh server netconf port 830
ssh timeout 120
netconf-yang agent ssh
commit
end

crypto key generate rsa
```

```text:必要に応じて
conf t
control-plane
 management-plane
  inband
   interface GigabitEthernet0/0/0/0
    allow all
commit
end
```
今回の構成では、***192.168.99.1***がルータのアドレス、ユーザー名/パスワードが***cisco/cisco***となります。

##running-configを取得
まずは、現状のルータ(**IOS-XR**)からコンフィグを取得することから確認します。
NETCONF経由で設定したいコンフィグについては、まず最初に、ルータ上でコマンドラインから従来通り設定して、ここで取得したrunning-configの結果を参照して、以降で紹介しているように、スクリプトの中に引用するという流れが便利かなと思います。

```python:get_running_001.py
#!/usr/bin/python

from ncclient import manager

# connect to netconf agent
m = manager.connect(host='192.168.99.1', port=830, username='cisco', password='cisco', hostkey_verify=False, device_params={'name': 'iosxr'})

#response = m.get_config(source='running', filter=payload)
print '###########################################################################'
print '### XML'
print '###########################################################################'
response = m.get_config(source='running').data_xml
print response
```

下記は、取得した結果を**JSON**で表示したいだけです。特に深い理由はありません。

```python:get_running_002.py
#!/usr/bin/python

from ncclient import manager
from xml.dom import minidom
import lxml.etree as ET
import xmltodict
import json

# connect to netconf agent
m = manager.connect(host='192.168.99.1', port=830, username='cisco', password='cisco', hostkey_verify=False, device_params={'name': 'iosxr'})

#response = m.get_config(source='running', filter=payload)
print '###########################################################################'
print '### XML'
print '###########################################################################'
response = m.get_config(source='running').data_xml
print response

print '###########################################################################'
print '### XML Formatstring'
print '###########################################################################'
data = ET.fromstring(response)
print(ET.tostring(data, pretty_print=True))

print '###########################################################################'
print '### Convert to Dictionary'
print '###########################################################################'
data_dict = xmltodict.parse(response)
print(data_dict)

print '###########################################################################'
print '### Convert to JSON'
print '###########################################################################'
data_jason = json.dumps(data_dict, indent=4)
print(data_jason)
```

##hostnameを変更する

###XMLを直書きした場合

`<config></config>`に挟まれた設定内容(hostnameのコンフィグ部分)は、先の**running-configを取得**した結果からコピー&ペーストで引用しています。

```python:edit_hostname.py
#!/usr/bin/env python

from ncclient import manager
from lxml import etree

config = """
<config>
    <host-names xmlns="http://cisco.com/ns/yang/Cisco-IOS-XR-shellutil-cfg">
        <host-name>CE01</host-name>
    </host-names>
</config>
"""

# connect to netconf agent
m = manager.connect(host='192.168.99.1', port=830, username='cisco', password='cisco', hostkey_verify=False, device_params={'name': 'default'}, look_for_keys=False, allow_agent=False)

print '======================================================================'
print '=====                          response                          ====='
print '======================================================================'
response = m.edit_config(config=config, default_operation="merge", target="candidate").xml
print response
response = m.commit()
print response
```


###ElementTreeモジュールを利用した場合

```python:edit_hostname_etree.py
#!/usr/bin/env python

from ncclient import manager
from lxml import etree
from xml.dom import minidom
import xmltodict
import json
import datetime

# build xml
config_e = etree.Element("config")
hostname = etree.SubElement(config_e, "host-names", nsmap = {None: 'http://cisco.com/ns/yang/Cisco-IOS-XR-shellutil-cfg'})
etree.SubElement(hostname, "host-name").text = 'CE01'

print '###########################################################################'
print '### XML'
print '###########################################################################'
print etree.tostring(config_e)

print '###########################################################################'
print '### XML Formatstring'
print '###########################################################################'
print etree.tostring(config_e, pretty_print=True)

print '###########################################################################'
print '### Convert to Dictionary'
print '###########################################################################'
data_dict = xmltodict.parse(etree.tostring(config_e))
print data_dict

print '###########################################################################'
print '### Convert to JSON'
print '###########################################################################'
data_jason = json.dumps(data_dict, indent=4)
print data_jason

# connect to netconf agent
m = manager.connect(host='192.168.99.1', port=830, username='cisco', password='cisco', hostkey_verify=False, device_params={'name': 'default'}, look_for_keys=False, allow_agent=False)

print '======================================================================'
print '=====                          response                          ====='
print '======================================================================'
response = m.edit_config(config=config_e, default_operation="merge", target="candidate")
print response
response = m.commit()
print response
```

参考までに、下記が実行結果になります。`XML Formatstring`の出力部分が`XMLを直書き`した場合と同じ内容であることが確認できます。

```text:実行結果
$ python edit_hostname_etree.py 
###########################################################################
### XML
###########################################################################
<config><host-names xmlns="http://cisco.com/ns/yang/Cisco-IOS-XR-shellutil-cfg"><host-name>CE01</host-name></host-names></config>
###########################################################################
### XML Formatstring
###########################################################################
<config>
  <host-names xmlns="http://cisco.com/ns/yang/Cisco-IOS-XR-shellutil-cfg">
    <host-name>CE01</host-name>
  </host-names>
</config>

###########################################################################
### Convert to Dictionary
###########################################################################
OrderedDict([(u'config', OrderedDict([(u'host-names', OrderedDict([(u'@xmlns', u'http://cisco.com/ns/yang/Cisco-IOS-XR-shellutil-cfg'), (u'host-name', u'CE01')]))]))])
###########################################################################
### Convert to JSON
###########################################################################
{
    "config": {
        "host-names": {
            "@xmlns": "http://cisco.com/ns/yang/Cisco-IOS-XR-shellutil-cfg", 
            "host-name": "CE01"
        }
    }
}
======================================================================
=====                          response                          =====
======================================================================
<?xml version="1.0"?>
<rpc-reply message-id="urn:uuid:3b59ff85-d686-4aa0-b0d9-8d7a952c09df" xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
 <ok/>
</rpc-reply>

<?xml version="1.0"?>
<rpc-reply message-id="urn:uuid:bc03f285-9b91-41b1-9bb2-1c86e6a31cd0" xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
 <ok/>
</rpc-reply>
```

##interfaceのdescriptionを変更する

###XMLを直書きした場合

```python:edit_interface-description.py
#!/usr/bin/env python

from ncclient import manager
from lxml import etree

config = """
<config>
    <interface-configurations xmlns="http://cisco.com/ns/yang/Cisco-IOS-XR-ifmgr-cfg">
        <interface-configuration>
            <active>act</active>
            <interface-name>GigabitEthernet0/0/0/0</interface-name>
            <description>NETCONF configured - 2019-03-12 00:00:00</description>
        </interface-configuration>
    </interface-configurations>
</config>
"""

# connect to netconf agent
m = manager.connect(host='192.168.99.1', port=830, username='cisco', password='cisco', hostkey_verify=False, device_params={'name': 'default'}, look_for_keys=False, allow_agent=False)

print '======================================================================'
print '=====                          response                          ====='
print '======================================================================'
response = m.edit_config(config=config, default_operation="merge", target="candidate").xml
print response
response = m.commit()
print response
```

###ElementTreeモジュールを利用した場合

```python:edit_interface-description_etree.py
#!/usr/bin/env python

from ncclient import manager
from lxml import etree

import datetime
from xml.dom import minidom
import xmltodict
import json

current_time = datetime.datetime.strftime(datetime.datetime.now(), '%Y-%m-%d %H:%M:%S')

# build xml
config_e = etree.Element("config")
interface01 = etree.SubElement(config_e, "interface-configurations", nsmap = {None: 'http://cisco.com/ns/yang/Cisco-IOS-XR-ifmgr-cfg'})
interface01_cfg = etree.SubElement(interface01, "interface-configuration")
etree.SubElement(interface01_cfg, "active").text = 'act'
etree.SubElement(interface01_cfg, "interface-name").text = 'GigabitEthernet0/0/0/10'
etree.SubElement(interface01_cfg, "description").text  = 'NETCONF configured - ' + current_time
interface02 = etree.SubElement(config_e, "interface-configurations", nsmap = {None: 'http://cisco.com/ns/yang/Cisco-IOS-XR-ifmgr-cfg'})
interface02_cfg = etree.SubElement(interface01, "interface-configuration")
etree.SubElement(interface02_cfg, "active").text = 'act'
etree.SubElement(interface02_cfg, "interface-name").text = 'GigabitEthernet0/0/0/11'
etree.SubElement(interface02_cfg, "description").text  = 'NETCONF configured - ' + current_time

print '###########################################################################'
print '### XML'
print '###########################################################################'
print etree.tostring(config_e)

print '###########################################################################'
print '### XML Formatstring'
print '###########################################################################'
print etree.tostring(config_e, pretty_print=True)

print '###########################################################################'
print '### Convert to Dictionary'
print '###########################################################################'
data_dict = xmltodict.parse(etree.tostring(config_e))
print data_dict

print '###########################################################################'
print '### Convert to JSON'
print '###########################################################################'
data_jason = json.dumps(data_dict, indent=4)
print data_jason

# connect to netconf agent
m = manager.connect(host='192.168.99.1', port=830, username='cisco', password='cisco', hostkey_verify=False, device_params={'name': 'default'}, look_for_keys=False, allow_agent=False)

print '======================================================================'
print '=====                          response                          ====='
print '======================================================================'
response = m.edit_config(config=config_e, default_operation="merge", target="candidate")
print response
response = m.commit()
print response
```

参考までに、下記が実行結果から`XML Formatstring`の出力部分を抽出しています。

```text:実行結果
###########################################################################
### XML Formatstring
###########################################################################
<config>
  <interface-configurations xmlns="http://cisco.com/ns/yang/Cisco-IOS-XR-ifmgr-cfg">
    <interface-configuration>
      <active>act</active>
      <interface-name>GigabitEthernet0/0/0/10</interface-name>
      <description>NETCONF configured - 2019-03-18 11:18:27</description>
    </interface-configuration>
    <interface-configuration>
      <active>act</active>
      <interface-name>GigabitEthernet0/0/0/11</interface-name>
      <description>NETCONF configured - 2019-03-18 11:18:27</description>
    </interface-configuration>
  </interface-configurations>
  <interface-configurations xmlns="http://cisco.com/ns/yang/Cisco-IOS-XR-ifmgr-cfg"/>
</config>
```


##interfaceに`shutdown`を設定する

###XMLを直書きした場合

```python:sample.py
#!/usr/bin/env python

from ncclient import manager
from lxml import etree

config = """
<config>
    <interface-configurations xmlns="http://cisco.com/ns/yang/Cisco-IOS-XR-ifmgr-cfg">
        <interface-configuration>
            <active>act</active>
            <interface-name>GigabitEthernet0/0/0/10</interface-name>
            <shutdown/>
            <description>NETCONF configured - shutdown</description>
       </interface-configuration>
    </interface-configurations>
    <interface-configurations xmlns="http://cisco.com/ns/yang/Cisco-IOS-XR-ifmgr-cfg">
        <interface-configuration>
            <active>act</active>
            <interface-name>GigabitEthernet0/0/0/11</interface-name>
            <shutdown/>
            <description>NETCONF configured - shutdown</description>
       </interface-configuration>
    </interface-configurations>
</config>
"""

# connect to netconf agent
m = manager.connect(host='192.168.99.1', port=830, username='cisco', password='cisco', hostkey_verify=False, device_params={'name': 'default'}, look_for_keys=False, allow_agent=False)

print '======================================================================'
print '=====                          response                          ====='
print '======================================================================'
response = m.edit_config(config=config, default_operation="merge", target="candidate").xml
print response
response = m.commit()
print response
```

##interfaceに`no shut`を設定する

###XMLを直書きした場合

```python:sample.py
#!/usr/bin/env python

from ncclient import manager
from lxml import etree

config = """
<config>
    <interface-configurations xmlns="http://cisco.com/ns/yang/Cisco-IOS-XR-ifmgr-cfg">
        <interface-configuration>
            <active>act</active>
            <interface-name>GigabitEthernet0/0/0/10</interface-name>
            <shutdown nc:operation="remove"></shutdown>
            <description>NETCONF configured - no shutdown</description>
       </interface-configuration>
    </interface-configurations>
    <interface-configurations xmlns="http://cisco.com/ns/yang/Cisco-IOS-XR-ifmgr-cfg">
        <interface-configuration>
            <active>act</active>
            <interface-name>GigabitEthernet0/0/0/11</interface-name>
            <shutdown nc:operation="remove"></shutdown>
            <description>NETCONF configured - no shutdown</description>
       </interface-configuration>
    </interface-configurations>
</config>
"""

# connect to netconf agent
m = manager.connect(host='192.168.99.1', port=830, username='cisco', password='cisco', hostkey_verify=False, device_params={'name': 'default'}, look_for_keys=False, allow_agent=False)

print '======================================================================'
print '=====                          response                          ====='
print '======================================================================'
response = m.edit_config(config=config, default_operation="merge", target="candidate").xml
print response
response = m.commit()
print response
```