ncclient模塊


Ncclient簡介

Ncclient是一個開源的Python模塊,用來在NETCONF客戶端開發各種和NETCONF相關的網絡運維腳本和應用程序。截止2020年10月最新的版本為0.6.9,對系統環境有如下要求:

Python 2.7 或 Python 3.4+
setuptools 0.6+
Paramiko 1.7+
lxml 3.3.0+
libxml2
libxslt

安裝ncclient

pip3 install ncclient -i https://mirrors.aliyun.com/pypi/simple

如果報錯可以嘗試先更新pip
pip3 install --upgrade pip

H3C交換機

開啟NETCONF

H3C配置netconfig,參考官方文檔http://www.h3c.com/cn/d_201907/1218555_30005_0.htm

NETCONF支持標准協議規范中描述的NETCONF over SSH功能,使能該功能后,用戶可以通過使用支持NETCONF over SSH登錄方式的客戶端配置工具給設備下發NETCONF指令來實現對設備的訪問。

system-view
netconf ssh server enable #缺省情況下,NETCONF over SSH功能處於關閉狀態
netconf ssh server port #缺省情況下,NETCONF over SSH使用端口830

配置NETCONF日志功能

NETCONF日志功能可以記錄操作源對設備進行的NETCONF操作,用戶通過指定NETCONF操作源和NETCONF操作類型,配置系統輸出指定類型的NETCONF日志。

system-view
netconf log source all protocol-operation all #缺省情況下,未配置NETCONF日志功能

1、導入模塊
manager模塊用於通過netconf協議連接設備

>>> from ncclient import manager
>>> from lxml import etree

2、連接設備
通過manager對象下的connect()函數連接網絡設備
device_params用來規定設備的類型和操作系統,比如思科包括iosxe, csr, iosxr, nxos等,這里我們登錄的是一台h3c交換機

