Skip to content

Commit 73f90d2

Browse files
committed
mclagsyncd enhancements as per HLD at sonic-net/SONiC#596
1 parent 07a4e26 commit 73f90d2

File tree

1 file changed

+278
-0
lines changed

1 file changed

+278
-0
lines changed

config/main.py

+278
Original file line numberDiff line numberDiff line change
@@ -336,6 +336,43 @@ def interface_name_is_valid(config_db, interface_name):
336336
return True
337337
return False
338338

339+
def mclag_domain_id_valid(domain_id):
340+
"""Check if the domain id is in acceptable range (between 1 and 4095)
341+
"""
342+
343+
if domain_id<1 or domain_id>4095:
344+
return False
345+
346+
return True
347+
348+
def mclag_ka_session_dep_check(ka, session_tmout):
349+
"""Check if the MCLAG Keepalive timer and session timeout values are multiples of each other and keepalive is < session timeout value
350+
"""
351+
if not session_tmout >= ( 3 * ka):
352+
return False, "MCLAG Keepalive:{} Session_timeout:{} values not satisfying session_timeout >= (3 * KA) ".format(session_tmout, ka)
353+
354+
if session_tmout % ka:
355+
return False, "MCLAG keepalive:{} Session_timeout:{} Values not satisfying session_timeout should be a multiple of KA".format(ka, session_tmout)
356+
357+
return True, ""
358+
359+
360+
def mclag_ka_interval_valid(ka):
361+
"""Check if the MCLAG Keepalive timer is in acceptable range (between 1 and 60)
362+
"""
363+
if ka < 1 or ka > 60:
364+
return False, "Keepalive %s not in valid range[1-60]" % ka
365+
return True, ""
366+
367+
def mclag_session_timeout_valid(session_tmout):
368+
"""Check if the MCLAG session timeout in valid range (between 3 and 3600)
369+
"""
370+
if session_tmout < 3 or session_tmout > 3600:
371+
return False, "Session timeout %s not in valid range[3-3600]" % session_tmout
372+
return True, ""
373+
374+
375+
339376
def interface_name_to_alias(config_db, interface_name):
340377
"""Return alias interface name if default name is given as argument
341378
"""
@@ -3668,6 +3705,247 @@ def delete(ctx):
36683705

