Python library for NETCONF clients with support for SSH, TLS, and device-specific operations
—
Tools for creating, parsing, and manipulating XML documents and NETCONF payloads. NCClient provides a comprehensive XML toolkit with namespace support, NETCONF-specific helpers, and efficient parsing capabilities.
Functions for creating XML elements and building XML documents programmatically.
def new_ele(tag, attrs=None, **extra):
"""
Create new XML element.
Parameters:
- tag: str, element tag name (may include namespace)
- attrs: dict, element attributes
- **extra: additional attributes as keyword arguments
Returns:
Element: New XML element
"""
def new_ele_ns(tag, ns, attrs=None, **extra):
"""
Create new XML element with explicit namespace.
Parameters:
- tag: str, element tag name
- ns: str, namespace URI
- attrs: dict, element attributes
- **extra: additional attributes as keyword arguments
Returns:
Element: New namespaced XML element
"""
def sub_ele(parent, tag, attrs=None, **extra):
"""
Create XML sub-element under parent.
Parameters:
- parent: Element, parent element
- tag: str, sub-element tag name
- attrs: dict, element attributes
- **extra: additional attributes as keyword arguments
Returns:
Element: New sub-element
"""
def sub_ele_ns(parent, tag, ns, attrs=None, **extra):
"""
Create namespaced XML sub-element under parent.
Parameters:
- parent: Element, parent element
- tag: str, sub-element tag name
- ns: str, namespace URI
- attrs: dict, element attributes
- **extra: additional attributes as keyword arguments
Returns:
Element: New namespaced sub-element
"""Functions for parsing XML documents and converting between formats.
def parse_root(raw):
"""
Parse XML document and return root element.
Parameters:
- raw: str or bytes, XML document content
Returns:
Element: Root element of parsed document
"""
def to_xml(ele, encoding="UTF-8", pretty_print=False):
"""
Convert XML element to string representation.
Parameters:
- ele: Element, XML element to convert
- encoding: str, character encoding (default UTF-8)
- pretty_print: bool, format with indentation
Returns:
str: XML string representation
"""
def to_ele(x):
"""
Convert various inputs to XML element.
Parameters:
- x: str, bytes, file-like object, or Element
Returns:
Element: XML element
"""Helper functions for XML validation and manipulation.
def validate_xml(xml_string, schema=None):
"""
Validate XML against schema.
Parameters:
- xml_string: str, XML content to validate
- schema: Schema object, validation schema (optional)
Returns:
bool: True if valid, False otherwise
"""
def unqualify(tag):
"""
Remove namespace qualification from tag name.
Parameters:
- tag: str, qualified tag name
Returns:
str: Unqualified tag name
"""
def qualify(tag, ns=None):
"""
Add namespace qualification to tag name.
Parameters:
- tag: str, tag name to qualify
- ns: str, namespace URI
Returns:
str: Qualified tag name
"""Parser objects for handling different XML processing requirements.
# Standard XML parser
parser = etree.XMLParser(recover=False)
# Parser with huge tree support for large documents
huge_parser = etree.XMLParser(recover=False, huge_tree=True)
def _get_parser(huge_tree=False):
"""
Get appropriate parser based on requirements.
Parameters:
- huge_tree: bool, whether to use huge tree support
Returns:
XMLParser: Configured XML parser
"""Predefined namespace URIs for common NETCONF and network management schemas.
# Base NETCONF namespace (RFC 6241)
BASE_NS_1_0 = "urn:ietf:params:xml:ns:netconf:base:1.0"
# YANG namespace (RFC 6020/7950)
YANG_NS_1_0 = "urn:ietf:params:xml:ns:yang:1"
# Cisco NXOS namespaces
NXOS_1_0 = "http://www.cisco.com/nxos:1.0"
NXOS_IF = "http://www.cisco.com/nxos:1.0:if_manager"
CISCO_CPI_1_0 = "http://www.cisco.com/cpi_10/schema"
# Juniper namespace
JUNIPER_1_1 = "http://xml.juniper.net/xnm/1.1/xnm"
# Huawei namespaces
HUAWEI_NS = "http://www.huawei.com/netconf/vrp"
HW_PRIVATE_NS = "http://www.huawei.com/netconf/capability/base/1.0"
# H3C namespaces
H3C_DATA_1_0 = "http://www.h3c.com/netconf/data:1.0"
H3C_CONFIG_1_0 = "http://www.h3c.com/netconf/config:1.0"
H3C_ACTION_1_0 = "http://www.h3c.com/netconf/action:1.0"
# Nokia/Alcatel-Lucent namespaces
ALU_CONFIG = "urn:alcatel-lucent.com:sros:ns:yang:conf-r13"
SROS_GLOBAL_OPS_NS = "urn:nokia.com:sros:ns:yang:sr:oper-global"
# NETCONF standard namespaces
NETCONF_MONITORING_NS = "urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring"
NETCONF_NOTIFICATION_NS = "urn:ietf:params:xml:ns:netconf:notification:1.0"
NETCONF_WITH_DEFAULTS_NS = "urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults"
# Tail-f namespaces
TAILF_AAA_1_1 = "http://tail-f.com/ns/aaa/1.1"
TAILF_EXECD_1_1 = "http://tail-f.com/ns/execd/1.1"
# Flowmon namespace
FLOWMON_1_0 = "http://www.liberouter.org/ns/netopeer/flowmon/1.0"Exception classes for XML processing errors.
class XMLError(NCClientError):
"""Exception for XML processing errors."""
def __init__(self, message, xml_content=None):
"""
Initialize XML error.
Parameters:
- message: str, error description
- xml_content: str, XML content that caused error (optional)
"""Specialized functions for NETCONF-specific XML operations.
def build_filter(spec, filter_type="subtree"):
"""
Build NETCONF filter element.
Parameters:
- spec: str or tuple, filter specification
- filter_type: str, filter type ('subtree' or 'xpath')
Returns:
Element: NETCONF filter element
"""
def build_rpc(operation, *args, **kwargs):
"""
Build NETCONF RPC element.
Parameters:
- operation: str, RPC operation name
- *args: positional arguments for operation
- **kwargs: keyword arguments for operation
Returns:
Element: NETCONF RPC element
"""
def wrap_rpc_reply(data):
"""
Wrap data in NETCONF RPC reply envelope.
Parameters:
- data: Element, reply data content
Returns:
Element: NETCONF rpc-reply element
"""from ncclient.xml_ import new_ele, sub_ele, to_xml
# Create root element
config = new_ele("config")
# Add sub-elements
interface = sub_ele(config, "interface")
sub_ele(interface, "name").text = "eth0"
sub_ele(interface, "description").text = "Management Interface"
# Convert to XML string
xml_string = to_xml(config, pretty_print=True)
print(xml_string)from ncclient.xml_ import new_ele_ns, sub_ele_ns, BASE_NS_1_0
# Create namespaced elements
rpc = new_ele_ns("rpc", BASE_NS_1_0, {"message-id": "101"})
get_config = sub_ele_ns(rpc, "get-config", BASE_NS_1_0)
source = sub_ele_ns(get_config, "source", BASE_NS_1_0)
sub_ele_ns(source, "running", BASE_NS_1_0)
print(to_xml(rpc, pretty_print=True))from ncclient.xml_ import parse_root, to_ele
# Parse XML from string
xml_data = '''
<rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
<data>
<interfaces>
<interface>
<name>eth0</name>
<type>ethernet</type>
</interface>
</interfaces>
</data>
</rpc-reply>
'''
# Parse and extract data
root = parse_root(xml_data)
data = root.find(".//{urn:ietf:params:xml:ns:netconf:base:1.0}data")
# Work with parsed elements
interfaces = data.find(".//interfaces")
for interface in interfaces.findall(".//interface"):
name = interface.find("name").text
iface_type = interface.find("type").text
print(f"Interface: {name}, Type: {iface_type}")from ncclient.xml_ import build_filter, new_ele, sub_ele
# Subtree filter
filter_spec = '''
<interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces">
<interface>
<name/>
<type/>
<admin-status/>
</interface>
</interfaces>
'''
# Build filter element
filter_elem = build_filter(filter_spec, "subtree")
# XPath filter
xpath_filter = "/interfaces/interface[name='eth0']"
xpath_elem = build_filter(xpath_filter, "xpath")from ncclient.xml_ import parse_root, new_ele, sub_ele, to_xml
def extract_interface_config(config_xml):
"""Extract interface configuration from NETCONF data."""
root = parse_root(config_xml)
interfaces = []
# Find all interface elements
for interface in root.findall(".//interface"):
interface_data = {
'name': interface.find("name").text if interface.find("name") is not None else None,
'description': interface.find("description").text if interface.find("description") is not None else None,
'admin_status': interface.find("admin-status").text if interface.find("admin-status") is not None else None
}
interfaces.append(interface_data)
return interfaces
def build_interface_config(interface_data):
"""Build interface configuration XML."""
config = new_ele("config")
interfaces = sub_ele(config, "interfaces")
for iface in interface_data:
interface = sub_ele(interfaces, "interface")
sub_ele(interface, "name").text = iface['name']
if iface.get('description'):
sub_ele(interface, "description").text = iface['description']
if iface.get('admin_status'):
sub_ele(interface, "admin-status").text = iface['admin_status']
return to_xml(config, pretty_print=True)
# Usage
config_data = [
{'name': 'eth0', 'description': 'Management', 'admin_status': 'up'},
{'name': 'eth1', 'description': 'Data', 'admin_status': 'up'}
]
xml_config = build_interface_config(config_data)
print(xml_config)from ncclient.xml_ import parse_root, XMLError
def safe_parse_xml(xml_string):
"""Safely parse XML with error handling."""
try:
root = parse_root(xml_string)
return root
except XMLError as e:
print(f"XML parsing error: {e}")
return None
except Exception as e:
print(f"Unexpected error: {e}")
return None
# Parse potentially malformed XML
malformed_xml = "<config><interface><name>eth0</interface></config>" # Missing closing tag
result = safe_parse_xml(malformed_xml)
if result is None:
print("Failed to parse XML")from ncclient.xml_ import _get_parser, etree
def parse_large_xml(xml_data):
"""Parse large XML documents with huge tree support."""
parser = _get_parser(huge_tree=True)
try:
root = etree.fromstring(xml_data, parser)
return root
except etree.XMLSyntaxError as e:
print(f"XML syntax error: {e}")
return None
# Process large configuration files
large_xml_data = open('large_config.xml', 'rb').read()
root = parse_large_xml(large_xml_data)
if root is not None:
print(f"Parsed large XML with {len(root)} child elements")Install with Tessl CLI
npx tessl i tessl/pypi-ncclient