From b1ac46d995e0010554831a855ca392bfe477a6ec Mon Sep 17 00:00:00 2001 From: yaqiangz Date: Mon, 6 Mar 2023 09:35:44 +0000 Subject: [PATCH 1/2] [202012][dhcp_relay] Add dhcp_relay show cli --- clear/main.py | 25 ++++ show/dhcp6relay_counters.py | 95 --------------- show/dhcp_relay.py | 201 +++++++++++++++++++++++++++++++ show/dhcp_relay_helper.py | 37 ------ show/main.py | 8 +- tests/config_dhcp_relay_test.py | 1 + tests/dhcp_relay_helper_test.py | 22 ---- tests/mock_tables/config_db.json | 3 + tests/show_dhcp_relay_test.py | 135 +++++++++++++++++++++ 9 files changed, 369 insertions(+), 158 deletions(-) delete mode 100644 show/dhcp6relay_counters.py create mode 100644 show/dhcp_relay.py delete mode 100644 show/dhcp_relay_helper.py delete mode 100644 tests/dhcp_relay_helper_test.py create mode 100644 tests/show_dhcp_relay_test.py diff --git a/clear/main.py b/clear/main.py index c9189081e6..31eabb4b3d 100755 --- a/clear/main.py +++ b/clear/main.py @@ -455,5 +455,30 @@ def translations(): cmd = "natclear -t" run_command(cmd) + +@cli.group(name="dhcp_relay", cls=AliasedGroup) +def dhcp_relay(): + """Clear dhcp_relay """ + pass + + +@dhcp_relay.group(name="ipv6") +def dhcp_relay_ipv6(): + pass + + +@dhcp_relay_ipv6.command(name="counters") +@click.argument("interface", required=False) +def dhcp_relay_ipv6_counters(interface): + """ Clear dhcp_relay_ipv6 message counts """ + + if interface: + cmd = "show dhcp6relay_counters clear -i " + interface + else: + cmd = "show dhcp6relay_counters clear" + + run_command(cmd) + + if __name__ == '__main__': cli() diff --git a/show/dhcp6relay_counters.py b/show/dhcp6relay_counters.py deleted file mode 100644 index 82bfe1e8d5..0000000000 --- a/show/dhcp6relay_counters.py +++ /dev/null @@ -1,95 +0,0 @@ -import click -import utilities_common.cli as clicommon -from tabulate import tabulate - -from swsscommon.swsscommon import SonicV2Connector - - -# STATE_DB Table -DHCPv6_COUNTER_TABLE = 'DHCPv6_COUNTER_TABLE' - -# DHCPv6 Counter Messages -messages = ["Unknown", "Solicit", "Advertise", "Request", "Confirm", "Renew", "Rebind", "Reply", "Release", "Decline", "Relay-Forward", "Relay-Reply"] - -class DHCPv6_Counter(object): - def __init__(self): - self.db = SonicV2Connector(use_unix_socket_path=False) - 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 = [] - for key in self.db.keys(self.db.STATE_DB): - 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 """ - count = self.db.get(self.db.STATE_DB, self.table_name + str(interface), str(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') - -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") - - -# -# 'dhcp6relay_counters' group ### -# - - -@click.group(cls=clicommon.AliasedGroup, name="dhcp6relay_counters") -def dhcp6relay_counters(): - """Show DHCPv6 counter""" - 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""" - - counter = DHCPv6_Counter() - counter_intf = counter.get_interface() - - if interface: - print_count(counter, interface) - else: - for intf in counter_intf: - print_count(counter, intf) - - -# 'clear' subcommand ("clear dhcp6relay_counters counts") -@dhcp6relay_counters.command('clear') -@click.option('-i', '--interface', required=False) -@click.option('--verbose', is_flag=True, help="Enable verbose output") -def clear(interface, verbose): - """Clear dhcp6relay message counts""" - - counter = DHCPv6_Counter() - counter_intf = counter.get_interface() - - if interface: - counter.clear_table(interface) - print("Cleared DHCPv6 Relay Counter " + interface) - else: - for intf in counter_intf: - counter.clear_table(intf) - print("Cleared DHCPv6 Relay Counters") - diff --git a/show/dhcp_relay.py b/show/dhcp_relay.py new file mode 100644 index 0000000000..4cfe730504 --- /dev/null +++ b/show/dhcp_relay.py @@ -0,0 +1,201 @@ +import click +from tabulate import tabulate +from swsscommon.swsscommon import ConfigDBConnector, SonicV2Connector + + +import utilities_common.cli as clicommon + +DHCP_RELAY = 'DHCP_RELAY' +VLAN = "VLAN" +DHCPV6_SERVERS = "dhcpv6_servers" +DHCPV4_SERVERS = "dhcp_servers" +# STATE_DB Table +DHCPv6_COUNTER_TABLE = 'DHCPv6_COUNTER_TABLE' + +# DHCPv6 Counter Messages +messages = ["Unknown", "Solicit", "Advertise", "Request", "Confirm", "Renew", "Rebind", "Reply", "Release", "Decline", "Relay-Forward", "Relay-Reply"] +config_db = ConfigDBConnector() + + +@click.group(cls=clicommon.AliasedGroup, name="dhcprelay_helper") +def dhcp_relay_helper(): + """Show DHCP_Relay helper information""" + pass + + +@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) + + +def get_data(table_data, vlan): + vlan_data = table_data.get(vlan, {}) + helpers_data = vlan_data.get(DHCPV6_SERVERS) + addr = {vlan: []} + output = '' + if helpers_data is not None: + for ip in helpers_data: + addr[vlan].append(ip) + output = tabulate({'Interface': [vlan], vlan: addr.get(vlan)}, tablefmt='simple', stralign='right') + '\n' + return output + + +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) + + +@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) + + +class DHCPv6_Counter(object): + def __init__(self): + self.db = SonicV2Connector(use_unix_socket_path=False) + 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 = [] + for key in self.db.keys(self.db.STATE_DB): + 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 """ + count = self.db.get(self.db.STATE_DB, self.table_name + str(interface), str(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') + + +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") + + +# +# 'dhcp6relay_counters' group ### +# + + +@click.group(cls=clicommon.AliasedGroup, name="dhcp6relay_counters") +def dhcp6relay_counters(): + """Show DHCPv6 counter""" + 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""" + ipv6_counters(interface) + + +def ipv6_counters(interface): + counter = DHCPv6_Counter() + counter_intf = counter.get_interface() + + if interface: + print_count(counter, interface) + else: + for intf in counter_intf: + print_count(counter, intf) + + +# 'clear' subcommand ("clear dhcp6relay_counters counts") +@dhcp6relay_counters.command('clear') +@click.option('-i', '--interface', required=False) +@click.option('--verbose', is_flag=True, help="Enable verbose output") +def clear(interface, verbose): + """Clear dhcp6relay message counts""" + counter = DHCPv6_Counter() + counter_intf = counter.get_interface() + + if interface: + counter.clear_table(interface) + print("Cleared DHCPv6 Relay Counter " + interface) + else: + for intf in counter_intf: + counter.clear_table(intf) + print("Cleared DHCPv6 Relay Counters") + + +@dhcp_relay_ipv6.command("counters") +@click.option('-i', '--interface', required=False) +def dhcp_relay_ip6counters(interface): + ipv6_counters(interface) diff --git a/show/dhcp_relay_helper.py b/show/dhcp_relay_helper.py deleted file mode 100644 index f1d734ecc7..0000000000 --- a/show/dhcp_relay_helper.py +++ /dev/null @@ -1,37 +0,0 @@ -import click -from tabulate import tabulate -from swsscommon.swsscommon import ConfigDBConnector - -import utilities_common.cli as clicommon - -DHCP_RELAY = 'DHCP_RELAY' -config_db = ConfigDBConnector() - -@click.group(cls=clicommon.AliasedGroup, name="dhcprelay_helper") -def dhcp_relay_helper(): - """Show DHCP_Relay helper information""" - pass - -@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) - - -def get_data(table_data, vlan): - vlan_data = table_data.get(vlan) - helpers_data = vlan_data.get('dhcpv6_servers') - 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' - return output - diff --git a/show/main.py b/show/main.py index 73798cc0be..73d8089ee5 100644 --- a/show/main.py +++ b/show/main.py @@ -19,8 +19,7 @@ from . import acl from . import bgp_common from . import chassis_modules -from . import dhcp6relay_counters -from . import dhcp_relay_helper +from . import dhcp_relay from . import dropcounters from . import feature from . import fgnhg @@ -162,8 +161,9 @@ def cli(ctx): # Add groups from other modules cli.add_command(acl.acl) cli.add_command(chassis_modules.chassis_modules) -cli.add_command(dhcp_relay_helper.dhcp_relay_helper) -cli.add_command(dhcp6relay_counters.dhcp6relay_counters) +cli.add_command(dhcp_relay.dhcp_relay_helper) +cli.add_command(dhcp_relay.dhcp_relay) +cli.add_command(dhcp_relay.dhcp6relay_counters) cli.add_command(dropcounters.dropcounters) cli.add_command(feature.feature) cli.add_command(fgnhg.fgnhg) diff --git a/tests/config_dhcp_relay_test.py b/tests/config_dhcp_relay_test.py index 24ea893734..ef4104d1fa 100644 --- a/tests/config_dhcp_relay_test.py +++ b/tests/config_dhcp_relay_test.py @@ -282,6 +282,7 @@ def test_config_add_dhcp_relay_ipv6_with_non_entry(self): db = Db() table = IP_VER_TEST_PARAM_MAP[ip_version]["table"] db.cfgdb.set_entry(table, "Vlan1000", None) + db.cfgdb.set_entry(table, "Vlan2000", None) assert db.cfgdb.get_entry(table, "Vlan1000") == {} assert len(db.cfgdb.get_keys(table)) == 0 diff --git a/tests/dhcp_relay_helper_test.py b/tests/dhcp_relay_helper_test.py deleted file mode 100644 index 220c433cf5..0000000000 --- a/tests/dhcp_relay_helper_test.py +++ /dev/null @@ -1,22 +0,0 @@ -import sys -import os - -import show.main as show -from click.testing import CliRunner - -expected_table = """\ --------- ------------ -Vlan1000 fc02:2000::1 - fc02:2000::2 --------- ------------ - -""" - -class TestDhcpRelayHelper(object): - - def test_show_dhcpv6_helper(self): - runner = CliRunner() - result = runner.invoke(show.cli.commands["dhcprelay_helper"].commands["ipv6"]) - print(result.output) - assert result.output == expected_table - diff --git a/tests/mock_tables/config_db.json b/tests/mock_tables/config_db.json index 0ef3e75d94..68dd883fb1 100644 --- a/tests/mock_tables/config_db.json +++ b/tests/mock_tables/config_db.json @@ -2436,5 +2436,8 @@ }, "DHCP_RELAY|Vlan1000": { "dhcpv6_servers@": "fc02:2000::1,fc02:2000::2" + }, + "DHCP_RELAY|Vlan2000": { + "dhcpv6_servers@": "fc02:2000::3,fc02:2000::4" } } diff --git a/tests/show_dhcp_relay_test.py b/tests/show_dhcp_relay_test.py new file mode 100644 index 0000000000..2aefbce8d6 --- /dev/null +++ b/tests/show_dhcp_relay_test.py @@ -0,0 +1,135 @@ +import pytest +import show.main as show +from click.testing import CliRunner +from utilities_common.db import Db +from unittest import mock + +expected_show_dhcpv6_table = """\ +-------- ------------ +Vlan1000 fc02:2000::1 + fc02:2000::2 +-------- ------------ + +-------- ------------ +Vlan2000 fc02:2000::3 + fc02:2000::4 +-------- ------------ + +""" +expected_show_dhcpv6_counter = """\ + Message Type Vlan1000 +-------------- ---------- + Unknown 0 + Solicit 0 + Advertise 0 + Request 0 + Confirm 0 + Renew 0 + Rebind 0 + Reply 0 + Release 0 + Decline 0 + Relay-Forward 0 + Relay-Reply 0 + +""" + +expected_show_dhcp_relay_ipv4_helper = """\ ++-------------+----------------------+ +| Interface | DHCP Relay Address | ++=============+======================+ +| Vlan1000 | 192.0.0.1 | +| | 192.0.0.2 | +| | 192.0.0.3 | +| | 192.0.0.4 | ++-------------+----------------------+ +| Vlan2000 | 192.0.0.1 | +| | 192.0.0.2 | +| | 192.0.0.3 | +| | 192.0.0.4 | ++-------------+----------------------+ + +""" + +expected_show_dhcp_relay_ipv6_destination = """\ ++-------------+----------------------+ +| Interface | DHCP Relay Address | ++=============+======================+ +| Vlan1000 | fc02:2000::1 | +| | fc02:2000::2 | ++-------------+----------------------+ +| Vlan2000 | fc02:2000::3 | +| | fc02:2000::4 | ++-------------+----------------------+ + +""" + +IP_VER_TEST_PARAM_MAP = { + "ipv4": { + "command": "helper", + "ips": [ + "192.0.0.5", + "192.0.0.6", + "192.0.0.7" + ], + "exist_ip": "192.0.0.1", + "nonexist_ip": "192.0.0.8", + "invalid_ip": "192.0.0", + "table": "VLAN" + }, + "ipv6": { + "command": "destination", + "ips": [ + "fc02:2000::3", + "fc02:2000::4", + "fc02:2000::5" + ], + "exist_ip": "fc02:2000::1", + "nonexist_ip": "fc02:2000::6", + "invalid_ip": "fc02:2000:", + "table": "DHCP_RELAY" + } +} + + +@pytest.fixture(scope="module", params=["ipv4", "ipv6"]) +def ip_version(request): + """ + Parametrize Ip version + Args: + request: pytest request object + Returns: + Ip version needed for test case + """ + return request.param + + +def test_show_dhcprelay_helper(): + runner = CliRunner() + result = runner.invoke(show.cli.commands["dhcprelay_helper"].commands["ipv6"]) + print(result.output) + assert result.output == expected_show_dhcpv6_table + + +def test_show_dhcp6relay_counters(): + runner = CliRunner() + result = runner.invoke(show.cli.commands["dhcp6relay_counters"].commands["counts"], ["-i", "Vlan1000"]) + print(result.output) + assert result.output == expected_show_dhcpv6_counter + + +def test_show_dhcp_relay_ipv6_counter(): + runner = CliRunner() + result = runner.invoke(show.cli.commands["dhcp_relay"].commands["ipv6"].commands["counters"], ["-i", "Vlan1000"]) + print(result.output) + assert result.output == expected_show_dhcpv6_counter + + +def test_show_dhcp_relay(ip_version): + runner = CliRunner() + result = runner.invoke(show.cli.commands["dhcp_relay"].commands[ip_version] + .commands[IP_VER_TEST_PARAM_MAP[ip_version]["command"]]) + print(result.output) + expected_output = expected_show_dhcp_relay_ipv4_helper \ + if ip_version == "ipv4" else expected_show_dhcp_relay_ipv6_destination + assert result.output == expected_output From f76b5f150d9496665a18405264e8b839f6c1b60a Mon Sep 17 00:00:00 2001 From: yaqiangz Date: Tue, 7 Mar 2023 12:06:06 +0000 Subject: [PATCH 2/2] remove useless import --- tests/dhcp6relay_counter_test.py | 32 -------------------------------- tests/show_dhcp_relay_test.py | 2 -- 2 files changed, 34 deletions(-) delete mode 100644 tests/dhcp6relay_counter_test.py diff --git a/tests/dhcp6relay_counter_test.py b/tests/dhcp6relay_counter_test.py deleted file mode 100644 index 45e36fcf6a..0000000000 --- a/tests/dhcp6relay_counter_test.py +++ /dev/null @@ -1,32 +0,0 @@ -import sys -import os - -import show.main as show -from click.testing import CliRunner - -expected_counts = """\ - Message Type Vlan1000 --------------- ---------- - Unknown 0 - Solicit 0 - Advertise 0 - Request 0 - Confirm 0 - Renew 0 - Rebind 0 - Reply 0 - Release 0 - Decline 0 - Relay-Forward 0 - Relay-Reply 0 - -""" - -class TestDhcp6RelayCounters(object): - - def test_show_counts(self): - runner = CliRunner() - result = runner.invoke(show.cli.commands['dhcp6relay_counters'].commands["counts"], ["-i", "Vlan1000"]) - print(result.output) - assert result.output == expected_counts - diff --git a/tests/show_dhcp_relay_test.py b/tests/show_dhcp_relay_test.py index 2aefbce8d6..b3902dbf03 100644 --- a/tests/show_dhcp_relay_test.py +++ b/tests/show_dhcp_relay_test.py @@ -1,8 +1,6 @@ import pytest import show.main as show from click.testing import CliRunner -from utilities_common.db import Db -from unittest import mock expected_show_dhcpv6_table = """\ -------- ------------