36693706
sflow_tbl['global'].pop('agent_id')
36703707
config_db.set_entry('SFLOW', 'global', sflow_tbl['global'])
3708+
######
3709+
#
3710+
# 'mclag' group ('config mclag ...')
3711+
#
3712+
@config.group()
3713+
@click.pass_context
3714+
def mclag(ctx):
3715+
config_db = ConfigDBConnector()
3716+
config_db.connect()
3717+
ctx.obj = {'db': config_db}
3718+
pass
3719+
3720+
3721+
#mclag domain add
3722+
@mclag.command('add')
3723+
@click.argument('domain_id', metavar='<domain_id>', required=True, type=int)
3724+
@click.argument('source_ip_addr', metavar='<source_ip_addr>', required=True)
3725+
@click.argument('peer_ip_addr', metavar='<peer_ip_addr>', required=True)
3726+
@click.argument('peer_ifname', metavar='<peer_ifname>', required=False)
3727+
@click.pass_context
3728+
def add_mclag_domain(ctx, domain_id, source_ip_addr, peer_ip_addr, peer_ifname):
3729+
"""Add MCLAG Domain"""
3730+
3731+
if not mclag_domain_id_valid(domain_id):
3732+
ctx.fail("{} invalid domain ID, valid range is 1 to 4095".format(domain_id))
3733+
if not is_ip4_addr_valid(source_ip_addr, True):
3734+
ctx.fail("{} invalid local ip address".format(source_ip_addr))
3735+
if not is_ip4_addr_valid(peer_ip_addr, True):
3736+
ctx.fail("{} invalid peer ip address".format(peer_ip_addr))
3737+
3738+
db = ctx.obj['db']
3739+
fvs = {}
3740+
fvs['source_ip'] = str(source_ip_addr)
3741+
fvs['peer_ip'] = str(peer_ip_addr)
3742+
if peer_ifname is not None:
3743+
if (peer_ifname.startswith("Ethernet") is False) and (peer_ifname.startswith("PortChannel") is False):
3744+
ctx.fail("peer interface is invalid, should be Ethernet interface or portChannel !!")
3745+
if (peer_ifname.startswith("Ethernet") is True) and (interface_name_is_valid(peer_ifname) is False):
3746+
ctx.fail("peer Ethernet interface name is invalid. it is not present in port table of configDb!!")
3747+
if (peer_ifname.startswith("PortChannel")) and (is_portchannel_name_valid(peer_ifname) is False):
3748+
ctx.fail("peer PortChannel interface name is invalid !!")
3749+
fvs['peer_link'] = str(peer_ifname)
3750+
mclag_domain_keys = db.get_table('MCLAG_DOMAIN').keys()
3751+
if len(mclag_domain_keys) == 0:
3752+
db.set_entry('MCLAG_DOMAIN', domain_id, fvs)
3753+
else:
3754+
if domain_id in mclag_domain_keys:
3755+
db.mod_entry('MCLAG_DOMAIN', domain_id, fvs)
3756+
else:
3757+
ctx.fail("only one mclag Domain can be configured. Already one domain {} configured ".format(mclag_domain_keys[0]))
3758+
3759+
3760+
#mclag domain delete
3761+
#MCLAG Domain del involves deletion of associated MCLAG Ifaces also
3762+
@mclag.command('del')
3763+
@click.argument('domain_id', metavar='<domain_id>', required=True, type=int)
3764+
@click.pass_context
3765+
def del_mclag_domain(ctx, domain_id):
3766+
"""Delete MCLAG Domain"""
3767+
3768+
if not mclag_domain_id_valid(domain_id):
3769+
ctx.fail("{} invalid domain ID, valid range is 1 to 4095".format(domain_id))
3770+
3771+
db = ctx.obj['db']
3772+
entry = db.get_entry('MCLAG_DOMAIN', domain_id)
3773+
if entry is None:
3774+
ctx.fail("MCLAG Domain {} not configured ".format(domain_id))
3775+
return
3776+
3777+
click.echo("MCLAG Domain delete takes care of deleting all associated MCLAG Interfaces")
3778+
3779+
#get all MCLAG Interface associated with this domain and delete
3780+
interface_table_keys = db.get_table('MCLAG_INTERFACE').keys()
3781+
3782+
#delete associated mclag interfaces
3783+
for iface_domain_id, iface_name in interface_table_keys:
3784+
if (int(iface_domain_id) == domain_id):
3785+
db.set_entry('MCLAG_INTERFACE', (iface_domain_id, iface_name), None )
3786+
3787+
#delete mclag domain
3788+
db.set_entry('MCLAG_DOMAIN', domain_id, None)
3789+
3790+
3791+
#keepalive timeout config
3792+
@mclag.command('keepalive-interval')
3793+
@click.argument('domain_id', metavar='<domain_id>', required=True)
3794+
@click.argument('time_in_secs', metavar='<time_in_secs>', required=True, type=int)
3795+
@click.pass_context
3796+
def config_mclag_keepalive_timer(ctx, domain_id, time_in_secs):
3797+
"""Configure MCLAG Keepalive timer value in secs"""
3798+
db = ctx.obj['db']
3799+
3800+
entry = db.get_entry('MCLAG_DOMAIN', domain_id)
3801+
if len(entry) == 0:
3802+
ctx.fail("MCLAG Domain " + domain_id + " not configured, configure mclag domain first")
3803+
3804+
status, error_info = mclag_ka_interval_valid(time_in_secs)
3805+
if status is not True:
3806+
ctx.fail(error_info)
3807+
3808+
session_timeout_value = entry.get('session_timeout')
3809+
3810+
if session_timeout_value is None:
3811+
# assign default value
3812+
int_sess_tmout = 15
3813+
else:
3814+
int_sess_tmout = int(session_timeout_value)
3815+
3816+
status, error_info = mclag_ka_session_dep_check(time_in_secs, int_sess_tmout)
3817+
if status is not True:
3818+
ctx.fail(error_info)
3819+
3820+
fvs = {}
3821+
fvs['keepalive_interval'] = str(time_in_secs)
3822+
db.mod_entry('MCLAG_DOMAIN', domain_id, fvs)
3823+
3824+
3825+
#session timeout config
3826+
@mclag.command('session-timeout')
3827+
@click.argument('domain_id', metavar='<domain_id>', required=True)
3828+
@click.argument('time_in_secs', metavar='<time_in_secs>', required=True, type=int)
3829+
@click.pass_context
3830+
def config_mclag_session_timeout(ctx, domain_id, time_in_secs):
3831+
"""Configure MCLAG Session timeout value in secs"""
3832+
db = ctx.obj['db']
3833+
entry = db.get_entry('MCLAG_DOMAIN', domain_id)
3834+
if len(entry) == 0:
3835+
ctx.fail("MCLAG Domain " + domain_id + " not configured, configure mclag domain first")
3836+
3837+
status, error_info = mclag_session_timeout_valid(time_in_secs)
3838+
if status is not True:
3839+
ctx.fail(error_info)
3840+
3841+
ka = entry.get('keepalive_interval')
3842+
if ka is None:
3843+
# assign default value
3844+
int_ka = 1
3845+
else:
3846+
int_ka = int(ka)
3847+
3848+
status, error_info = mclag_ka_session_dep_check(int_ka, time_in_secs)
3849+
if status is not True:
3850+
ctx.fail(error_info)
3851+
3852+
fvs = {}
3853+
fvs['session_timeout'] = str(time_in_secs)
3854+
db.mod_entry('MCLAG_DOMAIN', domain_id, fvs)
3855+
3856+
3857+
#mclag interface config
3858+
@mclag.group('member')
3859+
@click.pass_context
3860+
def mclag_member(ctx):
3861+
pass
3862+
3863+
@mclag_member.command('add')
3864+
@click.argument('domain_id', metavar='<domain_id>', required=True)
3865+
@click.argument('portchannel_names', metavar='<portchannel_names>', required=True)
3866+
@click.pass_context
3867+
def add_mclag_member(ctx, domain_id, portchannel_names):
3868+
"""Add member MCLAG interfaces from MCLAG Domain"""
3869+
db = ctx.obj['db']
3870+
portchannel_list = portchannel_names.split(",")
3871+
for portchannel_name in portchannel_list:
3872+
if is_portchannel_name_valid(portchannel_name) != True:
3873+
ctx.fail("{} is invalid!, name should have prefix '{}' and suffix '{}'" .format(portchannel_name, CFG_PORTCHANNEL_PREFIX, CFG_PORTCHANNEL_NO))
3874+
db.set_entry('MCLAG_INTERFACE', (domain_id, portchannel_name), {'if_type':"PortChannel"} )
3875+
3876+
@mclag_member.command('del')
3877+
@click.argument('domain_id', metavar='<domain_id>', required=True)
3878+
@click.argument('portchannel_names', metavar='<portchannel_names>', required=True)
3879+
@click.pass_context
3880+
def del_mclag_member(ctx, domain_id, portchannel_names):
3881+
"""Delete member MCLAG interfaces from MCLAG Domain"""
3882+
db = ctx.obj['db']
3883+
#split comma seperated portchannel names
3884+
portchannel_list = portchannel_names.split(",")
3885+
for portchannel_name in portchannel_list:
3886+
if is_portchannel_name_valid(portchannel_name) != True:
3887+
ctx.fail("{} is invalid!, name should have prefix '{}' and suffix '{}'" .format(portchannel_name, CFG_PORTCHANNEL_PREFIX, CFG_PORTCHANNEL_NO))
3888+
db.set_entry('MCLAG_INTERFACE', (domain_id, portchannel_name), None )
3889+
3890+
#mclag unique ip config
3891+
@mclag.group('unique-ip')
3892+
@click.pass_context
3893+
def mclag_unique_ip(ctx):
3894+
"""Add unique ip on MCLAG vlan interface"""
3895+
pass
3896+
3897+
@mclag_unique_ip.command('add')
3898+
@click.argument('interface_names', metavar='<interface_names>', required=True)
3899+
@click.pass_context
3900+
def add_mclag_unique_ip(ctx, interface_names):
3901+
"""Add unique ip on MCLAG vlan interface"""
3902+
db = ctx.obj['db']
3903+
mclag_domain_keys = db.get_table('MCLAG_DOMAIN').keys()
3904+
if len(mclag_domain_keys) == 0:
3905+
ctx.fail("MCLAG not configured. MCLAG should be configured.")
3906+
3907+
#split comma seperated interface names
3908+
interface_list = interface_names.split(",")
3909+
for interface_name in interface_list:
3910+
if not interface_name.startswith("Vlan"):
3911+
ctx.fail("{} is invalid!, name should have prefix '{}' and suffix '{}'" .format(interface_name, "Vlan", "vlan id"))
3912+
#VRF should be configured after unique IP configuration
3913+
intf_vrf = get_intf_vrf_bind_unique_ip(db, interface_name, "VLAN_INTERFACE")
3914+
if intf_vrf:
3915+
ctx.fail("%s is configured with Non default VRF, remove the VRF configuration and reconfigure after enabling unique IP configuration."%(str(interface_name)))
3916+
3917+
#IP should be configured after unique IP configuration
3918+
for k,v in db.get_table('VLAN_INTERFACE').iteritems():
3919+
if type(k) == tuple:
3920+
(intf_name, ip) = k
3921+
if intf_name == interface_name and ip != 0:
3922+
ctx.fail("%s is configured with IP %s, remove the IP configuration and reconfigure after enabling unique IP configuration."%(str(intf_name), str(ip)))
3923+
db.set_entry('MCLAG_UNIQUE_IP', (interface_name), {'unique_ip':"enable"} )
3924+
3925+
@mclag_unique_ip.command('del')
3926+
@click.argument('interface_names', metavar='<interface_names>', required=True)
3927+
@click.pass_context
3928+
def del_mclag_unique_ip(ctx, interface_names):
3929+
"""Delete unique ip from MCLAG vlan interface"""
3930+
db = ctx.obj['db']
3931+
#split comma seperated interface names
3932+
interface_list = interface_names.split(",")
3933+
for interface_name in interface_list:
3934+
if not interface_name.startswith("Vlan"):
3935+
ctx.fail("{} is invalid!, name should have prefix '{}' and suffix '{}'" .format(interface_name, "Vlan", "vlan id"))
3936+
#VRF should be configured after removing unique IP configuration
3937+
intf_vrf = get_intf_vrf_bind_unique_ip(db, interface_name, "VLAN_INTERFACE")
3938+
if intf_vrf:
3939+
ctx.fail("%s is configured with Non default VRF, remove the VRF configuration and reconfigure after disabling unique IP configuration."%(str(interface_name)))
3940+
#IP should be configured after removing unique IP configuration
3941+
for k,v in db.get_table('VLAN_INTERFACE').iteritems():
3942+
if type(k) == tuple:
3943+
(intf_name, ip) = k
3944+
if intf_name == interface_name and ip != 0:
3945+
ctx.fail("%s is configured with IP %s, remove the IP configuration and reconfigure after disabling unique IP configuration."%(str(intf_name), str(ip)))
3946+
db.set_entry('MCLAG_UNIQUE_IP', (interface_name), None )
3947+
3948+
#######
36713949

36723950
if __name__ == '__main__':
36733951
config()

0 commit comments

Comments
 (0)