diff --git a/dockers/docker-dhcp-relay/cli-plugin-tests/mock_config.py b/dockers/docker-dhcp-relay/cli-plugin-tests/mock_config.py index ed04367fbba7..1549e1937e09 100644 --- a/dockers/docker-dhcp-relay/cli-plugin-tests/mock_config.py +++ b/dockers/docker-dhcp-relay/cli-plugin-tests/mock_config.py @@ -1,6 +1,6 @@ -TEST_DATA = [ +COMMON_TEST_DATA = [ [ - "DHCPv6_Helpers", + "ipv6_with_header", { "config_db": { "DHCP_RELAY": { @@ -12,7 +12,123 @@ "dhcpv6_option|rfc6939_support": "true" } } - }, + } }, ], + [ + "ipv6_without_header", + { + "config_db": { + "DHCP_RELAY": { + "Vlan1000": { + "dhcpv6_servers": [ + "fc02:2000::1", + "fc02:2000::2" + ], + "dhcpv6_option|rfc6939_support": "true" + } + } + } + }, + ], + [ + "ipv4_with_header", + { + "config_db": { + "VLAN": { + "Vlan1000": { + "dhcp_servers": [ + "192.0.0.1", + "192.0.0.2" + ] + } + } + } + } + ] +] + +NEW_ADDED_TEST_DATA = [ + [ + "ipv6", + { + "config_db": { + "DHCP_RELAY": { + "Vlan1000": { + "dhcpv6_servers": [ + "fc02:2000::1", + "fc02:2000::2" + ], + "dhcpv6_option|rfc6939_support": "true" + } + } + } + }, + ], + [ + "ipv4", + { + "config_db": { + "VLAN": { + "Vlan1000": { + "dhcp_servers": [ + "192.0.0.1", + "192.0.0.2" + ] + }, + "Vlan1001": { + "vlanid": "1001" + } + } + } + } + ] +] + +MULTI_TEST_DATA = [ + [ + "ipv6", + { + "config_db": { + "DHCP_RELAY": { + "Vlan1000": { + "dhcpv6_servers": [ + "fc02:2000::1", + "fc02:2000::2" + ], + "dhcpv6_option|rfc6939_support": "true" + }, + "Vlan1001": { + "dhcpv6_servers": [ + "fc02:2000::3", + "fc02:2000::4" + ], + "dhcpv6_option|rfc6939_support": "true" + } + } + } + }, + ], + [ + "ipv4", + { + "config_db": { + "VLAN": { + "Vlan1000": { + "dhcp_servers": [ + "192.0.0.1", + "192.0.0.2" + ] + }, + "Vlan1001": { + "vlanid": "1001", + "dhcp_servers": [ + "192.0.0.3", + "192.0.0.4" + ] + } + } + } + } + ] ] diff --git a/dockers/docker-dhcp-relay/cli-plugin-tests/test_show_dhcp_relay.py b/dockers/docker-dhcp-relay/cli-plugin-tests/test_show_dhcp_relay.py index b8219fcc6ad3..de679972665f 100644 --- a/dockers/docker-dhcp-relay/cli-plugin-tests/test_show_dhcp_relay.py +++ b/dockers/docker-dhcp-relay/cli-plugin-tests/test_show_dhcp_relay.py @@ -1,28 +1,155 @@ -import os +import pytest import sys -import traceback +import os +sys.path.append('../cli/show/plugins/') +import show_dhcp_relay as show +import show.vlan as vlan +from swsscommon import swsscommon +from mock_config import COMMON_TEST_DATA, NEW_ADDED_TEST_DATA, MULTI_TEST_DATA +from parameterized import parameterized +from pyfakefs.fake_filesystem_unittest import patchfs from unittest import mock -from click.testing import CliRunner +try: + sys.path.insert(0, '../../../src/sonic-host-services/tests/common') + from mock_configdb import MockConfigDb + swsscommon.ConfigDBConnector = MockConfigDb +except KeyError: + pass + +expected_ipv6_table_with_header = """\ ++-------------+----------------------+ +| Interface | DHCP Relay Address | ++=============+======================+ +| Vlan1000 | fc02:2000::1 | +| | fc02:2000::2 | ++-------------+----------------------+ +""" + +expected_ipv4_table_with_header = """\ ++-------------+----------------------+ +| Interface | DHCP Relay Address | ++=============+======================+ +| Vlan1000 | 192.0.0.1 | +| | 192.0.0.2 | ++-------------+----------------------+ +""" + +expected_ipv6_table_without_header = """\ +-------- ------------ +Vlan1000 fc02:2000::1 + fc02:2000::2 +-------- ------------ +""" + +expected_ipv6_table_multi_with_header = """\ ++-------------+----------------------+ +| Interface | DHCP Relay Address | ++=============+======================+ +| Vlan1000 | fc02:2000::1 | +| | fc02:2000::2 | ++-------------+----------------------+ +| Vlan1001 | fc02:2000::3 | +| | fc02:2000::4 | ++-------------+----------------------+ +""" + +expected_ipv4_table_multi_with_header = """\ ++-------------+----------------------+ +| Interface | DHCP Relay Address | ++=============+======================+ +| Vlan1000 | 192.0.0.1 | +| | 192.0.0.2 | ++-------------+----------------------+ +| Vlan1001 | 192.0.0.3 | +| | 192.0.0.4 | ++-------------+----------------------+ +""" + +DBCONFIG_PATH = '/var/run/redis/sonic-db/database_config.json' + +IP_VER_TEST_PARAM_MAP = { + "ipv4": { + "entry": "dhcp_servers", + "table": "VLAN" + }, + "ipv6": { + "entry": "dhcpv6_servers", + "table": "DHCP_RELAY" + } +} + + +def test_plugin_registration(): + cli = mock.MagicMock() + show.register(cli) + assert 'DHCP Helper Address' in dict(vlan.VlanBrief.COLUMNS) + + +def test_dhcp_relay_column_output(): + ctx = ( + ({'Vlan1001': {'dhcp_servers': ['192.0.0.1', '192.168.0.2']}}, {}, {}), + (), + ) + assert show.get_dhcp_helper_address(ctx, 'Vlan1001') == '192.0.0.1\n192.168.0.2' -import show.vlan as vlan -from utilities_common.db import Db -sys.path.insert(0, '../cli/show/plugins/') -import show_dhcp_relay +@parameterized.expand(COMMON_TEST_DATA) +@patchfs +def test_show_dhcp_relay(test_name, test_data, fs): + if not os.path.exists(DBCONFIG_PATH): + fs.create_file(DBCONFIG_PATH) + MockConfigDb.set_config_db(test_data["config_db"]) + config_db = MockConfigDb() + ip_version = "ipv4" if "ipv4" in test_name else "ipv6" + table = config_db.get_table(IP_VER_TEST_PARAM_MAP[ip_version]["table"]) + if test_name == "ipv4_with_header": + result = show.get_dhcp_relay_data_with_header(table, IP_VER_TEST_PARAM_MAP[ip_version]["entry"]) + expected_output = expected_ipv4_table_with_header + elif test_name == "ipv6_with_header": + result = show.get_dhcp_relay_data_with_header(table, IP_VER_TEST_PARAM_MAP[ip_version]["entry"]) + expected_output = expected_ipv6_table_with_header + elif test_name == "ipv6_without_header": + result = show.get_data(table, "Vlan1000") + expected_output = expected_ipv6_table_without_header + assert result == expected_output -class TestVlanDhcpRelay(object): - def test_plugin_registration(self): - cli = mock.MagicMock() - show_dhcp_relay.register(cli) - assert 'DHCP Helper Address' in dict(vlan.VlanBrief.COLUMNS) +@parameterized.expand(NEW_ADDED_TEST_DATA) +@patchfs +def test_show_new_added_dhcp_relay(test_name, test_data, fs): + if not os.path.exists(DBCONFIG_PATH): + fs.create_file(DBCONFIG_PATH) + MockConfigDb.set_config_db(test_data["config_db"]) + config_db = MockConfigDb() + ip_version = test_name + table = config_db.get_table(IP_VER_TEST_PARAM_MAP[ip_version]["table"]) + if ip_version == "ipv4": + result = show.get_dhcp_relay_data_with_header(table, IP_VER_TEST_PARAM_MAP[ip_version]["entry"]) + expected_output = expected_ipv4_table_with_header + assert result == expected_output + else: + result = show.get_dhcp_relay_data_with_header(table, IP_VER_TEST_PARAM_MAP[ip_version]["entry"]) + expected_output = expected_ipv6_table_with_header + assert result == expected_output - def test_dhcp_relay_column_output(self): - ctx = ( - ({'Vlan100': {'dhcp_servers': ['192.0.0.1', '192.168.0.2']}}, {}, {}), - (), - ) - assert show_dhcp_relay.get_dhcp_helper_address(ctx, 'Vlan100') == '192.0.0.1\n192.168.0.2' + result = show.get_data(table, "Vlan1001") + expected_output = "" + assert result == expected_output +@parameterized.expand(MULTI_TEST_DATA) +@patchfs +def test_show_multi_dhcp_relay(test_name, test_data, fs): + if not os.path.exists(DBCONFIG_PATH): + fs.create_file(DBCONFIG_PATH) + MockConfigDb.set_config_db(test_data["config_db"]) + config_db = MockConfigDb() + ip_version = test_name + table = config_db.get_table(IP_VER_TEST_PARAM_MAP[ip_version]["table"]) + result = show.get_dhcp_relay_data_with_header(table, IP_VER_TEST_PARAM_MAP[ip_version]["entry"]) + if ip_version == "ipv4": + expected_output = expected_ipv4_table_multi_with_header + else: + expected_output = expected_ipv6_table_multi_with_header + assert result == expected_output diff --git a/dockers/docker-dhcp-relay/cli-plugin-tests/test_show_dhcpv6_helper.py b/dockers/docker-dhcp-relay/cli-plugin-tests/test_show_dhcpv6_helper.py deleted file mode 100644 index fcb15b98082d..000000000000 --- a/dockers/docker-dhcp-relay/cli-plugin-tests/test_show_dhcpv6_helper.py +++ /dev/null @@ -1,41 +0,0 @@ -import pytest -import sys -import os -sys.path.append('../cli/show/plugins/') -import show_dhcp_relay as show -from click.testing import CliRunner -from swsscommon import swsscommon -from mock_config import TEST_DATA -from parameterized import parameterized -from pyfakefs.fake_filesystem_unittest import patchfs - -try: - sys.path.insert(0, '../../../src/sonic-host-services/tests/common') - from mock_configdb import MockConfigDb - swsscommon.ConfigDBConnector = MockConfigDb -except KeyError: - pass - -expected_table = """\ --------- ------------ -Vlan1000 fc02:2000::1 - fc02:2000::2 --------- ------------ -""" - -DBCONFIG_PATH = '/var/run/redis/sonic-db/database_config.json' - -class TestDhcpRelayHelper(object): - - @parameterized.expand(TEST_DATA) - @patchfs - def test_show_dhcpv6_helper(self, test_name, test_data, fs): - if not os.path.exists(DBCONFIG_PATH): - fs.create_file(DBCONFIG_PATH) - MockConfigDb.set_config_db(test_data["config_db"]) - runner = CliRunner() - config_db = MockConfigDb() - table = config_db.get_table("DHCP_RELAY") - result = show.get_data(table, "Vlan1000") - assert result == expected_table - diff --git a/dockers/docker-dhcp-relay/cli/clear/plugins/clear_dhcp6relay_counter.py b/dockers/docker-dhcp-relay/cli/clear/plugins/clear_dhcp6relay_counter.py index 59bdbbf2cfb5..bfa5af45ae77 100644 --- a/dockers/docker-dhcp-relay/cli/clear/plugins/clear_dhcp6relay_counter.py +++ b/dockers/docker-dhcp-relay/cli/clear/plugins/clear_dhcp6relay_counter.py @@ -1,4 +1,3 @@ -import sys import click import importlib dhcp6_relay = importlib.import_module('show.plugins.dhcp-relay') @@ -6,27 +5,52 @@ import utilities_common.cli as clicommon +def clear_dhcp_relay_ipv6_counter(interface): + counter = dhcp6_relay.DHCPv6_Counter() + counter_intf = counter.get_interface() + + if interface: + counter.clear_table(interface) + else: + for intf in counter_intf: + counter.clear_table(intf) + + # sonic-clear dhcp6relay_counters @click.group(cls=clicommon.AliasedGroup) def dhcp6relay_clear(): pass + @dhcp6relay_clear.command('dhcp6relay_counters') @click.option('-i', '--interface', required=False) def dhcp6relay_clear_counters(interface): """ Clear dhcp6relay message counts """ + clear_dhcp_relay_ipv6_counter(interface) - counter = dhcp6_relay.DHCPv6_Counter() - counter_intf = counter.get_interface() - if interface: - counter.clear_table(interface) - else: - for intf in counter_intf: - counter.clear_table(intf) +@click.group(cls=clicommon.AliasedGroup, name="dhcp_relay") +def dhcp_relay(): + pass + + +@dhcp_relay.group(cls=clicommon.AliasedGroup, name="ipv6") +def dhcp_relay_ipv6(): + pass + + +@dhcp_relay_ipv6.command('counters') +@click.option('-i', '--interface', required=False) +def clear_dhcp_relay_ipv6_counters(interface): + """ Clear dhcp_relay ipv6 message counts """ + clear_dhcp_relay_ipv6_counter(interface) + def register(cli): cli.add_command(dhcp6relay_clear_counters) + cli.add_command(dhcp_relay) + if __name__ == '__main__': dhcp6relay_clear_counters() + dhcp_relay() diff --git a/dockers/docker-dhcp-relay/cli/show/plugins/show_dhcp_relay.py b/dockers/docker-dhcp-relay/cli/show/plugins/show_dhcp_relay.py index 4178915fe0b2..d76d5f6fa6b8 100644 --- a/dockers/docker-dhcp-relay/cli/show/plugins/show_dhcp_relay.py +++ b/dockers/docker-dhcp-relay/cli/show/plugins/show_dhcp_relay.py @@ -1,7 +1,7 @@ import click from natsort import natsorted from tabulate import tabulate -import show.vlan as vlan +import show.vlan as show_vlan import utilities_common.cli as clicommon from swsscommon.swsscommon import ConfigDBConnector @@ -12,12 +12,17 @@ DHCPv6_COUNTER_TABLE = 'DHCPv6_COUNTER_TABLE' # DHCPv6 Counter Messages -messages = ["Unknown", "Solicit", "Advertise", "Request", "Confirm", "Renew", "Rebind", "Reply", "Release", "Decline", "Reconfigure", "Information-Request", "Relay-Forward", "Relay-Reply", "Malformed"] +messages = ["Unknown", "Solicit", "Advertise", "Request", "Confirm", "Renew", "Rebind", "Reply", "Release", "Decline", + "Reconfigure", "Information-Request", "Relay-Forward", "Relay-Reply", "Malformed"] # DHCP_RELAY Config Table DHCP_RELAY = 'DHCP_RELAY' +VLAN = "VLAN" +DHCPV6_SERVERS = "dhcpv6_servers" +DHCPV4_SERVERS = "dhcp_servers" config_db = ConfigDBConnector() + def get_dhcp_helper_address(ctx, vlan): cfg, _ = ctx vlan_dhcp_helper_data, _, _ = cfg @@ -30,7 +35,7 @@ def get_dhcp_helper_address(ctx, vlan): return '\n'.join(natsorted(dhcp_helpers)) -vlan.VlanBrief.register_column('DHCP Helper Address', get_dhcp_helper_address) +show_vlan.VlanBrief.register_column('DHCP Helper Address', get_dhcp_helper_address) class DHCPv6_Counter(object): @@ -39,7 +44,6 @@ def __init__(self): self.db.connect(self.db.STATE_DB) self.table_name = DHCPv6_COUNTER_TABLE + self.db.get_db_separator(self.db.STATE_DB) - def get_interface(self): """ Get all names of all interfaces in DHCPv6_COUNTER_TABLE """ vlans = [] @@ -47,7 +51,6 @@ def get_interface(self): if DHCPv6_COUNTER_TABLE in key: vlans.append(key[21:]) return vlans - def get_dhcp6relay_msg_count(self, interface, msg): """ Get count of a dhcp6relay message """ @@ -55,18 +58,18 @@ def get_dhcp6relay_msg_count(self, interface, msg): data = [str(msg), count] return data - def clear_table(self, interface): """ Reset all message counts to 0 """ for msg in messages: - self.db.set(self.db.STATE_DB, self.table_name + str(interface), str(msg), '0') + self.db.set(self.db.STATE_DB, self.table_name + str(interface), str(msg), '0') + def print_count(counter, intf): """Print count of each message""" data = [] for i in messages: data.append(counter.get_dhcp6relay_msg_count(intf, i)) - print(tabulate(data, headers = ["Message Type", intf], tablefmt='simple', stralign='right') + "\n") + print(tabulate(data, headers=["Message Type", intf], tablefmt='simple', stralign='right') + "\n") # @@ -80,13 +83,7 @@ def dhcp6relay_counters(): pass -# 'counts' subcommand ("show dhcp6relay_counters counts") -@dhcp6relay_counters.command('counts') -@click.option('-i', '--interface', required=False) -@click.option('--verbose', is_flag=True, help="Enable verbose output") -def counts(interface, verbose): - """Show dhcp6relay message counts""" - +def ipv6_counters(interface): counter = DHCPv6_Counter() counter_intf = counter.get_interface() @@ -94,38 +91,114 @@ def counts(interface, verbose): print_count(counter, interface) else: for intf in counter_intf: - print_count(counter, intf) + print_count(counter, intf) +# 'counts' subcommand ("show dhcp6relay_counters counts") +@dhcp6relay_counters.command('counts') +@click.option('-i', '--interface', required=False) +@click.option('--verbose', is_flag=True, help="Enable verbose output") +def counts(interface, verbose): + """Show dhcp6relay message counts""" + + ipv6_counters(interface) + @click.group(cls=clicommon.AliasedGroup, name="dhcprelay_helper") def dhcp_relay_helper(): """Show DHCP_Relay helper information""" pass + +def get_dhcp_relay_data_with_header(table_data, entry_name): + vlan_relay = {} + vlans = table_data.keys() + for vlan in vlans: + vlan_data = table_data.get(vlan) + dhcp_relay_data = vlan_data.get(entry_name) + if dhcp_relay_data is None or len(dhcp_relay_data) == 0: + continue + + vlan_relay[vlan] = [] + for address in dhcp_relay_data: + vlan_relay[vlan].append(address) + + dhcp_relay_vlan_keys = vlan_relay.keys() + relay_address_list = ["\n".join(vlan_relay[key]) for key in dhcp_relay_vlan_keys] + data = {"Interface": dhcp_relay_vlan_keys, "DHCP Relay Address": relay_address_list} + return tabulate(data, tablefmt='grid', stralign='right', headers='keys') + '\n' + + +def get_dhcp_relay(table_name, entry_name, with_header): + if config_db is None: + return + + config_db.connect() + table_data = config_db.get_table(table_name) + if table_data is None: + return + + if with_header: + output = get_dhcp_relay_data_with_header(table_data, entry_name) + print(output) + else: + vlans = config_db.get_keys(table_name) + for vlan in vlans: + output = get_data(table_data, vlan) + print(output) + + @dhcp_relay_helper.command('ipv6') def get_dhcpv6_helper_address(): """Parse through DHCP_RELAY table for each interface in config_db.json and print dhcpv6 helpers in table format""" - if config_db is not None: - config_db.connect() - table_data = config_db.get_table(DHCP_RELAY) - if table_data is not None: - vlans = config_db.get_keys(DHCP_RELAY) - for vlan in vlans: - output = get_data(table_data, vlan) - print(output) - + get_dhcp_relay(DHCP_RELAY, DHCPV6_SERVERS, with_header=False) + def get_data(table_data, vlan): - vlan_data = table_data.get(vlan) + vlan_data = table_data.get(vlan, {}) helpers_data = vlan_data.get('dhcpv6_servers') + addr = {vlan:[]} + output = '' if helpers_data is not None: - addr = {vlan:[]} for ip in helpers_data: addr[vlan].append(ip) - output = tabulate({'Interface':[vlan], vlan:addr.get(vlan)}, tablefmt='simple', stralign='right') + '\n' + output = tabulate({'Interface':[vlan], vlan:addr.get(vlan)}, tablefmt='simple', stralign='right') + '\n' return output + +@click.group(cls=clicommon.AliasedGroup, name="dhcp_relay") +def dhcp_relay(): + """show DHCP_Relay information""" + pass + + +@dhcp_relay.group(cls=clicommon.AliasedGroup, name="ipv6") +def dhcp_relay_ipv6(): + pass + + +@dhcp_relay.group(cls=clicommon.AliasedGroup, name="ipv4") +def dhcp_relay_ipv4(): + pass + + +@dhcp_relay_ipv4.command("helper") +def dhcp_relay_ipv4_destination(): + get_dhcp_relay(VLAN, DHCPV4_SERVERS, with_header=True) + + +@dhcp_relay_ipv6.command("destination") +def dhcp_relay_ipv6_destination(): + get_dhcp_relay(DHCP_RELAY, DHCPV6_SERVERS, with_header=True) + + +@dhcp_relay_ipv6.command("counters") +@click.option('-i', '--interface', required=False) +def dhcp_relay_ip6counters(interface): + ipv6_counters(interface) + + def register(cli): cli.add_command(dhcp6relay_counters) cli.add_command(dhcp_relay_helper) + cli.add_command(dhcp_relay)