From b3ca4b49ef72724a6fc1eb13453cb7aee9960756 Mon Sep 17 00:00:00 2001 From: "A. Wilcox" Date: Wed, 2 Dec 2020 20:36:24 -0600 Subject: ietf-ip: Add WIP module --- ncserver/module/ip.py | 207 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 207 insertions(+) create mode 100644 ncserver/module/ip.py diff --git a/ncserver/module/ip.py b/ncserver/module/ip.py new file mode 100644 index 0000000..065a4d4 --- /dev/null +++ b/ncserver/module/ip.py @@ -0,0 +1,207 @@ +""" +NETCONF for APK Distributions server: + ietf-ip module + +Copyright © 2020 Adélie Software in the Public Benefit, Inc. + +Released under the terms of the NCSA license. See the LICENSE file included +with this source distribution for more information. + +SPDX-License-Identifier: NCSA +""" + +import logging + +from lxml import etree +from netconf import util + +from ncserver.base.modman import MODMAN +from ncserver.base.util import _ + + +QName = etree.QName # pylint: disable=I1101 + + +LOGGER = logging.getLogger(__name__) +"""The object used for logging informational messages.""" + + +M_ABI_VERSION = 1 +"""The ABI version of this NETCONF module.""" + + +M_PREFIX = "ip" +"""The XML tag prefix for this module's tags.""" + + +M_NS = "urn:ietf:params:xml:ns:yang:ietf-ip" +"""The XML namespace for this module.""" + + +M_NAME = "ietf-ip" +"""The YANG model name for this module.""" + + +M_REVISION = "2018-02-22" +"""The YANG revision date for this module.""" + + +M_IMPORTS = { + 'ietf-interfaces@2018-02-20': { + 'ns': "urn:ietf:params:xml:ns:yang:ietf-interfaces", 'prefix': "if" + }, + 'iana-inet-types@2013-07-15': { + 'ns': "urn:ietf:params:xml:ns:yang:iana-inet-types", 'prefix': "inet" + }, + 'ietf-yang-types@2013-07-15': { + 'ns': "urn:ietf:params:xml:ns:yang:ietf-yang-types", 'prefix': "yang" + } +} +"""The imported YANG modules for this module.""" + + +M_FEATURES = ('ipv6-privacy-autoconf',) +"""The supported features declared in YANG for this module.""" + + +IF_NS = "urn:ietf:params:xml:ns:yang:ietf-interfaces" +"""The namespace of the ietf-interfaces module.""" + + +M_AUGMENTS = (IF_NS,) +"""The namespaces that this YANG module augments.""" + + +def _get_ifaces(node): + """Retrieve the /if:interfaces node.""" + ifaces = node.find('{'+IF_NS+'}interfaces') + if ifaces is None: + LOGGER.error(_("interfaces node not found: " + "This module requires ietf-interfaces to be loaded")) + + return ifaces + + +def _get_nmsa(): + """Retrieve our NMSA module handle.""" + nmsa_ns = "http://netconf.adelielinux.org/ns/netmgmt" + nmsa = MODMAN._module_for_ns(nmsa_ns) # pylint: disable=W0212 + + return nmsa + + +def from_bool(value: bool) -> str: + """Turn a Python bool into an XML bool.""" + return str(value).lower() + + +def _add_ipv4(iface, getter): + """Add IPv4 configuration nodes.""" + name = iface.find('{'+IF_NS+'}name').text + ipv4 = util.subelm(iface, 'ip:ipv4') + ipv4.append(util.leaf_elm('ip:enabled', + from_bool(getter(name, 'ipv4_enabled')))) + ipv4.append(util.leaf_elm('ip:forwarding', + from_bool(getter(name, 'ipv4_forwarding')))) + v4mtu = getter(name, 'ipv4_mtu') + if v4mtu is not None: + ipv4.append(util.leaf_elm('ip:mtu', v4mtu)) + + return ipv4 + + +def _add_ipv6(iface, getter): + """Add IPv6 configuration nodes.""" + name = iface.find('{'+IF_NS+'}name').text + ipv6 = util.subelm(iface, 'ip:ipv6') + ipv6.append(util.leaf_elm('ip:enabled', + from_bool(getter(name, 'ipv6_enabled')))) + ipv6.append(util.leaf_elm('ip:forwarding', + from_bool(getter(name, 'ipv6_forwarding')))) + v6mtu = getter(name, 'ipv6_mtu') + if v6mtu is not None: + ipv6.append(util.leaf_elm('ip:mtu', v6mtu)) + ipv6.append( + util.leaf_elm('ip:dup-addr-detect-transmits', + getter(name, 'ipv6_dad_xmit')) + ) + if any((getter(name, 'ipv6_slaac_globaladdr'), + getter(name, 'ipv6_slaac_tempaddr'))): + autoconf = util.subelm(ipv6, 'ip:autoconf') + autoconf.append(util.leaf_elm('ip:create-global-addresses', + from_bool( + getter(name, 'ipv6_slaac_globaladdr') + ))) + autoconf.append(util.leaf_elm('ip:create-temporary-addresses', + from_bool( + getter(name, 'ipv6_slaac_tempaddr') + ))) + autoconf.append(util.leaf_elm('ip:temporary-valid-lifetime', + getter(name, 'ipv6_slaac_validlft'))) + autoconf.append(util.leaf_elm('ip:temporary-preferred-lifetime', + getter(name, 'ipv6_slaac_preflft'))) + + return ipv6 + + +def running(node): + """Retrieve the IP configuration for this device.""" + ifaces = _get_ifaces(node) + nmsa = _get_nmsa() + + if None in (ifaces, nmsa): + # We can't retrieve configuration unless both the ietf-interfaces and + # the NMSA module is loaded. + return + + for iface in ifaces.iterchildren(): + name = iface.find('{'+IF_NS+'}name').text + + # IPv4 + ipv4 = _add_ipv4(iface, nmsa.get_param) + for address in nmsa.list_addresses(name): + # Only IPv4 addresses count. + if '.' not in address: + continue + + ipaddr, subnet = address.split('/') + + addr = util.subelm(ipv4, 'ip:address') + addr.append(util.leaf_elm('ip:ip', ipaddr)) + addr.append(util.leaf_elm('ip:prefix-length', subnet)) + # No neighbor support. + + # IPv6 + ipv6 = _add_ipv6(iface, nmsa.get_param) + for address in nmsa.list_addresses(name): + # Only IPv6 addesses count. + if ':' not in address: + continue + + ipaddr, length = address.split('/') + + addr = util.subelm(ipv6, 'ip:address') + addr.append(util.leaf_elm('ip:ip', ipaddr)) + addr.append(util.leaf_elm('ip:prefix-length', length)) + # No neighbor support. + + +def operational(node): + """Retrieve the IP state for this device.""" + ifaces = _get_ifaces(node) + nmsa = _get_nmsa() + + if None in (ifaces, nmsa): + # We can't retrieve configuration unless both the ietf-interfaces and + # the NMSA module is loaded. + return + + for iface in ifaces.iterchildren(): + # IPv4 + _add_ipv4(iface, nmsa.curr_param) + # IPv6 + _add_ipv6(iface, nmsa.curr_param) + + +def edit(session, rpc, node, def_op): + """Edit the IP configuration for this device.""" -- cgit v1.2.3-60-g2f50