diff options
-rw-r--r-- | interface-definitions/protocols-igmp.xml.in | 61 | ||||
-rw-r--r-- | interface-definitions/protocols-pim.xml.in | 84 | ||||
-rw-r--r-- | op-mode-definitions/show-ip-igmp.xml | 48 | ||||
-rw-r--r-- | op-mode-definitions/show-ip-multicast.xml | 12 | ||||
-rw-r--r-- | op-mode-definitions/show-ip-pim.xml | 72 | ||||
-rw-r--r-- | python/vyos/ifconfig/control.py | 17 | ||||
-rw-r--r-- | python/vyos/ifconfig/ethernet.py | 29 | ||||
-rw-r--r-- | python/vyos/ifconfig/interface.py | 16 | ||||
-rw-r--r-- | python/vyos/ifconfig/wireless.py | 3 | ||||
-rwxr-xr-x | src/conf_mode/interfaces-wireless.py | 69 | ||||
-rwxr-xr-x | src/conf_mode/protocols_igmp.py | 130 | ||||
-rwxr-xr-x | src/conf_mode/protocols_pim.py | 175 | ||||
-rwxr-xr-x | src/helpers/vyos-merge-config.py | 5 |
13 files changed, 660 insertions, 61 deletions
diff --git a/interface-definitions/protocols-igmp.xml.in b/interface-definitions/protocols-igmp.xml.in new file mode 100644 index 000000000..a6a2f1420 --- /dev/null +++ b/interface-definitions/protocols-igmp.xml.in @@ -0,0 +1,61 @@ +<?xml version="1.0"?> +<!-- Internet Group Management Protocol (IGMP) configuration --> +<interfaceDefinition> + <node name="protocols"> + <children> + <node name="igmp" owner="${vyos_conf_scripts_dir}/protocols_igmp.py"> + <properties> + <help>Internet Group Management Protocol (IGMP)</help> + </properties> + <children> + <tagNode name="interface"> + <properties> + <help>IGMP interface</help> + <completionHelp> + <script>${vyos_completion_dir}/list_interfaces.py</script> + </completionHelp> + </properties> + <children> + <leafNode name="version"> + <properties> + <help>IGMP version</help> + <valueHelp> + <format>2-3</format> + <description>IGMP version</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 2-3"/> + </constraint> + </properties> + </leafNode> + <leafNode name="query-interval"> + <properties> + <help>IGMP host query interval</help> + <valueHelp> + <format>1-1800</format> + <description>Query interval in seconds</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 1-1800"/> + </constraint> + </properties> + </leafNode> + <leafNode name="query-max-response-time"> + <properties> + <help>IGMP max query response time</help> + <valueHelp> + <format>10-250</format> + <description>Query response value in deci-seconds</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 10-250"/> + </constraint> + </properties> + </leafNode> + </children> + </tagNode> + </children> + </node> + </children> + </node> +</interfaceDefinition> diff --git a/interface-definitions/protocols-pim.xml.in b/interface-definitions/protocols-pim.xml.in new file mode 100644 index 000000000..944224f7b --- /dev/null +++ b/interface-definitions/protocols-pim.xml.in @@ -0,0 +1,84 @@ +<?xml version="1.0"?> +<!-- Protocol Independent Multicast (PIM) configuration --> +<interfaceDefinition> + <node name="protocols"> + <children> + <node name="pim" owner="${vyos_conf_scripts_dir}/protocols_pim.py"> + <properties> + <help>Protocol Independent Multicast (PIM)</help> + <priority>400</priority> + </properties> + <children> + <tagNode name="interface"> + <properties> + <help>PIM interface</help> + <completionHelp> + <script>${vyos_completion_dir}/list_interfaces.py</script> + </completionHelp> + </properties> + <children> + <leafNode name="hello"> + <properties> + <help>Hello Interval</help> + <valueHelp> + <format>1-180</format> + <description>Hello Interval in seconds</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 1-180"/> + </constraint> + </properties> + </leafNode> + </children> + </tagNode> + <node name="rp"> + <properties> + <help>Rendezvous Point</help> + </properties> + <children> + <tagNode name="address"> + <properties> + <help>Rendezvous Point address</help> + <valueHelp> + <format>ipv4</format> + <description>Rendezvous Point address</description> + </valueHelp> + <constraint> + <validator name="ipv4-address"/> + </constraint> + </properties> + <children> + <leafNode name="group"> + <properties> + <help>Group Address range</help> + <valueHelp> + <format>ipv4net</format> + <description>Group Address range RFC 3171</description> + </valueHelp> + <constraint> + <validator name="ip-prefix"/> + </constraint> + <multi/> + </properties> + </leafNode> + </children> + </tagNode> + <leafNode name="keep-alive-timer"> + <properties> + <help>Keep alive Timer</help> + <valueHelp> + <format>31-60000</format> + <description>Keep alive Timer in seconds</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 31-60000"/> + </constraint> + </properties> + </leafNode> + </children> + </node> + </children> + </node> + </children> + </node> +</interfaceDefinition> diff --git a/op-mode-definitions/show-ip-igmp.xml b/op-mode-definitions/show-ip-igmp.xml new file mode 100644 index 000000000..b8f2f9107 --- /dev/null +++ b/op-mode-definitions/show-ip-igmp.xml @@ -0,0 +1,48 @@ +<?xml version="1.0"?> +<interfaceDefinition> + <node name="show"> + <children> + <node name="ip"> + <children> + <node name="igmp"> + <properties> + <help>Show IGMP (Internet Group Management Protocol) information</help> + </properties> + <children> + <leafNode name="groups"> + <properties> + <help>IGMP groups information</help> + </properties> + <command>/usr/bin/vtysh -c "show ip igmp groups"</command> + </leafNode> + <leafNode name="interfaces"> + <properties> + <help>IGMP interfaces information</help> + </properties> + <command>/usr/bin/vtysh -c "show ip igmp interface"</command> + </leafNode> + <leafNode name="join"> + <properties> + <help>IGMP static join information</help> + </properties> + <command>/usr/bin/vtysh -c "show ip igmp join"</command> + </leafNode> + <leafNode name="sources"> + <properties> + <help>IGMP sources information</help> + </properties> + <command>/usr/bin/vtysh -c "show ip igmp sources"</command> + </leafNode> + <leafNode name="statistics"> + <properties> + <help>IGMP statistics</help> + </properties> + <command>/usr/bin/vtysh -c "show ip igmp statistics"</command> + </leafNode> + </children> + </node> + </children> + </node> + </children> + </node> +</interfaceDefinition> diff --git a/op-mode-definitions/show-ip-multicast.xml b/op-mode-definitions/show-ip-multicast.xml index 6ffe40436..5331d2e35 100644 --- a/op-mode-definitions/show-ip-multicast.xml +++ b/op-mode-definitions/show-ip-multicast.xml @@ -21,6 +21,18 @@ </properties> <command>if ps -C igmpproxy &>/dev/null; then ${vyos_op_scripts_dir}/show_igmpproxy.py --mfc; else echo IGMP proxy not configured; fi</command> </leafNode> + <leafNode name="summary"> + <properties> + <help>IP multicast information</help> + </properties> + <command>/usr/bin/vtysh -c "show ip multicast"</command> + </leafNode> + <leafNode name="route"> + <properties> + <help>IP multicast routing table</help> + </properties> + <command>/usr/bin/vtysh -c "show ip mroute"</command> + </leafNode> </children> </node> </children> diff --git a/op-mode-definitions/show-ip-pim.xml b/op-mode-definitions/show-ip-pim.xml new file mode 100644 index 000000000..3f4edc779 --- /dev/null +++ b/op-mode-definitions/show-ip-pim.xml @@ -0,0 +1,72 @@ +<?xml version="1.0"?> +<interfaceDefinition> + <node name="show"> + <children> + <node name="ip"> + <children> + <node name="pim"> + <properties> + <help>Show PIM (Protocol Independent Multicast) information</help> + </properties> + <children> + <leafNode name="interfaces"> + <properties> + <help>PIM interfaces information</help> + </properties> + <command>/usr/bin/vtysh -c "show ip pim interface"</command> + </leafNode> + <leafNode name="join"> + <properties> + <help>PIM join information</help> + </properties> + <command>/usr/bin/vtysh -c "show ip pim join"</command> + </leafNode> + <leafNode name="neighbor"> + <properties> + <help>PIM neighbor information</help> + </properties> + <command>/usr/bin/vtysh -c "show ip pim neighbor"</command> + </leafNode> + <leafNode name="nexthop"> + <properties> + <help>PIM cached nexthop rpf information</help> + </properties> + <command>/usr/bin/vtysh -c "show ip pim nexthop"</command> + </leafNode> + <leafNode name="state"> + <properties> + <help>PIM state information</help> + </properties> + <command>/usr/bin/vtysh -c "show ip pim state"</command> + </leafNode> + <leafNode name="statistics"> + <properties> + <help>PIM statistics</help> + </properties> + <command>/usr/bin/vtysh -c "show ip pim statistics"</command> + </leafNode> + <leafNode name="rp"> + <properties> + <help>PIM RP (Rendevous Point) information</help> + </properties> + <command>/usr/bin/vtysh -c "show ip pim rp-info"</command> + </leafNode> + <leafNode name="rpf"> + <properties> + <help>PIM cached source rpf information</help> + </properties> + <command>/usr/bin/vtysh -c "show ip pim rpf"</command> + </leafNode> + <leafNode name="upstream"> + <properties> + <help>PIM upstream information</help> + </properties> + <command>/usr/bin/vtysh -c "show ip pim upstream"</command> + </leafNode> + </children> + </node> + </children> + </node> + </children> + </node> +</interfaceDefinition> diff --git a/python/vyos/ifconfig/control.py b/python/vyos/ifconfig/control.py index e8f25c014..1c9f7e284 100644 --- a/python/vyos/ifconfig/control.py +++ b/python/vyos/ifconfig/control.py @@ -28,15 +28,20 @@ class Control(Register): if os.path.isfile('/tmp/vyos.ifconfig.debug'): print('DEBUG/{:<6} {}'.format(self.config['ifname'], msg)) - def _cmd(self, command): + def _popen(self, command): p = Popen(command, stdout=PIPE, stderr=STDOUT, shell=True) tmp = p.communicate()[0].strip() self._debug_msg(f"cmd '{command}'") decoded = tmp.decode() if decoded: self._debug_msg(f"returned:\n{decoded}") - if p.returncode != 0: - raise RuntimeError(f'{command}\nreturned: {decoded}') + return decoded, p.returncode + + def _cmd(self, command): + decoded, code = self._popen(command) + if code != 0: + # error code can be recovered with .errno + raise OSError(code, f'{command}\nreturned: {decoded}') return decoded def _get_command(self, config, name): @@ -50,9 +55,6 @@ class Control(Register): """ Using the defined names, set data write to sysfs. """ - if not value and not self._command_set[name].get('force', False): - return None - # the code can pass int as int value = str(value) @@ -110,9 +112,6 @@ class Control(Register): """ Using the defined names, set data write to sysfs. """ - if not value and not self._sysfs_set[name].get('force', False): - return None - # the code can pass int as int value = str(value) diff --git a/python/vyos/ifconfig/ethernet.py b/python/vyos/ifconfig/ethernet.py index b3e652409..50552dc71 100644 --- a/python/vyos/ifconfig/ethernet.py +++ b/python/vyos/ifconfig/ethernet.py @@ -101,6 +101,8 @@ class EthernetIf(Interface): >>> i = EthernetIf('eth0') >>> i.set_flow_control(True) """ + ifname = self.config['ifname'] + if enable not in ['on', 'off']: raise ValueError("Value out of range") @@ -110,8 +112,15 @@ class EthernetIf(Interface): return # Get current flow control settings: - cmd = '/sbin/ethtool --show-pause {0}'.format(self.config['ifname']) - tmp = self._cmd(cmd) + cmd = f'/sbin/ethtool --show-pause {ifname}' + output, code = self._popen(cmd) + if code == 76: + # the interface does not support it + return '' + if code: + # never fail here as it prevent vyos to boot + print(f'unexpected return code {code} from {cmd}') + return '' # The above command returns - with tabs: # @@ -119,23 +128,21 @@ class EthernetIf(Interface): # Autonegotiate: on # RX: off # TX: off - if re.search("Autonegotiate:\ton", tmp): + if re.search("Autonegotiate:\ton", output): if enable == "on": # flowcontrol is already enabled - no need to re-enable it again # this will prevent the interface from flapping as applying the # flow-control settings will take the interface down and bring # it back up every time. - return + return '' # Assemble command executed on system. Unfortunately there is no way # to change this setting via sysfs - cmd = '/sbin/ethtool --pause {0} autoneg {1} tx {1} rx {1}'.format( - self.config['ifname'], enable) - try: - # An exception will be thrown if the settings are not changed - return self._cmd(cmd) - except RuntimeError: - pass + cmd = f'/sbin/ethtool --pause {ifname} autoneg {enable} tx {enable} rx {enable}' + output, code = self._popen(cmd) + if code: + print(f'could not set flowcontrol for {ifname}') + return output def set_speed_duplex(self, speed, duplex): """ diff --git a/python/vyos/ifconfig/interface.py b/python/vyos/ifconfig/interface.py index 9fd0dcca5..0fddc67f3 100644 --- a/python/vyos/ifconfig/interface.py +++ b/python/vyos/ifconfig/interface.py @@ -66,13 +66,15 @@ class Interface(DHCP): 'shellcmd': 'ip link set dev {ifname} address {value}', }, 'vrf': { - 'force': True, 'convert': lambda v: f'master {v}' if v else 'nomaster', 'shellcmd': 'ip link set dev {ifname} {value}', }, } _sysfs_get = { + 'alias': { + 'location': '/sys/class/net/{ifname}/ifalias', + }, 'mac': { 'location': '/sys/class/net/{ifname}/address', }, @@ -259,7 +261,7 @@ class Interface(DHCP): >>> Interface('eth0').get_mac() '00:50:ab:cd:ef:00' """ - self.get_interface('mac') + return self.get_interface('mac') def set_mac(self, mac): """ @@ -394,6 +396,16 @@ class Interface(DHCP): """ return self.set_interface('link_detect', link_filter) + def get_alias(self): + """ + Get interface alias name used by e.g. SNMP + + Example: + >>> Interface('eth0').get_alias() + 'interface description as set by user' + """ + return self.get_interface('alias') + def set_alias(self, ifalias=''): """ Set interface alias name used by e.g. SNMP diff --git a/python/vyos/ifconfig/wireless.py b/python/vyos/ifconfig/wireless.py index a1f50b71d..932d07d01 100644 --- a/python/vyos/ifconfig/wireless.py +++ b/python/vyos/ifconfig/wireless.py @@ -46,6 +46,9 @@ class WiFiIf(Interface): .format(**self.config) self._cmd(cmd) + # wireless interface is administratively down by default + self.set_state('down') + def _delete(self): cmd = 'iw dev {ifname} del' \ .format(**self.config) diff --git a/src/conf_mode/interfaces-wireless.py b/src/conf_mode/interfaces-wireless.py index 3afd65a76..1e99ae12a 100755 --- a/src/conf_mode/interfaces-wireless.py +++ b/src/conf_mode/interfaces-wireless.py @@ -1520,41 +1520,40 @@ def apply(wifi): if not wifi['disable']: w.set_admin_state('up') - - # Physical interface is now configured. Proceed by starting hostapd or - # wpa_supplicant daemon. When type is monitor we can just skip this. - if wifi['op_mode'] == 'ap': - cmd = 'start-stop-daemon' - cmd += ' --start ' - cmd += ' --quiet' - cmd += ' --oknodo' - cmd += ' --pidfile ' + get_pid('hostapd', wifi['intf']) - cmd += ' --exec /usr/sbin/hostapd' - # now pass arguments to hostapd binary - cmd += ' -- ' - cmd += ' -B' - cmd += ' -P ' + get_pid('hostapd', wifi['intf']) - cmd += ' ' + get_conf_file('hostapd', wifi['intf']) - - # execute assembled command - subprocess_cmd(cmd) - - elif wifi['op_mode'] == 'station': - cmd = 'start-stop-daemon' - cmd += ' --start ' - cmd += ' --quiet' - cmd += ' --oknodo' - cmd += ' --pidfile ' + get_pid('hostapd', wifi['intf']) - cmd += ' --exec /sbin/wpa_supplicant' - # now pass arguments to hostapd binary - cmd += ' -- ' - cmd += ' -s -B -D nl80211' - cmd += ' -P ' + get_pid('wpa_supplicant', wifi['intf']) - cmd += ' -i ' + wifi['intf'] - cmd += ' -c ' + get_conf_file('wpa_supplicant', wifi['intf']) - - # execute assembled command - subprocess_cmd(cmd) + # Physical interface is now configured. Proceed by starting hostapd or + # wpa_supplicant daemon. When type is monitor we can just skip this. + if wifi['op_mode'] == 'ap': + cmd = 'start-stop-daemon' + cmd += ' --start ' + cmd += ' --quiet' + cmd += ' --oknodo' + cmd += ' --pidfile ' + get_pid('hostapd', wifi['intf']) + cmd += ' --exec /usr/sbin/hostapd' + # now pass arguments to hostapd binary + cmd += ' -- ' + cmd += ' -B' + cmd += ' -P ' + get_pid('hostapd', wifi['intf']) + cmd += ' ' + get_conf_file('hostapd', wifi['intf']) + + # execute assembled command + subprocess_cmd(cmd) + + elif wifi['op_mode'] == 'station': + cmd = 'start-stop-daemon' + cmd += ' --start ' + cmd += ' --quiet' + cmd += ' --oknodo' + cmd += ' --pidfile ' + get_pid('hostapd', wifi['intf']) + cmd += ' --exec /sbin/wpa_supplicant' + # now pass arguments to hostapd binary + cmd += ' -- ' + cmd += ' -s -B -D nl80211' + cmd += ' -P ' + get_pid('wpa_supplicant', wifi['intf']) + cmd += ' -i ' + wifi['intf'] + cmd += ' -c ' + get_conf_file('wpa_supplicant', wifi['intf']) + + # execute assembled command + subprocess_cmd(cmd) return None diff --git a/src/conf_mode/protocols_igmp.py b/src/conf_mode/protocols_igmp.py new file mode 100755 index 000000000..8d25fe9d5 --- /dev/null +++ b/src/conf_mode/protocols_igmp.py @@ -0,0 +1,130 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2020 VyOS maintainers and contributors +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 or later as +# published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# + +import sys +import jinja2 +import copy +import os +import vyos.validate + +from vyos import ConfigError +from vyos.config import Config + +config_file = r'/tmp/igmp.frr' + +# Please be careful if you edit the template. +config_tmpl = """ +! +{% for iface in old_ifaces -%} +interface {{ iface }} +no ip igmp +! +{% endfor -%} +{% for iface in ifaces -%} +interface {{ iface }} +{% if ifaces[iface].version -%} +ip igmp version {{ ifaces[iface].version }} +{% else -%} +{# IGMP default version 3 #} +ip igmp +{% endif -%} +{% if ifaces[iface].query_interval -%} +ip igmp query-interval {{ ifaces[iface].query_interval }} +{% endif -%} +{% if ifaces[iface].query_max_resp_time -%} +ip igmp query-max-response-time {{ ifaces[iface].query_max_resp_time }} +{% endif -%} +! +{% endfor -%} +! +""" + +def get_config(): + conf = Config() + igmp_conf = { + 'igmp_conf' : False, + 'old_ifaces' : {}, + 'ifaces' : {} + } + if not (conf.exists('protocols igmp') or conf.exists_effective('protocols igmp')): + return None + + if conf.exists('protocols igmp'): + igmp_conf['igmp_conf'] = True + + conf.set_level('protocols igmp') + + # # Get interfaces + for iface in conf.list_effective_nodes('interface'): + igmp_conf['old_ifaces'].update({ + iface : { + 'version' : conf.return_effective_value('interface {0} version'.format(iface)), + 'query_interval' : conf.return_effective_value('interface {0} query-interval'.format(iface)), + 'query_max_resp_time' : conf.return_effective_value('interface {0} query-max-response-time'.format(iface)) + } + }) + + for iface in conf.list_nodes('interface'): + igmp_conf['ifaces'].update({ + iface : { + 'version' : conf.return_value('interface {0} version'.format(iface)), + 'query_interval' : conf.return_value('interface {0} query-interval'.format(iface)), + 'query_max_resp_time' : conf.return_value('interface {0} query-max-response-time'.format(iface)) + } + }) + + return igmp_conf + +def verify(igmp): + if igmp is None: + return None + + if igmp['igmp_conf']: + # Check interfaces + if not igmp['ifaces']: + raise ConfigError(f"IGMP require defined interfaces!") + +def generate(igmp): + if igmp is None: + return None + + tmpl = jinja2.Template(config_tmpl) + config_text = tmpl.render(igmp) + with open(config_file, 'w') as f: + f.write(config_text) + + return None + +def apply(igmp): + if igmp is None: + return None + + if os.path.exists(config_file): + os.system("sudo vtysh -d pimd -f " + config_file) + os.remove(config_file) + + return None + +if __name__ == '__main__': + try: + c = get_config() + verify(c) + generate(c) + apply(c) + except ConfigError as e: + print(e) + sys.exit(1) diff --git a/src/conf_mode/protocols_pim.py b/src/conf_mode/protocols_pim.py new file mode 100755 index 000000000..dc65a59a6 --- /dev/null +++ b/src/conf_mode/protocols_pim.py @@ -0,0 +1,175 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2020 VyOS maintainers and contributors +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 or later as +# published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# + +import sys +import jinja2 +import copy +import os +import vyos.validate +from ipaddress import IPv4Address + +from vyos import ConfigError +from vyos.config import Config + +config_file = r'/tmp/pimd.frr' + +# Please be careful if you edit the template. +config_tmpl = """ +! +{% for rp_addr in old_pim.rp -%} +{% for group in old_pim.rp[rp_addr] -%} +no ip pim rp {{ rp_addr }} {{ group }} +{% endfor -%} +{% endfor -%} +{% if old_pim.rp_keep_alive -%} +no ip pim rp keep-alive-timer {{ old_pim.rp_keep_alive }} +{% endif -%} +{% for iface in old_pim.ifaces -%} +interface {{ iface }} +no ip pim +! +{% endfor -%} +{% for iface in pim.ifaces -%} +interface {{ iface }} +{% if pim.ifaces[iface].hello -%} +ip pim hello {{ pim.ifaces[iface].hello }} +{% endif -%} +ip pim +! +{% endfor -%} +{% for rp_addr in pim.rp -%} +{% for group in pim.rp[rp_addr] -%} +ip pim rp {{ rp_addr }} {{ group }} +{% endfor -%} +{% endfor -%} +{% if pim.rp_keep_alive -%} +ip pim rp keep-alive-timer {{ pim.rp_keep_alive }} +{% endif -%} +! +""" + +def get_config(): + conf = Config() + pim_conf = { + 'pim_conf' : False, + 'old_pim' : { + 'ifaces' : {}, + 'rp' : {} + }, + 'pim' : { + 'ifaces' : {}, + 'rp' : {} + } + } + if not (conf.exists('protocols pim') or conf.exists_effective('protocols pim')): + return None + + if conf.exists('protocols pim'): + pim_conf['pim_conf'] = True + + conf.set_level('protocols pim') + + # Get interfaces + for iface in conf.list_effective_nodes('interface'): + pim_conf['old_pim']['ifaces'].update({ + iface : { + 'hello' : conf.return_effective_value('interface {0} hello'.format(iface)) + } + }) + + for iface in conf.list_nodes('interface'): + pim_conf['pim']['ifaces'].update({ + iface : { + 'hello' : conf.return_value('interface {0} hello'.format(iface)) + } + }) + + conf.set_level('protocols pim rp') + + # Get RPs addresses + for rp_addr in conf.list_effective_nodes('address'): + pim_conf['old_pim']['rp'][rp_addr] = conf.return_effective_values('address {0} group'.format(rp_addr)) + + for rp_addr in conf.list_nodes('address'): + pim_conf['pim']['rp'][rp_addr] = conf.return_values('address {0} group'.format(rp_addr)) + + # Get RP keep-alive-timer + if conf.exists_effective('rp keep-alive-timer'): + pim_conf['old_pim']['rp_keep_alive'] = conf.return_effective_value('rp keep-alive-timer') + if conf.exists('rp keep-alive-timer'): + pim_conf['pim']['rp_keep_alive'] = conf.return_value('rp keep-alive-timer') + + return pim_conf + +def verify(pim): + if pim is None: + return None + + if pim['pim_conf']: + # Check interfaces + if not pim['pim']['ifaces']: + raise ConfigError(f"PIM require defined interfaces!") + + if not pim['pim']['rp']: + raise ConfigError(f"RP address required") + + # Check unique multicast groups + uniq_groups = [] + for rp_addr in pim['pim']['rp']: + if not pim['pim']['rp'][rp_addr]: + raise ConfigError(f"Group should be specified for RP " + rp_addr) + for group in pim['pim']['rp'][rp_addr]: + if (group in uniq_groups): + raise ConfigError(f"Group range " + group + " specified cannot exact match another") + + # Check, is this multicast group + gr_addr = group.split('/') + if IPv4Address(gr_addr[0]) < IPv4Address('224.0.0.0'): + raise ConfigError(group + " not a multicast group") + + uniq_groups.extend(pim['pim']['rp'][rp_addr]) + +def generate(pim): + if pim is None: + return None + + tmpl = jinja2.Template(config_tmpl) + config_text = tmpl.render(pim) + with open(config_file, 'w') as f: + f.write(config_text) + + return None + +def apply(pim): + if pim is None: + return None + + if os.path.exists(config_file): + os.system("sudo vtysh -d pimd -f " + config_file) + os.remove(config_file) + + return None + +if __name__ == '__main__': + try: + c = get_config() + verify(c) + generate(c) + apply(c) + except ConfigError as e: + print(e) + sys.exit(1) diff --git a/src/helpers/vyos-merge-config.py b/src/helpers/vyos-merge-config.py index 7ae62cfb3..c5216daa6 100755 --- a/src/helpers/vyos-merge-config.py +++ b/src/helpers/vyos-merge-config.py @@ -70,10 +70,7 @@ with tempfile.NamedTemporaryFile() as file_to_migrate: merge_config_tree = ConfigTree(config_file) effective_config = Config() - -output_effective_config = effective_config.show_config() - -effective_config_tree = ConfigTree(output_effective_config) +effective_config_tree = effective_config._running_config effective_cmds = effective_config_tree.to_commands() merge_cmds = merge_config_tree.to_commands() |