Juniper: device_params={'name':'junos'}
Cisco CSR: device_params={‘name':'csr'}
Cisco Nexus: device_params={‘name':'nexus'}
Huawei: device_params={‘name':'huawei'}
H3C: device_params={‘name':'h3c'}

timeout超時時間,默認60秒,如果設備的running config配置過大,通過get_config獲取設備所有配置時可能會超時,可以將timeout改大一點

conn = manager.connect(host='x.x.x.x',
port=830,
username='***',
password='***',
device_params={'name':'h3c'},
timeout=300,
hostkey_verify=False,
look_for_keys=False)

 

通過ssh協議連接設備時會先保存對端的key,並從本機查找驗證,這里使用hostkey_verify=False和look_for_keys=False來跳過檢查

manager.py中包含了如下方法

OPERATIONS = {
"get": operations.Get,
"get_config": operations.GetConfig,
"get_schema": operations.GetSchema,
"dispatch": operations.Dispatch,
"edit_config": operations.EditConfig,
"copy_config": operations.CopyConfig,
"validate": operations.Validate,
"commit": operations.Commit,
"discard_changes": operations.DiscardChanges,
"cancel_commit": operations.CancelCommit,
"delete_config": operations.DeleteConfig,
"lock": operations.Lock,
"unlock": operations.Unlock,
"create_subscription": operations.CreateSubscription,
"close_session": operations.CloseSession,
"kill_session": operations.KillSession,
"poweroff_machine": operations.PoweroffMachine,
"reboot_machine": operations.RebootMachine,
"rpc": operations.GenericRPC,
}

 

class Get(RPC):
    def request(self, filter=None, with_defaults=None):

檢索運行配置和設備狀態信息。查詢的是設備當前運行的狀態數據,即只能從配置數據庫中獲取數據。
不需要使用source參數指定配置數據庫。
操作成功,Server回復的元素中含有參數,中封裝了獲取的結果數據。否則在消息中返回。

class GetConfig(RPC):
    def request(self, source, filter=None, with_defaults=None):

source:指定需要查詢的數據庫名稱。有running(正在運行的數據庫),startup(下次設備啟動配置數據庫),candidate(兩階段運行數據庫,需要commit提交生效)
fileter:過濾器。過濾器需要是一個集合,該集合內包含類型和xml內容,A tuple of (type, criteria),這里的type必須是xpath或者subtree。

def edit_config(config, default_operation=None, test_option=None, error_option=None):

將指定配置的全部或部分加載到目標配置數據存儲中。
target:指定要配置的數據庫。
config:必須放在元素中, 它可以指定為字符串或Element
default_operation: 如果指定,必須是{ “merge”, “replace”, or “none” } 其中之一。
test_option: { “test_then_set”, “set” } 之一。
error_option: { “stop-on-error”, “continue-on-error”, “rollback-on-error” } 之一。

edit_config:編輯配置內容,參數為要下發的xml內容,必須以config為根,可以是xml字符串,也可以是通過ElementMaker生成的xml類

 

class EditConfig(RPC):
    "`edit-config` RPC"

    def request(self, config, format='xml', target='candidate', 
    default_operation=None,test_option=None, error_option=None):
        """Loads all or part of the specified *config* to the *target* configuration datastore.
        *target* is the name of the configuration datastore being edited
        *config* is the configuration, which must be rooted in the `config` element. It can be specified either as a string or an :class:`~xml.etree.ElementTree.Element`.
        *default_operation* if specified must be one of { `"merge"`, `"replace"`, or `"none"` }
        *test_option* if specified must be one of { `"test-then-set"`, `"set"`, `"test-only"` }
        *error_option* if specified must be one of { `"stop-on-error"`, `"continue-on-error"`, `"rollback-on-error"` }
        The `"rollback-on-error"` *error_option* depends on the `:rollback-on-error` capability.
        """
。。。        

 

例子1、獲取交換機running config所有配置

conn = manager.connect(host='x.x.x.x',
                        port=830,
                        username='***',
                        password='***',
                        device_params={'name':'h3c'},
                        timeout=300,
                        hostkey_verify=False,
                        look_for_keys=False)

def running_config_get():
    # 獲取running所有配置
    conn = netconf_conn()
    ret = conn.get_config(source='running')
    return ret

running_config = running_config_get()
print(running_config)

例子2、獲取所有接口信息,包括配置和狀態,指定需要顯示的字段名稱

conn = manager.connect(host='x.x.x.x',
                        port=830,
                        username='***',
                        password='***',
                        device_params={'name':'h3c'},
                        timeout=300,
                        hostkey_verify=False,
                        look_for_keys=False)

1、構建xml文件

get_all_iface = """
<top xmlns="http://www.h3c.com/netconf/data:1.0">
    <Ifmgr>
        <Interfaces>
            <Interface>
                <Name></Name>
                <InetAddressIPV4></InetAddressIPV4>
                    <InetAddressIPV4Mask></InetAddressIPV4Mask>
                <AdminStatus></AdminStatus>
            </Interface>
        </Interfaces>
    </Ifmgr>
</top>
"""

2、執行 XML

ret = conn.get(('subtree',get_all_iface))  #指定了一個過濾器,類型是集合
print(ret)

上面代碼中使用了 ncclient 封裝的get操作,我們只需要傳入Content層的XML信息即可

執行結果如下,只截取了部分內容:

<?xml version="1.0" encoding="utf-8"?>
<rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="urn:uuid:664ee1ba-5d45-41d7-a96f-9e88f7c68e5e">
<data>
<top xmlns="http://www.h3c.com/netconf/data:1.0">
<Ifmgr>
<Interfaces>
<Interface><IfIndex>1</IfIndex><Name>Ten-GigabitEthernet1/0/1</Name><AdminStatus>1</AdminStatus></Interface>
<Interface><IfIndex>2</IfIndex><Name>Ten-GigabitEthernet1/0/2</Name><AdminStatus>1</AdminStatus></Interface>
<Interface><IfIndex>2148</IfIndex><Name>NULL0</Name><AdminStatus>1</AdminStatus></Interface>
<Interface><IfIndex>2215</IfIndex><Name>LoopBack0</Name><AdminStatus>1</AdminStatus><InetAddressIPV4>10.x.x.x</InetAddressIPV4><InetAddressIPV4Mask>255.255.255.192</InetAddressIPV4Mask></Interface>
<Interface><IfIndex>2233</IfIndex><Name>Vlan-interface2999</Name><AdminStatus>1</AdminStatus></Interface>
</Interfaces>
</Ifmgr>
</top>
</data>
</rpc-reply>

 

例子3、獲取指定接口的配置和狀態信息,不指定具體參數名稱,可以查詢到該接口的所有信息

conn = manager.connect(host='x.x.x.x',
                        port=830,
                        username='***',
                        password='***',
                        device_params={'name':'h3c'},
                        timeout=300,
                        hostkey_verify=False,
                        look_for_keys=False)

def ifcfg_get(index_num):
    # 獲取指定接口的配置信息
    get_ifcfg_xml = """
<top xmlns="http://www.h3c.com/netconf/data:1.0">
<Ifmgr>
<Interfaces>
<Interface>
<IfIndex>%s</IfIndex>
</Interface>
</Interfaces>
</Ifmgr>
</top>
        """ % index_num
    conn = netconf_conn()
    ifcfg_ret = conn.get(('subtree', get_ifcfg_xml))
    return ifcfg_ret

ifcfg = ifcfg_get('2233')  #接口的索引值
print(ifcfg)

結果

<?xml version="1.0" encoding="UTF-8"?>
<rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="urn:uuid:544d5d6a-b8aa-4676-a55e-9babcb0683c4">
    <data>
     <top xmlns="http://www.h3c.com/netconf/data:1.0">
    <Ifmgr>
      <Interfaces>
        <Interface>
        <IfIndex>2233</IfIndex>
        <Name>Vlan-interface2999</Name>
        <AbbreviatedName>Vlan2999</AbbreviatedName>
        <ifTypeExt>41</ifTypeExt>
        <ifType>136</ifType>
        <Description>netconf</Description>
        <AdminStatus>1</AdminStatus>
        <OperStatus>1</OperStatus>
        <PortLayer>2</PortLayer>
        <InetAddressIPV4>10.1.1.1</InetAddressIPV4>
        <InetAddressIPV4Mask>255.255.255.0</InetAddressIPV4Mask>
        <MAC>30-B0-37-BC-68-52</MAC>
        <ForwardingAttributes>17</ForwardingAttributes>
        <ConfigMTU>1500</ConfigMTU>
        <ActualMTU>1500</ActualMTU>
        <ActualBandwidth>100000000</ActualBandwidth>
        <SubPort>false</SubPort>
        <Interval>300</Interval>
        <Actual64Bandwidth>100000000</Actual64Bandwidth>
        </Interface>
      </Interfaces>
    </Ifmgr>
    </top>
  </data>
</rpc-reply>

獲取所有acl信息

def acl_all_get():
    #獲取所有acl信息
    get_all_acl_xml = """
    <top xmlns="http://www.h3c.com/netconf/data:1.0">
        <ACL>
            <IPv4BasicRules>
                <Rule>
                    <GroupID></GroupID>
                    <RuleID></RuleID>
                    <Action></Action>
                    <SrcAny></SrcAny>
                    <SrcIPv4>
                        <SrcIPv4Addr></SrcIPv4Addr>
                        <SrcIPv4Wildcard></SrcIPv4Wildcard>
                    </SrcIPv4>
                    <Fragment></Fragment>
                    <Counting></Counting>
                    <Logging></Logging>
                    <Status></Status>
                    <Count></Count>
                </Rule>
            </IPv4BasicRules>
        </ACL>
    </top>
    """
    conn = netconf_conn()
    ifcft_ret = conn.get(('subtree', get_all_acl_xml))
    return ifcft_ret

acl_all = acl_all_get()
print(acl_all)

結果:

<?xml version="1.0" encoding="utf-8"?>

<rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="urn:uuid:2d0d709f-8a7c-409e-b567-93ad6717dbad">
  <data>
    <top xmlns="http://www.h3c.com/netconf/data:1.0">
      <ACL>
        <IPv4BasicRules>
          <Rule>
            <GroupID>2999</GroupID>
            <RuleID>0</RuleID>
            <Action>1</Action>
            <SrcAny>false</SrcAny>
            <SrcIPv4>
              <SrcIPv4Addr>1.1.1.1</SrcIPv4Addr>
              <SrcIPv4Wildcard>0.0.0.0</SrcIPv4Wildcard>
            </SrcIPv4>
            <Fragment>false</Fragment>
            <Counting>false</Counting>
            <Logging>false</Logging>
            <Status>1</Status>
            <Count>0</Count>
          </Rule>
          <Rule>
            <GroupID>2999</GroupID>
            <RuleID>5</RuleID>
            <Action>1</Action>
            <SrcAny>false</SrcAny>
            <SrcIPv4>
              <SrcIPv4Addr>1.1.1.2</SrcIPv4Addr>
              <SrcIPv4Wildcard>0.0.0.0</SrcIPv4Wildcard>
            </SrcIPv4>
            <Fragment>false</Fragment>
            <Counting>false</Counting>
            <Logging>false</Logging>
            <Status>1</Status>
            <Count>0</Count>
          </Rule>
        </IPv4BasicRules>
      </ACL>
    </top>
  </data>
</rpc-reply>

 

例子4:獲取指定acl的配置信息,可以指定想要獲取的參數

conn = manager.connect(host='x.x.x.x',
                        port=830,
                        username='***',
                        password='***',
                        device_params={'name':'h3c'},
                        timeout=300,
                        hostkey_verify=False,
                        look_for_keys=False)


def acl_get(acl_number):
    #獲取指定acl信息
    get_acl_xml = """
    <top xmlns="http://www.h3c.com/netconf/data:1.0">
        <ACL>
            <IPv4BasicRules>
                <Rule>
                    <GroupID>%s</GroupID>
                    <Action></Action>
                    <SrcAny></SrcAny>
                    <SrcIPv4>
                        <SrcIPv4Addr></SrcIPv4Addr>
                        <SrcIPv4Wildcard></SrcIPv4Wildcard>
                    </SrcIPv4>
                </Rule>
            </IPv4BasicRules>
        </ACL>
    </top>
    """%acl_number
    conn = netconf_conn()
    ifcft_ret = conn.get(('subtree', get_acl_xml))
    return ifcft_ret

acl = acl_get('2999')
print(acl)

結果:

<?xml version="1.0" encoding="utf-8"?>

<rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="urn:uuid:84b6e836-051a-451a-8449-7171af5dd403">  
  <data>
    <top xmlns="http://www.h3c.com/netconf/data:1.0">  
      <ACL>
        <IPv4BasicRules> 
          <Rule> 
            <GroupID>2999</GroupID>  
            <RuleID>0</RuleID>  
            <Action>1</Action>  
            <SrcAny>false</SrcAny>  
            <SrcIPv4> 
              <SrcIPv4Addr>1.1.1.1</SrcIPv4Addr>  
              <SrcIPv4Wildcard>0.0.0.0</SrcIPv4Wildcard> 
            </SrcIPv4> 
          </Rule>  
          <Rule> 
            <GroupID>2999</GroupID>  
            <RuleID>5</RuleID>
            <Action>1</Action>
            <SrcAny>false</SrcAny>
            <SrcIPv4>
              <SrcIPv4Addr>1.1.1.2</SrcIPv4Addr>
              <SrcIPv4Wildcard>0.0.0.0</SrcIPv4Wildcard>
            </SrcIPv4>
          </Rule>
          <Rule>
            <GroupID>2999</GroupID>
            <RuleID>6</RuleID>
            <Action>2</Action>
            <SrcAny>false</SrcAny>
            <SrcIPv4>
              <SrcIPv4Addr>1.1.1.3</SrcIPv4Addr>
              <SrcIPv4Wildcard>0.0.0.0</SrcIPv4Wildcard>
            </SrcIPv4>
          </Rule>
        </IPv4BasicRules>
      </ACL> 
    </top>
  </data> 
</rpc-reply>

方式二:通過ElementMaker構建xml

def netconf_conn():
    conn = manager.connect(host='10.255.64.3',
                           port=830,
                           username='cecloud',
                           password='b200.fsp.cecloud.com',
                           device_params={'name': 'h3c'},
                           timeout=300,
                           hostkey_verify=False,
                           look_for_keys=False
                           )
    return conn


BASE_NS_1_0 = "urn:ietf:params:xml:ns:netconf:base:1:0"
H3C_DATA_1_0 = "http://www.h3c.com/netconf/data:1.0"
H3C_CONFIG_1_0 = "http://www.h3c.com/netconf/config:1.0"
C = ElementMaker(namespace=BASE_NS_1_0, nsmap={None: BASE_NS_1_0})
E_data = ElementMaker(namespace=H3C_DATA_1_0, nsmap={None: H3C_DATA_1_0})
E_config = ElementMaker(namespace=H3C_CONFIG_1_0, nsmap={None: H3C_CONFIG_1_0})

def acl_get(acl_number):
    #獲取指定acl信息
    acl_etree = E_data.top(
        E_data.ACL(
            E_data.IPv4BasicRules(
                E_data.Rule(
                    E_data.GroupID(acl_number),
                    E_data.Action(),
                    E_data.SrcIPv4(
                        E_data.SrcIPv4Addr(),
                        E_data.SrcIPv4Wildcard(),
                    )
                )
            )
        )
    )


    conn = netconf_conn()
    # ifcft_ret = conn.get(('subtree', get_acl_xml))
    ifcft_ret = conn.get(('subtree',acl_etree))
    return ifcft_ret

acl_get_ret = acl_get('2999')
print(acl_get_ret)

結果:

<?xml version="1.0" encoding="UTF-8"?>
<rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="urn:uuid:c4719bd6-5b17-4c28-af4c-a71e1c7196a8">
<data>
<top xmlns="http://www.h3c.com/netconf/data:1.0">
<ACL>
<IPv4BasicRules>
<Rule>
<GroupID>2999</GroupID>
<RuleID>0</RuleID>
<Action>1</Action>
<SrcAny>false</SrcAny>
<SrcIPv4><SrcIPv4Addr>1.1.1.1</SrcIPv4Addr>
<SrcIPv4Wildcard>0.0.0.0</SrcIPv4Wildcard>
</SrcIPv4>
</Rule>
<Rule>
<GroupID>2999</GroupID>
<RuleID>5</RuleID>
<Action>1</Action>
<SrcAny>false</SrcAny>
<SrcIPv4>
<SrcIPv4Addr>1.1.1.2</SrcIPv4Addr>
<SrcIPv4Wildcard>0.0.0.0</SrcIPv4Wildcard>
</SrcIPv4></Rule>
<Rule>
<GroupID>2999</GroupID>
<RuleID>6</RuleID>
<Action>2</Action>
<SrcAny>false</SrcAny>
<SrcIPv4>
<SrcIPv4Addr>1.1.1.3</SrcIPv4Addr>
<SrcIPv4Wildcard>0.0.0.0</SrcIPv4Wildcard>
</SrcIPv4>
</Rule>
<Rule>
<GroupID>2999</GroupID>
<RuleID>10</RuleID>
<Action>1</Action>
<SrcAny>false</SrcAny>
<SrcIPv4>
<SrcIPv4Addr>1.1.1.10</SrcIPv4Addr>
<SrcIPv4Wildcard>0.0.0.0</SrcIPv4Wildcard>
</SrcIPv4>
</Rule>
<Rule>
<GroupID>2999</GroupID>
<RuleID>11</RuleID>
<Action>1</Action>
<SrcAny>false</SrcAny>
<SrcIPv4>
<SrcIPv4Addr>1.1.1.11</SrcIPv4Addr>
<SrcIPv4Wildcard>0.0.0.0</SrcIPv4Wildcard>
</SrcIPv4>
</Rule>
<Rule>
<GroupID>2999</GroupID>
<RuleID>13</RuleID>
<Action>1</Action><
SrcAny>false</SrcAny>
<SrcIPv4>
<SrcIPv4Addr>1.1.1.13</SrcIPv4Addr>
<SrcIPv4Wildcard>0.0.0.0</SrcIPv4Wildcard>
</SrcIPv4>
</Rule>
</IPv4BasicRules>
</ACL>
</top>
</data>
</rpc-reply> print(type(acl_get_ret)) <class 'ncclient.operations.retrieve.GetReply'>

配置acl 2999

在acl2999中添加一條 rule 10 deny source 1.1.1.10 0

def acl_edit(acl_number,rule_id,ip,wildcard):
    # 編輯指定acl的配置
    acl_edit_xml = """
    <config>
        <top xmlns="http://www.h3c.com/netconf/config:1.0">
            <ACL>
                <IPv4NamedBasicRules>
                  <Rule>
                    <GroupIndex>%s</GroupIndex>
                    <RuleID>%s</RuleID>
                    <Action>1</Action>
                    <SrcAny>false</SrcAny>
                    <SrcIPv4>
                      <SrcIPv4Addr>%s</SrcIPv4Addr>
                      <SrcIPv4Wildcard>%s</SrcIPv4Wildcard>
                    </SrcIPv4>
                  </Rule>
                </IPv4NamedBasicRules>
            </ACL>
        </top>
    </config>
    """%(acl_number,rule_id,ip,wildcard)

    conn = netconf_conn()
    try:
        ifcft_ret = conn.edit_config(acl_edit_xml,target='running')
        return ifcft_ret
    except Exception as e:
        return(e)


acl_config = acl_edit('2999','10','1.1.1.10','0.0.0.0')
print(acl_config)

結果:

<?xml version="1.0" encoding="UTF-8"?>
<rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="urn:uuid:4259ba18-4c57-4f2b-aa99-1726a1268658"><ok/>
</rpc-reply>
def check_response(rpc_obj, snippet_name):
    #檢查執行結果是否成功
    xml_str = rpc_obj.xml
    if "<ok/>" in xml_str:
        print("%s successful" % snippet_name)
    else:
        print("%s failed!!" % snippet_name)

acl_config_res = check_response(acl_config,'ACL')
print(acl_config_res)

結果:

ACL successful

配置vlan2999接口

from lxml import etree
def ifcfg_edit(vlan_id,desc,ip,mask):
    ifcfg_edit_xml = '''
    <config>
        <top xmlns="http://www.h3c.com/netconf/config:1.0">
            <VLAN>
                <VLANs>
                    <VLANID>
                    <ID>%s</ID>
                    <Description>%s</Description>
                    <Ipv4>
                        <Ipv4Address>%s</Ipv4Address>
                        <Ipv4Mask>%s</Ipv4Mask>
                    </Ipv4>
                    </VLANID>
                </VLANs>
            </VLAN>
        </top>
    </config>
'''%(vlan_id,desc,ip,mask)
    try:
        with manager.connect(**h3c_host) as m:
            ifcft_ret = m.edit_config(ifcfg_edit_xml, target='running')
            return ifcft_ret
    except Exception as e:
        return (e)

def check_response(rpc_obj, snippet_name):
    xml_str = rpc_obj.xml
    if "<ok/>" in xml_str:
        print("%s successful" % snippet_name)
    else:
        print("%s failed!!" % snippet_name)


ifcfg_config = ifcfg_edit('2999','10.1.1.2','255.255.255.192')
print(ifcfg_config)

acl_config_res = check_response(ifcfg_config,'Ifcfg')

結果:

<?xml version="1.0" encoding="UTF-8"?>
<rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="urn:uuid:15d06834-d6cd-4972-bcc4-76456f77d9af"><ok/>
</
rpc-reply> Ifcfg successful

查看設備上的配置變化

#
vlan 2999
 description netconf
#
<ASW>dis cu int vl 2999
#
interface Vlan-interface2999
 ip address 10.1.1.2 255.255.255.192
#
return

 

設備連接的另一種方式

h3c_host = {
    'host': '10.255.1.1',
    'port': 830,
    'username': 'user123',
    'password': 'password123',
    'device_params': {'name': 'h3c'},
    'timeout': 300,
    'hostkey_verify': False,
    'look_for_keys': False
}

try:
    with manager.connect(**h3c_host) as m:
        ifcft_ret = m.edit_config(acl_edit_xml,target='running')
        return ifcft_ret
except Exception as e:
    return(e)

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM