diff options
-rw-r--r-- | data/configd-include.json | 1 | ||||
-rw-r--r-- | data/templates/frr/igmp.frr.j2 | 41 | ||||
-rw-r--r-- | data/templates/frr/pimd.frr.j2 | 17 | ||||
-rw-r--r-- | interface-definitions/include/source-address-ipv4-multi.xml.i | 18 | ||||
-rw-r--r-- | interface-definitions/include/version/pim-version.xml.i | 3 | ||||
-rw-r--r-- | interface-definitions/protocols-igmp.xml.in | 95 | ||||
-rw-r--r-- | interface-definitions/protocols-pim.xml.in | 43 | ||||
-rw-r--r-- | interface-definitions/xml-component-version.xml.in | 1 | ||||
-rw-r--r-- | op-mode-definitions/show-ip-igmp.xml.in | 12 | ||||
-rw-r--r-- | smoketest/config-tests/igmp-pim-small | 17 | ||||
-rw-r--r-- | smoketest/configs/igmp-pim-small | 84 | ||||
-rwxr-xr-x | smoketest/scripts/cli/test_protocols_pim.py | 47 | ||||
-rwxr-xr-x | src/conf_mode/protocols_igmp.py | 140 | ||||
-rwxr-xr-x | src/conf_mode/protocols_pim.py | 34 | ||||
-rwxr-xr-x | src/migration-scripts/pim/0-to-1 | 72 |
15 files changed, 322 insertions, 303 deletions
diff --git a/data/configd-include.json b/data/configd-include.json index 84bc1f14e..a762a6d4c 100644 --- a/data/configd-include.json +++ b/data/configd-include.json @@ -44,7 +44,6 @@ "policy-local-route.py", "protocols_bfd.py", "protocols_bgp.py", -"protocols_igmp.py", "protocols_isis.py", "protocols_mpls.py", "protocols_nhrp.py", diff --git a/data/templates/frr/igmp.frr.j2 b/data/templates/frr/igmp.frr.j2 deleted file mode 100644 index b75884484..000000000 --- a/data/templates/frr/igmp.frr.j2 +++ /dev/null @@ -1,41 +0,0 @@ -! -{% for iface in old_ifaces %} -interface {{ iface }} -{% for group in old_ifaces[iface].gr_join %} -{% if old_ifaces[iface].gr_join[group] %} -{% for source in old_ifaces[iface].gr_join[group] %} - no ip igmp join {{ group }} {{ source }} -{% endfor %} -{% else %} - no ip igmp join {{ group }} -{% endif %} -{% endfor %} - no ip igmp -! -{% endfor %} -{% for interface, interface_config in ifaces.items() %} -interface {{ interface }} -{% if interface_config.version %} - ip igmp version {{ interface_config.version }} -{% else %} -{# IGMP default version 3 #} - ip igmp -{% endif %} -{% if interface_config.query_interval %} - ip igmp query-interval {{ interface_config.query_interval }} -{% endif %} -{% if interface_config.query_max_resp_time %} - ip igmp query-max-response-time {{ interface_config.query_max_resp_time }} -{% endif %} -{% for group, sources in interface_config.gr_join.items() %} -{% if sources is vyos_defined %} -{% for source in sources %} - ip igmp join {{ group }} {{ source }} -{% endfor %} -{% else %} - ip igmp join {{ group }} -{% endif %} -{% endfor %} -! -{% endfor %} -! diff --git a/data/templates/frr/pimd.frr.j2 b/data/templates/frr/pimd.frr.j2 index 7d6ddf8d4..97c5ff58b 100644 --- a/data/templates/frr/pimd.frr.j2 +++ b/data/templates/frr/pimd.frr.j2 @@ -27,9 +27,26 @@ interface {{ iface }} {% if iface_config.igmp is vyos_defined %} ip igmp {% endif %} +{% if iface_config.igmp.query_interval %} + ip igmp query-interval {{ iface_config.igmp.query_interval }} +{% endif %} +{% if iface_config.igmp.query_max_response_time %} + ip igmp query-max-response-time {{ iface_config.igmp.query_max_response_time }} +{% endif %} {% if iface_config.igmp.version is vyos_defined %} ip igmp version {{ iface_config.igmp.version }} {% endif %} +{% if iface_config.igmp.join is vyos_defined %} +{% for join, join_config in iface_config.igmp.join.items() %} +{% if join_config.source_address is vyos_defined %} +{% for source_address in join_config.source_address %} + ip igmp join {{ join }} {{ source_address }} +{% endfor %} +{% else %} + ip igmp join {{ join }} +{% endif %} +{% endfor %} +{% endif %} exit {% endfor %} {% endif %} diff --git a/interface-definitions/include/source-address-ipv4-multi.xml.i b/interface-definitions/include/source-address-ipv4-multi.xml.i new file mode 100644 index 000000000..319a118f3 --- /dev/null +++ b/interface-definitions/include/source-address-ipv4-multi.xml.i @@ -0,0 +1,18 @@ +<!-- include start from source-address-ipv4-multi.xml.i --> +<leafNode name="source-address"> + <properties> + <help>IPv4 source address used to initiate connection</help> + <completionHelp> + <script>${vyos_completion_dir}/list_local_ips.sh --ipv4</script> + </completionHelp> + <valueHelp> + <format>ipv4</format> + <description>IPv4 source address</description> + </valueHelp> + <constraint> + <validator name="ipv4-address"/> + </constraint> + <multi/> + </properties> +</leafNode> +<!-- include end --> diff --git a/interface-definitions/include/version/pim-version.xml.i b/interface-definitions/include/version/pim-version.xml.i new file mode 100644 index 000000000..24cc38cdf --- /dev/null +++ b/interface-definitions/include/version/pim-version.xml.i @@ -0,0 +1,3 @@ +<!-- include start from include/version/pim-version.xml.i --> +<syntaxVersion component='pim' version='1'></syntaxVersion> +<!-- include end --> diff --git a/interface-definitions/protocols-igmp.xml.in b/interface-definitions/protocols-igmp.xml.in deleted file mode 100644 index a055db71e..000000000 --- a/interface-definitions/protocols-igmp.xml.in +++ /dev/null @@ -1,95 +0,0 @@ -<?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</script> - </completionHelp> - </properties> - <children> - <tagNode name="join"> - <properties> - <help>IGMP join multicast group</help> - <valueHelp> - <format>ipv4</format> - <description>Multicast group address</description> - </valueHelp> - <constraint> - <validator name="ipv4-address"/> - </constraint> - </properties> - <children> - <leafNode name="source"> - <properties> - <help>Source address</help> - <valueHelp> - <format>ipv4</format> - <description>Source address</description> - </valueHelp> - <constraint> - <validator name="ipv4-address"/> - </constraint> - <multi/> - </properties> - </leafNode> - </children> - </tagNode> - <leafNode name="version"> - <properties> - <help>IGMP version</help> - <completionHelp> - <list>2 3</list> - </completionHelp> - <valueHelp> - <format>2</format> - <description>IGMP version 2</description> - </valueHelp> - <valueHelp> - <format>3</format> - <description>IGMP version 3</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>u32: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>u32: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 index 02a8a6f5e..c1fa1b489 100644 --- a/interface-definitions/protocols-pim.xml.in +++ b/interface-definitions/protocols-pim.xml.in @@ -5,7 +5,7 @@ <children> <node name="pim" owner="${vyos_conf_scripts_dir}/protocols_pim.py"> <properties> - <help>Protocol Independent Multicast (PIM)</help> + <help>Protocol Independent Multicast (PIM) and IGMP</help> <priority>400</priority> </properties> <children> @@ -31,11 +31,50 @@ <help>Internet Group Management Protocol (IGMP) options</help> </properties> <children> + <tagNode name="join"> + <properties> + <help>IGMP join multicast group</help> + <valueHelp> + <format>ipv4</format> + <description>Multicast group address</description> + </valueHelp> + <constraint> + <validator name="ipv4-address"/> + </constraint> + </properties> + <children> + #include <include/source-address-ipv4-multi.xml.i> + </children> + </tagNode> + <leafNode name="query-interval"> + <properties> + <help>IGMP host query interval</help> + <valueHelp> + <format>u32: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>u32:10-250</format> + <description>Query response value in deci-seconds</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 10-250"/> + </constraint> + </properties> + </leafNode> <leafNode name="version"> <properties> <help>Interface IGMP version</help> <completionHelp> - <list>1 2</list> + <list>2 3</list> </completionHelp> <valueHelp> <format>2</format> diff --git a/interface-definitions/xml-component-version.xml.in b/interface-definitions/xml-component-version.xml.in index 8c9e816d1..51a28ef57 100644 --- a/interface-definitions/xml-component-version.xml.in +++ b/interface-definitions/xml-component-version.xml.in @@ -29,6 +29,7 @@ #include <include/version/ntp-version.xml.i> #include <include/version/openconnect-version.xml.i> #include <include/version/ospf-version.xml.i> + #include <include/version/pim-version.xml.i> #include <include/version/policy-version.xml.i> #include <include/version/pppoe-server-version.xml.i> #include <include/version/pptp-version.xml.i> diff --git a/op-mode-definitions/show-ip-igmp.xml.in b/op-mode-definitions/show-ip-igmp.xml.in index 855c5d508..1fd86ba54 100644 --- a/op-mode-definitions/show-ip-igmp.xml.in +++ b/op-mode-definitions/show-ip-igmp.xml.in @@ -13,31 +13,31 @@ <properties> <help>IGMP groups information</help> </properties> - <command>vtysh -c "show ip igmp groups"</command> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> </leafNode> - <leafNode name="interfaces"> + <leafNode name="interface"> <properties> <help>IGMP interfaces information</help> </properties> - <command>vtysh -c "show ip igmp interface"</command> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> </leafNode> <leafNode name="join"> <properties> <help>IGMP static join information</help> </properties> - <command>vtysh -c "show ip igmp join"</command> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> </leafNode> <leafNode name="sources"> <properties> <help>IGMP sources information</help> </properties> - <command>vtysh -c "show ip igmp sources"</command> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> </leafNode> <leafNode name="statistics"> <properties> <help>IGMP statistics</help> </properties> - <command>vtysh -c "show ip igmp statistics"</command> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> </leafNode> </children> </node> diff --git a/smoketest/config-tests/igmp-pim-small b/smoketest/config-tests/igmp-pim-small new file mode 100644 index 000000000..207c17d45 --- /dev/null +++ b/smoketest/config-tests/igmp-pim-small @@ -0,0 +1,17 @@ +set interfaces ethernet eth1 address '100.64.0.1/24' +set interfaces ethernet eth2 address '172.16.0.2/24' +set protocols pim interface eth1 igmp join 224.1.0.0 source-address '1.1.1.1' +set protocols pim interface eth1 igmp join 224.1.0.0 source-address '1.1.1.2' +set protocols pim interface eth1 igmp query-interval '1000' +set protocols pim interface eth1 igmp query-max-response-time '30' +set protocols pim interface eth1 igmp version '2' +set protocols pim interface eth2 +set protocols pim rp address 172.16.255.1 group '224.0.0.0/4' +set service ntp server 0.pool.ntp.org +set service ntp server 1.pool.ntp.org +set service ntp server 2.pool.ntp.org +set system domain-name 'vyos.io' +set system host-name 'vyos' +set system login user vyos authentication encrypted-password '$6$2Ta6TWHd/U$NmrX0x9kexCimeOcYK1MfhMpITF9ELxHcaBU/znBq.X2ukQOj61fVI2UYP/xBzP4QtiTcdkgs7WOQMHWsRymO/' +set system login user vyos authentication plaintext-password '' +set system console device ttyS0 speed '115200' diff --git a/smoketest/configs/igmp-pim-small b/smoketest/configs/igmp-pim-small new file mode 100644 index 000000000..f433ff8d7 --- /dev/null +++ b/smoketest/configs/igmp-pim-small @@ -0,0 +1,84 @@ +interfaces { + ethernet eth0 { + duplex auto + speed auto + } + ethernet eth1 { + address 100.64.0.1/24 + duplex auto + speed auto + } + ethernet eth2 { + address 172.16.0.2/24 + duplex auto + speed auto + } +} +protocols { + igmp { + interface eth1 { + join 224.1.0.0 { + source 1.1.1.1 + source 1.1.1.2 + } + query-interval 1000 + query-max-response-time 30 + version 2 + } + } + pim { + interface eth1 { + } + interface eth2 { + } + rp { + address 172.16.255.1 { + group 224.0.0.0/4 + } + } + } +} +system { + config-management { + commit-revisions 200 + } + console { + device ttyS0 { + speed 115200 + } + } + domain-name vyos.io + host-name vyos + login { + user vyos { + authentication { + encrypted-password $6$2Ta6TWHd/U$NmrX0x9kexCimeOcYK1MfhMpITF9ELxHcaBU/znBq.X2ukQOj61fVI2UYP/xBzP4QtiTcdkgs7WOQMHWsRymO/ + plaintext-password "" + } + } + } + ntp { + server 0.pool.ntp.org { + } + server 1.pool.ntp.org { + } + server 2.pool.ntp.org { + } + } + syslog { + global { + facility all { + level info + } + facility protocols { + level debug + } + } + } + time-zone Europe/Berlin +} + + +// Warning: Do not remove the following line. +// vyos-config-version: "broadcast-relay@1:cluster@1:config-management@1:conntrack@1:conntrack-sync@1:dhcp-relay@2:dhcp-server@5:dhcpv6-server@1:dns-forwarding@3:firewall@5:https@2:interfaces@18:ipoe-server@1:ipsec@5:l2tp@3:lldp@1:mdns@1:nat@5:ntp@1:pppoe-server@5:pptp@2:qos@1:quagga@7:rpki@1:salt@1:snmp@2:ssh@2:sstp@3:system@20:vrrp@2:vyos-accel-ppp@2:wanloadbalance@3:webproxy@2:zone-policy@1" +// Release version: 1.3.0 diff --git a/smoketest/scripts/cli/test_protocols_pim.py b/smoketest/scripts/cli/test_protocols_pim.py index 07c806126..ef134b195 100755 --- a/smoketest/scripts/cli/test_protocols_pim.py +++ b/smoketest/scripts/cli/test_protocols_pim.py @@ -77,8 +77,6 @@ class TestProtocolsPIM(VyOSUnitTestSHIM.TestCase): igmp_proxy = ['protocols', 'igmp-proxy'] rp = '127.0.0.1' group = '224.0.0.0/4' - hello = '100' - dr_priority = '64' self.cli_set(base_path) self.cli_set(igmp_proxy) @@ -97,5 +95,50 @@ class TestProtocolsPIM(VyOSUnitTestSHIM.TestCase): # commit changes self.cli_commit() + def test_03_igmp(self): + watermark_warning = '2000' + query_interval = '1000' + query_max_response_time = '200' + version = '2' + + igmp_join = { + '224.1.1.1' : { 'source' : ['1.1.1.1', '2.2.2.2', '3.3.3.3'] }, + '224.1.2.2' : { 'source' : [] }, + '224.1.3.3' : {}, + } + + self.cli_set(base_path + ['igmp', 'watermark-warning', watermark_warning]) + interfaces = Section.interfaces('ethernet') + for interface in interfaces: + self.cli_set(base_path + ['interface', interface , 'igmp', 'version', version]) + self.cli_set(base_path + ['interface', interface , 'igmp', 'query-interval', query_interval]) + self.cli_set(base_path + ['interface', interface , 'igmp', 'query-max-response-time', query_max_response_time]) + + for join, join_config in igmp_join.items(): + self.cli_set(base_path + ['interface', interface , 'igmp', 'join', join]) + if 'source' in join_config: + for source in join_config['source']: + self.cli_set(base_path + ['interface', interface , 'igmp', 'join', join, 'source-address', source]) + + self.cli_commit() + + frrconfig = self.getFRRconfig(daemon=PROCESS_NAME) + self.assertIn(f'ip igmp watermark-warn {watermark_warning}', frrconfig) + + for interface in interfaces: + frrconfig = self.getFRRconfig(f'interface {interface}', daemon=PROCESS_NAME) + self.assertIn(f'interface {interface}', frrconfig) + self.assertIn(f' ip igmp', frrconfig) + self.assertIn(f' ip igmp version {version}', frrconfig) + self.assertIn(f' ip igmp query-interval {query_interval}', frrconfig) + self.assertIn(f' ip igmp query-max-response-time {query_max_response_time}', frrconfig) + + for join, join_config in igmp_join.items(): + if 'source' in join_config: + for source in join_config['source']: + self.assertIn(f' ip igmp join {join} {source}', frrconfig) + else: + self.assertIn(f' ip igmp join {join}', frrconfig) + if __name__ == '__main__': unittest.main(verbosity=2, failfast=True) diff --git a/src/conf_mode/protocols_igmp.py b/src/conf_mode/protocols_igmp.py deleted file mode 100755 index 435189025..000000000 --- a/src/conf_mode/protocols_igmp.py +++ /dev/null @@ -1,140 +0,0 @@ -#!/usr/bin/env python3 -# -# Copyright (C) 2020-2023 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 os - -from ipaddress import IPv4Address -from sys import exit - -from vyos import ConfigError -from vyos.config import Config -from vyos.utils.process import process_named_running -from vyos.utils.process import call -from vyos.template import render -from signal import SIGTERM - -from vyos import airbag -airbag.enable() - -# Required to use the full path to pimd, in another case daemon will not be started -pimd_cmd = f'/usr/lib/frr/pimd -d -F traditional --daemon -A 127.0.0.1' - -config_file = r'/tmp/igmp.frr' - -def get_config(config=None): - if config: - conf = config - else: - conf = Config() - igmp_conf = { - 'igmp_conf' : False, - 'pim_conf' : False, - 'igmp_proxy_conf' : False, - 'old_ifaces' : {}, - 'ifaces' : {} - } - if not (conf.exists('protocols igmp') or conf.exists_effective('protocols igmp')): - return None - - if conf.exists('protocols igmp-proxy'): - igmp_conf['igmp_proxy_conf'] = True - - if conf.exists('protocols pim'): - igmp_conf['pim_conf'] = True - - 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)), - 'gr_join' : {} - } - }) - for gr_join in conf.list_effective_nodes('interface {0} join'.format(iface)): - igmp_conf['old_ifaces'][iface]['gr_join'][gr_join] = conf.return_effective_values('interface {0} join {1} source'.format(iface, gr_join)) - - 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)), - 'gr_join' : {} - } - }) - for gr_join in conf.list_nodes('interface {0} join'.format(iface)): - igmp_conf['ifaces'][iface]['gr_join'][gr_join] = conf.return_values('interface {0} join {1} source'.format(iface, gr_join)) - - return igmp_conf - -def verify(igmp): - if igmp is None: - return None - - if igmp['igmp_conf']: - # Check conflict with IGMP-Proxy - if igmp['igmp_proxy_conf']: - raise ConfigError(f"IGMP proxy and PIM cannot be both configured at the same time") - - # Check interfaces - if not igmp['ifaces']: - raise ConfigError(f"IGMP require defined interfaces!") - # Check, is this multicast group - for intfc in igmp['ifaces']: - for gr_addr in igmp['ifaces'][intfc]['gr_join']: - if not IPv4Address(gr_addr).is_multicast: - raise ConfigError(gr_addr + " not a multicast group") - -def generate(igmp): - if igmp is None: - return None - - render(config_file, 'frr/igmp.frr.j2', igmp) - return None - -def apply(igmp): - if igmp is None: - return None - - pim_pid = process_named_running('pimd') - if igmp['igmp_conf'] or igmp['pim_conf']: - if not pim_pid: - call(pimd_cmd) - - if os.path.exists(config_file): - call(f'vtysh -d pimd -f {config_file}') - os.remove(config_file) - elif pim_pid: - os.kill(int(pim_pid), SIGTERM) - - return None - -if __name__ == '__main__': - try: - c = get_config() - verify(c) - generate(c) - apply(c) - except ConfigError as e: - print(e) - exit(1) diff --git a/src/conf_mode/protocols_pim.py b/src/conf_mode/protocols_pim.py index 89db69b87..fbe95c404 100755 --- a/src/conf_mode/protocols_pim.py +++ b/src/conf_mode/protocols_pim.py @@ -93,22 +93,24 @@ def verify(pim): if 'interface' not in pim: raise ConfigError('PIM require defined interfaces!') - if dict_search('rp.address', pim) == None: - raise ConfigError('PIM rendezvous point needs to be defined!') - - # Check unique multicast groups - unique = [] - for address, address_config in pim['rp']['address'].items(): - if 'group' not in address_config: - raise ConfigError(f'PIM rendezvous point group should be defined for "{address}"!') - - # Check if it is a multicast group - for gr_addr in address_config['group']: - if not IPv4Network(gr_addr).is_multicast: - raise ConfigError(f'PIM rendezvous point group "{gr_addr}" is not a multicast group!') - if gr_addr in unique: - raise ConfigError('PIM rendezvous point group must be unique!') - unique.append(gr_addr) + if 'rp' in pim: + if 'address' not in pim['rp']: + raise ConfigError('PIM rendezvous point needs to be defined!') + + # Check unique multicast groups + unique = [] + pim_base_error = 'PIM rendezvous point group' + for address, address_config in pim['rp']['address'].items(): + if 'group' not in address_config: + raise ConfigError(f'{pim_base_error} should be defined for "{address}"!') + + # Check if it is a multicast group + for gr_addr in address_config['group']: + if not IPv4Network(gr_addr).is_multicast: + raise ConfigError(f'{pim_base_error} "{gr_addr}" is not a multicast group!') + if gr_addr in unique: + raise ConfigError(f'{pim_base_error} must be unique!') + unique.append(gr_addr) def generate(pim): if not pim or 'deleted' in pim: diff --git a/src/migration-scripts/pim/0-to-1 b/src/migration-scripts/pim/0-to-1 new file mode 100755 index 000000000..bf8af733c --- /dev/null +++ b/src/migration-scripts/pim/0-to-1 @@ -0,0 +1,72 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2023 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/>. + +# T5736: igmp: migrate "protocols igmp" to "protocols pim" + +import sys +from vyos.configtree import ConfigTree + +if len(sys.argv) < 2: + print("Must specify file name!") + sys.exit(1) + +file_name = sys.argv[1] + +with open(file_name, 'r') as f: + config_file = f.read() + +config = ConfigTree(config_file) + +base = ['protocols', 'igmp'] +pim_base = ['protocols', 'pim'] +if not config.exists(base): + # Nothing to do + sys.exit(0) + +for interface in config.list_nodes(base + ['interface']): + base_igmp_iface = base + ['interface', interface] + pim_base_iface = pim_base + ['interface', interface] + + # Create IGMP note under PIM interface + if not config.exists(pim_base_iface + ['igmp']): + config.set(pim_base_iface + ['igmp']) + + if config.exists(base_igmp_iface + ['join']): + config.copy(base_igmp_iface + ['join'], pim_base_iface + ['igmp', 'join']) + config.set_tag(pim_base_iface + ['igmp', 'join']) + + new_join_base = pim_base_iface + ['igmp', 'join'] + for address in config.list_nodes(new_join_base): + if config.exists(new_join_base + [address, 'source']): + config.rename(new_join_base + [address, 'source'], 'source-address') + + if config.exists(base_igmp_iface + ['query-interval']): + config.copy(base_igmp_iface + ['query-interval'], pim_base_iface + ['igmp', 'query-interval']) + + if config.exists(base_igmp_iface + ['query-max-response-time']): + config.copy(base_igmp_iface + ['query-max-response-time'], pim_base_iface + ['igmp', 'query-max-response-time']) + + if config.exists(base_igmp_iface + ['version']): + config.copy(base_igmp_iface + ['version'], pim_base_iface + ['igmp', 'version']) + +config.delete(base) + +try: + with open(file_name, 'w') as f: + f.write(config.to_string()) +except OSError as e: + print("Failed to save the modified config: {}".format(e)) + sys.exit(1) |