Skip to content

Commit

Permalink
apply: don't assume the NM loopback connection is called "lo"
Browse files Browse the repository at this point in the history
It might have any name, so we need to get the connection name
dynamically.
  • Loading branch information
daniloegea committed Dec 12, 2023
1 parent 369c027 commit eb74543
Show file tree
Hide file tree
Showing 3 changed files with 37 additions and 7 deletions.
18 changes: 11 additions & 7 deletions netplan_cli/cli/commands/apply.py
Original file line number Diff line number Diff line change
Expand Up @@ -176,9 +176,12 @@ def command_apply(self, run_generate=True, sync=False, exit_on_error=True, state
else:
logging.debug('no netplan generated networkd configuration exists')

loopback_connection = ''
if restart_nm:
logging.debug('netplan generated NM configuration changed, restarting NM')
if utils.nm_running():
if 'lo' in nm_ifaces:
loopback_connection = utils.nm_get_connection_for_interface('lo')
# restarting NM does not cause new config to be applied, need to shut down devices first
for device in devices:
if device not in nm_ifaces:
Expand Down Expand Up @@ -293,8 +296,10 @@ def command_apply(self, run_generate=True, sync=False, exit_on_error=True, state
# re-set via an udev rule setting "NM_UNMANAGED=1"
shutil.rmtree('/run/NetworkManager/devices', ignore_errors=True)
utils.systemctl_network_manager('start', sync=sync)
# If 'lo' is in the nm_interfaces set we flushed it's IPs (see above) and will need to bring it
# back manually. For that, we need NM up and ready to accept commands

# If 'lo' is in the nm_interfaces set we flushed it's IPs (see above) and disconnected it.
# NM will not bring it back automatically after restarting and we need to do that manually.
# For that, we need NM up and ready to accept commands
if 'lo' in nm_interfaces:
sync = True

Expand All @@ -315,13 +320,12 @@ def command_apply(self, run_generate=True, sync=False, exit_on_error=True, state
break
time.sleep(0.5)

# If "lo" is managed by NM through Netplan, apply will flush its addresses and NM
# will not bring it back automatically like other connections.
# If "lo" is managed by NM through Netplan, apply will flush its addresses and disconnect it.
# NM will not bring it back automatically.
# This is a possible scenario with netplan-everywhere. If a user tries to change the 'lo'
# connection with nmcli for example, NM will create a persistent nmconnection file and emit a YAML for it.
if 'lo' in nm_interfaces:
cmd = ['nmcli', 'con', 'up', 'lo']
subprocess.call(cmd, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
if 'lo' in nm_interfaces and loopback_connection:
utils.nm_bring_interface_up(loopback_connection)

@staticmethod
def is_composite_member(composites, phy):
Expand Down
14 changes: 14 additions & 0 deletions netplan_cli/cli/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,20 @@ def nm_interfaces(paths, devices):
return interfaces


def nm_get_connection_for_interface(interface: str) -> str:
output = nmcli_out(['-m', 'tabular', '-f', 'GENERAL.CONNECTION', 'device', 'show', interface])
lines = output.strip().split('\n')
connection = lines[1]
return connection if connection != '--' else ''


def nm_bring_interface_up(connection: str) -> None: # pragma: nocover (must be covered by NM autopkgtests)
try:
nmcli(['connection', 'up', connection])
except subprocess.CalledProcessError:
pass


def systemctl_network_manager(action, sync=False):
# If the network-manager snap is installed use its service
# name rather than the one of the deb packaged NetworkManager
Expand Down
12 changes: 12 additions & 0 deletions tests/test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -371,3 +371,15 @@ def test_ip_addr_flush(self):
self.assertEqual(self.mock_cmd.calls(), [
['ip', 'addr', 'flush', 'eth42']
])

@patch('netplan_cli.cli.utils.nmcli_out')
def test_nm_get_connection_for_interface(self, nmcli):
nmcli.return_value = 'CONNECTION \nlo \n'
out = utils.nm_get_connection_for_interface('lo')
self.assertEqual(out, 'lo')

@patch('netplan_cli.cli.utils.nmcli_out')
def test_nm_get_connection_for_interface_no_connection(self, nmcli):
nmcli.return_value = 'CONNECTION \n-- \n'
out = utils.nm_get_connection_for_interface('asd0')
self.assertEqual(out, '')

0 comments on commit eb74543

Please sign in to comment.