Skip to content

Commit

Permalink
Switchport Mode & CLI Modified Fix (sonic-net#3247)
Browse files Browse the repository at this point in the history
This PR is Fixture for sonic-net#3108 The PR was reverted due to backward compatibility issues. As per new suggestions, removed db migrator changes from this new change along with vlan.py & switchport.py changes

To Fix issues as per suggestions removed default mode from YANG model and removed minigraph changes:

       1. Removed Db migrator changes from code.
       2. Modified Vlan.py & Switchport.py changes

New commands have been added in Command-Reference.md  All the syntax and examples have been added there and they can be verified by running the specific command
  • Loading branch information
sabakram authored and arfeigin committed Jun 16, 2024
1 parent bbce555 commit aee2d04
Show file tree
Hide file tree
Showing 13 changed files with 2,021 additions and 403 deletions.
39 changes: 32 additions & 7 deletions config/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,10 @@
from .config_mgmt import ConfigMgmtDPB, ConfigMgmt
from . import mclag
from . import syslog
from . import switchport
from . import dns


# mock masic APIs for unit test
try:
if os.environ["UTILITIES_UNIT_TESTING"] == "1" or os.environ["UTILITIES_UNIT_TESTING"] == "2":
Expand Down Expand Up @@ -104,6 +106,7 @@
PORT_SPEED = "speed"
PORT_TPID = "tpid"
DEFAULT_TPID = "0x8100"
PORT_MODE = "switchport_mode"

DOM_CONFIG_SUPPORTED_SUBPORTS = ['0', '1']

Expand Down Expand Up @@ -1230,6 +1233,9 @@ def config(ctx):
# DNS module
config.add_command(dns.dns)

# Switchport module
config.add_command(switchport.switchport)

@config.command()
@click.option('-y', '--yes', is_flag=True, callback=_abort_if_false,
expose_value=False, prompt='Existing files will be overwritten, continue?')
Expand Down Expand Up @@ -4638,19 +4644,38 @@ def add(ctx, interface_name, ip_addr, gw):
if interface_name is None:
ctx.fail("'interface_name' is None!")

# Add a validation to check this interface is not a member in vlan before
# changing it to a router port
vlan_member_table = config_db.get_table('VLAN_MEMBER')
if (interface_is_in_vlan(vlan_member_table, interface_name)):
click.echo("Interface {} is a member of vlan\nAborting!".format(interface_name))
return

portchannel_member_table = config_db.get_table('PORTCHANNEL_MEMBER')

if interface_is_in_portchannel(portchannel_member_table, interface_name):
ctx.fail("{} is configured as a member of portchannel."
.format(interface_name))

# Add a validation to check this interface is in routed mode before
# assigning an IP address to it

sub_intf = False

if clicommon.is_valid_port(config_db, interface_name):
is_port = True
elif clicommon.is_valid_portchannel(config_db, interface_name):
is_port = False
else:
sub_intf = True

if not sub_intf:
interface_mode = None
if is_port:
interface_data = config_db.get_entry('PORT', interface_name)
else:
interface_data = config_db.get_entry('PORTCHANNEL', interface_name)

if "mode" in interface_data:
interface_mode = interface_data["mode"]

if interface_mode == "trunk" or interface_mode == "access":
click.echo("Interface {} is in {} mode and needs to be in routed mode!".format(
interface_name, interface_mode))
return
try:
ip_address = ipaddress.ip_interface(ip_addr)
except ValueError as err:
Expand Down
135 changes: 135 additions & 0 deletions config/switchport.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
import click
from .utils import log
import utilities_common.cli as clicommon

#
# 'switchport' mode ('config switchport ...')
#


@click.group(cls=clicommon.AbbreviationGroup, name='switchport', invoke_without_command=False)
def switchport():
"""Switchport mode configuration tasks"""
pass


@switchport.command("mode")
@click.argument("type", metavar="<mode_type>", required=True, type=click.Choice(["access", "trunk", "routed"]))
@click.argument("port", metavar="port", required=True)
@clicommon.pass_db
def switchport_mode(db, type, port):
"""switchport mode help commands.Mode_type can be access or trunk or routed"""

ctx = click.get_current_context()

log.log_info("'switchport mode {} {}' executing...".format(type, port))
mode_exists_status = True

# checking if port name with alias exists
if clicommon.get_interface_naming_mode() == "alias":
alias = port
iface_alias_converter = clicommon.InterfaceAliasConverter(db)
port = iface_alias_converter.alias_to_name(alias)

if clicommon.is_port_mirror_dst_port(db.cfgdb, port):
ctx.fail("{} is configured as mirror destination port".format(port))

if clicommon.is_valid_port(db.cfgdb, port):
is_port = True
elif clicommon.is_valid_portchannel(db.cfgdb, port):
is_port = False
else:
ctx.fail("{} does not exist".format(port))

portchannel_member_table = db.cfgdb.get_table('PORTCHANNEL_MEMBER')

if (is_port and clicommon.interface_is_in_portchannel(portchannel_member_table, port)):
ctx.fail("{} is part of portchannel!".format(port))

if is_port:
port_data = db.cfgdb.get_entry('PORT', port)
else:
port_data = db.cfgdb.get_entry('PORTCHANNEL', port)

# mode type is either access or trunk
if type != "routed":

if "mode" in port_data:
existing_mode = port_data["mode"]
else:
mode_exists_status = False

if (is_port and clicommon.is_port_router_interface(db.cfgdb, port)) or \
(not is_port and clicommon.is_pc_router_interface(db.cfgdb, port)):
ctx.fail("Remove IP from {} to change mode!".format(port))

if not mode_exists_status:
port_data["mode"] = type
if is_port:
db.cfgdb.set_entry("PORT", port, port_data)
# if not port then is a port channel
elif not is_port:
db.cfgdb.set_entry("PORTCHANNEL", port, port_data)

if mode_exists_status:
if existing_mode == "routed":
# if the port in an interface
if is_port:
db.cfgdb.mod_entry("PORT", port, {"mode": "{}".format(type)})
# if not port then is a port channel
elif not is_port:
db.cfgdb.mod_entry("PORTCHANNEL", port, {"mode": "{}".format(type)})

if existing_mode == type:
ctx.fail("{} is already in {} mode".format(port, type))
else:
if existing_mode == "access" and type == "trunk":
pass
if existing_mode == "trunk" and type == "access":
if clicommon.interface_is_tagged_member(db.cfgdb, port):
ctx.fail(
"{} is in {} mode and have tagged member(s).\nRemove "
"tagged member(s) from {} to switch to {} mode".format(port, existing_mode, port, type))
if is_port:
db.cfgdb.mod_entry("PORT", port, {"mode": "{}".format(type)})
# if not port then is a port channel
elif not is_port:
db.cfgdb.mod_entry("PORTCHANNEL", port, {"mode": "{}".format(type)})

click.echo("{} switched to {} mode".format(port, type))

# if mode type is routed
else:

if clicommon.interface_is_tagged_member(db.cfgdb, port):
ctx.fail("{} has tagged member(s). \nRemove them to change mode to {}".format(port, type))

if clicommon.interface_is_untagged_member(db.cfgdb, port):
ctx.fail("{} has untagged member. \nRemove it to change mode to {}".format(port, type))

if "mode" in port_data:
existing_mode = port_data["mode"]
else:
mode_exists_status = False

if not mode_exists_status:
port_data["mode"] = type
if is_port:
db.cfgdb.set_entry("PORT", port, port_data)

# if not port then is a port channel
elif not is_port:
db.cfgdb.set_entry("PORTCHANNEL", port, port_data)
pass

if mode_exists_status:
if existing_mode == type:
ctx.fail("{} is already in {} mode".format(port, type))
else:
if is_port:
db.cfgdb.mod_entry("PORT", port, {"mode": "{}".format(type)})
# if not port then is a port channel
elif not is_port:
db.cfgdb.mod_entry("PORTCHANNEL", port, {"mode": "{}".format(type)})

click.echo("{} switched to {} mode".format(port, type))
Loading

0 comments on commit aee2d04

Please sign in to comment.