From 1b9c295e7fa4f8a05dec87252f8857fba8478509 Mon Sep 17 00:00:00 2001 From: "A. Wilcox" Date: Tue, 8 Dec 2020 23:20:50 -0600 Subject: ietf-ip: Initial enablement of write support Here be dragons, for now. --- ncserver/module/ip.py | 206 ++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 198 insertions(+), 8 deletions(-) (limited to 'ncserver/module/ip.py') diff --git a/ncserver/module/ip.py b/ncserver/module/ip.py index cd4cde0..8329c81 100644 --- a/ncserver/module/ip.py +++ b/ncserver/module/ip.py @@ -13,9 +13,10 @@ SPDX-License-Identifier: NCSA import logging from lxml.etree import QName # pylint: disable=E0611 -from netconf import util +from netconf import error, util -from ncserver.base.util import _ +from ncserver.base.log import log_config_change +from ncserver.base.util import _, node_operation from ncserver.util import get_nmsa @@ -99,18 +100,25 @@ def _add_ipv4(iface, getter): def _add_ipv6(iface, getter): """Add IPv6 configuration nodes.""" name = iface.find('{'+IF_NS+'}name').text + if getter(name, 'ipv6_enabled') is None: + return None # Unset means we don't have IPv6 config at all. + 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')))) + + forwarding = getter(name, 'ipv6_forwarding') + if forwarding is not None: + ipv6.append(util.leaf_elm('ip:forwarding', from_bool(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')) - ) + + dad_xmit = getter(name, 'ipv6_dad_xmit') + if dad_xmit is not None: + ipv6.append(util.leaf_elm('ip:dup-addr-detect-transmits', dad_xmit)) + if any((getter(name, 'ipv6_slaac_globaladdr'), getter(name, 'ipv6_slaac_tempaddr'))): autoconf = util.subelm(ipv6, 'ip:autoconf') @@ -159,6 +167,9 @@ def running(node): # IPv6 ipv6 = _add_ipv6(iface, nmsa.get_param) + if not ipv6: + continue + for address in nmsa.list_addresses(name): # Only IPv6 addesses count. if ':' not in address: @@ -189,5 +200,184 @@ def operational(node): _add_ipv6(iface, nmsa.curr_param) +def _edit_param(iface: str, param: str, operation: str, rpc, node): + """Edit an NMSA-controlled parameter.""" + nmsa = get_nmsa() + value = nmsa.get_param(iface, param) + + if operation == 'create' and value is not None: + raise error.DataExistsAppError(rpc) + if operation == 'delete' and value is None: + raise error.DataMissingAppError(rpc) + + if operation in ('delete', 'remove'): + nmsa.unset_param(iface, param) + return + + if operation not in ('create', 'merge', 'replace'): + raise error.OperationNotSupportedAppError(rpc) + + if node.text in ('true', 'false'): + nmsa.set_param(iface, param, node.text == 'true') + else: + nmsa.set_param(iface, param, node.text) + + +def _clear_ipv4(iface: str): + """Remove all IPv4 configuration from a given interface.""" + nmsa = get_nmsa() + + nmsa.set_param(iface, 'ipv4_enabled', False) + nmsa.unset_param(iface, 'ipv4_forwarding') + nmsa.unset_param(iface, 'ipv4_mtu') + + +def _edit_ipv4(session, rpc, node, def_op, iface: str): + """Edit IPv4 configuration for a given interface.""" + _params = {'enabled': 'ipv4_enabled', 'forwarding': 'ipv4_forwarding', + 'mtu': 'ipv4_mtu'} + + operation = node_operation(node, def_op) + if operation in ('delete', 'remove'): + log_config_change(session, "[ietf-ip %s]" % iface, + "removing IPv4 configuration") + + _clear_ipv4(iface) + return + + if operation not in ('create', 'merge', 'replace'): + raise error.OperationNotSupportedAppError(rpc) + + for xparam in node: + operation = node_operation(xparam, operation) + qparam = QName(xparam.tag) + if qparam.localname in _params.keys(): + param = _params[qparam.localname] + log_config_change(session, "[ietf-ip %s]" % iface, + "IPv4 %s: -> %s" % (param, node.text)) + _edit_param(iface, param, operation, rpc, node) + elif qparam.localname == 'address': + # Oh no. + raise NotImplementedError + elif qparam.localname == 'neighbor': + # Oh *no*! + raise NotImplementedError + else: + raise error.UnknownElementAppError(rpc, xparam) + + +def _clear_ipv6(iface: str): + """Remove all IPv6 configuration from a given interface.""" + nmsa = get_nmsa() + + nmsa.set_param(iface, 'ipv6_enabled', False) + for param in ('ipv6_forwarding', 'ipv6_mtu', 'ipv6_dad_xmit', + 'ipv6_slaac_globaladdr', 'ipv6_slaac_tempaddr', + 'ipv6_slaac_validlft', 'ipv6_slaac_preflft'): + nmsa.unset_param(iface, param) + + +def _clear_slaac(iface: str): + """Remove all SLAAC configuration from a given interface.""" + nmsa = get_nmsa() + + for param in ('ipv6_slaac_globaladdr', 'ipv6_slaac_tempaddr', + 'ipv6_slaac_validlft', 'ipv6_slaac_preflft'): + nmsa.unset_param(iface, param) + + +def _edit_slaac(session, rpc, node, def_op, iface: str): + """Edit SLAAC configuration for a given interface.""" + _params = {'create-global-addresses': 'ipv6_slaac_globaladdr', + 'create-temporary-addresses': 'ipv6_slaac_tempaddr', + 'temporary-valid-lifetime': 'ipv6_slaac_validlft', + 'temporary-preferred-lifetime': 'ipv6_slaac_preflft'} + + operation = node_operation(node, def_op) + if operation in ('delete', 'remove'): + log_config_change(session, "[ietf-ip %s]" % iface, + "removing IPv6 SLAAC configuration") + _clear_slaac(iface) + return + + if operation not in ('create', 'merge', 'replace'): + raise error.OperationNotSupportedAppError(rpc) + + for param in node: + p_name = QName(param.tag).localname + if p_name not in _params.keys(): + raise error.UnknownElementAppError(rpc, param) + + p_op = node_operation(param, operation) + _edit_param(iface, _params[p_name], p_op, rpc, param) + + +def _edit_ipv6(session, rpc, node, def_op, iface: str): + """Edit IPv6 configuration for a given interface.""" + _params = {'enabled': 'ipv6_enabled', 'forwarding': 'ipv6_forwarding', + 'mtu': 'ipv6_mtu', 'dup-addr-detect-transmits': 'ipv6_dad_xmit'} + + operation = node_operation(node, def_op) + if operation in ('delete', 'remove'): + log_config_change(session, "[ietf-ip %s]" % iface, + "removing IPv6 configuration") + + _clear_ipv6(iface) + return + + if operation not in ('create', 'merge', 'replace'): + raise error.OperationNotSupportedAppError(rpc) + + for xparam in node: + p_op = node_operation(xparam, operation) + qparam = QName(xparam.tag) + if qparam.localname in _params.keys(): + param = _params[qparam.localname] + log_config_change(session, "[ietf-ip %s]" % iface, + "IPv6 %s: -> %s" % (param, node.text)) + _edit_param(iface, param, p_op, rpc, node) + elif qparam.localname == 'address': + # Oh no. + raise NotImplementedError + elif qparam.localname == 'neighbor': + # Oh *no*! + raise NotImplementedError + elif qparam.localname == 'autoconf': + # Configure SLAAC parameters. + _edit_slaac(session, rpc, node, def_op, iface) + else: + raise error.UnknownElementAppError(rpc, xparam) + + def edit(session, rpc, node, def_op): """Edit the IP configuration for this device.""" + methods = {'ipv4': _edit_ipv4, 'ipv6': _edit_ipv6} + + nmsa = get_nmsa() + if nmsa is None: + raise error.OperationNotSupportedAppError(rpc) + + for interface in node: + if QName(interface.tag).localname != 'interface' or\ + QName(interface.tag).namespace != IF_NS: + continue # Ignore unknown tags given to us. + + name_node = interface.find('{'+IF_NS+'}name') + if name_node is None: + raise error.MissingElementAppError(rpc, interface) + iface = name_node.text + + operation = node_operation(interface, def_op) + if operation in ('delete', 'remove'): + # ietf-interfaces already removed the configuration for us. + continue + + for conf in interface: + if QName(conf.tag).namespace == M_NS: + name = QName(conf.tag).localname + if name in methods: + methods[name](session, rpc, conf, operation, iface) + else: + raise error.UnknownElementAppError(rpc, conf) + else: + continue -- cgit v1.2.3-60-g2f50