diff options
Diffstat (limited to 'smoketest')
-rwxr-xr-x | smoketest/scripts/cli/test_protocols_bgp.py | 197 | ||||
-rwxr-xr-x | smoketest/scripts/cli/test_protocols_isis.py | 35 | ||||
-rwxr-xr-x | smoketest/scripts/cli/test_protocols_ospf.py | 6 | ||||
-rwxr-xr-x | smoketest/scripts/cli/test_protocols_ospfv3.py | 32 |
4 files changed, 159 insertions, 111 deletions
diff --git a/smoketest/scripts/cli/test_protocols_bgp.py b/smoketest/scripts/cli/test_protocols_bgp.py index db1587ba7..f1db5350a 100755 --- a/smoketest/scripts/cli/test_protocols_bgp.py +++ b/smoketest/scripts/cli/test_protocols_bgp.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2021 VyOS maintainers and contributors +# Copyright (C) 2021-2022 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 @@ -36,112 +36,118 @@ bfd_profile = 'foo-bar-baz' neighbor_config = { '192.0.2.1' : { - 'bfd' : '', - 'cap_dynamic' : '', - 'cap_ext_next' : '', - 'remote_as' : '100', - 'adv_interv' : '400', - 'passive' : '', - 'password' : 'VyOS-Secure123', - 'shutdown' : '', - 'cap_over' : '', - 'ttl_security' : '5', - 'local_as' : '300', - 'route_map_in' : route_map_in, - 'route_map_out': route_map_out, + 'bfd' : '', + 'cap_dynamic' : '', + 'cap_ext_next' : '', + 'remote_as' : '100', + 'adv_interv' : '400', + 'passive' : '', + 'password' : 'VyOS-Secure123', + 'shutdown' : '', + 'cap_over' : '', + 'ttl_security' : '5', + 'local_as' : '300', + 'route_map_in' : route_map_in, + 'route_map_out' : route_map_out, 'no_send_comm_ext' : '', - 'addpath_all' : '', + 'addpath_all' : '', }, '192.0.2.2' : { - 'bfd_profile' : bfd_profile, - 'remote_as' : '200', - 'shutdown' : '', - 'no_cap_nego' : '', - 'port' : '667', - 'cap_strict' : '', - 'advertise_map': route_map_in, - 'non_exist_map': route_map_out, - 'pfx_list_in' : prefix_list_in, - 'pfx_list_out' : prefix_list_out, + 'bfd_profile' : bfd_profile, + 'remote_as' : '200', + 'shutdown' : '', + 'no_cap_nego' : '', + 'port' : '667', + 'cap_strict' : '', + 'advertise_map' : route_map_in, + 'non_exist_map' : route_map_out, + 'pfx_list_in' : prefix_list_in, + 'pfx_list_out' : prefix_list_out, 'no_send_comm_std' : '', }, '192.0.2.3' : { - 'advertise_map': route_map_in, - 'description' : 'foo bar baz', - 'remote_as' : '200', - 'passive' : '', - 'multi_hop' : '5', - 'update_src' : 'lo', - 'peer_group' : 'foo', + 'advertise_map' : route_map_in, + 'description' : 'foo bar baz', + 'remote_as' : '200', + 'passive' : '', + 'multi_hop' : '5', + 'update_src' : 'lo', + 'peer_group' : 'foo', + 'graceful_rst' : '', }, '2001:db8::1' : { - 'advertise_map': route_map_in, - 'exist_map' : route_map_out, - 'cap_dynamic' : '', - 'cap_ext_next' : '', - 'remote_as' : '123', - 'adv_interv' : '400', - 'passive' : '', - 'password' : 'VyOS-Secure123', - 'shutdown' : '', - 'cap_over' : '', - 'ttl_security' : '5', - 'local_as' : '300', - 'solo' : '', - 'route_map_in' : route_map_in, - 'route_map_out': route_map_out, + 'advertise_map' : route_map_in, + 'exist_map' : route_map_out, + 'cap_dynamic' : '', + 'cap_ext_next' : '', + 'remote_as' : '123', + 'adv_interv' : '400', + 'passive' : '', + 'password' : 'VyOS-Secure123', + 'shutdown' : '', + 'cap_over' : '', + 'ttl_security' : '5', + 'local_as' : '300', + 'solo' : '', + 'route_map_in' : route_map_in, + 'route_map_out' : route_map_out, 'no_send_comm_std' : '', 'addpath_per_as' : '', - 'peer_group' : 'foo-bar', + 'peer_group' : 'foo-bar', }, '2001:db8::2' : { - 'remote_as' : '456', - 'shutdown' : '', - 'no_cap_nego' : '', - 'port' : '667', - 'cap_strict' : '', - 'pfx_list_in' : prefix_list_in6, - 'pfx_list_out' : prefix_list_out6, + 'remote_as' : '456', + 'shutdown' : '', + 'no_cap_nego' : '', + 'port' : '667', + 'cap_strict' : '', + 'pfx_list_in' : prefix_list_in6, + 'pfx_list_out' : prefix_list_out6, 'no_send_comm_ext' : '', - 'peer_group' : 'foo-bar_baz', + 'peer_group' : 'foo-bar_baz', + 'graceful_rst_hlp' : '' }, } peer_group_config = { 'foo' : { - 'advertise_map': route_map_in, - 'exist_map' : route_map_out, - 'bfd' : '', - 'remote_as' : '100', - 'passive' : '', - 'password' : 'VyOS-Secure123', - 'shutdown' : '', - 'cap_over' : '', - 'ttl_security': '5', + 'advertise_map' : route_map_in, + 'exist_map' : route_map_out, + 'bfd' : '', + 'remote_as' : '100', + 'passive' : '', + 'password' : 'VyOS-Secure123', + 'shutdown' : '', + 'cap_over' : '', + 'ttl_security' : '5', + }, + 'bar' : { + 'remote_as' : '111', + 'graceful_rst_no' : '' }, 'foo-bar' : { - 'advertise_map': route_map_in, - 'description' : 'foo peer bar group', - 'remote_as' : '200', - 'shutdown' : '', - 'no_cap_nego' : '', - 'local_as' : '300', - 'pfx_list_in' : prefix_list_in, - 'pfx_list_out' : prefix_list_out, + 'advertise_map' : route_map_in, + 'description' : 'foo peer bar group', + 'remote_as' : '200', + 'shutdown' : '', + 'no_cap_nego' : '', + 'local_as' : '300', + 'pfx_list_in' : prefix_list_in, + 'pfx_list_out' : prefix_list_out, 'no_send_comm_ext' : '', }, 'foo-bar_baz' : { - 'advertise_map': route_map_in, - 'non_exist_map': route_map_out, - 'bfd_profile' : bfd_profile, - 'cap_dynamic' : '', - 'cap_ext_next' : '', - 'remote_as' : '200', - 'passive' : '', - 'multi_hop' : '5', - 'update_src' : 'lo', - 'route_map_in' : route_map_in, - 'route_map_out': route_map_out, + 'advertise_map' : route_map_in, + 'non_exist_map' : route_map_out, + 'bfd_profile' : bfd_profile, + 'cap_dynamic' : '', + 'cap_ext_next' : '', + 'remote_as' : '200', + 'passive' : '', + 'multi_hop' : '5', + 'update_src' : 'lo', + 'route_map_in' : route_map_in, + 'route_map_out' : route_map_out, }, } @@ -239,6 +245,12 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase): if 'non_exist_map' in peer_config: base = f'{base} non-exist-map {peer_config["non_exist_map"]}' self.assertIn(base, frrconfig) + if 'graceful_rst' in peer_config: + self.assertIn(f' neighbor {peer} graceful-restart', frrconfig) + if 'graceful_rst_no' in peer_config: + self.assertIn(f' neighbor {peer} graceful-restart-disable', frrconfig) + if 'graceful_rst_hlp' in peer_config: + self.assertIn(f' neighbor {peer} graceful-restart-helper', frrconfig) def test_bgp_01_simple(self): router_id = '127.0.0.1' @@ -320,6 +332,8 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase): def test_bgp_02_neighbors(self): # Test out individual neighbor configuration items, not all of them are # also available to a peer-group! + self.cli_set(base_path + ['parameters', 'deterministic-med']) + for peer, peer_config in neighbor_config.items(): afi = 'ipv4-unicast' if is_ipv6(peer): @@ -380,6 +394,12 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase): self.cli_set(base_path + ['neighbor', peer, 'address-family', afi, 'addpath-tx-all']) if 'addpath_per_as' in peer_config: self.cli_set(base_path + ['neighbor', peer, 'address-family', afi, 'addpath-tx-per-as']) + if 'graceful_rst' in peer_config: + self.cli_set(base_path + ['neighbor', peer, 'graceful-restart', 'enable']) + if 'graceful_rst_no' in peer_config: + self.cli_set(base_path + ['neighbor', peer, 'graceful-restart', 'disable']) + if 'graceful_rst_hlp' in peer_config: + self.cli_set(base_path + ['neighbor', peer, 'graceful-restart', 'restart-helper']) # Conditional advertisement if 'advertise_map' in peer_config: @@ -462,6 +482,12 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase): self.cli_set(base_path + ['peer-group', peer_group, 'address-family', 'ipv4-unicast', 'addpath-tx-all']) if 'addpath_per_as' in config: self.cli_set(base_path + ['peer-group', peer_group, 'address-family', 'ipv4-unicast', 'addpath-tx-per-as']) + if 'graceful_rst' in config: + self.cli_set(base_path + ['peer-group', peer_group, 'graceful-restart', 'enable']) + if 'graceful_rst_no' in config: + self.cli_set(base_path + ['peer-group', peer_group, 'graceful-restart', 'disable']) + if 'graceful_rst_hlp' in config: + self.cli_set(base_path + ['peer-group', peer_group, 'graceful-restart', 'restart-helper']) # Conditional advertisement if 'advertise_map' in config: @@ -481,6 +507,7 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase): if 'peer_group' in peer_config: self.cli_set(base_path + ['neighbor', peer, 'peer-group', peer_config['peer_group']]) + # commit changes self.cli_commit() @@ -856,4 +883,4 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase): self.assertIn(f' exit-address-family', afi_config) if __name__ == '__main__': - unittest.main(verbosity=2) + unittest.main(verbosity=2, failfast=True) diff --git a/smoketest/scripts/cli/test_protocols_isis.py b/smoketest/scripts/cli/test_protocols_isis.py index 7f51c7178..11c765793 100755 --- a/smoketest/scripts/cli/test_protocols_isis.py +++ b/smoketest/scripts/cli/test_protocols_isis.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2021 VyOS maintainers and contributors +# Copyright (C) 2021-2022 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 @@ -35,6 +35,10 @@ class TestProtocolsISIS(VyOSUnitTestSHIM.TestCase): # call base-classes classmethod super(cls, cls).setUpClass() + # ensure we can also run this test on a live system - so lets clean + # out the current configuration :) + cls.cli_delete(cls, base_path) + def tearDown(self): self.cli_delete(base_path) self.cli_commit() @@ -71,13 +75,13 @@ class TestProtocolsISIS(VyOSUnitTestSHIM.TestCase): self.cli_commit() # Verify all changes - tmp = self.getFRRconfig(f'router isis {domain}') + tmp = self.getFRRconfig(f'router isis {domain}', daemon='isisd') self.assertIn(f' net {net}', tmp) self.assertIn(f' log-adjacency-changes', tmp) self.assertIn(f' redistribute ipv4 connected level-2 route-map {route_map}', tmp) for interface in self._interfaces: - tmp = self.getFRRconfig(f'interface {interface}') + tmp = self.getFRRconfig(f'interface {interface}', daemon='isisd') self.assertIn(f' ip router isis {domain}', tmp) self.assertIn(f' ipv6 router isis {domain}', tmp) @@ -104,11 +108,11 @@ class TestProtocolsISIS(VyOSUnitTestSHIM.TestCase): self.cli_commit() # Verify FRR isisd configuration - tmp = self.getFRRconfig(f'router isis {domain}') + tmp = self.getFRRconfig(f'router isis {domain}', daemon='isisd') self.assertIn(f'router isis {domain}', tmp) self.assertIn(f' net {net}', tmp) - tmp = self.getFRRconfig(f'router isis {domain} vrf {vrf}') + tmp = self.getFRRconfig(f'router isis {domain} vrf {vrf}', daemon='isisd') self.assertIn(f'router isis {domain} vrf {vrf}', tmp) self.assertIn(f' net {net}', tmp) @@ -124,22 +128,26 @@ class TestProtocolsISIS(VyOSUnitTestSHIM.TestCase): self.isis_base_config() self.cli_set(base_path + ['redistribute', 'ipv4', 'connected', 'level-2', 'route-map', route_map]) self.cli_set(base_path + ['route-map', route_map]) + self.cli_set(base_path + ['level', 'level-2']) # commit changes self.cli_commit() # Verify FRR configuration zebra_route_map = f'ip protocol isis route-map {route_map}' - frrconfig = self.getFRRconfig(zebra_route_map) + frrconfig = self.getFRRconfig(zebra_route_map, daemon='zebra') self.assertIn(zebra_route_map, frrconfig) + tmp = self.getFRRconfig(f'router isis {domain}', daemon='isisd') + self.assertIn(' is-type level-2-only', tmp) + # Remove the route-map again self.cli_delete(base_path + ['route-map']) # commit changes self.cli_commit() # Verify FRR configuration - frrconfig = self.getFRRconfig(zebra_route_map) + frrconfig = self.getFRRconfig(zebra_route_map, daemon='zebra') self.assertNotIn(zebra_route_map, frrconfig) self.cli_delete(['policy', 'route-map', route_map]) @@ -159,7 +167,7 @@ class TestProtocolsISIS(VyOSUnitTestSHIM.TestCase): self.cli_commit() # Verify all changes - tmp = self.getFRRconfig(f'router isis {domain}') + tmp = self.getFRRconfig(f'router isis {domain}', daemon='isisd') self.assertIn(f' net {net}', tmp) for afi in ['ipv4', 'ipv6']: @@ -172,6 +180,8 @@ class TestProtocolsISIS(VyOSUnitTestSHIM.TestCase): password = 'foo' self.isis_base_config() + for interface in self._interfaces: + self.cli_set(base_path + ['interface', interface, 'password', 'plaintext-password', f'{password}-{interface}']) self.cli_set(base_path + ['area-password', 'plaintext-password', password]) self.cli_set(base_path + ['area-password', 'md5', password]) @@ -192,11 +202,14 @@ class TestProtocolsISIS(VyOSUnitTestSHIM.TestCase): self.cli_commit() # Verify all changes - tmp = self.getFRRconfig(f'router isis {domain}') + tmp = self.getFRRconfig(f'router isis {domain}', daemon='isisd') self.assertIn(f' net {net}', tmp) self.assertIn(f' domain-password clear {password}', tmp) self.assertIn(f' area-password clear {password}', tmp) + for interface in self._interfaces: + tmp = self.getFRRconfig(f'interface {interface}', daemon='isisd') + self.assertIn(f' isis password clear {password}-{interface}', tmp) def test_isis_06_spf_delay_bfd(self): network = 'point-to-point' @@ -237,12 +250,12 @@ class TestProtocolsISIS(VyOSUnitTestSHIM.TestCase): self.cli_commit() # Verify all changes - tmp = self.getFRRconfig(f'router isis {domain}') + tmp = self.getFRRconfig(f'router isis {domain}', daemon='isisd') self.assertIn(f' net {net}', tmp) self.assertIn(f' spf-delay-ietf init-delay {init_delay} short-delay {short_delay} long-delay {long_delay} holddown {holddown} time-to-learn {time_to_learn}', tmp) for interface in self._interfaces: - tmp = self.getFRRconfig(f'interface {interface}') + tmp = self.getFRRconfig(f'interface {interface}', daemon='isisd') self.assertIn(f' ip router isis {domain}', tmp) self.assertIn(f' ipv6 router isis {domain}', tmp) self.assertIn(f' isis network {network}', tmp) diff --git a/smoketest/scripts/cli/test_protocols_ospf.py b/smoketest/scripts/cli/test_protocols_ospf.py index 5d8e9cff2..e433d06d0 100755 --- a/smoketest/scripts/cli/test_protocols_ospf.py +++ b/smoketest/scripts/cli/test_protocols_ospf.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2021 VyOS maintainers and contributors +# Copyright (C) 2021-2022 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 @@ -40,6 +40,10 @@ class TestProtocolsOSPF(VyOSUnitTestSHIM.TestCase): cls.cli_set(cls, ['policy', 'route-map', route_map, 'rule', '10', 'action', 'permit']) cls.cli_set(cls, ['policy', 'route-map', route_map, 'rule', '20', 'action', 'permit']) + # ensure we can also run this test on a live system - so lets clean + # out the current configuration :) + cls.cli_delete(cls, base_path) + @classmethod def tearDownClass(cls): cls.cli_delete(cls, ['policy', 'route-map', route_map]) diff --git a/smoketest/scripts/cli/test_protocols_ospfv3.py b/smoketest/scripts/cli/test_protocols_ospfv3.py index 2fc694fd7..944190089 100755 --- a/smoketest/scripts/cli/test_protocols_ospfv3.py +++ b/smoketest/scripts/cli/test_protocols_ospfv3.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2021 VyOS maintainers and contributors +# Copyright (C) 2021-2022 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 @@ -38,6 +38,10 @@ class TestProtocolsOSPFv3(VyOSUnitTestSHIM.TestCase): cls.cli_set(cls, ['policy', 'route-map', route_map, 'rule', '10', 'action', 'permit']) cls.cli_set(cls, ['policy', 'route-map', route_map, 'rule', '20', 'action', 'permit']) + # ensure we can also run this test on a live system - so lets clean + # out the current configuration :) + cls.cli_delete(cls, base_path) + @classmethod def tearDownClass(cls): cls.cli_delete(cls, ['policy', 'route-map', route_map]) @@ -70,7 +74,7 @@ class TestProtocolsOSPFv3(VyOSUnitTestSHIM.TestCase): self.cli_commit() # Verify FRR ospfd configuration - frrconfig = self.getFRRconfig('router ospf6') + frrconfig = self.getFRRconfig('router ospf6', daemon='ospf6d') self.assertIn(f'router ospf6', frrconfig) self.assertIn(f' area {default_area} range {prefix}', frrconfig) self.assertIn(f' ospf6 router-id {router_id}', frrconfig) @@ -78,7 +82,7 @@ class TestProtocolsOSPFv3(VyOSUnitTestSHIM.TestCase): self.assertIn(f' area {default_area} export-list {acl_name}', frrconfig) for interface in interfaces: - if_config = self.getFRRconfig(f'interface {interface}') + if_config = self.getFRRconfig(f'interface {interface}', daemon='ospf6d') self.assertIn(f'ipv6 ospf6 area {default_area}', if_config) self.cli_delete(['policy', 'access-list6', acl_name]) @@ -99,7 +103,7 @@ class TestProtocolsOSPFv3(VyOSUnitTestSHIM.TestCase): self.cli_commit() # Verify FRR ospfd configuration - frrconfig = self.getFRRconfig('router ospf6') + frrconfig = self.getFRRconfig('router ospf6', daemon='ospf6d') self.assertIn(f'router ospf6', frrconfig) self.assertIn(f' distance {dist_global}', frrconfig) self.assertIn(f' distance ospf6 intra-area {dist_intra_area} inter-area {dist_inter_area} external {dist_external}', frrconfig) @@ -119,7 +123,7 @@ class TestProtocolsOSPFv3(VyOSUnitTestSHIM.TestCase): self.cli_commit() # Verify FRR ospfd configuration - frrconfig = self.getFRRconfig('router ospf6') + frrconfig = self.getFRRconfig('router ospf6', daemon='ospf6d') self.assertIn(f'router ospf6', frrconfig) for protocol in redistribute: self.assertIn(f' redistribute {protocol} route-map {route_map}', frrconfig) @@ -150,13 +154,13 @@ class TestProtocolsOSPFv3(VyOSUnitTestSHIM.TestCase): self.cli_commit() # Verify FRR ospfd configuration - frrconfig = self.getFRRconfig('router ospf6') + frrconfig = self.getFRRconfig('router ospf6', daemon='ospf6d') self.assertIn(f'router ospf6', frrconfig) cost = '100' priority = '10' for interface in interfaces: - if_config = self.getFRRconfig(f'interface {interface}') + if_config = self.getFRRconfig(f'interface {interface}', daemon='ospf6d') self.assertIn(f'interface {interface}', if_config) self.assertIn(f' ipv6 ospf6 bfd', if_config) self.assertIn(f' ipv6 ospf6 bfd profile {bfd_profile}', if_config) @@ -180,7 +184,7 @@ class TestProtocolsOSPFv3(VyOSUnitTestSHIM.TestCase): self.cli_commit() # Verify FRR ospfd configuration - frrconfig = self.getFRRconfig('router ospf6') + frrconfig = self.getFRRconfig('router ospf6', daemon='ospf6d') self.assertIn(f'router ospf6', frrconfig) self.assertIn(f' area {area_stub} stub', frrconfig) self.assertIn(f' area {area_stub_nosum} stub no-summary', frrconfig) @@ -206,7 +210,7 @@ class TestProtocolsOSPFv3(VyOSUnitTestSHIM.TestCase): self.cli_commit() # Verify FRR ospfd configuration - frrconfig = self.getFRRconfig('router ospf6') + frrconfig = self.getFRRconfig('router ospf6', daemon='ospf6d') self.assertIn(f'router ospf6', frrconfig) self.assertIn(f' area {area_nssa} nssa', frrconfig) self.assertIn(f' area {area_nssa_nosum} nssa default-information-originate no-summary', frrconfig) @@ -226,7 +230,7 @@ class TestProtocolsOSPFv3(VyOSUnitTestSHIM.TestCase): self.cli_commit() # Verify FRR ospfd configuration - frrconfig = self.getFRRconfig('router ospf6') + frrconfig = self.getFRRconfig('router ospf6', daemon='ospf6d') self.assertIn(f'router ospf6', frrconfig) self.assertIn(f' default-information originate metric {metric} metric-type {metric_type} route-map {route_map}', frrconfig) @@ -235,7 +239,7 @@ class TestProtocolsOSPFv3(VyOSUnitTestSHIM.TestCase): self.cli_commit() # Verify FRR ospfd configuration - frrconfig = self.getFRRconfig('router ospf6') + frrconfig = self.getFRRconfig('router ospf6', daemon='ospf6d') self.assertIn(f' default-information originate always metric {metric} metric-type {metric_type} route-map {route_map}', frrconfig) @@ -261,15 +265,15 @@ class TestProtocolsOSPFv3(VyOSUnitTestSHIM.TestCase): self.cli_commit() # Verify FRR ospfd configuration - frrconfig = self.getFRRconfig('router ospf6') + frrconfig = self.getFRRconfig('router ospf6', daemon='ospf6d') self.assertIn(f'router ospf6', frrconfig) self.assertIn(f' ospf6 router-id {router_id}', frrconfig) - frrconfig = self.getFRRconfig(f'interface {vrf_iface}') + frrconfig = self.getFRRconfig(f'interface {vrf_iface}', daemon='ospf6d') self.assertIn(f'interface {vrf_iface}', frrconfig) self.assertIn(f' ipv6 ospf6 bfd', frrconfig) - frrconfig = self.getFRRconfig(f'router ospf6 vrf {vrf}') + frrconfig = self.getFRRconfig(f'router ospf6 vrf {vrf}', daemon='ospf6d') self.assertIn(f'router ospf6 vrf {vrf}', frrconfig) self.assertIn(f' ospf6 router-id {router_id_vrf}', frrconfig) |