diff --git a/crm/dash_config.py b/crm/dash_config.py new file mode 100644 index 0000000000..5412d66c51 --- /dev/null +++ b/crm/dash_config.py @@ -0,0 +1,155 @@ +import click + +def get_attr_full_name(ctx, threshold): + attr = 'dash_' + + if ctx.obj["crm"].addr_family: + attr += ctx.obj["crm"].addr_family + '_' + + if ctx.obj["crm"].direction: + attr += ctx.obj["crm"].direction + '_' + + attr += ctx.obj["crm"].res_type + '_' + threshold + return attr + +@click.command('type') +@click.argument('value', type=click.Choice(['percentage', 'used', 'free'])) +@click.pass_context +def config_dash_type(ctx, value): + """CRM threshold type configuration""" + ctx.obj["crm"].config(get_attr_full_name(ctx, 'threshold_type'), value) + +@click.command('low') +@click.argument('value', type=click.INT) +@click.pass_context +def config_dash_low(ctx, value): + """CRM low threshold configuration""" + ctx.obj["crm"].config(get_attr_full_name(ctx, 'low_threshold'), value) + +@click.command('high') +@click.argument('value', type=click.INT) +@click.pass_context +def config_dash_high(ctx, value): + """CRM high threshold configuration""" + ctx.obj["crm"].config(get_attr_full_name(ctx, 'high_threshold'), value) + +def group_add_thresholds(group): + group.add_command(config_dash_type) + group.add_command(config_dash_low) + group.add_command(config_dash_high) + +@click.group('dash') +@click.pass_context +def config_dash(ctx): + """CRM configuration for DASH resource""" + pass + +@config_dash.group('ipv4') +@click.pass_context +def config_dash_ipv4(ctx): + """DASH CRM resource IPv4 address family""" + ctx.obj["crm"].addr_family = 'ipv4' + +@config_dash.group('ipv6') +@click.pass_context +def config_dash_ipv6(ctx): + """DASH CRM resource IPv6 address family""" + ctx.obj["crm"].addr_family = 'ipv6' + +@click.group('inbound') +@click.pass_context +def config_dash_inbound(ctx): + """DASH CRM inbound direction resource""" + ctx.obj["crm"].direction = 'inbound' + +config_dash_ipv4.add_command(config_dash_inbound) +config_dash_ipv6.add_command(config_dash_inbound) + +@click.group('outbound') +@click.pass_context +def config_dash_outbound(ctx): + """DASH CRM outbound direction resource""" + ctx.obj["crm"].direction = 'outbound' + +config_dash_ipv4.add_command(config_dash_outbound) +config_dash_ipv6.add_command(config_dash_outbound) + +@config_dash.group('eni') +@click.pass_context +def config_dash_eni(ctx): + """CRM configuration for DASH ENI resource""" + ctx.obj["crm"].res_type = 'eni' + +group_add_thresholds(config_dash_eni) + +@config_dash.group('eni-ether-address') +@click.pass_context +def config_dash_eni_ether_address_map(ctx): + """CRM configuration for DASH ENI ETHER address map entry""" + ctx.obj["crm"].res_type = 'eni_ether_address_map' + +group_add_thresholds(config_dash_eni_ether_address_map) + +@config_dash.group('vnet') +@click.pass_context +def config_dash_vnet(ctx): + """CRM configuration for DASH VNET resource""" + ctx.obj["crm"].res_type = 'vnet' + +group_add_thresholds(config_dash_vnet) + +@click.group('routing') +@click.pass_context +def config_dash_routing(ctx): + """CRM configuration for DASH inbound routes""" + ctx.obj["crm"].res_type = 'routing' + +group_add_thresholds(config_dash_routing) +config_dash_inbound.add_command(config_dash_routing) +config_dash_outbound.add_command(config_dash_routing) + +@click.group('pa-validation') +@click.pass_context +def config_dash_pa_validation(ctx): + """CRM configuration for DASH PA validation entries""" + ctx.obj["crm"].res_type = 'pa_validation' + +group_add_thresholds(config_dash_pa_validation) +config_dash_ipv4.add_command(config_dash_pa_validation) +config_dash_ipv6.add_command(config_dash_pa_validation) + +@click.group('ca-to-pa') +@click.pass_context +def config_dash_ca_to_pa(ctx): + """CRM configuration for DASH CA to PA entries""" + ctx.obj["crm"].res_type = 'ca_to_pa' + +group_add_thresholds(config_dash_ca_to_pa) +config_dash_outbound.add_command(config_dash_ca_to_pa) + +@click.group('acl') +@click.pass_context +def config_dash_acl(ctx): + """DASH CRM ACL resource""" + +config_dash_ipv4.add_command(config_dash_acl) +config_dash_ipv6.add_command(config_dash_acl) + +@click.group('group') +@click.pass_context +def config_dash_acl_group(ctx): + """CRM configuration for DASH ACL group entries""" + ctx.obj["crm"].res_type = 'acl_group' + +group_add_thresholds(config_dash_acl_group) +config_dash_acl.add_command(config_dash_acl_group) + +@click.group('rule') +@click.pass_context +def config_dash_acl_rule(ctx): + """CRM configuration for DASH ACL rule entries""" + ctx.obj["crm"].res_type = 'acl_rule' + +group_add_thresholds(config_dash_acl_rule) +config_dash_acl.add_command(config_dash_acl_rule) + diff --git a/crm/dash_show.py b/crm/dash_show.py new file mode 100644 index 0000000000..6fa59dc580 --- /dev/null +++ b/crm/dash_show.py @@ -0,0 +1,119 @@ +import click + +def show_resource(ctx, resource): + if ctx.obj["crm"].cli_mode == 'thresholds': + ctx.obj["crm"].show_thresholds(resource) + elif ctx.obj["crm"].cli_mode == 'resources': + ctx.obj["crm"].show_resources(resource) + +@click.group('dash') +@click.pass_context +def show_dash(ctx): + """Show CRM information for DASH""" + pass + +@show_dash.group('ipv4') +@click.pass_context +def show_dash_ipv4(ctx): + """Show CRM information for IPv4 address family""" + ctx.obj["crm"].addr_family = 'ipv4' + +@show_dash.group('ipv6') +@click.pass_context +def show_dash_ipv6(ctx): + """Show CRM information for IPv6 address family""" + ctx.obj["crm"].addr_family = 'ipv6' + +@click.group('inbound') +@click.pass_context +def show_dash_inbound(ctx): + """Show CRM information for inbound direction""" + ctx.obj["crm"].direction = 'inbound' + +show_dash_ipv4.add_command(show_dash_inbound) +show_dash_ipv6.add_command(show_dash_inbound) + +@click.group('outbound') +@click.pass_context +def show_dash_outbound(ctx): + """Show CRM information for outbound direction""" + ctx.obj["crm"].direction = 'outbound' + +show_dash_ipv4.add_command(show_dash_outbound) +show_dash_ipv6.add_command(show_dash_outbound) + +@show_dash.command('vnet') +@click.pass_context +def show_dash_vnet(ctx): + """Show CRM information for VNETs""" + show_resource(ctx, 'dash_vnet') + +@show_dash.command('eni') +@click.pass_context +def show_dash_eni(ctx): + """Show CRM information for ENIs""" + show_resource(ctx, 'dash_eni') + +@show_dash.command('eni-ether-address') +@click.pass_context +def show_dash_eni_ether_address_map(ctx): + """Show CRM information for ENI ETHER address map entries""" + show_resource(ctx, 'dash_eni_ether_address_map') + +@click.command('routing') +@click.pass_context +def show_dash_routing(ctx): + """Show CRM information for inbound routes""" + resource = f'dash_{ctx.obj["crm"].addr_family}_{ctx.obj["crm"].direction}_routing' + show_resource(ctx, resource) + +show_dash_inbound.add_command(show_dash_routing) +show_dash_outbound.add_command(show_dash_routing) + +@click.command('pa-validation') +@click.pass_context +def show_dash_pa_validation(ctx): + """Show CRM information for PA validation entries""" + resource = f'dash_{ctx.obj["crm"].addr_family}_pa_validation' + show_resource(ctx, resource) + +show_dash_ipv4.add_command(show_dash_pa_validation) +show_dash_ipv6.add_command(show_dash_pa_validation) + +@click.command('ca-to-pa') +@click.pass_context +def show_dash_ca_to_pa(ctx): + """Show CRM information for CA to PA entries""" + resource = f'dash_{ctx.obj["crm"].addr_family}_{ctx.obj["crm"].direction}_ca_to_pa' + show_resource(ctx, resource) + +show_dash_outbound.add_command(show_dash_ca_to_pa) + +@click.group('acl') +@click.pass_context +def show_dash_acl(ctx): + """Show CRM information for ACL resources""" + +show_dash_ipv4.add_command(show_dash_acl) +show_dash_ipv6.add_command(show_dash_acl) + +@click.command('group') +@click.pass_context +def show_dash_acl_group(ctx): + """Show CRM information for ACL group entries""" + resource = f'dash_{ctx.obj["crm"].addr_family}_acl_group' + show_resource(ctx, resource) + +show_dash_acl.add_command(show_dash_acl_group) + +@click.command('rule') +@click.pass_context +def show_dash_acl_rule(ctx): + """Show CRM information for ACL rule entries""" + resource = f'dash_{ctx.obj["crm"].addr_family}_acl_rule' + if ctx.obj["crm"].cli_mode == 'thresholds': + ctx.obj["crm"].show_thresholds(resource) + elif ctx.obj["crm"].cli_mode == 'resources': + ctx.obj["crm"].show_acl_group_resources(resource) + +show_dash_acl.add_command(show_dash_acl_rule) diff --git a/crm/main.py b/crm/main.py index 9b0d06e89a..998ff23fc4 100644 --- a/crm/main.py +++ b/crm/main.py @@ -3,11 +3,38 @@ import click from swsscommon.swsscommon import ConfigDBConnector from tabulate import tabulate - from sonic_py_common import multi_asic from utilities_common.general import load_db_config from utilities_common import multi_asic as multi_asic_util + +from sonic_py_common import device_info + +from .dash_config import config_dash +from .dash_show import show_dash + + +platform_info = device_info.get_platform_info() + + class Crm: + + thresholds = ( + "ipv4_route", "ipv6_route", "ipv4_nexthop", "ipv6_nexthop", "ipv4_neighbor", "ipv6_neighbor", + "nexthop_group_member", "nexthop_group", "acl_table", "acl_group", "acl_entry", + "acl_counter", "fdb_entry", "ipmc_entry", "snat_entry", "dnat_entry", "mpls_inseg", + "mpls_nexthop","srv6_nexthop", "srv6_my_sid_entry" + ) + + resources = ( + "ipv4_route", "ipv6_route", "ipv4_nexthop", "ipv6_nexthop", "ipv4_neighbor", "ipv6_neighbor", + "nexthop_group_member", "nexthop_group", "fdb_entry", "ipmc_entry", "snat_entry", "dnat_entry", + "mpls_inseg", "mpls_nexthop","srv6_nexthop", "srv6_my_sid_entry" + ) + + acl_resources = ( + "acl_table", "acl_group", "acl_entry", "acl_counter" + ) + def __init__(self, db=None): self.cli_mode = None self.addr_family = None @@ -16,6 +43,12 @@ def __init__(self, db=None): self.cfgdb = db self.multi_asic = multi_asic_util.MultiAsic() + def get_thresholds_list(self): + return list(self.thresholds) + + def get_resources_list(self): + return list(self.resources) + @multi_asic_util.run_on_multi_asic def config(self, attr, val): """ @@ -53,7 +86,6 @@ def show_thresholds(self, resource): """ CRM Handler to display thresholds information. """ - configdb = self.cfgdb if configdb is None: # Get the namespace list @@ -69,10 +101,7 @@ def show_thresholds(self, resource): if crm_info: if resource == 'all': - for res in ["ipv4_route", "ipv6_route", "ipv4_nexthop", "ipv6_nexthop", "ipv4_neighbor", "ipv6_neighbor", - "nexthop_group_member", "nexthop_group", "acl_table", "acl_group", "acl_entry", - "acl_counter", "fdb_entry", "ipmc_entry", "snat_entry", "dnat_entry", "mpls_inseg", - "mpls_nexthop","srv6_nexthop", "srv6_my_sid_entry"]: + for res in self.get_thresholds_list(): try: data.append([res, crm_info[res + "_threshold_type"], crm_info[res + "_low_threshold"], crm_info[res + "_high_threshold"]]) except KeyError: @@ -98,9 +127,7 @@ def get_resources(self, resource): if crm_stats: if resource == 'all': - for res in ["ipv4_route", "ipv6_route", "ipv4_nexthop", "ipv6_nexthop", "ipv4_neighbor", "ipv6_neighbor", - "nexthop_group_member", "nexthop_group", "fdb_entry", "ipmc_entry", "snat_entry", "dnat_entry", - "mpls_inseg", "mpls_nexthop","srv6_nexthop", "srv6_my_sid_entry"]: + for res in self.get_resources_list(): if 'crm_stats_' + res + "_used" in crm_stats.keys() and 'crm_stats_' + res + "_available" in crm_stats.keys(): data.append([res, crm_stats['crm_stats_' + res + "_used"], crm_stats['crm_stats_' + res + "_available"]]) else: @@ -205,6 +232,83 @@ def show_acl_table_resources(self): click.echo(tabulate(data, headers=header, tablefmt="simple", missingval="")) click.echo() + def show_all_thresholds(self): + self.show_thresholds('all') + + def show_all_resources(self): + self.show_resources('all') + self.show_acl_resources() + self.show_acl_table_resources() + + +class DashCrm(Crm): + + dash_resources = ( + "dash_vnet", "dash_eni", "dash_eni_ether_address_map", "dash_ipv4_inbound_routing", "dash_ipv6_inbound_routing", + "dash_ipv4_outbound_routing", "dash_ipv6_outbound_routing", "dash_ipv4_pa_validation", "dash_ipv6_pa_validation", + "dash_ipv4_outbound_ca_to_pa", "dash_ipv6_outbound_ca_to_pa", "dash_ipv4_acl_group","dash_ipv6_acl_group" + ) + + dash_acl_group_resources = ( + "dash_ipv4_acl_rule", "dash_ipv6_acl_rule" + ) + + dash_thresholds = dash_resources + dash_acl_group_resources + + def __init__(self, *args, **kwargs): + self.direction = None + + super().__init__(*args, *kwargs) + + def get_thresholds_list(self): + thresholds = super().get_thresholds_list() + thresholds.extend(self.dash_thresholds) + return list(thresholds) + + def get_resources_list(self): + resources = super().get_resources_list() + resources.extend(self.dash_resources) + return list(resources) + + def show_all_resources(self): + super().show_all_resources() + self.show_acl_group_resources() + + def get_dash_acl_group_resources(self, resource=None): + # Retrieve all ACL table keys from CRM:ACL_TABLE_STATS + crm_acl_keys = self.db.keys(self.db.COUNTERS_DB, 'CRM:DASH_ACL_GROUP_STATS*') + data = [] + + for key in crm_acl_keys: + id = key.replace('CRM:DASH_ACL_GROUP_STATS:', '') + + crm_stats = self.db.get_all(self.db.COUNTERS_DB, key) + + query = [resource] if resource else self.dash_acl_group_resources + for res in query: + used = f'crm_stats_{res}_used' + available = f'crm_stats_{res}_available' + if used in crm_stats and available in crm_stats: + data.append([id, res, crm_stats[used], crm_stats[available]]) + + return data + + @multi_asic_util.run_on_multi_asic + def show_acl_group_resources(self, resource=None): + if self.multi_asic.is_multi_asic: + click.echo('\nError! Could not get CRM configuration.\n') + return + + header = ("DASH ACL Group ID", "Resource Name", "Used Count", "Available Count") + + data = [] + data = self.get_dash_acl_group_resources(resource) + + click.echo() + click.echo(tabulate(data, headers=header, tablefmt="simple", missingval="")) + click.echo() + + @click.group() @click.pass_context def cli(ctx): @@ -217,8 +321,13 @@ def cli(ctx): # Load database config files load_db_config() + if device_info.get_platform_info().get('switch_type') == "dpu": + crm = DashCrm(db) + else: + crm = Crm(db) + context = { - "crm": Crm(db) + "crm": crm } ctx.obj = context @@ -290,7 +399,7 @@ def nexthop(ctx): """CRM configuration for nexthop resource""" ctx.obj["crm"].res_type = 'nexthop' -@route.command() +@click.command() @click.argument('value', type=click.Choice(['percentage', 'used', 'free'])) @click.pass_context def type(ctx, value): @@ -304,7 +413,7 @@ def type(ctx, value): ctx.obj["crm"].config(attr, value) -@route.command() +@click.command() @click.argument('value', type=click.INT) @click.pass_context def low(ctx, value): @@ -318,7 +427,7 @@ def low(ctx, value): ctx.obj["crm"].config(attr, value) -@route.command() +@click.command() @click.argument('value', type=click.INT) @click.pass_context def high(ctx, value): @@ -332,6 +441,9 @@ def high(ctx, value): ctx.obj["crm"].config(attr, value) +route.add_command(type) +route.add_command(low) +route.add_command(high) neighbor.add_command(type) neighbor.add_command(low) neighbor.add_command(high) @@ -480,6 +592,9 @@ def srv6_my_sid_entry(ctx): srv6_my_sid_entry.add_command(low) srv6_my_sid_entry.add_command(high) +if device_info.get_platform_info().get('switch_type') == "dpu": + thresholds.add_command(config_dash) + @cli.group() @click.pass_context def show(ctx): @@ -509,11 +624,9 @@ def thresholds(ctx): def all(ctx): """Show CRM information for all resources""" if ctx.obj["crm"].cli_mode == 'thresholds': - ctx.obj["crm"].show_thresholds('all') + ctx.obj["crm"].show_all_thresholds() elif ctx.obj["crm"].cli_mode == 'resources': - ctx.obj["crm"].show_resources('all') - ctx.obj["crm"].show_acl_resources() - ctx.obj["crm"].show_acl_table_resources() + ctx.obj["crm"].show_all_resources() @resources.group() @click.pass_context @@ -695,6 +808,9 @@ def srv6_my_sid_entry(ctx): thresholds.add_command(srv6_nexthop) thresholds.add_command(srv6_my_sid_entry) +if device_info.get_platform_info().get('switch_type') == "dpu": + resources.add_command(show_dash) + thresholds.add_command(show_dash) if __name__ == '__main__': cli() diff --git a/tests/crm_dash/config_db.json b/tests/crm_dash/config_db.json new file mode 100644 index 0000000000..f166b0b4f7 --- /dev/null +++ b/tests/crm_dash/config_db.json @@ -0,0 +1,55 @@ +{ + "CRM|Config": { + "expireat": 1680191314.928466, + "ttl": -0.001, + "type": "hash", + "value": { + "dash_eni_ether_address_map_high_threshold": "85", + "dash_eni_ether_address_map_low_threshold": "70", + "dash_eni_ether_address_map_threshold_type": "percentage", + "dash_eni_high_threshold": "85", + "dash_eni_low_threshold": "70", + "dash_eni_threshold_type": "percentage", + "dash_ipv4_acl_group_high_threshold": "85", + "dash_ipv4_acl_group_low_threshold": "70", + "dash_ipv4_acl_group_threshold_type": "percentage", + "dash_ipv4_acl_rule_high_threshold": "85", + "dash_ipv4_acl_rule_low_threshold": "70", + "dash_ipv4_acl_rule_threshold_type": "percentage", + "dash_ipv4_inbound_routing_high_threshold": "85", + "dash_ipv4_inbound_routing_low_threshold": "70", + "dash_ipv4_inbound_routing_threshold_type": "percentage", + "dash_ipv4_outbound_ca_to_pa_high_threshold": "85", + "dash_ipv4_outbound_ca_to_pa_low_threshold": "70", + "dash_ipv4_outbound_ca_to_pa_threshold_type": "percentage", + "dash_ipv4_outbound_routing_high_threshold": "85", + "dash_ipv4_outbound_routing_low_threshold": "70", + "dash_ipv4_outbound_routing_threshold_type": "percentage", + "dash_ipv4_pa_validation_high_threshold": "85", + "dash_ipv4_pa_validation_low_threshold": "70", + "dash_ipv4_pa_validation_threshold_type": "percentage", + "dash_ipv6_acl_group_high_threshold": "85", + "dash_ipv6_acl_group_low_threshold": "70", + "dash_ipv6_acl_group_threshold_type": "percentage", + "dash_ipv6_acl_rule_high_threshold": "85", + "dash_ipv6_acl_rule_low_threshold": "70", + "dash_ipv6_acl_rule_threshold_type": "percentage", + "dash_ipv6_inbound_routing_high_threshold": "85", + "dash_ipv6_inbound_routing_low_threshold": "70", + "dash_ipv6_inbound_routing_threshold_type": "percentage", + "dash_ipv6_outbound_ca_to_pa_high_threshold": "85", + "dash_ipv6_outbound_ca_to_pa_low_threshold": "70", + "dash_ipv6_outbound_ca_to_pa_threshold_type": "percentage", + "dash_ipv6_outbound_routing_high_threshold": "85", + "dash_ipv6_outbound_routing_low_threshold": "70", + "dash_ipv6_outbound_routing_threshold_type": "percentage", + "dash_ipv6_pa_validation_high_threshold": "85", + "dash_ipv6_pa_validation_low_threshold": "70", + "dash_ipv6_pa_validation_threshold_type": "percentage", + "dash_vnet_high_threshold": "85", + "dash_vnet_low_threshold": "70", + "dash_vnet_threshold_type": "percentage" + } + } +} + diff --git a/tests/crm_dash/counters_db.json b/tests/crm_dash/counters_db.json new file mode 100644 index 0000000000..b0576a72d1 --- /dev/null +++ b/tests/crm_dash/counters_db.json @@ -0,0 +1,54 @@ +{ + "CRM:DASH_ACL_GROUP_STATS:0x6a00000000002d": { + "expireat": 1680172664.591134, + "ttl": -0.001, + "type": "hash", + "value": { + "crm_stats_dash_ipv4_acl_rule_available": "200000000", + "crm_stats_dash_ipv4_acl_rule_used": "100" + } + }, + "CRM:DASH_ACL_GROUP_STATS:0x6a00000000009d": { + "expireat": 1680172664.5912013, + "ttl": -0.001, + "type": "hash", + "value": { + "crm_stats_dash_ipv6_acl_rule_available": "200000000", + "crm_stats_dash_ipv6_acl_rule_used": "1000" + } + }, + "CRM:STATS": { + "expireat": 1680172664.5911696, + "ttl": -0.001, + "type": "hash", + "value": { + "crm_stats_dash_eni_available": "1000000", + "crm_stats_dash_eni_ether_address_map_available": "1000000", + "crm_stats_dash_eni_ether_address_map_used": "9", + "crm_stats_dash_eni_used": "9", + "crm_stats_dash_ipv4_acl_group_available": "200000000", + "crm_stats_dash_ipv4_acl_group_used": "27", + "crm_stats_dash_ipv4_inbound_routing_available": "200000000", + "crm_stats_dash_ipv4_inbound_routing_used": "9", + "crm_stats_dash_ipv4_outbound_ca_to_pa_available": "1000000", + "crm_stats_dash_ipv4_outbound_ca_to_pa_used": "0", + "crm_stats_dash_ipv4_outbound_routing_available": "1000000", + "crm_stats_dash_ipv4_outbound_routing_used": "9", + "crm_stats_dash_ipv4_pa_validation_available": "1000000", + "crm_stats_dash_ipv4_pa_validation_used": "0", + "crm_stats_dash_ipv6_acl_group_available": "200000000", + "crm_stats_dash_ipv6_acl_group_used": "0", + "crm_stats_dash_ipv6_inbound_routing_available": "200000000", + "crm_stats_dash_ipv6_inbound_routing_used": "0", + "crm_stats_dash_ipv6_outbound_ca_to_pa_available": "1000000", + "crm_stats_dash_ipv6_outbound_ca_to_pa_used": "0", + "crm_stats_dash_ipv6_outbound_routing_available": "1000000", + "crm_stats_dash_ipv6_outbound_routing_used": "0", + "crm_stats_dash_ipv6_pa_validation_available": "1000000", + "crm_stats_dash_ipv6_pa_validation_used": "0", + "crm_stats_dash_vnet_available": "200000000", + "crm_stats_dash_vnet_used": "2" + } + } +} + diff --git a/tests/crm_dash_test.py b/tests/crm_dash_test.py new file mode 100644 index 0000000000..23b89658c7 --- /dev/null +++ b/tests/crm_dash_test.py @@ -0,0 +1,150 @@ +import os +import sys +import mock +import pytest +from importlib import reload +from tabulate import tabulate + +from click.testing import CliRunner +from utilities_common.db import Db +from .mock_tables import dbconnector + +import crm.main as crm + + +test_path = os.path.dirname(os.path.abspath(__file__)) +mock_db_path = os.path.join(test_path, "crm_dash") + + +@mock.patch('sonic_py_common.device_info.get_platform_info', mock.MagicMock(return_value={"switch_type": "dpu"})) +class TestCrmDash(object): + + dash_thresholds = [ + ("dash_vnet", ('dash', 'vnet')), + ("dash_eni", ('dash', 'eni')), + ("dash_eni_ether_address_map", ('dash', 'eni-ether-address')), + ("dash_ipv4_inbound_routing", ('dash', 'ipv4', 'inbound', 'routing')), + ("dash_ipv6_inbound_routing", ('dash', 'ipv6', 'inbound', 'routing')), + ("dash_ipv4_outbound_routing", ('dash', 'ipv4', 'outbound', 'routing')), + ("dash_ipv6_outbound_routing", ('dash', 'ipv6', 'outbound', 'routing')), + ("dash_ipv4_pa_validation", ('dash', 'ipv4', 'pa-validation')), + ("dash_ipv6_pa_validation", ('dash', 'ipv6', 'pa-validation')), + ("dash_ipv4_outbound_ca_to_pa", ('dash', 'ipv4', 'outbound', 'ca-to-pa')), + ("dash_ipv6_outbound_ca_to_pa", ('dash', 'ipv6', 'outbound', 'ca-to-pa')), + ("dash_ipv4_acl_group", ('dash', 'ipv4', 'acl', 'group')), + ("dash_ipv6_acl_group", ('dash', 'ipv6', 'acl', 'group')), + ("dash_ipv4_acl_rule", ('dash', 'ipv4', 'acl', 'rule')), + ("dash_ipv6_acl_rule", ('dash', 'ipv6', 'acl', 'rule')), + ] + + dash_resources = [ + ("dash_vnet", ('dash', 'vnet'), (2, 200000000)), + ("dash_eni", ('dash', 'eni'), (9, 1000000)), + ("dash_eni_ether_address_map", ('dash', 'eni-ether-address'), (9, 1000000)), + ("dash_ipv4_inbound_routing", ('dash', 'ipv4', 'inbound', 'routing'), (9, 200000000)), + ("dash_ipv4_outbound_routing", ('dash', 'ipv4', 'outbound', 'routing'), (9, 1000000)), + ("dash_ipv6_inbound_routing", ('dash', 'ipv6', 'inbound', 'routing'), (0, 200000000)), + ("dash_ipv6_outbound_routing", ('dash', 'ipv6', 'outbound', 'routing'), (0, 1000000)), + ("dash_ipv4_pa_validation", ('dash', 'ipv4', 'pa-validation'), (0, 1000000)), + ("dash_ipv6_pa_validation", ('dash', 'ipv6', 'pa-validation'), (0, 1000000)), + ("dash_ipv4_outbound_ca_to_pa", ('dash', 'ipv4', 'outbound', 'ca-to-pa'), (0, 1000000)), + ("dash_ipv6_outbound_ca_to_pa", ('dash', 'ipv6', 'outbound', 'ca-to-pa'), (0, 1000000)), + ("dash_ipv4_acl_group", ('dash', 'ipv4', 'acl', 'group'), (27, 200000000)), + ("dash_ipv6_acl_group", ('dash', 'ipv6', 'acl', 'group'), (0, 200000000)), + ] + + dash_acl_group_resources = [ + ("dash_ipv4_acl_rule", ('dash', 'ipv4', 'acl', 'rule'), "0x6a00000000002d", (100, 200000000)), + ("dash_ipv6_acl_rule", ('dash', 'ipv6', 'acl', 'rule'), "0x6a00000000009d", (1000, 200000000)), + ] + + dash_thresholds_header = ("Resource Name", "Threshold Type", "Low Threshold", "High Threshold") + dash_resources_header = ("Resource Name", "Used Count", "Available Count") + dash_acl_group_resources_header = ("DASH ACL Group ID", "Resource Name", "Used Count", "Available Count") + + @classmethod + def setup_class(cls): + print("SETUP") + os.environ["UTILITIES_UNIT_TESTING"] = "1" + dbconnector.dedicated_dbs['CONFIG_DB'] = os.path.join(mock_db_path, 'config_db') + dbconnector.dedicated_dbs['COUNTERS_DB'] = os.path.join(mock_db_path, "counters_db") + + @classmethod + def teardown_class(cls): + print("TEARDOWN") + os.environ["UTILITIES_UNIT_TESTING"] = "0" + os.environ["UTILITIES_UNIT_TESTING_TOPOLOGY"] = "" + dbconnector.dedicated_dbs['CONFIG_DB'] = None + dbconnector.dedicated_dbs['COUNTERS_DB'] = None + dbconnector.load_namespace_config() + + @pytest.mark.parametrize("obj, cmd", dash_thresholds) + def test_crm_show_thresholds(self, obj, cmd): + reload(crm) + + db = Db() + runner = CliRunner() + result = runner.invoke(crm.cli, ('show', 'thresholds') + cmd, obj=db) + print(sys.stderr, result.output) + assert result.exit_code == 0 + + expected_output = tabulate([(obj, "percentage", "70", "85")], headers=self.dash_thresholds_header, tablefmt="simple", missingval="") + assert result.output == "\n" + expected_output + "\n\n" + + result = runner.invoke(crm.cli, ('config', 'thresholds') + cmd + ('high', '90'), obj=db) + print(sys.stderr, result.output) + assert result.exit_code == 0 + + result = runner.invoke(crm.cli, ('config', 'thresholds') + cmd + ('low', '60'), obj=db) + print(sys.stderr, result.output) + assert result.exit_code == 0 + + result = runner.invoke(crm.cli, ('show', 'thresholds') + cmd, obj=db) + print(sys.stderr, result.output) + assert result.exit_code == 0 + + expected_output = tabulate([(obj, "percentage", "60", "90")], headers=self.dash_thresholds_header, tablefmt="simple", missingval="") + assert result.output == "\n" + expected_output + "\n\n" + + def test_crm_show_all_thresholds(self): + reload(crm) + + db = Db() + runner = CliRunner() + result = runner.invoke(crm.cli, ('show', 'thresholds', 'all'), obj=db) + print(sys.stderr, result.output) + assert result.exit_code == 0 + + table = [] + for obj in self.dash_thresholds: + table.append((obj[0], "percentage", "70", "85")) + + expected_output = tabulate(table, headers=self.dash_thresholds_header, tablefmt="simple", missingval="") + assert result.output == "\n" + expected_output + "\n\n" + + @pytest.mark.parametrize("obj, cmd, cnt", dash_resources) + def test_crm_show_resources(self, obj, cmd, cnt): + reload(crm) + + db = Db() + runner = CliRunner() + result = runner.invoke(crm.cli, ('show', 'resources') + cmd, obj=db) + print(sys.stderr, result.output) + assert result.exit_code == 0 + + expected_output = tabulate([(obj,) + cnt], headers=self.dash_resources_header, tablefmt="simple", missingval="") + assert result.output == "\n" + expected_output + "\n\n" + + @pytest.mark.parametrize("obj, cmd, obj_id, cnt", dash_acl_group_resources) + def test_crm_show_acl_group_resources(self, obj, cmd, obj_id, cnt): + reload(crm) + + db = Db() + runner = CliRunner() + result = runner.invoke(crm.cli, ('show', 'resources') + cmd, obj=db) + print(sys.stderr, result.output) + assert result.exit_code == 0 + + expected_output = tabulate([(obj_id, obj) + cnt], headers=self.dash_acl_group_resources_header, tablefmt="simple", missingval="") + assert result.output == "\n" + expected_output + "\n\n" +