summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--interface-definitions/service_dhcp-server.xml.in21
-rw-r--r--python/vyos/template.py15
-rwxr-xr-xsmoketest/scripts/cli/test_protocols_bgp.py71
-rwxr-xr-xsmoketest/scripts/cli/test_service_dhcp-server.py65
-rwxr-xr-xsrc/conf_mode/protocols_bgp.py13
-rwxr-xr-xsrc/conf_mode/service_dhcp-server.py8
6 files changed, 187 insertions, 6 deletions
diff --git a/interface-definitions/service_dhcp-server.xml.in b/interface-definitions/service_dhcp-server.xml.in
index 2afa05a8a..cb5f9a804 100644
--- a/interface-definitions/service_dhcp-server.xml.in
+++ b/interface-definitions/service_dhcp-server.xml.in
@@ -22,6 +22,27 @@
</properties>
<children>
#include <include/source-address-ipv4.xml.i>
+ <leafNode name="mode">
+ <properties>
+ <help>Configure high availability mode</help>
+ <completionHelp>
+ <list>active-active active-passive</list>
+ </completionHelp>
+ <valueHelp>
+ <format>active-active</format>
+ <description>Both server attend DHCP requests</description>
+ </valueHelp>
+ <valueHelp>
+ <format>active-passive</format>
+ <description>Only primary server attends DHCP requests</description>
+ </valueHelp>
+ <constraint>
+ <regex>(active-active|active-passive)</regex>
+ </constraint>
+ <constraintErrorMessage>Invalid DHCP high availability mode</constraintErrorMessage>
+ </properties>
+ <defaultValue>active-active</defaultValue>
+ </leafNode>
<leafNode name="remote">
<properties>
<help>IPv4 remote address used for connection</help>
diff --git a/python/vyos/template.py b/python/vyos/template.py
index 3e468eb82..ac77e8a3d 100644
--- a/python/vyos/template.py
+++ b/python/vyos/template.py
@@ -823,10 +823,19 @@ def kea_high_availability_json(config):
source_addr = config['source_address']
remote_addr = config['remote']
+ ha_mode = 'hot-standby' if config['mode'] == 'active-passive' else 'load-balancing'
+ ha_role = config['status']
+
+ if ha_role == 'primary':
+ peer1_role = 'primary'
+ peer2_role = 'standby' if ha_mode == 'hot-standby' else 'secondary'
+ else:
+ peer1_role = 'standby' if ha_mode == 'hot-standby' else 'secondary'
+ peer2_role = 'primary'
data = {
'this-server-name': os.uname()[1],
- 'mode': 'hot-standby',
+ 'mode': ha_mode,
'heartbeat-delay': 10000,
'max-response-delay': 10000,
'max-ack-delay': 5000,
@@ -835,13 +844,13 @@ def kea_high_availability_json(config):
{
'name': os.uname()[1],
'url': f'http://{source_addr}:647/',
- 'role': 'standby' if config['status'] == 'secondary' else 'primary',
+ 'role': peer1_role,
'auto-failover': True
},
{
'name': config['name'],
'url': f'http://{remote_addr}:647/',
- 'role': 'primary' if config['status'] == 'secondary' else 'standby',
+ 'role': peer2_role,
'auto-failover': True
}]
}
diff --git a/smoketest/scripts/cli/test_protocols_bgp.py b/smoketest/scripts/cli/test_protocols_bgp.py
index 60c49b8b4..03daa34aa 100755
--- a/smoketest/scripts/cli/test_protocols_bgp.py
+++ b/smoketest/scripts/cli/test_protocols_bgp.py
@@ -1259,6 +1259,77 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase):
self.assertIn('neighbor peer1 route-reflector-client', conf)
+ def test_bgp_28_peer_group_member_all_internal_or_external(self):
+ def _common_config_check(conf, include_ras=True):
+ if include_ras:
+ self.assertIn(f'neighbor {int_neighbors[0]} remote-as {ASN}', conf)
+ self.assertIn(f'neighbor {int_neighbors[1]} remote-as {ASN}', conf)
+ self.assertIn(f'neighbor {ext_neighbors[0]} remote-as {int(ASN) + 1}',conf)
+
+ self.assertIn(f'neighbor {int_neighbors[0]} peer-group {int_pg_name}', conf)
+ self.assertIn(f'neighbor {int_neighbors[1]} peer-group {int_pg_name}', conf)
+ self.assertIn(f'neighbor {ext_neighbors[0]} peer-group {ext_pg_name}', conf)
+
+ int_neighbors = ['192.0.2.2', '192.0.2.3']
+ ext_neighbors = ['192.122.2.2', '192.122.2.3']
+ int_pg_name, ext_pg_name = 'SMOKETESTINT', 'SMOKETESTEXT'
+
+ self.cli_set(base_path + ['neighbor', int_neighbors[0], 'peer-group', int_pg_name])
+ self.cli_set(base_path + ['neighbor', int_neighbors[0], 'remote-as', ASN])
+ self.cli_set(base_path + ['peer-group', int_pg_name, 'address-family', 'ipv4-unicast'])
+ self.cli_set(base_path + ['neighbor', ext_neighbors[0], 'peer-group', ext_pg_name])
+ self.cli_set(base_path + ['neighbor', ext_neighbors[0], 'remote-as', f'{int(ASN) + 1}'])
+ self.cli_set(base_path + ['peer-group', ext_pg_name, 'address-family', 'ipv4-unicast'])
+ self.cli_commit()
+
+ # test add external remote-as to internal group
+ self.cli_set(base_path + ['neighbor', int_neighbors[1], 'peer-group', int_pg_name])
+ self.cli_set(base_path + ['neighbor', int_neighbors[1], 'remote-as', f'{int(ASN) + 1}'])
+
+ with self.assertRaises(ConfigSessionError) as e:
+ self.cli_commit()
+ # self.assertIn('\nPeer-group members must be all internal or all external\n', str(e.exception))
+
+ # test add internal remote-as to internal group
+ self.cli_set(base_path + ['neighbor', int_neighbors[1], 'remote-as', ASN])
+ self.cli_commit()
+
+ conf = self.getFRRconfig(f'router bgp {ASN}')
+ _common_config_check(conf)
+
+ # test add internal remote-as to external group
+ self.cli_set(base_path + ['neighbor', ext_neighbors[1], 'peer-group', ext_pg_name])
+ self.cli_set(base_path + ['neighbor', ext_neighbors[1], 'remote-as', ASN])
+
+ with self.assertRaises(ConfigSessionError) as e:
+ self.cli_commit()
+ # self.assertIn('\nPeer-group members must be all internal or all external\n', str(e.exception))
+
+ # test add external remote-as to external group
+ self.cli_set(base_path + ['neighbor', ext_neighbors[1], 'remote-as', f'{int(ASN) + 2}'])
+ self.cli_commit()
+
+ conf = self.getFRRconfig(f'router bgp {ASN}')
+ _common_config_check(conf)
+ self.assertIn(f'neighbor {ext_neighbors[1]} remote-as {int(ASN) + 2}', conf)
+ self.assertIn(f'neighbor {ext_neighbors[1]} peer-group {ext_pg_name}', conf)
+
+ # test named remote-as
+ self.cli_set(base_path + ['neighbor', int_neighbors[0], 'remote-as', 'internal'])
+ self.cli_set(base_path + ['neighbor', int_neighbors[1], 'remote-as', 'internal'])
+ self.cli_set(base_path + ['neighbor', ext_neighbors[0], 'remote-as', 'external'])
+ self.cli_set(base_path + ['neighbor', ext_neighbors[1], 'remote-as', 'external'])
+ self.cli_commit()
+
+ conf = self.getFRRconfig(f'router bgp {ASN}')
+ _common_config_check(conf, include_ras=False)
+
+ self.assertIn(f'neighbor {int_neighbors[0]} remote-as internal', conf)
+ self.assertIn(f'neighbor {int_neighbors[1]} remote-as internal', conf)
+ self.assertIn(f'neighbor {ext_neighbors[0]} remote-as external', conf)
+ self.assertIn(f'neighbor {ext_neighbors[1]} remote-as external', conf)
+ self.assertIn(f'neighbor {ext_neighbors[1]} peer-group {ext_pg_name}', conf)
+
def test_bgp_99_bmp(self):
target_name = 'instance-bmp'
target_address = '127.0.0.1'
diff --git a/smoketest/scripts/cli/test_service_dhcp-server.py b/smoketest/scripts/cli/test_service_dhcp-server.py
index abf40cd3b..46c4e25a1 100755
--- a/smoketest/scripts/cli/test_service_dhcp-server.py
+++ b/smoketest/scripts/cli/test_service_dhcp-server.py
@@ -699,6 +699,7 @@ class TestServiceDHCPServer(VyOSUnitTestSHIM.TestCase):
self.cli_set(base_path + ['high-availability', 'name', failover_name])
self.cli_set(base_path + ['high-availability', 'remote', failover_remote])
self.cli_set(base_path + ['high-availability', 'status', 'primary'])
+ ## No mode defined -> its active-active mode by default
# commit changes
self.cli_commit()
@@ -717,7 +718,69 @@ class TestServiceDHCPServer(VyOSUnitTestSHIM.TestCase):
self.verify_config_object(
obj,
['Dhcp4', 'hooks-libraries', 0, 'parameters', 'high-availability', 0, 'peers'],
- {'name': failover_name, 'url': f'http://{failover_remote}:647/', 'role': 'standby', 'auto-failover': True})
+ {'name': failover_name, 'url': f'http://{failover_remote}:647/', 'role': 'secondary', 'auto-failover': True})
+
+ self.verify_config_value(obj, ['Dhcp4', 'shared-networks'], 'name', shared_net_name)
+ self.verify_config_value(obj, ['Dhcp4', 'shared-networks', 0, 'subnet4'], 'subnet', subnet)
+
+ # Verify options
+ self.verify_config_object(
+ obj,
+ ['Dhcp4', 'shared-networks', 0, 'subnet4', 0, 'option-data'],
+ {'name': 'routers', 'data': router})
+
+ # Verify pools
+ self.verify_config_object(
+ obj,
+ ['Dhcp4', 'shared-networks', 0, 'subnet4', 0, 'pools'],
+ {'pool': f'{range_0_start} - {range_0_stop}'})
+
+ # Check for running process
+ self.assertTrue(process_named_running(PROCESS_NAME))
+ self.assertTrue(process_named_running(CTRL_PROCESS_NAME))
+
+ def test_dhcp_high_availability_standby(self):
+ shared_net_name = 'FAILOVER'
+ failover_name = 'VyOS-Failover'
+
+ range_0_start = inc_ip(subnet, 10)
+ range_0_stop = inc_ip(subnet, 20)
+
+ pool = base_path + ['shared-network-name', shared_net_name, 'subnet', subnet]
+ self.cli_set(pool + ['subnet-id', '1'])
+ # we use the first subnet IP address as default gateway
+ self.cli_set(pool + ['option', 'default-router', router])
+ self.cli_set(pool + ['range', '0', 'start', range_0_start])
+ self.cli_set(pool + ['range', '0', 'stop', range_0_stop])
+
+ # failover
+ failover_local = router
+ failover_remote = inc_ip(router, 1)
+
+ self.cli_set(base_path + ['high-availability', 'source-address', failover_local])
+ self.cli_set(base_path + ['high-availability', 'name', failover_name])
+ self.cli_set(base_path + ['high-availability', 'remote', failover_remote])
+ self.cli_set(base_path + ['high-availability', 'status', 'secondary'])
+ self.cli_set(base_path + ['high-availability', 'mode', 'active-passive'])
+
+ # commit changes
+ self.cli_commit()
+
+ config = read_file(KEA4_CONF)
+ obj = loads(config)
+
+ # Verify failover
+ self.verify_config_value(obj, ['Dhcp4', 'control-socket'], 'socket-name', KEA4_CTRL)
+
+ self.verify_config_object(
+ obj,
+ ['Dhcp4', 'hooks-libraries', 0, 'parameters', 'high-availability', 0, 'peers'],
+ {'name': os.uname()[1], 'url': f'http://{failover_local}:647/', 'role': 'standby', 'auto-failover': True})
+
+ self.verify_config_object(
+ obj,
+ ['Dhcp4', 'hooks-libraries', 0, 'parameters', 'high-availability', 0, 'peers'],
+ {'name': failover_name, 'url': f'http://{failover_remote}:647/', 'role': 'primary', 'auto-failover': True})
self.verify_config_value(obj, ['Dhcp4', 'shared-networks'], 'name', shared_net_name)
self.verify_config_value(obj, ['Dhcp4', 'shared-networks', 0, 'subnet4'], 'subnet', subnet)
diff --git a/src/conf_mode/protocols_bgp.py b/src/conf_mode/protocols_bgp.py
index 512fa26e9..2b16de775 100755
--- a/src/conf_mode/protocols_bgp.py
+++ b/src/conf_mode/protocols_bgp.py
@@ -285,6 +285,7 @@ def verify(bgp):
elif tmp != 'default':
raise ConfigError(f'{error_msg} "{tmp}"!')
+ peer_groups_context = dict()
# Common verification for both peer-group and neighbor statements
for neighbor in ['neighbor', 'peer_group']:
# bail out early if there is no neighbor or peer-group statement
@@ -301,6 +302,18 @@ def verify(bgp):
raise ConfigError(f'Specified peer-group "{peer_group}" for '\
f'neighbor "{neighbor}" does not exist!')
+ if 'remote_as' in peer_config:
+ is_ibgp = True
+ if peer_config['remote_as'] != 'internal' and \
+ peer_config['remote_as'] != bgp['system_as']:
+ is_ibgp = False
+
+ if peer_group not in peer_groups_context:
+ peer_groups_context[peer_group] = is_ibgp
+ elif peer_groups_context[peer_group] != is_ibgp:
+ raise ConfigError(f'Peer-group members must be '
+ f'all internal or all external')
+
if 'local_role' in peer_config:
#Ensure Local Role has only one value.
if len(peer_config['local_role']) > 1:
diff --git a/src/conf_mode/service_dhcp-server.py b/src/conf_mode/service_dhcp-server.py
index bf4454fda..f4fb78f57 100755
--- a/src/conf_mode/service_dhcp-server.py
+++ b/src/conf_mode/service_dhcp-server.py
@@ -143,8 +143,12 @@ def get_config(config=None):
dhcp['shared_network_name'][network]['subnet'][subnet].update(
{'range' : new_range_dict})
- if dict_search('high_availability.certificate', dhcp):
- dhcp['pki'] = conf.get_config_dict(['pki'], key_mangling=('-', '_'), get_first_key=True, no_tag_node_value_mangle=True)
+ if len(dhcp['high_availability']) == 1:
+ ## only default value for mode is set, need to remove ha node
+ del dhcp['high_availability']
+ else:
+ if dict_search('high_availability.certificate', dhcp):
+ dhcp['pki'] = conf.get_config_dict(['pki'], key_mangling=('-', '_'), get_first_key=True, no_tag_node_value_mangle=True)
return dhcp