diff --git a/data/templates/frr/ldpd.frr.j2 b/data/templates/frr/ldpd.frr.j2 index 9a893cc55..b8fb0cfc7 100644 --- a/data/templates/frr/ldpd.frr.j2 +++ b/data/templates/frr/ldpd.frr.j2 @@ -1,149 +1,155 @@ ! {% if ldp is vyos_defined %} mpls ldp {% if ldp.router_id is vyos_defined %} router-id {{ ldp.router_id }} {% endif %} {% if ldp.parameters.cisco_interop_tlv is vyos_defined %} dual-stack cisco-interop {% endif %} {% if ldp.parameters.transport_prefer_ipv4 is vyos_defined %} dual-stack transport-connection prefer ipv4 {% endif %} {% if ldp.parameters.ordered_control is vyos_defined %} ordered-control {% endif %} {% if ldp.neighbor is vyos_defined %} {% for neighbor, neighbor_config in ldp.neighbor.items() %} {% if neighbor_config.password is vyos_defined %} neighbor {{ neighbor }} password {{ neighbor_config.password }} {% endif %} {% if neighbor_config.ttl_security is vyos_defined %} {% if neighbor_config.ttl_security.disable is vyos_defined %} neighbor {{ neighbor }} ttl-security disable {% else %} neighbor {{ neighbor }} ttl-security hops {{ neighbor_config.ttl_security }} {% endif %} {% endif %} {% if neighbor_config.session_holdtime is vyos_defined %} neighbor {{ neighbor }} session holdtime {{ neighbor_config.session_holdtime }} {% endif %} {% endfor %} {% endif %} ! {% if ldp.discovery.transport_ipv4_address is vyos_defined %} address-family ipv4 {% if ldp.allocation.ipv4.access_list is vyos_defined %} label local allocate for {{ ldp.allocation.ipv4.access_list }} {% else %} label local allocate host-routes {% endif %} {% if ldp.discovery.transport_ipv4_address is vyos_defined %} discovery transport-address {{ ldp.discovery.transport_ipv4_address }} {% endif %} {% if ldp.discovery.hello_ipv4_holdtime is vyos_defined %} discovery hello holdtime {{ ldp.discovery.hello_ipv4_holdtime }} {% endif %} {% if ldp.discovery.hello_ipv4_interval is vyos_defined %} discovery hello interval {{ ldp.discovery.hello_ipv4_interval }} {% endif %} {% if ldp.discovery.session_ipv4_holdtime is vyos_defined %} session holdtime {{ ldp.discovery.session_ipv4_holdtime }} {% endif %} {% if ldp.import.ipv4.import_filter.filter_access_list is vyos_defined %} {% if ldp.import.ipv4.import_filter.neighbor_access_list is vyos_defined %} label remote accept for {{ ldp.import.ipv4.import_filter.filter_access_list }} from {{ ldp.import.ipv4.import_filter.neighbor_access_list }} {% else %} label remote accept for {{ ldp.import.ipv4.import_filter.filter_access_list }} {% endif %} {% endif %} {% if ldp.export.ipv4.explicit_null is vyos_defined %} label local advertise explicit-null {% endif %} {% if ldp.export.ipv4.export_filter.filter_access_list is vyos_defined %} {% if ldp.export.ipv4.export_filter.neighbor_access_list is vyos_defined %} label local advertise for {{ ldp.export.ipv4.export_filter.filter_access_list }} to {{ ldp.export.ipv4.export_filter.neighbor_access_list }} {% else %} label local advertise for {{ ldp.export.ipv4.export_filter.filter_access_list }} {% endif %} {% endif %} {% if ldp.targeted_neighbor is vyos_defined %} {% if ldp.targeted_neighbor.ipv4.enable is vyos_defined %} discovery targeted-hello accept {% endif %} {% if ldp.targeted_neighbor.ipv4.hello_holdtime is vyos_defined %} discovery targeted-hello holdtime {{ ldp.targeted_neighbor.ipv4.hello_holdtime }} {% endif %} {% if ldp.targeted_neighbor.ipv4.hello_interval is vyos_defined %} discovery targeted-hello interval {{ ldp.targeted_neighbor.ipv4.hello_interval }} {% endif %} {% for addresses in ldp.targeted_neighbor.ipv4.address %} neighbor {{ addresses }} targeted {% endfor %} {% endif %} {% if ldp.interface is vyos_defined %} -{% for interface in ldp.interface %} +{% for interface, iface_config in ldp.interface.items() %} interface {{ interface }} +{% if iface_config.disable_establish_hello is vyos_defined %} + disable-establish-hello +{% endif %} exit {% endfor %} {% endif %} exit-address-family {% else %} no address-family ipv4 {% endif %} ! {% if ldp.discovery.transport_ipv6_address is vyos_defined %} address-family ipv6 {% if ldp.allocation.ipv6.access_list6 is vyos_defined %} label local allocate for {{ ldp.allocation.ipv6.access_list6 }} {% else %} label local allocate host-routes {% endif %} {% if ldp.discovery.transport_ipv6_address is vyos_defined %} discovery transport-address {{ ldp.discovery.transport_ipv6_address }} {% endif %} {% if ldp.discovery.hello_ipv6_holdtime is vyos_defined %} discovery hello holdtime {{ ldp.discovery.hello_ipv6_holdtime }} {% endif %} {% if ldp.discovery.hello_ipv6_interval is vyos_defined %} discovery hello interval {{ ldp.discovery.hello_ipv6_interval }} {% endif %} {% if ldp.discovery.session_ipv6_holdtime is vyos_defined %} session holdtime {{ ldp.discovery.session_ipv6_holdtime }} {% endif %} {% if ldp.import.ipv6.import_filter.filter_access_list6 is vyos_defined %} label remote accept for {{ ldp.import.ipv6.import_filter.filter_access_list6 }} {{ 'from ' ~ ldp.import.ipv6.import_filter.neighbor_access_list6 if ldp.import.ipv6.import_filter.neighbor_access_list6 is vyos_defined }} {% endif %} {% if ldp.export.ipv6.explicit_null is vyos_defined %} label local advertise explicit-null {% endif %} {% if ldp.export.ipv6.export_filter.filter_access_list6 is vyos_defined %} label local advertise for {{ ldp.export.ipv6.export_filter.filter_access_list6 }} {{ 'to ' ~ ldp.export.ipv6.export_filter.neighbor_access_list6 if ldp.export.ipv6.export_filter.neighbor_access_list6 is vyos_defined }} {% endif %} {% if ldp.targeted_neighbor is vyos_defined %} {% if ldp.targeted_neighbor.ipv6.enable is vyos_defined %} discovery targeted-hello accept {% endif %} {% if ldp.targeted_neighbor.ipv6.hello_holdtime is vyos_defined %} discovery targeted-hello holdtime {{ ldp.targeted_neighbor.ipv6.hello_holdtime }} {% endif %} {% if ldp.targeted_neighbor.ipv6.hello_interval is vyos_defined %} discovery targeted-hello interval {{ ldp.targeted_neighbor.ipv6.hello_interval }} {% endif %} {% for addresses in ldp.targeted_neighbor.ipv6.address %} neighbor {{ addresses }} targeted {% endfor %} {% endif %} {% if ldp.interface is vyos_defined %} -{% for interface in ldp.interface %} +{% for interface, iface_config in ldp.interface.items() %} interface {{ interface }} +{% if iface_config.disable_establish_hello is vyos_defined %} + disable-establish-hello +{% endif %} {% endfor %} {% endif %} exit-address-family {% else %} no address-family ipv6 {% endif %} ! exit {% endif %} ! diff --git a/interface-definitions/protocols_mpls.xml.in b/interface-definitions/protocols_mpls.xml.in index 831601fc6..fc1864f38 100644 --- a/interface-definitions/protocols_mpls.xml.in +++ b/interface-definitions/protocols_mpls.xml.in @@ -1,560 +1,582 @@ <?xml version="1.0"?> <!-- Multiprotocol Label Switching (MPLS) configuration --> <interfaceDefinition> <node name="protocols"> <children> <node name="mpls" owner="${vyos_conf_scripts_dir}/protocols_mpls.py"> <properties> <help>Multiprotocol Label Switching (MPLS)</help> <priority>490</priority> </properties> <children> <node name="ldp"> <properties> <help>Label Distribution Protocol (LDP)</help> </properties> <children> #include <include/router-id.xml.i> <node name="allocation"> <properties> <help>Forwarding equivalence class allocation from local routes</help> </properties> <children> <node name="ipv4"> <properties> <help>IPv4 routes</help> </properties> <children> <leafNode name="access-list"> <properties> <help>Access-list number</help> <valueHelp> <format>u32:1-2699</format> <description>Access list number</description> </valueHelp> <constraint> <validator name="numeric" argument="--range 1-2699"/> </constraint> </properties> </leafNode> </children> </node> <node name="ipv6"> <properties> <help>IPv6 routes</help> </properties> <children> <leafNode name="access-list6"> <properties> <help>Access-list6 number</help> <valueHelp> <format>u32:1-2699</format> <description>Access list number</description> </valueHelp> <constraint> <validator name="numeric" argument="--range 1-2699"/> </constraint> </properties> </leafNode> </children> </node> </children> </node> <tagNode name="neighbor"> <properties> <help>LDP neighbor parameters</help> <valueHelp> <format>ipv4</format> <description>Neighbor IPv4 address</description> </valueHelp> <constraint> <validator name="ipv4-address"/> </constraint> </properties> <children> <leafNode name="password"> <properties> <help>Neighbor password</help> </properties> </leafNode> <leafNode name="ttl-security"> <properties> <help>Neighbor TTL security</help> <completionHelp> <list>disable</list> </completionHelp> <valueHelp> <format>u32:1-254</format> <description>TTL</description> </valueHelp> <valueHelp> <format>disable</format> <description>Disable neighbor TTL security</description> </valueHelp> </properties> </leafNode> <leafNode name="session-holdtime"> <properties> <help>Session IPv4 hold time</help> <valueHelp> <format>u32:15-65535</format> <description>Time in seconds</description> </valueHelp> <constraint> <validator name="numeric" argument="--range 15-65535"/> </constraint> </properties> </leafNode> </children> </tagNode> <node name="discovery"> <properties> <help>Discovery parameters</help> <valueHelp> <format>ipv4</format> <description>Discovery parameters</description> </valueHelp> </properties> <children> <leafNode name="hello-ipv4-holdtime"> <properties> <help>Hello IPv4 hold time</help> <valueHelp> <format>u32:1-65535</format> <description>Time in seconds</description> </valueHelp> <constraint> <validator name="numeric" argument="--range 1-65535"/> </constraint> </properties> </leafNode> <leafNode name="hello-ipv4-interval"> <properties> <help>Hello IPv4 interval</help> <valueHelp> <format>u32:1-65535</format> <description>Time in seconds</description> </valueHelp> <constraint> <validator name="numeric" argument="--range 1-65535"/> </constraint> </properties> </leafNode> <leafNode name="hello-ipv6-holdtime"> <properties> <help>Hello IPv6 hold time</help> <valueHelp> <format>u32:1-65535</format> <description>Time in seconds</description> </valueHelp> <constraint> <validator name="numeric" argument="--range 1-65535"/> </constraint> </properties> </leafNode> <leafNode name="hello-ipv6-interval"> <properties> <help>Hello IPv6 interval</help> <valueHelp> <format>u32:1-65535</format> <description>Time in seconds</description> </valueHelp> <constraint> <validator name="numeric" argument="--range 1-65535"/> </constraint> </properties> </leafNode> <leafNode name="session-ipv4-holdtime"> <properties> <help>Session IPv4 hold time</help> <valueHelp> <format>u32:15-65535</format> <description>Time in seconds</description> </valueHelp> <constraint> <validator name="numeric" argument="--range 15-65535"/> </constraint> </properties> </leafNode> <leafNode name="session-ipv6-holdtime"> <properties> <help>Session IPv6 hold time</help> <valueHelp> <format>u32:15-65535</format> <description>Time in seconds</description> </valueHelp> <constraint> <validator name="numeric" argument="--range 15-65535"/> </constraint> </properties> </leafNode> <leafNode name="transport-ipv4-address"> <properties> <help>Transport IPv4 address</help> <valueHelp> <format>ipv4</format> <description>IPv4 bind as transport</description> </valueHelp> <constraint> <validator name="ipv4-address"/> </constraint> </properties> </leafNode> <leafNode name="transport-ipv6-address"> <properties> <help>Transport IPv6 address</help> <valueHelp> <format>ipv6</format> <description>IPv6 bind as transport</description> </valueHelp> <constraint> <validator name="ipv6-address"/> </constraint> </properties> </leafNode> </children> </node> <node name="targeted-neighbor"> <properties> <help>Targeted LDP neighbor/session parameters</help> </properties> <children> <node name="ipv4"> <properties> <help>Targeted IPv4 neighbor/session parameters</help> </properties> <children> <leafNode name="address"> <properties> <help>Neighbor/session address</help> <valueHelp> <format>ipv4</format> <description>Neighbor/session address</description> </valueHelp> <constraint> <validator name="ipv4-address"/> </constraint> <multi/> </properties> </leafNode> <leafNode name="enable"> <properties> <help>Accept and respond to targeted hellos</help> <valueless/> </properties> </leafNode> <leafNode name="hello-interval"> <properties> <help>Hello interval</help> <valueHelp> <format>u32:1-65535</format> <description>Time in seconds</description> </valueHelp> <constraint> <validator name="numeric" argument="--range 1-65535"/> </constraint> </properties> </leafNode> <leafNode name="hello-holdtime"> <properties> <help>Hello hold time</help> <valueHelp> <format>u32:1-65535</format> <description>Time in seconds</description> </valueHelp> <constraint> <validator name="numeric" argument="--range 1-65535"/> </constraint> </properties> </leafNode> </children> </node> <node name="ipv6"> <properties> <help>Targeted IPv6 neighbor/session parameters</help> </properties> <children> <leafNode name="address"> <properties> <help>Neighbor/session address</help> <valueHelp> <format>ipv6</format> <description>Neighbor/session address</description> </valueHelp> <constraint> <validator name="ipv6-address"/> </constraint> <multi/> </properties> </leafNode> <leafNode name="enable"> <properties> <help>Accept and respond to targeted hellos</help> <valueless/> </properties> </leafNode> <leafNode name="hello-interval"> <properties> <help>Hello interval</help> <valueHelp> <format>u32:1-65535</format> <description>Time in seconds</description> </valueHelp> <constraint> <validator name="numeric" argument="--range 1-65535"/> </constraint> </properties> </leafNode> <leafNode name="hello-holdtime"> <properties> <help>Hello hold time</help> <valueHelp> <format>u32:1-65535</format> <description>Time in seconds</description> </valueHelp> <constraint> <validator name="numeric" argument="--range 1-65535"/> </constraint> </properties> </leafNode> </children> </node> </children> </node> <node name="parameters"> <properties> <help>Label Distribution Protocol miscellaneous parameters</help> </properties> <children> <leafNode name="cisco-interop-tlv"> <properties> <help>Enable Cisco non-compliant format capability TLV</help> <valueless/> </properties> </leafNode> <leafNode name="transport-prefer-ipv4"> <properties> <help>Prefer IPv4 for TCP peer transport connection</help> <valueless/> </properties> </leafNode> <leafNode name="ordered-control"> <properties> <help>Enable LDP ordered label distribution control mode</help> <valueless/> </properties> </leafNode> </children> </node> <node name="export"> <properties> <help>Export parameters</help> </properties> <children> <node name="ipv4"> <properties> <help>IPv4 parameters</help> </properties> <children> <leafNode name="explicit-null"> <properties> <help>Explicit-Null Label</help> <valueless/> </properties> </leafNode> <node name="export-filter"> <properties> <help>Forwarding equivalence class export filter</help> </properties> <children> <leafNode name="filter-access-list"> <properties> <help>Access-list number to apply FEC filtering</help> <valueHelp> <format>u32:1-2699</format> <description>Access list number</description> </valueHelp> <constraint> <validator name="numeric" argument="--range 1-2699"/> </constraint> </properties> </leafNode> <leafNode name="neighbor-access-list"> <properties> <help>Access-list number for IPv4 neighbor selection to apply filtering</help> <valueHelp> <format>u32:1-2699</format> <description>Access list number</description> </valueHelp> <constraint> <validator name="numeric" argument="--range 1-2699"/> </constraint> </properties> </leafNode> </children> </node> </children> </node> <node name="ipv6"> <properties> <help>IPv6 parameters</help> </properties> <children> <leafNode name="explicit-null"> <properties> <help>Explicit-Null Label</help> <valueless/> </properties> </leafNode> <node name="export-filter"> <properties> <help>Forwarding equivalence class export filter</help> </properties> <children> <leafNode name="filter-access-list6"> <properties> <help>Access-list6 number to apply FEC filtering</help> <valueHelp> <format>u32:1-2699</format> <description>Access list number</description> </valueHelp> <constraint> <validator name="numeric" argument="--range 1-2699"/> </constraint> </properties> </leafNode> <leafNode name="neighbor-access-list6"> <properties> <help>Access-list6 number for IPv6 neighbor selection to apply filtering</help> <valueHelp> <format>u32:1-2699</format> <description>Access list number</description> </valueHelp> <constraint> <validator name="numeric" argument="--range 1-2699"/> </constraint> </properties> </leafNode> </children> </node> </children> </node> </children> </node> <node name="import"> <properties> <help>Import parameters</help> </properties> <children> <node name="ipv4"> <properties> <help>IPv4 parameters</help> </properties> <children> <node name="import-filter"> <properties> <help>Forwarding equivalence class import filter</help> </properties> <children> <leafNode name="filter-access-list"> <properties> <help>Access-list number to apply FEC filtering</help> <valueHelp> <format>u32:1-2699</format> <description>Access list number</description> </valueHelp> <constraint> <validator name="numeric" argument="--range 1-2699"/> </constraint> </properties> </leafNode> <leafNode name="neighbor-access-list"> <properties> <help>Access-list number for IPv4 neighbor selection to apply filtering</help> <valueHelp> <format>u32:1-2699</format> <description>Access list number</description> </valueHelp> <constraint> <validator name="numeric" argument="--range 1-2699"/> </constraint> </properties> </leafNode> </children> </node> </children> </node> <node name="ipv6"> <properties> <help>IPv6 parameters</help> </properties> <children> <node name="import-filter"> <properties> <help>Forwarding equivalence class import filter</help> </properties> <children> <leafNode name="filter-access-list6"> <properties> <help>Access-list6 number to apply FEC filtering</help> <valueHelp> <format>u32:1-2699</format> <description>Access list number</description> </valueHelp> <constraint> <validator name="numeric" argument="--range 1-2699"/> </constraint> </properties> </leafNode> <leafNode name="neighbor-access-list6"> <properties> <help>Access-list6 number for IPv6 neighbor selection to apply filtering</help> <valueHelp> <format>u32:1-2699</format> <description>Access list number</description> </valueHelp> <constraint> <validator name="numeric" argument="--range 1-2699"/> </constraint> </properties> </leafNode> </children> </node> </children> </node> </children> </node> - #include <include/generic-interface-multi.xml.i> + <tagNode name="interface"> + <properties> + <help>Interface</help> + <completionHelp> + <script>${vyos_completion_dir}/list_interfaces</script> + </completionHelp> + <valueHelp> + <format>txt</format> + <description>Interface name</description> + </valueHelp> + <constraint> + #include <include/constraint/interface-name.xml.i> + </constraint> + </properties> + <children> + <leafNode name="disable-establish-hello"> + <properties> + <help>Disable response to hello packet with an additional hello LDP packet</help> + <valueless/> + </properties> + </leafNode> + </children> + </tagNode> </children> </node> <node name="parameters"> <properties> <help>Multiprotocol Label Switching miscellaneous parameters</help> </properties> <children> <leafNode name="no-propagate-ttl"> <properties> <help>Disable copy of IP TTL to MPLS TTL</help> <valueless/> </properties> </leafNode> <leafNode name="maximum-ttl"> <properties> <help>Maximum TTL for MPLS packets</help> <valueHelp> <format>u32:1-255</format> <description>Maximum hops allowed</description> </valueHelp> <constraint> <validator name="numeric" argument="--range 1-255"/> </constraint> </properties> </leafNode> </children> </node> #include <include/generic-interface-multi.xml.i> </children> </node> </children> </node> </interfaceDefinition> diff --git a/smoketest/scripts/cli/test_protocols_mpls.py b/smoketest/scripts/cli/test_protocols_mpls.py index 654f2f099..3840c24f4 100755 --- a/smoketest/scripts/cli/test_protocols_mpls.py +++ b/smoketest/scripts/cli/test_protocols_mpls.py @@ -1,125 +1,194 @@ #!/usr/bin/env python3 # -# Copyright (C) 2021-2024 VyOS maintainers and contributors +# Copyright (C) 2021-2025 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 unittest from base_vyostest_shim import VyOSUnitTestSHIM from base_vyostest_shim import CSTORE_GUARD_TIME from vyos.configsession import ConfigSessionError from vyos.ifconfig import Section from vyos.frrender import ldpd_daemon from vyos.utils.process import process_named_running base_path = ['protocols', 'mpls', 'ldp'] peers = { '192.0.2.10' : { 'intv_rx' : '500', 'intv_tx' : '600', 'multihop' : '', 'source_addr': '192.0.2.254', }, '192.0.2.20' : { 'echo_mode' : '', 'intv_echo' : '100', 'intv_mult' : '100', 'intv_rx' : '222', 'intv_tx' : '333', 'passive' : '', 'shutdown' : '', }, '2001:db8::a' : { 'source_addr': '2001:db8::1', }, '2001:db8::b' : { 'source_addr': '2001:db8::1', 'multihop' : '', }, } profiles = { 'foo' : { 'echo_mode' : '', 'intv_echo' : '100', 'intv_mult' : '101', 'intv_rx' : '222', 'intv_tx' : '333', 'shutdown' : '', }, 'bar' : { 'intv_mult' : '102', 'intv_rx' : '444', 'passive' : '', }, } class TestProtocolsMPLS(VyOSUnitTestSHIM.TestCase): @classmethod def setUpClass(cls): super(TestProtocolsMPLS, cls).setUpClass() # Retrieve FRR daemon PID - it is not allowed to crash, thus PID must remain the same cls.daemon_pid = process_named_running(ldpd_daemon) # ensure we can also run this test on a live system - so lets clean # out the current configuration :) cls.cli_delete(cls, base_path) # Enable CSTORE guard time required by FRR related tests cls._commit_guard_time = CSTORE_GUARD_TIME def tearDown(self): self.cli_delete(base_path) self.cli_commit() # check process health and continuity self.assertEqual(self.daemon_pid, process_named_running(ldpd_daemon)) def test_mpls_basic(self): router_id = '1.2.3.4' transport_ipv4_addr = '5.6.7.8' interfaces = Section.interfaces('ethernet') self.cli_set(base_path + ['router-id', router_id]) # At least one LDP interface must be configured with self.assertRaises(ConfigSessionError): self.cli_commit() for interface in interfaces: self.cli_set(base_path + ['interface', interface]) # LDP transport address missing with self.assertRaises(ConfigSessionError): self.cli_commit() self.cli_set(base_path + ['discovery', 'transport-ipv4-address', transport_ipv4_addr]) # Commit changes self.cli_commit() # Validate configuration frrconfig = self.getFRRconfig('mpls ldp', endsection='^exit') self.assertIn(f'mpls ldp', frrconfig) self.assertIn(f' router-id {router_id}', frrconfig) # Validate AFI IPv4 afiv4_config = self.getFRRconfig('mpls ldp', endsection='^exit', substring=' address-family ipv4', endsubsection='^ exit-address-family') self.assertIn(f' discovery transport-address {transport_ipv4_addr}', afiv4_config) for interface in interfaces: self.assertIn(f' interface {interface}', afiv4_config) + def test_02_mpls_disable_establish_hello(self): + router_id = '1.2.3.4' + transport_ipv4_addr = '5.6.7.8' + transport_ipv6_addr = '2001:db8:1111::1111' + interfaces = Section.interfaces('ethernet') + + self.cli_set(base_path + ['router-id', router_id]) + + # At least one LDP interface must be configured + with self.assertRaises(ConfigSessionError): + self.cli_commit() + for interface in interfaces: + self.cli_set(base_path + ['interface', interface, 'disable-establish-hello']) + + # LDP transport address missing + with self.assertRaises(ConfigSessionError): + self.cli_commit() + self.cli_set(base_path + ['discovery', 'transport-ipv4-address', transport_ipv4_addr]) + self.cli_set(base_path + ['discovery', 'transport-ipv6-address', transport_ipv6_addr]) + + # Commit changes + self.cli_commit() + + # Validate configuration + frrconfig = self.getFRRconfig('mpls ldp', endsection='^exit') + self.assertIn(f'mpls ldp', frrconfig) + self.assertIn(f' router-id {router_id}', frrconfig) + + # Validate AFI IPv4 + afiv4_config = self.getFRRconfig('mpls ldp', endsection='^exit', + substring=' address-family ipv4', + endsubsection='^ exit-address-family') + self.assertIn(f' discovery transport-address {transport_ipv4_addr}', afiv4_config) + for interface in interfaces: + self.assertIn(f' interface {interface}', afiv4_config) + self.assertIn(f' disable-establish-hello', afiv4_config) + + # Validate AFI IPv6 + afiv6_config = self.getFRRconfig('mpls ldp', endsection='^exit', + substring=' address-family ipv6', + endsubsection='^ exit-address-family') + self.assertIn(f' discovery transport-address {transport_ipv6_addr}', afiv6_config) + for interface in interfaces: + self.assertIn(f' interface {interface}', afiv6_config) + self.assertIn(f' disable-establish-hello', afiv6_config) + + # Delete disable-establish-hello + for interface in interfaces: + self.cli_delete(base_path + ['interface', interface, 'disable-establish-hello']) + + # Commit changes + self.cli_commit() + + # Validate AFI IPv4 + afiv4_config = self.getFRRconfig('mpls ldp', endsection='^exit', + substring=' address-family ipv4', + endsubsection='^ exit-address-family') + # Validate AFI IPv6 + afiv6_config = self.getFRRconfig('mpls ldp', endsection='^exit', + substring=' address-family ipv6', + endsubsection='^ exit-address-family') + # Check deleted 'disable-establish-hello' option per interface + for interface in interfaces: + self.assertIn(f' interface {interface}', afiv4_config) + self.assertNotIn(f' disable-establish-hello', afiv4_config) + self.assertIn(f' interface {interface}', afiv6_config) + self.assertNotIn(f' disable-establish-hello', afiv6_config) + + if __name__ == '__main__': unittest.main(verbosity=2)