From f66a8cc4edb9f9c2ecb2a5a34c339fd01f4af203 Mon Sep 17 00:00:00 2001 From: Rupesh Kumar Date: Thu, 4 Jun 2020 07:12:44 -0700 Subject: [PATCH 1/8] Add support for port mirroring CLIs Signed-off-by: Rupesh Kumar --- acl_loader/main.py | 26 ++++--- config/main.py | 180 +++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 192 insertions(+), 14 deletions(-) diff --git a/acl_loader/main.py b/acl_loader/main.py index f9201846b3..ec885c4bd6 100644 --- a/acl_loader/main.py +++ b/acl_loader/main.py @@ -718,21 +718,29 @@ def show_session(self, session_name): :param session_name: Optional. Mirror session name. Filter sessions by specified name. :return: """ - header = ("Name", "Status", "SRC IP", "DST IP", "GRE", "DSCP", "TTL", "Queue", "Policer", "Monitor Port") + erspan_header = ("Name", "Status", "SRC IP", "DST IP", "GRE", "DSCP", "TTL", "Queue", + "Policer", "Monitor Port", "SRC Port", "Direction") + span_header = ("Name", "Status", "DST Port", "SRC Port", "Direction", "Queue", "Policer") - data = [] + erspan_data = [] + span_data = [] for key, val in self.get_sessions_db_info().iteritems(): if session_name and key != session_name: continue - # For multi-mpu platform status and monitor port will be dict() - # of 'asic-x':value - data.append([key, val["status"], val["src_ip"], val["dst_ip"], - val.get("gre_type", ""), val.get("dscp", ""), - val.get("ttl", ""), val.get("queue", ""), val.get("policer", ""), - val.get("monitor_port", "")]) - print(tabulate.tabulate(data, headers=header, tablefmt="simple", missingval="")) + if val.get("type") == "SPAN": + span_data.append([key, val.get("status", ""), val.get("dst_port", ""), + val.get("src_port", ""), val.get("direction", "").lower(),val.get("queue", ""), val.get("policer", "")]) + else: + erspan_data.append([key, val.get("status", ""), val.get("src_ip", ""), + val.get("dst_ip", ""), val.get("gre_type", ""), val.get("dscp", ""), + val.get("ttl", ""), val.get("queue", ""), val.get("policer", ""), + val.get("monitor_port", ""), val.get("src_port", ""), val.get("direction", "").lower()]) + print 'ERSPAN Sessions' + print(tabulate.tabulate(erspan_data, headers=erspan_header, tablefmt="simple", missingval="")) + print '\nSPAN Sessions' + print(tabulate.tabulate(span_data, headers=span_header, tablefmt="simple", missingval="")) def show_policer(self, policer_name): """ diff --git a/config/main.py b/config/main.py index 22dd494854..376823be52 100755 --- a/config/main.py +++ b/config/main.py @@ -570,6 +570,82 @@ def is_ipaddress(val): return False return True +# +# Check if an interface_name is in a vlan +# +def interface_is_in_vlan(vlan_member_table, interface_name): + + for k,v in vlan_member_table: + if v == interface_name: + return True + + return False + + +# +# Check if port is already configured as mirror destination port +# +def interface_is_mirror_dst_port(config_db, interface_name): + mirror_table = config_db.get_table('MIRROR_SESSION') + for k,v in mirror_table.items(): + if 'dst_port' in v and v['dst_port'] == interface_name: + return True + + return False +# +# Check if port is already configured with mirror config +# +def interface_has_mirror_config(mirror_table, interface_name): + for k,v in mirror_table.items(): + if 'src_port' in v and v['src_port'] == interface_name: + return True + if 'dst_port' in v and v['dst_port'] == interface_name: + return True + + return False + +# +# Check if SPAN mirror-session config is valid. +# +def validate_mirror_session_config(config_db, session_name, dst_port, src_port, direction): + if len(config_db.get_entry('MIRROR_SESSION', session_name)) != 0: + click.echo("Error: {} already exists".format(session_name)) + return False + + vlan_member_table = config_db.get_table('VLAN_MEMBER') + mirror_table = config_db.get_table('MIRROR_SESSION') + + if dst_port is not None: + if interface_name_is_valid(dst_port) is False: + click.echo("Error: Destination Interface {} is invalid".format(dst_port)) + return False + + if interface_is_in_vlan(vlan_member_table, dst_port): + click.echo("Error: Destination Interface {} has vlan config".format(dst_port)) + return False + + if interface_has_mirror_config(mirror_table, dst_port): + click.echo("Error: Destination Interface {} already has mirror config".format(dst_port)) + return False + + if src_port is not None: + for port in src_port.split(","): + if interface_name_is_valid(port) is False: + click.echo("Error: Source Interface {} is invalid".format(port)) + return False + if dst_port is not None and dst_port == port: + click.echo("Error: Destination Interface cant be same as Source Interface") + return False + if interface_has_mirror_config(mirror_table, port): + click.echo("Error: Source Interface {} already has mirror config".format(port)) + return False + + if direction is not None: + if not any ( [direction == 'rx', direction == 'tx', direction == 'both'] ): + click.echo("Error: Direction {} is invalid".format(direction)) + return False + + return True # This is our main entrypoint - the main 'config' command @click.group(cls=AbbreviationGroup, context_settings=CONTEXT_SETTINGS) @@ -1017,7 +1093,21 @@ def del_portchannel_member(ctx, portchannel_name, port_name): def mirror_session(): pass -@mirror_session.command() +# +# 'add' subgroup ('config mirror_session add ...') +# + +@mirror_session.group(cls=AbbreviationGroup, name='add') +@click.pass_context +def add(ctx): + """Add mirror_session""" + pass + +# +# 'add' subcommand +# + +@add.command('erspan') @click.argument('session_name', metavar='', required=True) @click.argument('src_ip', metavar='', required=True) @click.argument('dst_ip', metavar='', required=True) @@ -1025,12 +1115,15 @@ def mirror_session(): @click.argument('ttl', metavar='', required=True) @click.argument('gre_type', metavar='[gre_type]', required=False) @click.argument('queue', metavar='[queue]', required=False) +@click.argument('src_port', metavar='[src_port]', required=False) +@click.argument('direction', metavar='[direction]', required=False) @click.option('--policer') -def add(session_name, src_ip, dst_ip, dscp, ttl, gre_type, queue, policer): +def erspan(session_name, src_ip, dst_ip, dscp, ttl, gre_type, queue, policer, src_port, direction): """ - Add mirror session + Add ERSPAN mirror session """ session_info = { + "type" : "ERSPAN", "src_ip": src_ip, "dst_ip": dst_ip, "dscp": dscp, @@ -1045,7 +1138,19 @@ def add(session_name, src_ip, dst_ip, dscp, ttl, gre_type, queue, policer): if queue is not None: session_info['queue'] = queue - + + if src_port is not None: + if get_interface_naming_mode() == "alias": + src_port_list = [] + for port in src_port.split(","): + src_port_list.append(interface_alias_to_name(port)) + src_port=",".join(src_port_list) + + session_info['src_port'] = src_port + + if direction is not None: + session_info['direction'] = direction.upper() + """ For multi-npu platforms we need to program all front asic namespaces """ @@ -1053,12 +1158,74 @@ def add(session_name, src_ip, dst_ip, dscp, ttl, gre_type, queue, policer): if not namespaces['front_ns']: config_db = ConfigDBConnector() config_db.connect() + if validate_mirror_session_config(config_db, session_name, None, src_port, direction) is False: + return + config_db.set_entry("MIRROR_SESSION", session_name, session_info) + else: + per_npu_configdb = {} + for front_asic_namespaces in namespaces['front_ns']: + per_npu_configdb[front_asic_namespaces] = ConfigDBConnector(use_unix_socket_path=True, namespace=front_asic_namespaces) + per_npu_configdb[front_asic_namespaces].connect() + if validate_mirror_session_config(per_npu_configdb[front_asic_namespaces], session_name, None, src_port, direction) is False: + return + per_npu_configdb[front_asic_namespaces].set_entry("MIRROR_SESSION", session_name, session_info) + +@add.command('span') +@click.argument('session_name', metavar='', required=True) +@click.argument('dst_port', metavar='', required=True) +@click.argument('src_port', metavar='[src_port]', required=False) +@click.argument('direction', metavar='[direction]', required=False) +@click.argument('queue', metavar='[queue]', required=False) +@click.option('--policer') +def span(session_name, dst_port, src_port, direction, queue, policer): + """ + Add port mirror session + """ + if get_interface_naming_mode() == "alias": + dst_port = interface_alias_to_name(dst_port) + if dst_port is None: + click.echo("Error: Destination Interface {} is invalid".format(dst_port)) + return + if src_port is not None: + src_port_list = [] + for port in src_port.split(","): + src_port_list.append(interface_alias_to_name(port)) + src_port=",".join(src_port_list) + + session_info = { + "type" : "SPAN", + "dst_port": dst_port, + } + + if src_port is not None: + session_info['src_port'] = src_port + + if direction is not None: + session_info['direction'] = direction.upper() + + if policer is not None: + session_info['policer'] = policer + + if queue is not None: + session_info['queue'] = queue + + """ + For multi-npu platforms we need to program all front asic namespaces + """ + namespaces = sonic_device_util.get_all_namespaces() + if not namespaces['front_ns']: + config_db = ConfigDBConnector() + config_db.connect() + if validate_mirror_session_config(config_db, session_name, dst_port, src_port, direction) is False: + return config_db.set_entry("MIRROR_SESSION", session_name, session_info) else: per_npu_configdb = {} for front_asic_namespaces in namespaces['front_ns']: per_npu_configdb[front_asic_namespaces] = ConfigDBConnector(use_unix_socket_path=True, namespace=front_asic_namespaces) per_npu_configdb[front_asic_namespaces].connect() + if validate_mirror_session_config(per_npu_configdb[front_asic_namespaces], session_name, dst_port, src_port, direction) is False: + return per_npu_configdb[front_asic_namespaces].set_entry("MIRROR_SESSION", session_name, session_info) @mirror_session.command() @@ -1356,6 +1523,9 @@ def add_vlan_member(ctx, vid, interface_name, untagged): if len(vlan) == 0: ctx.fail("{} doesn't exist".format(vlan_name)) + if interface_is_mirror_dst_port(db, interface_name): + ctx.fail("{} is configured as mirror destination port".format(interface_name)) + members = vlan.get('members', []) if interface_name in members: if get_interface_naming_mode() == "alias": @@ -1370,7 +1540,7 @@ def add_vlan_member(ctx, vid, interface_name, untagged): for entry in interface_table: if (interface_name == entry[0]): ctx.fail("{} is a L3 interface!".format(interface_name)) - + members.append(interface_name) vlan['members'] = members db.set_entry('VLAN', vlan_name, vlan) From c2dab24a57ca55887183088537dbfa9a43b102a1 Mon Sep 17 00:00:00 2001 From: Rupesh Kumar Date: Thu, 11 Jun 2020 07:35:58 -0700 Subject: [PATCH 2/8] Updated Command Reference Guide with config and examples --- doc/Command-Reference.md | 64 +++++++++++++++++++++++++++++++++------- 1 file changed, 53 insertions(+), 11 deletions(-) diff --git a/doc/Command-Reference.md b/doc/Command-Reference.md index a6598b717e..7d14b1ba78 100644 --- a/doc/Command-Reference.md +++ b/doc/Command-Reference.md @@ -3747,7 +3747,6 @@ This command deletes the SNMP Trap server IP address to which SNMP agent is expe Go Back To [Beginning of the document](#) or [Beginning of this section](#management-vrf) - ## Mirroring ### Mirroring Show commands @@ -3763,10 +3762,16 @@ This command displays all the mirror sessions that are configured. - Example: ``` - admin@sonic:~$ show mirror session - Name Status SRC IP DST IP GRE DSCP TTL Queue - --------- -------- --------- -------- ----- ------ ----- ------- + admin@sonic:~$ show mirror_session + ERSPAN Sessions + Name Status SRC IP DST IP GRE DSCP TTL Queue Policer Monitor Port SRC Port Direction + ------ -------- -------- -------- ----- ------ ----- ------- --------- -------------- ---------- ----------- everflow0 active 10.1.0.32 10.0.0.7 + + SPAN Sessions + Name Status DST Port SRC Port Direction + ------ -------- ---------- ------------- ----------- + port0 active Ethernet0 PortChannel10 rx ``` ### Mirroring Config commands @@ -3774,7 +3779,12 @@ This command displays all the mirror sessions that are configured. **config mirror_session** This command is used to add or remove mirroring sessions. Mirror session is identified by "session_name". -While adding a new session, users need to configure the following fields that are used while forwarding the mirrored packets. +This command supports configuring both SPAN/ERSPAN sessions. +In SPAN user can configure mirroring of list of source ports/LAG to destination port in ingress/egress/both directions. +In ERSPAN user can configure mirroring of list of source ports/LAG to a destination IP. +Both SPAN/ERSPAN support ACL based mirroring and can be used in ACL configurations. + +While adding a new ERSPAN session, users need to configure the following fields that are used while forwarding the mirrored packets. 1) source IP address, 2) destination IP address, @@ -3782,20 +3792,52 @@ While adding a new session, users need to configure the following fields that ar 4) TTL value 5) optional - GRE Type in case if user wants to send the packet via GRE tunnel. GRE type could be anything; it could also be left as empty; by default, it is 0x8949 for Mellanox; and 0x88be for the rest of the chips. 6) optional - Queue in which packets shall be sent out of the device. Valid values 0 to 7 for most of the devices. Users need to know their device and the number of queues supported in that device. +7) optional - Policer which will be used to control the rate at which frames are mirrored. +8) optional - List of source ports which can have both Ethernet and LAG ports. +9) optional - Direction - Mirror session direction when configured along with Source port. - Usage: ``` - config mirror_session add [gre_type] [queue] + config mirror_session add erspan [gre_type] [queue] [policer ] [source-port-list] [direction] ``` - Example: ``` - admin@sonic:~$ sudo config mirror_session add mrr_abcd 1.2.3.4 20.21.22.23 8 100 0x6558 0 - admin@sonic:~$ show mirror_session - Name Status SRC IP DST IP GRE DSCP TTL Queue - --------- -------- ----------- ----------- ------ ------ ----- ------- - mrr_abcd inactive 1.2.3.4 20.21.22.23 0x6558 8 100 0 + root@T1-2:~# config mirror_session add erspan mrr_abcd 1.2.3.4 20.21.22.23 8 100 0x6558 0 + root@T1-2:~# show mirror_session + Name Status SRC IP DST IP GRE DSCP TTL Queue Policer Monitor Port SRC Port Direction + --------- -------- -------- ----------- ------ ------ ----- ------- --------- -------------- ---------- ----------- + mrr_abcd inactive 1.2.3.4 20.21.22.23 0x6558 8 100 0 + root@T1-2:~# + + root@T1-2:~# config mirror_session add erspan mrr_port 1.2.3.4 20.21.22.23 8 100 0x6558 0 Ethernet0 both + root@T1-2:~# show mirror_session + Name Status SRC IP DST IP GRE DSCP TTL Queue Policer Monitor Port SRC Port Direction + --------- -------- -------- ----------- ------ ------ ----- ------- --------- -------------- ---------- ----------- + mrr_port inactive 1.2.3.4 20.21.22.23 0x6558 8 100 0 Ethernet0 both + root@T1-2:~# + ``` + +While adding a new SPAN session, users need to configure the following fields that are used while forwarding the mirrored packets. +1) destination port, +2) optional - List of source ports- List of source ports which can have both Ethernet and LAG ports. +3) optional - Direction - Mirror session direction when configured along with Source port. +4) optional - Queue in which packets shall be sent out of the device. Valid values 0 to 7 for most of the devices. Users need to know their device and the number of queues supported in that device. +5) optional - Policer which will be used to control the rate at which frames are mirrored. + +- Usage: + ``` + config mirror_session add span [source-port-list] [direction] [queue] [policer ] + ``` + +- Example: ``` + root@T1-2:~# config mirror_session add span port0 Ethernet0 Ethernet4,PortChannel001,Ethernet8 both + root@T1-2:~# show mirror_session + Name Status DST Port SRC Port Direction + ------ -------- ---------- --------------------------------- ----------- + port0 active Ethernet0 Ethernet4,PortChannel10,Ethernet8 both + root@T1-2:~# Go Back To [Beginning of the document](#) or [Beginning of this section](#mirroring) From 47f4a93c41766965f665d1a51aa5ab3617ca82b7 Mon Sep 17 00:00:00 2001 From: Rupesh Kumar Date: Thu, 25 Jun 2020 11:12:15 -0700 Subject: [PATCH 3/8] Set default direction as both when source port is specified in session config --- config/main.py | 4 ++++ doc/Command-Reference.md | 4 ++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/config/main.py b/config/main.py index 376823be52..28f15dadd5 100755 --- a/config/main.py +++ b/config/main.py @@ -1147,6 +1147,8 @@ def erspan(session_name, src_ip, dst_ip, dscp, ttl, gre_type, queue, policer, sr src_port=",".join(src_port_list) session_info['src_port'] = src_port + if direction is None: + direction = "both" if direction is not None: session_info['direction'] = direction.upper() @@ -1199,6 +1201,8 @@ def span(session_name, dst_port, src_port, direction, queue, policer): if src_port is not None: session_info['src_port'] = src_port + if direction is None: + direction = "both" if direction is not None: session_info['direction'] = direction.upper() diff --git a/doc/Command-Reference.md b/doc/Command-Reference.md index 7d14b1ba78..fc11a7693b 100644 --- a/doc/Command-Reference.md +++ b/doc/Command-Reference.md @@ -3794,7 +3794,7 @@ While adding a new ERSPAN session, users need to configure the following fields 6) optional - Queue in which packets shall be sent out of the device. Valid values 0 to 7 for most of the devices. Users need to know their device and the number of queues supported in that device. 7) optional - Policer which will be used to control the rate at which frames are mirrored. 8) optional - List of source ports which can have both Ethernet and LAG ports. -9) optional - Direction - Mirror session direction when configured along with Source port. +9) optional - Direction - Mirror session direction when configured along with Source port. (Supported rx/tx/both. default direction is both) - Usage: ``` @@ -3821,7 +3821,7 @@ While adding a new ERSPAN session, users need to configure the following fields While adding a new SPAN session, users need to configure the following fields that are used while forwarding the mirrored packets. 1) destination port, 2) optional - List of source ports- List of source ports which can have both Ethernet and LAG ports. -3) optional - Direction - Mirror session direction when configured along with Source port. +3) optional - Direction - Mirror session direction when configured along with Source port. (Supported rx/tx/both. default direction is both) 4) optional - Queue in which packets shall be sent out of the device. Valid values 0 to 7 for most of the devices. Users need to know their device and the number of queues supported in that device. 5) optional - Policer which will be used to control the rate at which frames are mirrored. From cd2317597e938cefe0b55a81f79d4fc09910c734 Mon Sep 17 00:00:00 2001 From: Rupesh Kumar Date: Mon, 29 Jun 2020 06:46:21 -0700 Subject: [PATCH 4/8] Addressed review comments --- acl_loader/main.py | 13 ++-- config/main.py | 143 ++++++++++++++++++++++++--------------- doc/Command-Reference.md | 24 +++++-- 3 files changed, 113 insertions(+), 67 deletions(-) diff --git a/acl_loader/main.py b/acl_loader/main.py index ec885c4bd6..16a69b7311 100644 --- a/acl_loader/main.py +++ b/acl_loader/main.py @@ -730,16 +730,17 @@ def show_session(self, session_name): if val.get("type") == "SPAN": span_data.append([key, val.get("status", ""), val.get("dst_port", ""), - val.get("src_port", ""), val.get("direction", "").lower(),val.get("queue", ""), val.get("policer", "")]) + val.get("src_port", ""), val.get("direction", "").lower(), + val.get("queue", ""), val.get("policer", "")]) else: erspan_data.append([key, val.get("status", ""), val.get("src_ip", ""), - val.get("dst_ip", ""), val.get("gre_type", ""), val.get("dscp", ""), - val.get("ttl", ""), val.get("queue", ""), val.get("policer", ""), - val.get("monitor_port", ""), val.get("src_port", ""), val.get("direction", "").lower()]) + val.get("dst_ip", ""), val.get("gre_type", ""), val.get("dscp", ""), + val.get("ttl", ""), val.get("queue", ""), val.get("policer", ""), + val.get("monitor_port", ""), val.get("src_port", ""), val.get("direction", "").lower()]) - print 'ERSPAN Sessions' + print("ERSPAN Sessions") print(tabulate.tabulate(erspan_data, headers=erspan_header, tablefmt="simple", missingval="")) - print '\nSPAN Sessions' + print("\nSPAN Sessions") print(tabulate.tabulate(span_data, headers=span_header, tablefmt="simple", missingval="")) def show_policer(self, policer_name): diff --git a/config/main.py b/config/main.py index 28f15dadd5..931f364517 100755 --- a/config/main.py +++ b/config/main.py @@ -570,33 +570,42 @@ def is_ipaddress(val): return False return True -# -# Check if an interface_name is in a vlan -# def interface_is_in_vlan(vlan_member_table, interface_name): + """ Check if an interface is in a vlan """ + for _,v in vlan_member_table.keys(): + if v == interface_name: + return True + + return False - for k,v in vlan_member_table: +def interface_is_in_portchannel(portchannel_member_table, interface_name): + """ Check if an interface is part of portchannel """ + for _,v in portchannel_member_table.keys(): if v == interface_name: return True return False +def interface_is_router_port(interface_table, interface_name): + """ Check if an interface has router config """ + for entry in interface_table.keys(): + if (interface_name == entry[0]): + return True + + return False -# -# Check if port is already configured as mirror destination port -# def interface_is_mirror_dst_port(config_db, interface_name): + """ Check if port is already configured as mirror destination port """ mirror_table = config_db.get_table('MIRROR_SESSION') - for k,v in mirror_table.items(): + for _,v in mirror_table.items(): if 'dst_port' in v and v['dst_port'] == interface_name: return True return False -# -# Check if port is already configured with mirror config -# + def interface_has_mirror_config(mirror_table, interface_name): - for k,v in mirror_table.items(): + """ Check if port is already configured with mirror config """ + for _,v in mirror_table.items(): if 'src_port' in v and v['src_port'] == interface_name: return True if 'dst_port' in v and v['dst_port'] == interface_name: @@ -608,12 +617,15 @@ def interface_has_mirror_config(mirror_table, interface_name): # Check if SPAN mirror-session config is valid. # def validate_mirror_session_config(config_db, session_name, dst_port, src_port, direction): + """ Check if SPAN mirror-session config is valid """ if len(config_db.get_entry('MIRROR_SESSION', session_name)) != 0: click.echo("Error: {} already exists".format(session_name)) return False vlan_member_table = config_db.get_table('VLAN_MEMBER') mirror_table = config_db.get_table('MIRROR_SESSION') + portchannel_member_table = config_db.get_table('PORTCHANNEL_MEMBER') + interface_table = config_db.get_table('INTERFACE') if dst_port is not None: if interface_name_is_valid(dst_port) is False: @@ -628,6 +640,14 @@ def validate_mirror_session_config(config_db, session_name, dst_port, src_port, click.echo("Error: Destination Interface {} already has mirror config".format(dst_port)) return False + if interface_is_in_portchannel(portchannel_member_table, dst_port): + click.echo("Error: Destination Interface {} has portchannel config".format(dst_port)) + return False + + if interface_is_router_port(interface_table, dst_port): + click.echo("Error: Destination Interface {} is a L3 interface".format(dst_port)) + return False + if src_port is not None: for port in src_port.split(","): if interface_name_is_valid(port) is False: @@ -1072,6 +1092,8 @@ def portchannel_member(ctx): def add_portchannel_member(ctx, portchannel_name, port_name): """Add member to port channel""" db = ctx.obj['db'] + if interface_is_mirror_dst_port(db, port_name): + ctx.fail("{} is configured as mirror destination port".format(interface_name)) db.set_entry('PORTCHANNEL_MEMBER', (portchannel_name, port_name), {'NULL': 'NULL'}) @@ -1097,17 +1119,31 @@ def mirror_session(): # 'add' subgroup ('config mirror_session add ...') # -@mirror_session.group(cls=AbbreviationGroup, name='add') +@mirror_session.command('add') +@click.argument('session_name', metavar='', required=True) +@click.argument('src_ip', metavar='', required=True) +@click.argument('dst_ip', metavar='', required=True) +@click.argument('dscp', metavar='', required=True) +@click.argument('ttl', metavar='', required=True) +@click.argument('gre_type', metavar='[gre_type]', required=False) +@click.argument('queue', metavar='[queue]', required=False) +@click.option('--policer') +def add(session_name, src_ip, dst_ip, dscp, ttl, gre_type, queue, policer): + """ Add ERSPAN mirror session.(Legacy support) """ + add_erspan(session_name, src_ip, dst_ip, dscp, ttl, gre_type, queue, policer) + +@mirror_session.group(cls=AbbreviationGroup, name='erspan') @click.pass_context -def add(ctx): - """Add mirror_session""" +def erspan(ctx): + """ ERSPAN mirror_session """ pass + # # 'add' subcommand # -@add.command('erspan') +@erspan.command('add') @click.argument('session_name', metavar='', required=True) @click.argument('src_ip', metavar='', required=True) @click.argument('dst_ip', metavar='', required=True) @@ -1118,24 +1154,14 @@ def add(ctx): @click.argument('src_port', metavar='[src_port]', required=False) @click.argument('direction', metavar='[direction]', required=False) @click.option('--policer') -def erspan(session_name, src_ip, dst_ip, dscp, ttl, gre_type, queue, policer, src_port, direction): - """ - Add ERSPAN mirror session - """ - session_info = { - "type" : "ERSPAN", - "src_ip": src_ip, - "dst_ip": dst_ip, - "dscp": dscp, - "ttl": ttl - } +def add(session_name, src_ip, dst_ip, dscp, ttl, gre_type, queue, policer, src_port, direction): + """ Add ERSPAN mirror session """ + add_erspan(session_name, src_ip, dst_ip, dscp, ttl, gre_type, queue, policer, src_port, direction) +def mirror_common_init(session_info, policer, queue, src_port, direction): if policer is not None: session_info['policer'] = policer - if gre_type is not None: - session_info['gre_type'] = gre_type - if queue is not None: session_info['queue'] = queue @@ -1152,6 +1178,21 @@ def erspan(session_name, src_ip, dst_ip, dscp, ttl, gre_type, queue, policer, sr if direction is not None: session_info['direction'] = direction.upper() + return session_info + +def add_erspan(session_name, src_ip, dst_ip, dscp, ttl, gre_type, queue, policer, src_port=None, direction=None): + session_info = { + "type" : "ERSPAN", + "src_ip": src_ip, + "dst_ip": dst_ip, + "dscp": dscp, + "ttl": ttl + } + + if gre_type is not None: + session_info['gre_type'] = gre_type + + session_info = mirror_common_init(session_info, policer, queue, src_port, direction) """ For multi-npu platforms we need to program all front asic namespaces @@ -1172,46 +1213,36 @@ def erspan(session_name, src_ip, dst_ip, dscp, ttl, gre_type, queue, policer, sr return per_npu_configdb[front_asic_namespaces].set_entry("MIRROR_SESSION", session_name, session_info) -@add.command('span') +@mirror_session.group(cls=AbbreviationGroup, name='span') +@click.pass_context +def span(ctx): + """ SPAN mirror session """ + pass + +@span.command('add') @click.argument('session_name', metavar='', required=True) @click.argument('dst_port', metavar='', required=True) @click.argument('src_port', metavar='[src_port]', required=False) @click.argument('direction', metavar='[direction]', required=False) @click.argument('queue', metavar='[queue]', required=False) @click.option('--policer') -def span(session_name, dst_port, src_port, direction, queue, policer): - """ - Add port mirror session - """ +def add(session_name, dst_port, src_port, direction, queue, policer): + """ Add SPAN mirror session """ + add_span(session_name, dst_port, src_port, direction, queue, policer) + +def add_span(session_name, dst_port, src_port, direction, queue, policer): if get_interface_naming_mode() == "alias": dst_port = interface_alias_to_name(dst_port) if dst_port is None: click.echo("Error: Destination Interface {} is invalid".format(dst_port)) return - if src_port is not None: - src_port_list = [] - for port in src_port.split(","): - src_port_list.append(interface_alias_to_name(port)) - src_port=",".join(src_port_list) session_info = { "type" : "SPAN", "dst_port": dst_port, } - if src_port is not None: - session_info['src_port'] = src_port - if direction is None: - direction = "both" - - if direction is not None: - session_info['direction'] = direction.upper() - - if policer is not None: - session_info['policer'] = policer - - if queue is not None: - session_info['queue'] = queue + session_info = mirror_common_init(session_info, policer, queue, src_port, direction) """ For multi-npu platforms we need to program all front asic namespaces @@ -1232,12 +1263,11 @@ def span(session_name, dst_port, src_port, direction, queue, policer): return per_npu_configdb[front_asic_namespaces].set_entry("MIRROR_SESSION", session_name, session_info) + @mirror_session.command() @click.argument('session_name', metavar='', required=True) def remove(session_name): - """ - Delete mirror session - """ + """ Delete mirror session """ """ For multi-npu platforms we need to program all front asic namespaces @@ -1253,6 +1283,7 @@ def remove(session_name): per_npu_configdb[front_asic_namespaces] = ConfigDBConnector(use_unix_socket_path=True, namespace=front_asic_namespaces) per_npu_configdb[front_asic_namespaces].connect() per_npu_configdb[front_asic_namespaces].set_entry("MIRROR_SESSION", session_name, None) + # # 'pfcwd' group ('config pfcwd ...') # diff --git a/doc/Command-Reference.md b/doc/Command-Reference.md index fc11a7693b..c552616f8e 100644 --- a/doc/Command-Reference.md +++ b/doc/Command-Reference.md @@ -3798,19 +3798,32 @@ While adding a new ERSPAN session, users need to configure the following fields - Usage: ``` - config mirror_session add erspan [gre_type] [queue] [policer ] [source-port-list] [direction] + config mirror_session erspan add [gre_type] [queue] [policer ] [source-port-list] [direction] + ``` + + The following command is also supported to be backward compatible. + This command will be deprecated in future releases. + ``` + config mirror_session add [gre_type] [queue] ``` - Example: ``` - root@T1-2:~# config mirror_session add erspan mrr_abcd 1.2.3.4 20.21.22.23 8 100 0x6558 0 + root@T1-2:~# config mirror_session add mrr_legacy 1.2.3.4 20.21.22.23 8 100 0x6558 0 + root@T1-2:~# show mirror_session + Name Status SRC IP DST IP GRE DSCP TTL Queue Policer Monitor Port SRC Port Direction + --------- -------- -------- ----------- ------ ------ ----- ------- --------- -------------- ---------- ----------- + mrr_legacy inactive 1.2.3.4 20.21.22.23 0x6558 8 100 0 + + + root@T1-2:~# config mirror_session erspan add mrr_abcd 1.2.3.4 20.21.22.23 8 100 0x6558 0 root@T1-2:~# show mirror_session Name Status SRC IP DST IP GRE DSCP TTL Queue Policer Monitor Port SRC Port Direction --------- -------- -------- ----------- ------ ------ ----- ------- --------- -------------- ---------- ----------- mrr_abcd inactive 1.2.3.4 20.21.22.23 0x6558 8 100 0 root@T1-2:~# - root@T1-2:~# config mirror_session add erspan mrr_port 1.2.3.4 20.21.22.23 8 100 0x6558 0 Ethernet0 both + root@T1-2:~# config mirror_session erspan add mrr_port 1.2.3.4 20.21.22.23 8 100 0x6558 0 Ethernet0 root@T1-2:~# show mirror_session Name Status SRC IP DST IP GRE DSCP TTL Queue Policer Monitor Port SRC Port Direction --------- -------- -------- ----------- ------ ------ ----- ------- --------- -------------- ---------- ----------- @@ -3827,17 +3840,18 @@ While adding a new SPAN session, users need to configure the following fields th - Usage: ``` - config mirror_session add span [source-port-list] [direction] [queue] [policer ] + config mirror_session span add [source-port-list] [direction] [queue] [policer ] ``` - Example: ``` - root@T1-2:~# config mirror_session add span port0 Ethernet0 Ethernet4,PortChannel001,Ethernet8 both + root@T1-2:~# config mirror_session span add port0 Ethernet0 Ethernet4,PortChannel001,Ethernet8 root@T1-2:~# show mirror_session Name Status DST Port SRC Port Direction ------ -------- ---------- --------------------------------- ----------- port0 active Ethernet0 Ethernet4,PortChannel10,Ethernet8 both root@T1-2:~# + ``` Go Back To [Beginning of the document](#) or [Beginning of this section](#mirroring) From 9588c9408dd3cc3bfc5a3d677a983ea6db453eba Mon Sep 17 00:00:00 2001 From: Rupesh Kumar Date: Tue, 30 Jun 2020 10:56:16 -0700 Subject: [PATCH 5/8] Addressed review comments --- acl_loader/main.py | 10 +++++----- config/main.py | 38 +++++++++++++++++--------------------- 2 files changed, 22 insertions(+), 26 deletions(-) diff --git a/acl_loader/main.py b/acl_loader/main.py index 16a69b7311..40885a9b21 100644 --- a/acl_loader/main.py +++ b/acl_loader/main.py @@ -730,13 +730,13 @@ def show_session(self, session_name): if val.get("type") == "SPAN": span_data.append([key, val.get("status", ""), val.get("dst_port", ""), - val.get("src_port", ""), val.get("direction", "").lower(), - val.get("queue", ""), val.get("policer", "")]) + val.get("src_port", ""), val.get("direction", "").lower(), + val.get("queue", ""), val.get("policer", "")]) else: erspan_data.append([key, val.get("status", ""), val.get("src_ip", ""), - val.get("dst_ip", ""), val.get("gre_type", ""), val.get("dscp", ""), - val.get("ttl", ""), val.get("queue", ""), val.get("policer", ""), - val.get("monitor_port", ""), val.get("src_port", ""), val.get("direction", "").lower()]) + val.get("dst_ip", ""), val.get("gre_type", ""), val.get("dscp", ""), + val.get("ttl", ""), val.get("queue", ""), val.get("policer", ""), + val.get("monitor_port", ""), val.get("src_port", ""), val.get("direction", "").lower()]) print("ERSPAN Sessions") print(tabulate.tabulate(erspan_data, headers=erspan_header, tablefmt="simple", missingval="")) diff --git a/config/main.py b/config/main.py index 931f364517..0ff70dff16 100755 --- a/config/main.py +++ b/config/main.py @@ -572,24 +572,24 @@ def is_ipaddress(val): def interface_is_in_vlan(vlan_member_table, interface_name): """ Check if an interface is in a vlan """ - for _,v in vlan_member_table.keys(): - if v == interface_name: + for _,intf in vlan_member_table.keys(): + if intf == interface_name: return True return False def interface_is_in_portchannel(portchannel_member_table, interface_name): """ Check if an interface is part of portchannel """ - for _,v in portchannel_member_table.keys(): - if v == interface_name: + for _,intf in portchannel_member_table.keys(): + if intf == interface_name: return True return False def interface_is_router_port(interface_table, interface_name): """ Check if an interface has router config """ - for entry in interface_table.keys(): - if (interface_name == entry[0]): + for intf in interface_table.keys(): + if (interface_name == intf[0]): return True return False @@ -613,9 +613,6 @@ def interface_has_mirror_config(mirror_table, interface_name): return False -# -# Check if SPAN mirror-session config is valid. -# def validate_mirror_session_config(config_db, session_name, dst_port, src_port, direction): """ Check if SPAN mirror-session config is valid """ if len(config_db.get_entry('MIRROR_SESSION', session_name)) != 0: @@ -627,8 +624,8 @@ def validate_mirror_session_config(config_db, session_name, dst_port, src_port, portchannel_member_table = config_db.get_table('PORTCHANNEL_MEMBER') interface_table = config_db.get_table('INTERFACE') - if dst_port is not None: - if interface_name_is_valid(dst_port) is False: + if dst_port: + if not interface_name_is_valid(dst_port): click.echo("Error: Destination Interface {} is invalid".format(dst_port)) return False @@ -648,19 +645,19 @@ def validate_mirror_session_config(config_db, session_name, dst_port, src_port, click.echo("Error: Destination Interface {} is a L3 interface".format(dst_port)) return False - if src_port is not None: + if src_port: for port in src_port.split(","): - if interface_name_is_valid(port) is False: + if not interface_name_is_valid(port): click.echo("Error: Source Interface {} is invalid".format(port)) return False - if dst_port is not None and dst_port == port: + if dst_port and dst_port == port: click.echo("Error: Destination Interface cant be same as Source Interface") return False if interface_has_mirror_config(mirror_table, port): click.echo("Error: Source Interface {} already has mirror config".format(port)) return False - if direction is not None: + if direction: if not any ( [direction == 'rx', direction == 'tx', direction == 'both'] ): click.echo("Error: Direction {} is invalid".format(direction)) return False @@ -1093,7 +1090,7 @@ def add_portchannel_member(ctx, portchannel_name, port_name): """Add member to port channel""" db = ctx.obj['db'] if interface_is_mirror_dst_port(db, port_name): - ctx.fail("{} is configured as mirror destination port".format(interface_name)) + ctx.fail("{} is configured as mirror destination port".format(port_name)) db.set_entry('PORTCHANNEL_MEMBER', (portchannel_name, port_name), {'NULL': 'NULL'}) @@ -1158,7 +1155,7 @@ def add(session_name, src_ip, dst_ip, dscp, ttl, gre_type, queue, policer, src_p """ Add ERSPAN mirror session """ add_erspan(session_name, src_ip, dst_ip, dscp, ttl, gre_type, queue, policer, src_port, direction) -def mirror_common_init(session_info, policer, queue, src_port, direction): +def gather_session_info(session_info, policer, queue, src_port, direction): if policer is not None: session_info['policer'] = policer @@ -1175,9 +1172,8 @@ def mirror_common_init(session_info, policer, queue, src_port, direction): session_info['src_port'] = src_port if direction is None: direction = "both" - - if direction is not None: session_info['direction'] = direction.upper() + return session_info def add_erspan(session_name, src_ip, dst_ip, dscp, ttl, gre_type, queue, policer, src_port=None, direction=None): @@ -1192,7 +1188,7 @@ def add_erspan(session_name, src_ip, dst_ip, dscp, ttl, gre_type, queue, policer if gre_type is not None: session_info['gre_type'] = gre_type - session_info = mirror_common_init(session_info, policer, queue, src_port, direction) + session_info = gather_session_info(session_info, policer, queue, src_port, direction) """ For multi-npu platforms we need to program all front asic namespaces @@ -1242,7 +1238,7 @@ def add_span(session_name, dst_port, src_port, direction, queue, policer): "dst_port": dst_port, } - session_info = mirror_common_init(session_info, policer, queue, src_port, direction) + session_info = gather_session_info(session_info, policer, queue, src_port, direction) """ For multi-npu platforms we need to program all front asic namespaces From d492005a069e73d2849016e7c4f9b59735d83222 Mon Sep 17 00:00:00 2001 From: Rupesh Kumar Date: Tue, 30 Jun 2020 11:06:54 -0700 Subject: [PATCH 6/8] Misc fix --- config/main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/main.py b/config/main.py index 0ff70dff16..679c0be625 100755 --- a/config/main.py +++ b/config/main.py @@ -658,7 +658,7 @@ def validate_mirror_session_config(config_db, session_name, dst_port, src_port, return False if direction: - if not any ( [direction == 'rx', direction == 'tx', direction == 'both'] ): + if direction not in ['rx', 'tx', 'both']: click.echo("Error: Direction {} is invalid".format(direction)) return False From 869642315a0d00d21d77ed9274ef04797dcecc2d Mon Sep 17 00:00:00 2001 From: Rupesh Kumar Date: Tue, 30 Jun 2020 12:04:57 -0700 Subject: [PATCH 7/8] Fixed validation --- config/main.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/config/main.py b/config/main.py index 679c0be625..baec774155 100755 --- a/config/main.py +++ b/config/main.py @@ -1156,13 +1156,13 @@ def add(session_name, src_ip, dst_ip, dscp, ttl, gre_type, queue, policer, src_p add_erspan(session_name, src_ip, dst_ip, dscp, ttl, gre_type, queue, policer, src_port, direction) def gather_session_info(session_info, policer, queue, src_port, direction): - if policer is not None: + if policer: session_info['policer'] = policer - if queue is not None: + if queue: session_info['queue'] = queue - if src_port is not None: + if src_port: if get_interface_naming_mode() == "alias": src_port_list = [] for port in src_port.split(","): @@ -1170,7 +1170,7 @@ def gather_session_info(session_info, policer, queue, src_port, direction): src_port=",".join(src_port_list) session_info['src_port'] = src_port - if direction is None: + if not direction: direction = "both" session_info['direction'] = direction.upper() @@ -1185,7 +1185,7 @@ def add_erspan(session_name, src_ip, dst_ip, dscp, ttl, gre_type, queue, policer "ttl": ttl } - if gre_type is not None: + if not gre_type: session_info['gre_type'] = gre_type session_info = gather_session_info(session_info, policer, queue, src_port, direction) From 8b332bb13086bd07093184ae03d070ccae7644ef Mon Sep 17 00:00:00 2001 From: Rupesh Kumar Date: Tue, 30 Jun 2020 12:12:05 -0700 Subject: [PATCH 8/8] Fixed validation --- config/main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/main.py b/config/main.py index baec774155..756c4b4a22 100755 --- a/config/main.py +++ b/config/main.py @@ -1185,7 +1185,7 @@ def add_erspan(session_name, src_ip, dst_ip, dscp, ttl, gre_type, queue, policer "ttl": ttl } - if not gre_type: + if gre_type: session_info['gre_type'] = gre_type session_info = gather_session_info(session_info, policer, queue, src_port, direction)