2929from vyos .configdict import node_changed
3030from vyos .configdict import is_node_changed
3131from vyos .configverify import verify_vrf
32+ from vyos .container import get_container_host_ifname
3233from vyos .container import restart_network
3334from vyos .utils .configfs import delete_cli_node
3435from vyos .utils .configfs import add_cli_node
@@ -122,6 +123,7 @@ def verify(container):
122123 net_dict = {}
123124 net_dict ['mac' ] = {}
124125 net_dict ['address' ] = {}
126+ net_dict ['host_ifname' ] = {}
125127
126128 for name , container_config in container ['name' ].items ():
127129 # Container image is a mandatory option
@@ -158,6 +160,19 @@ def verify(container):
158160 if network_name not in container .get ('network' , {}):
159161 raise ConfigError (f'Container network "{ network_name } " does not exist!' )
160162
163+ # T7736: two distinct (long) container names could truncate to
164+ # the same host_interface_name - not applicable to macvlan networks,
165+ # they attach without a paired host veth
166+ network_type = dict_search (f'{ network_name } .type' , container ['network' ])
167+ if dict_search ('macvlan' , network_type ) is None :
168+ host_ifname = get_container_host_ifname (name )
169+ if host_ifname in net_dict ['host_ifname' ]:
170+ raise ConfigError (
171+ f'Container "{ name } " and "{ net_dict ["host_ifname" ][host_ifname ]} " '
172+ f'both generate the host interface name "{ host_ifname } " - please '
173+ f'use less similar container names!' )
174+ net_dict ['host_ifname' ][host_ifname ] = name
175+
161176 if 'name_server' in container_config and 'no_name_server' not in container ['network' ][network_name ]:
162177 raise ConfigError (f'Setting name server has no effect when attached container network has DNS enabled!' )
163178
@@ -361,7 +376,7 @@ def verify(container):
361376 return None
362377
363378
364- def generate_run_arguments (name , container_config , host_ident ):
379+ def generate_run_arguments (name , container_config , host_ident , network_config ):
365380 image = container_config ['image' ]
366381 cpu_quota = container_config ['cpu_quota' ]
367382 memory = container_config ['memory' ]
@@ -511,9 +526,19 @@ def generate_run_arguments(name, container_config, host_ident):
511526 else :
512527 ip_param = ''
513528 addr_info = ''
514- networks = "," . join ( container_config [ 'network' ])
529+ network_opts = []
515530 for network in container_config ['network' ]:
516531 network_name = network
532+ # T7736: give the host-side veth a name that can never collide
533+ # with a VyOS "virtual-ethernet vethN" interface.
534+ type_config = dict_search (f'{ network } .type' , network_config )
535+ is_macvlan = dict_search ('macvlan' , type_config ) is not None
536+ net_opt = network
537+ if not is_macvlan :
538+ ifname = get_container_host_ifname (name )
539+ net_opt += f':host_interface_name={ ifname } '
540+ network_opts .append (net_opt )
541+
517542 if 'address' not in container_config ['network' ][network ]:
518543 continue
519544 for address in container_config ['network' ][network ]['address' ]:
@@ -524,6 +549,8 @@ def generate_run_arguments(name, container_config, host_ident):
524549
525550 addr_info = '' .join (container_config ['network' ][network ]['address' ])
526551
552+ networks = ' ' .join (f'--network { opt } ' for opt in network_opts )
553+
527554 get_mac = dict_search (f'network.{ network_name } .mac' , container_config )
528555 if get_mac == 'auto' or get_mac is None :
529556 mac_add = gen_mac (name , addr_info , host_ident )
@@ -546,7 +573,7 @@ def generate_run_arguments(name, container_config, host_ident):
546573 delete_cli_node (mac_config_path )
547574 add_cli_node (mac_config_path , value = mac_add )
548575
549- net = f'--net { networks } { ip_param } { mac_address } '
576+ net = f'{ networks } { ip_param } { mac_address } '
550577
551578 return f'{ container_base_cmd } { healthcheck } { net } { entrypoint } { image } { command } { command_arguments } ' .strip ()
552579
@@ -626,12 +653,13 @@ def generate(container):
626653
627654 if 'name' in container :
628655 host_ident = get_host_identity ()
656+ network_config = container .get ('network' , {})
629657 for name , container_config in container ['name' ].items ():
630658 if 'disable' in container_config :
631659 continue
632660
633661 file_path = os .path .join (systemd_unit_path , f'vyos-container-{ name } .service' )
634- run_args = generate_run_arguments (name , container_config , host_ident )
662+ run_args = generate_run_arguments (name , container_config , host_ident , network_config )
635663 render (file_path , 'container/systemd-unit.j2' , {'name' : name , 'run_args' : run_args , },
636664 formatter = lambda _ : _ .replace (""" , '"' ).replace ("'" , "'" ))
637665
0 commit comments