@@ -343,18 +343,22 @@ def interface_name_to_alias(config_db, interface_name):
343343
344344 return None
345345
346- def interface_ipaddr_dependent_on_interface (config_db , interface_name ):
347- """Get table keys including ipaddress
346+ def get_interface_ipaddresses (config_db , interface_name ):
347+ """Get IP addresses attached to interface
348348 """
349- data = []
349+ ipaddresses = set ()
350350 table_name = get_interface_table_name (interface_name )
351- if table_name == "" :
352- return data
351+ if not table_name :
352+ return ipaddresses
353+
353354 keys = config_db .get_keys (table_name )
354355 for key in keys :
355- if interface_name in key and len (key ) == 2 :
356- data .append (key )
357- return data
356+ if isinstance (key , tuple ) and len (key ) == 2 :
357+ iface , interface_ip = key
358+ if iface == interface_name :
359+ ipaddresses .add (ipaddress .ip_interface (interface_ip ))
360+
361+ return ipaddresses
358362
359363def is_interface_bind_to_vrf (config_db , interface_name ):
360364 """Get interface if bind to vrf or not
@@ -448,9 +452,9 @@ def del_interface_bind_to_vrf(config_db, vrf_name):
448452 if interface_dict :
449453 for interface_name in interface_dict :
450454 if 'vrf_name' in interface_dict [interface_name ] and vrf_name == interface_dict [interface_name ]['vrf_name' ]:
451- interface_dependent = interface_ipaddr_dependent_on_interface (config_db , interface_name )
452- for interface_del in interface_dependent :
453- config_db . set_entry ( table_name , interface_del , None )
455+ interface_ipaddresses = get_interface_ipaddresses (config_db , interface_name )
456+ for ipaddress in interface_ipaddresses :
457+ remove_router_interface_ip_address ( config_db , interface_name , ipaddress )
454458 config_db .set_entry (table_name , interface_name , None )
455459
456460def set_interface_naming_mode (mode ):
@@ -854,27 +858,6 @@ def validate_mirror_session_config(config_db, session_name, dst_port, src_port,
854858
855859 return True
856860
857- def is_valid_ip_interface (ctx , ip_addr ):
858- split_ip_mask = ip_addr .split ("/" )
859- if len (split_ip_mask ) < 2 :
860- return False
861-
862- # Check if the IP address is correct or if there are leading zeros.
863- ip_obj = ipaddress .ip_address (split_ip_mask [0 ])
864-
865- if isinstance (ip_obj , ipaddress .IPv4Address ):
866- # Since the IP address is used as a part of a key in Redis DB,
867- # do not tolerate extra zeros in IPv4.
868- if str (ip_obj ) != split_ip_mask [0 ]:
869- return False
870-
871- # Check if the mask is correct
872- net = ipaddress .ip_network (ip_addr , strict = False )
873- if str (net .prefixlen ) != split_ip_mask [1 ] or net .prefixlen == 0 :
874- return False
875-
876- return True
877-
878861def cli_sroute_to_config (ctx , command_str , strict_nh = True ):
879862 if len (command_str ) < 2 or len (command_str ) > 9 :
880863 ctx .fail ("argument is not in pattern prefix [vrf <vrf_name>] <A.B.C.D/M> nexthop <[vrf <vrf_name>] <A.B.C.D>>|<dev <dev_name>>!" )
@@ -985,6 +968,20 @@ def cache_arp_entries():
985968 open (restore_flag_file , 'w' ).close ()
986969 return success
987970
971+ def remove_router_interface_ip_address (config_db , interface_name , ipaddress_to_remove ):
972+ table_name = get_interface_table_name (interface_name )
973+ keys = config_db .get_keys (table_name )
974+
975+ for key in keys :
976+ if not isinstance (key , tuple ) or len (key ) != 2 :
977+ continue
978+
979+ iface , ipaddress_string = key
980+ if iface != interface_name :
981+ continue
982+
983+ if ipaddress .ip_interface (ipaddress_string ) == ipaddress_to_remove :
984+ config_db .set_entry (table_name , (interface_name , ipaddress_string ), None )
988985
989986def validate_ipv4_address (ctx , param , ip_addr ):
990987 """Helper function to validate ipv4 address
@@ -3749,50 +3746,44 @@ def add(ctx, interface_name, ip_addr, gw):
37493746 return
37503747
37513748 try :
3752- net = ipaddress .ip_network (ip_addr , strict = False )
3753- if '/' not in ip_addr :
3754- ip_addr += '/' + str (net .prefixlen )
3755-
3756- if not is_valid_ip_interface (ctx , ip_addr ):
3757- raise ValueError ('' )
3758-
3759- if interface_name == 'eth0' :
3760-
3761- # Configuring more than 1 IPv4 or more than 1 IPv6 address fails.
3762- # Allow only one IPv4 and only one IPv6 address to be configured for IPv6.
3763- # If a row already exist, overwrite it (by doing delete and add).
3764- mgmtintf_key_list = _get_all_mgmtinterface_keys ()
3765-
3766- for key in mgmtintf_key_list :
3767- # For loop runs for max 2 rows, once for IPv4 and once for IPv6.
3768- # No need to capture the exception since the ip_addr is already validated earlier
3769- ip_input = ipaddress .ip_interface (ip_addr )
3770- current_ip = ipaddress .ip_interface (key [1 ])
3771- if (ip_input .version == current_ip .version ):
3772- # If user has configured IPv4/v6 address and the already available row is also IPv4/v6, delete it here.
3773- config_db .set_entry ("MGMT_INTERFACE" , ("eth0" , key [1 ]), None )
3774-
3775- # Set the new row with new value
3776- if not gw :
3777- config_db .set_entry ("MGMT_INTERFACE" , (interface_name , ip_addr ), {"NULL" : "NULL" })
3778- else :
3779- config_db .set_entry ("MGMT_INTERFACE" , (interface_name , ip_addr ), {"gwaddr" : gw })
3780- mgmt_ip_restart_services ()
3749+ ip_address = ipaddress .ip_interface (ip_addr )
3750+ except ValueError as err :
3751+ ctx .fail ("IP address is not valid: {}" .format (err ))
3752+
3753+ if interface_name == 'eth0' :
3754+
3755+ # Configuring more than 1 IPv4 or more than 1 IPv6 address fails.
3756+ # Allow only one IPv4 and only one IPv6 address to be configured for IPv6.
3757+ # If a row already exist, overwrite it (by doing delete and add).
3758+ mgmtintf_key_list = _get_all_mgmtinterface_keys ()
3759+
3760+ for key in mgmtintf_key_list :
3761+ # For loop runs for max 2 rows, once for IPv4 and once for IPv6.
3762+ # No need to capture the exception since the ip_addr is already validated earlier
3763+ current_ip = ipaddress .ip_interface (key [1 ])
3764+ if (ip_address .version == current_ip .version ):
3765+ # If user has configured IPv4/v6 address and the already available row is also IPv4/v6, delete it here.
3766+ config_db .set_entry ("MGMT_INTERFACE" , ("eth0" , key [1 ]), None )
3767+
3768+ # Set the new row with new value
3769+ if not gw :
3770+ config_db .set_entry ("MGMT_INTERFACE" , (interface_name , str (ip_address )), {"NULL" : "NULL" })
3771+ else :
3772+ config_db .set_entry ("MGMT_INTERFACE" , (interface_name , str (ip_address )), {"gwaddr" : gw })
3773+ mgmt_ip_restart_services ()
37813774
3782- return
3775+ return
37833776
3784- table_name = get_interface_table_name (interface_name )
3785- if table_name == "" :
3786- ctx .fail ("'interface_name' is not valid. Valid names [Ethernet/PortChannel/Vlan/Loopback]" )
3787- interface_entry = config_db .get_entry (table_name , interface_name )
3788- if len (interface_entry ) == 0 :
3789- if table_name == "VLAN_SUB_INTERFACE" :
3790- config_db .set_entry (table_name , interface_name , {"admin_status" : "up" })
3791- else :
3792- config_db .set_entry (table_name , interface_name , {"NULL" : "NULL" })
3793- config_db .set_entry (table_name , (interface_name , ip_addr ), {"NULL" : "NULL" })
3794- except ValueError :
3795- ctx .fail ("ip address or mask is not valid." )
3777+ table_name = get_interface_table_name (interface_name )
3778+ if table_name == "" :
3779+ ctx .fail ("'interface_name' is not valid. Valid names [Ethernet/PortChannel/Vlan/Loopback]" )
3780+ interface_entry = config_db .get_entry (table_name , interface_name )
3781+ if len (interface_entry ) == 0 :
3782+ if table_name == "VLAN_SUB_INTERFACE" :
3783+ config_db .set_entry (table_name , interface_name , {"admin_status" : "up" })
3784+ else :
3785+ config_db .set_entry (table_name , interface_name , {"NULL" : "NULL" })
3786+ config_db .set_entry (table_name , (interface_name , str (ip_address )), {"NULL" : "NULL" })
37963787
37973788#
37983789# 'del' subcommand
@@ -3813,52 +3804,47 @@ def remove(ctx, interface_name, ip_addr):
38133804 ctx .fail ("'interface_name' is None!" )
38143805
38153806 try :
3816- net = ipaddress .ip_network (ip_addr , strict = False )
3817- if '/' not in ip_addr :
3818- ip_addr += '/' + str ( net . prefixlen )
3807+ ip_address = ipaddress .ip_interface (ip_addr )
3808+ except ValueError as err :
3809+ ctx . fail ( "IP address is not valid: {}" . format ( err ) )
38193810
3820- if not is_valid_ip_interface (ctx , ip_addr ):
3821- raise ValueError ('' )
3811+ if interface_name == 'eth0' :
3812+ config_db .set_entry ("MGMT_INTERFACE" , (interface_name , str (ip_address )), None )
3813+ mgmt_ip_restart_services ()
3814+ return
38223815
3823- if interface_name == 'eth0' :
3824- config_db .set_entry ("MGMT_INTERFACE" , (interface_name , ip_addr ), None )
3825- mgmt_ip_restart_services ()
3826- return
3816+ table_name = get_interface_table_name (interface_name )
3817+ if table_name == "" :
3818+ ctx .fail ("'interface_name' is not valid. Valid names [Ethernet/PortChannel/Vlan/Loopback]" )
3819+ interface_addresses = get_interface_ipaddresses (config_db , interface_name )
3820+ # If we deleting the last IP entry of the interface, check whether a static route present for the RIF
3821+ # before deleting the entry and also the RIF.
3822+ if interface_addresses == {ip_address }:
3823+ # Check both IPv4 and IPv6 routes.
3824+ ip_versions = [ "ip" , "ipv6" ]
3825+ for ip_ver in ip_versions :
3826+ # Compete the command and ask Zebra to return the routes.
3827+ # Scopes of all VRFs will be checked.
3828+ cmd = "show {} route vrf all static" .format (ip_ver )
3829+ if multi_asic .is_multi_asic ():
3830+ output = bgp_util .run_bgp_command (cmd , ctx .obj ['namespace' ])
3831+ else :
3832+ output = bgp_util .run_bgp_command (cmd )
3833+ # If there is output data, check is there a static route,
3834+ # bound to the interface.
3835+ if output != "" :
3836+ if any (interface_name in output_line for output_line in output .splitlines ()):
3837+ ctx .fail ("Cannot remove the last IP entry of interface {}. A static {} route is still bound to the RIF." .format (interface_name , ip_ver ))
3838+ remove_router_interface_ip_address (config_db , interface_name , ip_address )
3839+ interface_addresses = get_interface_ipaddresses (config_db , interface_name )
3840+ if len (interface_addresses ) == 0 and is_interface_bind_to_vrf (config_db , interface_name ) is False and get_intf_ipv6_link_local_mode (ctx , interface_name , table_name ) != "enable" :
3841+ config_db .set_entry (table_name , interface_name , None )
38273842
3828- table_name = get_interface_table_name (interface_name )
3829- if table_name == "" :
3830- ctx .fail ("'interface_name' is not valid. Valid names [Ethernet/PortChannel/Vlan/Loopback]" )
3831- interface_dependent = interface_ipaddr_dependent_on_interface (config_db , interface_name )
3832- # If we deleting the last IP entry of the interface, check whether a static route present for the RIF
3833- # before deleting the entry and also the RIF.
3834- if len (interface_dependent ) == 1 and interface_dependent [0 ][1 ] == ip_addr :
3835- # Check both IPv4 and IPv6 routes.
3836- ip_versions = [ "ip" , "ipv6" ]
3837- for ip_ver in ip_versions :
3838- # Compete the command and ask Zebra to return the routes.
3839- # Scopes of all VRFs will be checked.
3840- cmd = "show {} route vrf all static" .format (ip_ver )
3841- if multi_asic .is_multi_asic ():
3842- output = bgp_util .run_bgp_command (cmd , ctx .obj ['namespace' ])
3843- else :
3844- output = bgp_util .run_bgp_command (cmd )
3845- # If there is output data, check is there a static route,
3846- # bound to the interface.
3847- if output != "" :
3848- if any (interface_name in output_line for output_line in output .splitlines ()):
3849- ctx .fail ("Cannot remove the last IP entry of interface {}. A static {} route is still bound to the RIF." .format (interface_name , ip_ver ))
3850- config_db .set_entry (table_name , (interface_name , ip_addr ), None )
3851- interface_dependent = interface_ipaddr_dependent_on_interface (config_db , interface_name )
3852- if len (interface_dependent ) == 0 and is_interface_bind_to_vrf (config_db , interface_name ) is False and get_intf_ipv6_link_local_mode (ctx , interface_name , table_name ) != "enable" :
3853- config_db .set_entry (table_name , interface_name , None )
3854-
3855- if multi_asic .is_multi_asic ():
3856- command = "sudo ip netns exec {} ip neigh flush dev {} {}" .format (ctx .obj ['namespace' ], interface_name , ip_addr )
3857- else :
3858- command = "ip neigh flush dev {} {}" .format (interface_name , ip_addr )
3859- clicommon .run_command (command )
3860- except ValueError :
3861- ctx .fail ("ip address or mask is not valid." )
3843+ if multi_asic .is_multi_asic ():
3844+ command = "sudo ip netns exec {} ip neigh flush dev {} {}" .format (ctx .obj ['namespace' ], interface_name , str (ip_address ))
3845+ else :
3846+ command = "ip neigh flush dev {} {}" .format (interface_name , str (ip_address ))
3847+ clicommon .run_command (command )
38623848
38633849
38643850#
@@ -4243,9 +4229,9 @@ def bind(ctx, interface_name, vrf_name):
42434229 config_db .get_entry (table_name , interface_name ).get ('vrf_name' ) == vrf_name :
42444230 return
42454231 # Clean ip addresses if interface configured
4246- interface_dependent = interface_ipaddr_dependent_on_interface (config_db , interface_name )
4247- for interface_del in interface_dependent :
4248- config_db . set_entry ( table_name , interface_del , None )
4232+ interface_addresses = get_interface_ipaddresses (config_db , interface_name )
4233+ for ipaddress in interface_addresses :
4234+ remove_router_interface_ip_address ( config_db , interface_name , ipaddress )
42494235 config_db .set_entry (table_name , interface_name , None )
42504236 # When config_db del entry and then add entry with same key, the DEL will lost.
42514237 if ctx .obj ['namespace' ] is DEFAULT_NAMESPACE :
@@ -4281,9 +4267,9 @@ def unbind(ctx, interface_name):
42814267 ctx .fail ("'interface_name' is not valid. Valid names [Ethernet/PortChannel/Vlan/Loopback]" )
42824268 if is_interface_bind_to_vrf (config_db , interface_name ) is False :
42834269 return
4284- interface_dependent = interface_ipaddr_dependent_on_interface (config_db , interface_name )
4285- for interface_del in interface_dependent :
4286- config_db . set_entry ( table_name , interface_del , None )
4270+ interface_ipaddresses = get_interface_ipaddresses (config_db , interface_name )
4271+ for ipaddress in interface_ipaddresses :
4272+ remove_router_interface_ip_address ( config_db , interface_name , ipaddress )
42874273 config_db .set_entry (table_name , interface_name , None )
42884274
42894275
0 commit comments