@@ -336,6 +336,43 @@ def interface_name_is_valid(config_db, interface_name):
336
336
return True
337
337
return False
338
338
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
+
339
376
def interface_name_to_alias (config_db , interface_name ):
340
377
"""Return alias interface name if default name is given as argument
341
378
"""
@@ -3668,6 +3705,247 @@ def delete(ctx):
3668
3705
3669
3706
sflow_tbl ['global' ].pop ('agent_id' )
3670
3707
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
+ #######
3671
3949
3672
3950
if __name__ == '__main__' :
3673
3951
config ()
0 commit comments