From 0a05b6bd5f454cc8cc53ba958a7f1aff622401b2 Mon Sep 17 00:00:00 2001 From: "Jasper.Hou" Date: Thu, 23 Oct 2025 15:59:26 +0800 Subject: [PATCH] cli support igmp snooping --- config/main.py | 405 +++++++++++++++++++++++++++++++++++++++++++++ config/vlan.py | 15 ++ show/main.py | 436 +++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 856 insertions(+) diff --git a/config/main.py b/config/main.py index 274af9ec75..b0986d05ea 100644 --- a/config/main.py +++ b/config/main.py @@ -9744,5 +9744,410 @@ def del_vnet_route(ctx, vnet_name, prefix): click.echo("All routes deleted for the VNET {}.".format(vnet_name)) +# +# 'igmp-snooping' group ('config igmp-snooping ...') +# +@config.group() +@click.pass_context +@click.option('-s', '--redis-unix-socket-path', help='unix socket path for redis connection') +def igmp_snooping(ctx, redis_unix_socket_path): + """igmp-snooping configuration tasks""" + kwargs = {} + if redis_unix_socket_path: + kwargs['unix_socket_path'] = redis_unix_socket_path + config_db = ConfigDBConnector(**kwargs) + config_db.connect(wait_for_init=False) + ctx.obj = {'db': config_db} + pass + +@igmp_snooping.command("enable") +@click.argument('vid', metavar='', required=True, type=int) +@click.pass_context +def igmp_snooping_enable(ctx, vid): + """Enable for a VLAN""" + db = ctx.obj['db'] + vlan_valid = clicommon.is_vlanid_in_range(int(vid)) + if vlan_valid == False: + ctx.fail("Invalid VlanId!!") + vlan_name = 'Vlan{}'.format(vid) + vlan = db.get_entry('VLAN', vlan_name) + if len(vlan) == 0: + ctx.fail("{} doesn't exist".format(vlan_name)) + l2mc_enable = db.get_entry('L2MC', vlan_name) + if len(l2mc_enable) < 1: + igmp_snooping_fvs = { + 'enabled' : 'true', + 'querier' : 'false', + 'version' : '2', + 'fast-leave': 'false', + 'query-interval':'125', + 'query-max-response-time': '10', + 'last-member-query-interval': '1000', + } + else : + igmp_snooping_fvs = {'enabled' : 'true',} + db.mod_entry('L2MC', vlan_name, igmp_snooping_fvs) + +@igmp_snooping.command('disable') +@click.argument('vid', metavar='', required=True, type=int) +@click.pass_context +def igmp_snooping_disable(ctx, vid): + """Disable for a VLAN""" + db = ctx.obj['db'] + vlan_valid = clicommon.is_vlanid_in_range(int(vid)) + if vlan_valid == False: + ctx.fail("Invalid VlanId!!") + vlan_name = 'Vlan{}'.format(vid) + vlan = db.get_entry('VLAN', vlan_name) + if len(vlan) == 0: + ctx.fail("{} doesn't exist".format(vlan_name)) + db.set_entry('L2MC', vlan_name, None) + +@igmp_snooping.command('last-member-query-interval') +@click.argument('vid', metavar='', required=True, type=int) +@click.argument('lm_query', metavar='<(100,25500)ms>', required=True, type=click.IntRange(100,25500)) +@click.pass_context +def igmp_snooping_lmq_interval(ctx, vid, lm_query): + """Last Member Query Interval for a VLAN""" + db = ctx.obj['db'] + vlan_valid = clicommon.is_vlanid_in_range(int(vid)) + if vlan_valid == False: + ctx.fail("Invalid VlanId!!") + vlan_name = 'Vlan{}'.format(vid) + vlan = db.get_entry('VLAN', vlan_name) + if len(vlan) == 0: + ctx.fail("{} doesn't exist".format(vlan_name)) + lmq_name = format(lm_query) + db.mod_entry('L2MC', vlan_name, {'last-member-query-interval': lmq_name}) + +@igmp_snooping.command('query-max-response-time') +@click.argument('vid', metavar='', required=True, type=int) +@click.argument('qmr_time', metavar='<(1,25)seconds>', required=True, type=click.IntRange(1,25)) +@click.pass_context +def igmp_snooping_qmr_interval(ctx, vid, qmr_time): + """Query Max Response Time for a VLAN""" + db = ctx.obj['db'] + vlan_valid = clicommon.is_vlanid_in_range(int(vid)) + if vlan_valid == False: + ctx.fail("Invalid VlanId!!") + vlan_name = 'Vlan{}'.format(vid) + vlan = db.get_entry('VLAN', vlan_name) + if len(vlan) == 0: + ctx.fail("{} doesn't exist".format(vlan_name)) + qmr_time_name = format(qmr_time) + db.mod_entry('L2MC', vlan_name, {'query-max-response-time': qmr_time_name}) + +@igmp_snooping.command('query-interval') +@click.argument('vid', metavar='', required=True, type=int) +@click.argument('q_time', metavar='<(1,18000)seconds>', required=True, type=click.IntRange(1,18000)) +@click.pass_context +def igmp_snooping_q_interval(ctx, vid, q_time): + """IGMP Query Interval for a VLAN""" + db = ctx.obj['db'] + vlan_valid = clicommon.is_vlanid_in_range(int(vid)) + if vlan_valid == False: + ctx.fail("Invalid VlanId!!") + vlan_name = 'Vlan{}'.format(vid) + vlan = db.get_entry('VLAN', vlan_name) + if len(vlan) == 0: + ctx.fail("{} doesn't exist".format(vlan_name)) + q_time_name = format(q_time) + db.mod_entry('L2MC', vlan_name, {'query-interval': q_time_name}) + + +@igmp_snooping.command('optimised_multicast_flood') +@click.argument('optimised_multicast_flood', metavar='', required=True) +@click.argument('vid', metavar='', required=True, type=int) +@click.pass_context +def igmp_snooping_optimised_multicast_flood(ctx, vid, optimised_multicast_flood): + """IGMP optimised multicast flood""" + db = ctx.obj['db'] + vlan_valid = clicommon.is_vlanid_in_range(int(vid)) + if vlan_valid == False: + ctx.fail("Invalid VlanId!!") + if optimised_multicast_flood != 'enable' and optimised_multicast_flood != 'disable': + ctx.fail("Error: Invalid argument {}, expect either enable or disable".format(optimised_multicast_flood)) + vlan_name = 'Vlan{}'.format(vid) + vlan = db.get_entry('VLAN', vlan_name) + if len(vlan) == 0: + ctx.fail("{} doesn't exist".format(vlan_name)) + l2mc_enable = db.get_entry('L2MC', vlan_name) + if len(l2mc_enable) < 1: + ctx.fail("{} doesn't enable IGMP Snooping".format(vlan_name)) + + db.mod_entry('L2MC_SUPPRESS', vlan_name, {'optimised-multicast-flood': optimised_multicast_flood}) + +@igmp_snooping.command('link_local_groups_suppression') +@click.argument('link_local_groups_suppression', metavar='', required=True) +@click.argument('vid', metavar='', required=True, type=int) +@click.pass_context +def igmp_snooping_optimised_multicast_flood(ctx, vid, link_local_groups_suppression): + """IGMP optimised multicast flood""" + db = ctx.obj['db'] + vlan_valid = clicommon.is_vlanid_in_range(int(vid)) + if vlan_valid == False: + ctx.fail("Invalid VlanId!!") + if link_local_groups_suppression != 'enable' and link_local_groups_suppression != 'disable': + ctx.fail("Error: Invalid argument {}, expect either enable or disable".format(link_local_groups_suppression)) + vlan_name = 'Vlan{}'.format(vid) + vlan = db.get_entry('VLAN', vlan_name) + if len(vlan) == 0: + ctx.fail("{} doesn't exist".format(vlan_name)) + l2mc_enable = db.get_entry('L2MC', vlan_name) + if len(l2mc_enable) < 1: + ctx.fail("{} doesn't enable IGMP Snooping".format(vlan_name)) + + db.mod_entry('L2MC_SUPPRESS', vlan_name, {'link-local-groups-suppression': link_local_groups_suppression}) + +@igmp_snooping.command('version') +@click.argument('vid', metavar='', required=True, type=int) +@click.argument('version', metavar='<(1,3)ver>', required=True, type=click.IntRange(1,3)) +@click.pass_context +def igmp_snooping_version(ctx, vid, version): + """IGMP Version""" + db = ctx.obj['db'] + vlan_valid = clicommon.is_vlanid_in_range(int(vid)) + if vlan_valid == False: + ctx.fail("Invalid VlanId!!") + vlan_name = 'Vlan{}'.format(vid) + vlan = db.get_entry('VLAN', vlan_name) + if len(vlan) == 0: + ctx.fail("{} doesn't exist".format(vlan_name)) + ver_name = format(version) + db.mod_entry('L2MC', vlan_name, {'version': ver_name}) + +@igmp_snooping.group('querier') +@clicommon.pass_db +def igmp_snooping_querier(config_db): + """Configure IGMP snooping querier""" + pass + +@igmp_snooping_querier.command('enable') +@click.argument('vid', metavar='', required=True, type=int) +@click.pass_context +def igmp_snooping_querier_enable(ctx, vid): + """Enable IGMP Querier for a VLAN""" + db = ctx.obj['db'] + vlan_valid = clicommon.is_vlanid_in_range(int(vid)) + if vlan_valid == False: + ctx.fail("Invalid VlanId!!") + vlan_name = 'Vlan{}'.format(vid) + vlan = db.get_entry('VLAN', vlan_name) + if len(vlan) == 0: + ctx.fail("{} doesn't exist".format(vlan_name)) + db.mod_entry('L2MC', vlan_name, {'querier': 'true'}) + +@igmp_snooping_querier.command('disable') +@click.argument('vid', metavar='', required=True, type=int) +@click.pass_context +def igmp_snooping_querier_disable(ctx, vid): + """Disable IGMP Querier for a VLAN""" + db = ctx.obj['db'] + vlan_valid = clicommon.is_vlanid_in_range(int(vid)) + if vlan_valid == False: + ctx.fail("Invalid VlanId!!") + vlan_name = 'Vlan{}'.format(vid) + vlan = db.get_entry('VLAN', vlan_name) + if len(vlan) == 0: + ctx.fail("{} doesn't exist".format(vlan_name)) + db.mod_entry('L2MC', vlan_name, {'querier': 'false'}) + +@igmp_snooping.group('fast-leave') +@clicommon.pass_db +def igmp_snooping_fast_leave(config_db): + """Configure IGMP snooping fast-leave""" + pass + +@igmp_snooping_fast_leave.command('enable') +@click.argument('vid', metavar='', required=True, type=int) +@click.pass_context +def igmp_snooping_fast_leave_enable(ctx, vid): + """Enable IGMP fast-leave for a VLAN""" + db = ctx.obj['db'] + vlan_valid = clicommon.is_vlanid_in_range(int(vid)) + if vlan_valid == False: + ctx.fail("Invalid VlanId!!") + vlan_name = 'Vlan{}'.format(vid) + vlan = db.get_entry('VLAN', vlan_name) + if len(vlan) == 0: + ctx.fail("{} doesn't exist".format(vlan_name)) + db.mod_entry('L2MC', vlan_name, {'fast-leave': 'true'}) + +@igmp_snooping_fast_leave.command('disable') +@click.argument('vid', metavar='', required=True, type=int) +@click.pass_context +def igmp_snooping_fast_leave_disable(ctx, vid): + """Disable IGMP fast-leave for a VLAN""" + db = ctx.obj['db'] + vlan_valid = clicommon.is_vlanid_in_range(int(vid)) + if vlan_valid == False: + ctx.fail("Invalid VlanId!!") + vlan_name = 'Vlan{}'.format(vid) + vlan = db.get_entry('VLAN', vlan_name) + if len(vlan) == 0: + ctx.fail("{} doesn't exist".format(vlan_name)) + db.mod_entry('L2MC', vlan_name, {'fast-leave': 'false'}) + +@igmp_snooping.group('static-group') +@clicommon.pass_db +def igmp_snooping_static_group(config_db): + """Configure IGMP snooping static-group""" + pass + +@igmp_snooping_static_group.command('add') +@click.argument('vid', metavar='', required=True, type=int) +@click.argument('interface_name', metavar='', required=True) +@click.argument("ip_addr", metavar="", required=True) +@click.pass_context +def igmp_snooping_static_add(ctx, vid, interface_name, ip_addr): + """Add static-group for VLAN""" + db = ctx.obj['db'] + vlan_valid = clicommon.is_vlanid_in_range(int(vid)) + if vlan_valid == False: + ctx.fail("Invalid VlanId!!") + vlan_name = 'Vlan{}'.format(vid) + vlan = db.get_entry('VLAN', vlan_name) + if len(vlan) == 0: + ctx.fail("{} doesn't exist".format(vlan_name)) + # check vlan l2mc enable + # l2mc_enable = db.get_entry('L2MC', vlan_name) + # if len(l2mc_enable) == 0: + # ctx.fail("{} is not L2MC enable".format(vlan_name)) + + if clicommon.get_interface_naming_mode() == "alias": + interface_name = interface_alias_to_name(interface_name) + if interface_name is None: + ctx.fail("'interface_name' is None!") + port_tables = db.get_table("PORT") + lag_tables = db.get_table("PORTCHANNEL") + if interface_name not in port_tables and interface_name not in lag_tables: + ctx.fail("Interface %s does not exist!" %interface_name) + + try: + ip_address = ipaddress.ip_address(ip_addr) + except ValueError as err: + ctx.fail("IP address is not valid: {}".format(err)) + + if not ip_address.is_multicast: + ctx.fail("IP address {} is not multicast".format(str(ip_address))) + + if ip_address < ipaddress.IPv4Address("224.0.0.0") or ip_address > ipaddress.IPv4Address("239.255.255.255"): + ctx.fail("IP address {} is outside the valid multicast range".format(str(ip_address))) + + if ip_address < ipaddress.IPv4Address("224.0.0.0") or ip_address <= ipaddress.IPv4Address("224.0.0.255"): + ctx.fail("IP address {} is reserved multicast".format(str(ip_address))) + + #check interface is a vlan member + # key = vlan_name +"|"+ interface_name + # vlanmember = db.get_entry('VLAN_MEMBER', key) + # if len(vlanmember) == 0: + # ctx.fail("{} is not a member of {}".format(interface_name,vlan_name)) + + static_group_key = vlan_name+'|'+ip_addr + l2mc_name = db.get_entry('L2MC_STATIC_GROUP', static_group_key) + members = l2mc_name.get('static-members', []) + if interface_name in members: + ctx.fail("{} is already a member of {}".format(interface_name,members)) + members.append(interface_name) + l2mc_name['static-members'] = members + db.set_entry('L2MC_STATIC_GROUP', static_group_key, l2mc_name) + static_group_mem_key = vlan_name+'|'+ip_addr+'|'+interface_name + db.set_entry('L2MC_STATIC_MEMBER', static_group_mem_key, {'port':interface_name}) + +@igmp_snooping_static_group.command('del') +@click.argument('vid', metavar='', required=True, type=int) +@click.argument('interface_name', metavar='', required=True) +@click.argument("ip_addr", metavar="", required=True) +@click.pass_context +def igmp_snooping_static_del(ctx, vid, interface_name, ip_addr): + """Del static-group for VLAN""" + db = ctx.obj['db'] + vlan_valid = clicommon.is_vlanid_in_range(int(vid)) + if vlan_valid == False: + ctx.fail("Invalid VlanId!!") + vlan_name = 'Vlan{}'.format(vid) + vlan = db.get_entry('VLAN', vlan_name) + if len(vlan) == 0: + ctx.fail("{} doesn't exist".format(vlan_name)) + if clicommon.get_interface_naming_mode() == "alias": + interface_name = interface_alias_to_name(interface_name) + if interface_name is None: + ctx.fail("'interface_name' is None!") + static_group_key = vlan_name+'|'+ip_addr + l2mc_name = db.get_entry('L2MC_STATIC_GROUP', static_group_key) + members = l2mc_name.get('static-members', []) + if interface_name not in members: + ctx.fail("{} is not a member of {}".format(interface_name, members)) + members.remove(interface_name) + if len(members) == 0: + del l2mc_name['static-members'] + db.set_entry('L2MC_STATIC_GROUP', static_group_key, None) + else: + l2mc_name['static-members'] = members + db.set_entry('L2MC_STATIC_GROUP', static_group_key, l2mc_name) + static_group_mem_key = vlan_name+'|'+ip_addr+'|'+interface_name + db.set_entry('L2MC_STATIC_MEMBER', static_group_mem_key, None) + +@igmp_snooping.group('mrouter') +@clicommon.pass_db +def igmp_snooping_mrouter(config_db): + """Configure IGMP snooping mrouter""" + pass + +@igmp_snooping_mrouter.command('add') +@click.argument('vid', metavar='', required=True, type=int) +@click.argument('interface_name', metavar='', required=True) +@click.pass_context +def igmp_snooping_mrouter_add(ctx, vid, interface_name): + """Add router interface for VLAN""" + db = ctx.obj['db'] + vlan_valid = clicommon.is_vlanid_in_range(int(vid)) + if vlan_valid == False: + ctx.fail("Invalid VlanId!!") + vlan_name = 'Vlan{}'.format(vid) + vlan = db.get_entry('VLAN', vlan_name) + if len(vlan) == 0: + ctx.fail("{} doesn't exist".format(vlan_name)) + if clicommon.get_interface_naming_mode() == "alias": + interface_name = interface_alias_to_name(interface_name) + if interface_name is None: + ctx.fail("'interface_name' is None!") + port_tables = db.get_table("PORT") + lag_tables = db.get_table("PORTCHANNEL") + if interface_name not in port_tables and interface_name not in lag_tables: + ctx.fail("Interface %s does not exist!" %interface_name) + + mrouter_mem_key = vlan_name+'|'+interface_name + l2mc_name = db.get_entry('L2MC_MROUTER', mrouter_mem_key) + if len(l2mc_name) != 0: + ctx.fail("{} is already a member of {}".format(interface_name,vlan_name)) + db.set_entry('L2MC_MROUTER', mrouter_mem_key, {'mrouter_port':interface_name}) + +@igmp_snooping_mrouter.command('del') +@click.argument('vid', metavar='', required=True, type=int) +@click.argument('interface_name', metavar='', required=True) +@click.pass_context +def igmp_snooping_mrouter_del(ctx, vid, interface_name): + """Remove router interface from VLAN""" + db = ctx.obj['db'] + vlan_valid = clicommon.is_vlanid_in_range(int(vid)) + if vlan_valid == False: + ctx.fail("Invalid VlanId!!") + vlan_name = 'Vlan{}'.format(vid) + vlan = db.get_entry('VLAN', vlan_name) + if len(vlan) == 0: + ctx.fail("{} doesn't exist".format(vlan_name)) + if clicommon.get_interface_naming_mode() == "alias": + interface_name = interface_alias_to_name(interface_name) + if interface_name is None: + ctx.fail("'interface_name' is None!") + + mrouter_mem_key = vlan_name+'|'+interface_name + l2mc_name = db.get_entry('L2MC_MROUTER', mrouter_mem_key) + if len(l2mc_name) == 0: + ctx.fail("{} is not a member of {}".format(interface_name,vlan_name)) + db.set_entry('L2MC_MROUTER', mrouter_mem_key, None) + + if __name__ == '__main__': config() diff --git a/config/vlan.py b/config/vlan.py index eae51eb312..8566cdec5a 100644 --- a/config/vlan.py +++ b/config/vlan.py @@ -147,6 +147,13 @@ def del_vlan(db, vid, multiple, no_restart_dhcp_relay): config_db = ValidatedConfigDBConnector(db.cfgdb) + keys = config_db.get_keys("L2MC") + + for key in keys: + vlan_key = key + if vlan_key == f"Vlan{vid}" : + ctx.fail(f"Vlan{vid} cannot be removed. First IGMP Snooping disable.") + if ADHOC_VALIDATION: for vid in vid_list: log.log_info("'vlan del {}' executing...".format(vid)) @@ -369,6 +376,14 @@ def del_vlan_member(db, vid, port, multiple, except_flag): vid_list = clicommon.vlan_member_input_parser(ctx, "del", db, except_flag, multiple, vid, port) config_db = ValidatedConfigDBConnector(db.cfgdb) + + keys = config_db.get_keys("L2MC_STATIC_MEMBER") + + for key in keys: + vlan_key, _, port_key = key + if vlan_key == f"Vlan{vid}" and port_key == port: + ctx.fail(f"Port {port} cannot be removed from Vlan{vid}. Please remove all associated static Layer 2 multicast group entries first.") + if ADHOC_VALIDATION: for vid in vid_list: log.log_info("'vlan member del {} {}' executing...".format(vid, port)) diff --git a/show/main.py b/show/main.py index edbf549edd..a9db83982e 100755 --- a/show/main.py +++ b/show/main.py @@ -2875,5 +2875,441 @@ def banner(db): helper = util_base.UtilHelper() helper.load_and_register_plugins(plugins, cli) + +@cli.group(cls=clicommon.AliasedGroup) +def igmp_snooping(): + """Show igmp-snooping related information""" + pass + +@igmp_snooping.command('vlan') +@click.argument('vlan-id', required=True) +def igmp_snooping_vlan(vlan_id): + """ IGMP snooping vlan """ + + app_mrouter_dict = {} + igmp_snooping_fvs = { + 'enabled' : 'true', + 'querier' : 'false', + 'version' : '2', + 'fast-leave': 'false', + 'query-interval':'125', + 'query-max-response-time': '10', + 'last-member-query-interval': '1000', + 'optimised-multicast-flood' : 'false', + 'link-local-groups-suppression' : 'false', + } + + config_db = ConfigDBConnector() + config_db.connect() + + cfg_l2mc_table = config_db.get_table('L2MC') + if not cfg_l2mc_table: + return + + app_db = SonicV2Connector(host='127.0.0.1') + app_db.connect(app_db.APPL_DB) + app_l2mc_mrouter_keys = app_db.keys(app_db.APPL_DB, 'L2MC_MROUTER_TABLE:*') + + if app_l2mc_mrouter_keys: + + for key in app_l2mc_mrouter_keys: + list_items = key.split(':') + + mrouter_vlan_id = str(list_items[1].strip("Vlan")) + mrouter_interface = list_items[2] + + if mrouter_vlan_id in app_mrouter_dict: + app_mrouter_dict [str(mrouter_vlan_id)].append(mrouter_interface) + else: + app_mrouter_dict [str(mrouter_vlan_id)] = [mrouter_interface] + + #click.echo ("APP_DB: {}".format(app_mrouter_dict)) + + cfg_l2mc_sorted_table = natsorted(cfg_l2mc_table) + + key = "Vlan"+str(vlan_id) + data = config_db.get_entry('L2MC', key) + app_l2mc_supress_key = "L2MC_SUPPRESS_TABLE:Vlan" + str(vlan_id) + + if not data: + return + + querier_status = data.get('querier') + if not querier_status: + querier_status = igmp_snooping_fvs['querier'] + + operation_mode = data.get('version') + if not operation_mode: + operation_mode = igmp_snooping_fvs['version'] + + fast_leave = data.get('fast-leave') + if not fast_leave: + fast_leave = igmp_snooping_fvs['fast-leave'] + + if fast_leave == 'false': + fast_leave_status = 'Disabled' + else: + fast_leave_status = 'Enabled' + + max_response_time = data.get('query-max-response-time') + if not max_response_time: + max_response_time = igmp_snooping_fvs['query-max-response-time'] + + last_member_query_interval = data.get('last-member-query-interval') + if not last_member_query_interval: + last_member_query_interval = igmp_snooping_fvs['last-member-query-interval'] + + query_interval = data.get('query-interval') + if not query_interval: + query_interval = igmp_snooping_fvs['query-interval'] + + optimised_multicast_flood = app_db.get(app_db.APPL_DB, app_l2mc_supress_key, 'optimised-multicast-flood') + if not optimised_multicast_flood: + optimised_multicast_flood = igmp_snooping_fvs['optimised-multicast-flood'] + else : + if optimised_multicast_flood == 'enable': + optimised_multicast_flood = 'true' + else: + optimised_multicast_flood = 'false' + + link_local_groups_suppression = app_db.get(app_db.APPL_DB, app_l2mc_supress_key, 'link-local-groups-suppression') + if not link_local_groups_suppression: + link_local_groups_suppression = igmp_snooping_fvs['link-local-groups-suppression'] + else : + if link_local_groups_suppression == 'enable': + link_local_groups_suppression = 'true' + else: + link_local_groups_suppression = 'false' + + + click.echo("\nVlan ID: " + vlan_id) + app_memb_intf_string = "" + if len(app_mrouter_dict) != 0: + if vlan_id in app_mrouter_dict: + for memb_intf in app_mrouter_dict[str(vlan_id)]: + #print comma in between + if app_memb_intf_string == "": + app_memb_intf_string = memb_intf + else: + app_memb_intf_string = app_memb_intf_string + ", " + memb_intf + click.echo("Multicast Router ports: " + app_memb_intf_string) + click.echo("Querier - " + querier_status) + click.echo("IGMP Operation mode: IGMPv" + operation_mode) + click.echo("Is Fast-Leave Enabled: " + fast_leave_status) + click.echo("Max Response time = " + max_response_time) + click.echo("Query Interval = " + query_interval) + click.echo("Last Member Query Interval = " + last_member_query_interval ) + click.echo("Optimised Multicast Flood = " + optimised_multicast_flood ) + click.echo("Link Local Groups Suppression = " + link_local_groups_suppression + "\n") + + return + +@igmp_snooping.command('all') +def igmp_snooping_vlan(): + """ IGMP snooping all""" + config_db = ConfigDBConnector() + config_db.connect() + vlan_dict = {} + + l2mc_keys = config_db.get_keys('L2MC') + if not l2mc_keys: + return + + for key in l2mc_keys: + vlan_id = str(key.strip("Vlan")) + if vlan_id in vlan_dict: + vlan_dict[vlan_id].append(vlan_id) + else: + vlan_dict[vlan_id] = [vlan_id] + + for vlan_id in natsorted(vlan_dict): + app_mrouter_dict = {} + igmp_snooping_fvs = { + 'enabled' : 'true', + 'querier' : 'false', + 'version' : '2', + 'fast-leave': 'false', + 'query-interval':'125', + 'query-max-response-time': '10', + 'last-member-query-interval': '1000', + 'optimised-multicast-flood' : 'false', + 'link-local-groups-suppression' : 'false', + } + + app_db = SonicV2Connector(host='127.0.0.1') + app_db.connect(app_db.APPL_DB) + app_l2mc_mrouter_keys = app_db.keys(app_db.APPL_DB, 'L2MC_MROUTER_TABLE:*') + + if app_l2mc_mrouter_keys: + + for key in app_l2mc_mrouter_keys: + list_items = key.split(':') + + mrouter_vlan_id = str(list_items[1].strip("Vlan")) + mrouter_interface = list_items[2] + + if mrouter_vlan_id in app_mrouter_dict: + app_mrouter_dict [str(mrouter_vlan_id)].append(mrouter_interface) + else: + app_mrouter_dict [str(mrouter_vlan_id)] = [mrouter_interface] + + #click.echo ("APP_DB: {}".format(app_mrouter_dict)) + + key = "Vlan"+str(vlan_id) + data = config_db.get_entry('L2MC', key) + app_l2mc_supress_key = "L2MC_SUPPRESS_TABLE:Vlan" + str(vlan_id) + + + if not data: + return + + querier_status = data.get('querier') + if not querier_status: + querier_status = igmp_snooping_fvs['querier'] + + operation_mode = data.get('version') + if not operation_mode: + operation_mode = igmp_snooping_fvs['version'] + + fast_leave = data.get('fast-leave') + if not fast_leave: + fast_leave = igmp_snooping_fvs['fast-leave'] + + if fast_leave == 'false': + fast_leave_status = 'Disabled' + else: + fast_leave_status = 'Enabled' + + max_response_time = data.get('query-max-response-time') + if not max_response_time: + max_response_time = igmp_snooping_fvs['query-max-response-time'] + + last_member_query_interval = data.get('last-member-query-interval') + if not last_member_query_interval: + last_member_query_interval = igmp_snooping_fvs['last-member-query-interval'] + + query_interval = data.get('query-interval') + if not query_interval: + query_interval = igmp_snooping_fvs['query-interval'] + + optimised_multicast_flood = app_db.get(app_db.APPL_DB, app_l2mc_supress_key, 'optimised-multicast-flood') + + if not optimised_multicast_flood: + optimised_multicast_flood = igmp_snooping_fvs['optimised-multicast-flood'] + else : + if optimised_multicast_flood == 'enable': + optimised_multicast_flood = 'true' + else: + optimised_multicast_flood = 'false' + + + link_local_groups_suppression = app_db.get(app_db.APPL_DB, app_l2mc_supress_key, 'link-local-groups-suppression') + if not link_local_groups_suppression: + link_local_groups_suppression = igmp_snooping_fvs['link-local-groups-suppression'] + else : + if link_local_groups_suppression == 'enable': + link_local_groups_suppression = 'true' + else: + link_local_groups_suppression = 'false' + + click.echo("\nVlan ID: " + vlan_id) + app_memb_intf_string = "" + if len(app_mrouter_dict) != 0: + if vlan_id in app_mrouter_dict: + for memb_intf in app_mrouter_dict[str(vlan_id)]: + #print comma in between + if app_memb_intf_string == "": + app_memb_intf_string = memb_intf + else: + app_memb_intf_string = app_memb_intf_string + ", " + memb_intf + click.echo("Multicast Router ports: " + app_memb_intf_string) + click.echo("Querier - " + querier_status) + click.echo("IGMP Operation mode: IGMPv" + operation_mode) + click.echo("Is Fast-Leave Enabled: " + fast_leave_status) + click.echo("Max Response time = " + max_response_time) + click.echo("Query Interval = " + query_interval) + click.echo("Last Member Query Interval = " + last_member_query_interval ) + click.echo("Optimised Multicast Flood = " + optimised_multicast_flood ) + click.echo("Link Local Groups Suppression = " + link_local_groups_suppression + "\n") + click.echo("Total number of entries: " + str(len(l2mc_keys))+"\n") + +# +# 'groups' group ("show igmp_snooping igmp_snooping_groups") +# +@igmp_snooping.group('groups') +@click.pass_context +def igmp_snooping_groups(ctx): + """Show IP IGMP snooping information""" + pass + +@igmp_snooping_groups.command('all') +def igmp_snooping_groups_all(): + """ IGMP snooping groups all """ + app_db = SonicV2Connector(host='127.0.0.1') + app_db.connect(app_db.APPL_DB) + + vlan_dict = {} + member_type_dict = {} + member_table_dict = {} + mrouter_type_dict = {} + mrouter_table_dict = {} + + vlan_table_keys = app_db.keys(app_db.APPL_DB, 'L2MC_VLAN_TABLE:*') + + member_table_keys = app_db.keys(app_db.APPL_DB, 'L2MC_MEMBER_TABLE:*') + mrouter_table_keys = app_db.keys(app_db.APPL_DB, 'L2MC_MROUTER_TABLE:*') + + for key in vlan_table_keys: + list_items = key.split(':') + vlan_id = str(list_items[1].strip("Vlan")) + if vlan_id in vlan_dict: + vlan_dict[vlan_id].append(vlan_id) + else: + vlan_dict[vlan_id] = [vlan_id] + + for key in member_table_keys: + list_items = key.split(':') + + vlan_id = str(list_items[1].strip("Vlan")) + source = list_items[2] + group = list_items[3] + member = list_items[4] + member_type = app_db.get(app_db.APPL_DB, key, 'type') + + if (vlan_id,source,group) in member_table_dict: + member_table_dict[(vlan_id,source,group)].append(member) + else: + member_table_dict[(vlan_id,source,group)] = [member] + member_type_dict[(vlan_id,source,group,member)] = [member_type] + + for key in mrouter_table_keys: + list_items = key.split(':') + + vlan_id = str(list_items[1].strip("Vlan")) + member = list_items[2] + mrouter_type = app_db.get(app_db.APPL_DB, key, 'type') + + if (vlan_id) in mrouter_table_dict: + mrouter_table_dict[(vlan_id)].append(member) + else: + mrouter_table_dict[(vlan_id)] = [member] + mrouter_type_dict[vlan_id,member] = [mrouter_type] + + for vlan_iter in natsorted(vlan_dict): + click.echo("\nVlan ID : " + vlan_iter) + click.echo("--------------") + count = 0 + for v in natsorted(mrouter_table_dict): + if v == vlan_iter: + mrout_intf_string = "" + mrouter_type_string = "" + click.echo("Mrouters Ports: " ) + for mrout_intf in mrouter_table_dict[(v)]: + mrout_intf_string = mrout_intf + for mrouter_type in mrouter_type_dict[(v,mrout_intf)]: + mrouter_type_string = mrouter_type + click.echo(" " + mrout_intf_string + "(" + mrouter_type_string + ")") + + for v,s,g in natsorted(member_table_dict): + + if v == vlan_iter: + count += 1 + s_entry = s + if s == "0.0.0.0": + s_entry = "*" + click.echo(str(count) + " " + "(" + s_entry + ", " + g + ")") + click.echo(" Members Ports: ") + memb_intf_string = "" + member_type_string = "" + for memb_intf in member_table_dict[(v,s,g)]: + memb_intf_string = memb_intf + for member_type in member_type_dict[(v,s,g,memb_intf)]: + member_type_string = member_type + click.echo(" " + memb_intf_string + "(" + member_type_string + ")") + click.echo("Total number of entries: "+str(count)+"\n") + + return + +@igmp_snooping_groups.command('vlan') +@click.argument('vlan-id', required=True) +def igmp_snooping_groups_vlan(vlan_id): + """ IGMP snooping groups vlan """ + app_db = SonicV2Connector(host='127.0.0.1') + app_db.connect(app_db.APPL_DB) + + member_type_dict = {} + member_table_dict = {} + mrouter_type_dict = {} + mrouter_table_dict = {} + + vlan_table_keys = app_db.keys(app_db.APPL_DB, 'L2MC_VLAN_TABLE:*') + + member_table_keys = app_db.keys(app_db.APPL_DB, 'L2MC_MEMBER_TABLE:*') + mrouter_table_keys = app_db.keys(app_db.APPL_DB, 'L2MC_MROUTER_TABLE:*') + + for key in member_table_keys: + list_items = key.split(':') + + vlan_id_val = str(list_items[1].strip("Vlan")) + if vlan_id_val != vlan_id: + continue + + source = list_items[2] + group = list_items[3] + member = list_items[4] + member_type = app_db.get(app_db.APPL_DB, key, 'type') + + if (vlan_id,source,group) in member_table_dict: + member_table_dict[(vlan_id,source,group)].append(member) + else: + member_table_dict[(vlan_id,source,group)] = [member] + member_type_dict[(vlan_id,source,group,member)] = [member_type] + + for key in mrouter_table_keys: + list_items = key.split(':') + + vlan_id_val = str(list_items[1].strip("Vlan")) + if vlan_id_val != vlan_id: + continue + member = list_items[2] + mrouter_type = app_db.get(app_db.APPL_DB, key, 'type') + + if (vlan_id) in mrouter_table_dict: + mrouter_table_dict[(vlan_id)].append(member) + else: + mrouter_table_dict[(vlan_id)] = [member] + mrouter_type_dict[(vlan_id,member)] = [mrouter_type] + + click.echo("\nVlan ID : " + vlan_id) + click.echo("--------------") + for v in natsorted(mrouter_table_dict): + mrout_intf_string = "" + mrouter_type_string = "" + click.echo("Mrouters Ports: " ) + for mrout_intf in mrouter_table_dict[(v)]: + mrout_intf_string = mrout_intf + for mrouter_type in mrouter_type_dict[(v,mrout_intf)]: + mrouter_type_string = mrouter_type + click.echo(" " + mrout_intf_string + "(" + mrouter_type_string + ")" ) + + count = 0 + for v,s,g in natsorted(member_table_dict): + count += 1 + s_entry = s + if s == "0.0.0.0": + s_entry = "*" + click.echo(str(count) + " " + "(" + s_entry + ", " + g + ")") + click.echo(" Members Ports: ") + memb_intf_string = "" + member_type_string = "" + for memb_intf in member_table_dict[(v,s,g)]: + memb_intf_string = memb_intf + for member_type in member_type_dict[(v,s,g,memb_intf)]: + member_type_string = member_type + click.echo(" " + memb_intf_string + "(" + member_type_string + ")" ) + click.echo("Total number of entries: "+str(count)+"\n") + + return + if __name__ == '__main__': cli()