Skip to content

Commit

Permalink
cli:apply:utils: use networkctl reload/reconfigure
Browse files Browse the repository at this point in the history
  • Loading branch information
slyon committed Mar 16, 2021
1 parent d63b161 commit 2b4f035
Show file tree
Hide file tree
Showing 3 changed files with 66 additions and 12 deletions.
16 changes: 12 additions & 4 deletions netplan/cli/commands/apply.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@
from netplan.cli.ovs import apply_ovs_cleanup


OVS_CLEANUP_SERVICE = 'netplan-ovs-cleanup.service'


class NetplanApply(utils.NetplanCommand):

Expand Down Expand Up @@ -149,7 +151,7 @@ def command_apply(self, run_generate=True, sync=False, exit_on_error=True): # p

# stop backends
if restart_networkd:
logging.debug('netplan generated networkd configuration changed, restarting networkd')
logging.debug('netplan generated networkd configuration changed, reloading networkd')
# Running 'systemctl daemon-reload' will re-run the netplan systemd generator,
# so let's make sure we only run it iff we're willing to run 'netplan generate'
if run_generate:
Expand All @@ -161,7 +163,7 @@ def command_apply(self, run_generate=True, sync=False, exit_on_error=True): # p
# upgraded system, we need to make sure to stop those.
if utils.systemctl_is_active('netplan-wpa@*.service'):
wpa_services.insert(0, 'netplan-wpa@*.service')
utils.systemctl_networkd('stop', sync=sync, extra_services=wpa_services)
utils.systemctl('stop', wpa_services, sync=sync)
else:
logging.debug('no netplan generated networkd configuration exists')

Expand Down Expand Up @@ -230,10 +232,16 @@ def command_apply(self, run_generate=True, sync=False, exit_on_error=True): # p
# (re)start backends
if restart_networkd:
netplan_wpa = [os.path.basename(f) for f in glob.glob('/run/systemd/system/*.wants/netplan-wpa-*.service')]
netplan_ovs = [os.path.basename(f) for f in glob.glob('/run/systemd/system/*.wants/netplan-ovs-*.service')]
# exclude the special 'netplan-ovs-cleanup.service' unit
netplan_ovs = [os.path.basename(f) for f in glob.glob('/run/systemd/system/*.wants/netplan-ovs-*.service')
if not f.endswith('/' + OVS_CLEANUP_SERVICE)]
# Run 'systemctl start' command synchronously, to avoid race conditions
# with 'oneshot' systemd service units, e.g. netplan-ovs-*.service.
utils.systemctl_networkd('start', sync=True, extra_services=netplan_wpa + netplan_ovs)
utils.networkctl_reconfigure(utils.networkd_interfaces())
# 1st: execute OVS cleanup, to avoid races while applying OVS config
utils.systemctl('start', [OVS_CLEANUP_SERVICE], sync=True)
# 2nd: start all other services
utils.systemctl('start', netplan_wpa + netplan_ovs, sync=True)
if restart_nm:
# Flush all IP addresses of NM managed interfaces, to avoid NM creating
# new, non netplan-* connection profiles, using the existing IPs.
Expand Down
31 changes: 23 additions & 8 deletions netplan/cli/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -117,18 +117,33 @@ def systemctl_network_manager(action, sync=False): # pragma: nocover (covered i
subprocess.check_call(command)


def systemctl_networkd(action, sync=False, extra_services=[]): # pragma: nocover (covered in autopkgtest)
command = ['systemctl', action]
def systemctl(action, services, sync=False):
if len(services) >= 1:
command = ['systemctl', action]

if not sync:
command.append('--no-block')
if not sync:
command.append('--no-block')

command.append('systemd-networkd.service')
for service in services:
command.append(service)

for service in extra_services:
command.append(service)
subprocess.check_call(command)

subprocess.check_call(command)

def networkd_interfaces():
interfaces = set()
out = subprocess.check_output(['networkctl', '--no-pager', '--no-legend'], universal_newlines=True)
for line in out.splitlines():
s = line.strip().split(' ')
if s[0].isnumeric() and s[-1] not in ['unmanaged', 'linger']:
interfaces.add(s[1])
return interfaces


def networkctl_reconfigure(interfaces):
subprocess.check_call(['networkctl', 'reload'])
if len(interfaces) >= 1:
subprocess.check_call(['networkctl', 'reconfigure'] + list(interfaces))


def systemctl_is_active(unit_pattern): # pragma: nocover (covered in autopkgtest)
Expand Down
31 changes: 31 additions & 0 deletions tests/test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -192,3 +192,34 @@ def test_netplan_get_filename_by_id_invalid(self):
remote: 0.0.0.0
key: 0.0.0.0''')
self.assertIsNone(utils.netplan_get_filename_by_id('some-id', self.workdir.name))

def test_systemctl(self):
self.mock_systemctl = MockCmd('systemctl')
path_env = os.environ['PATH']
os.environ['PATH'] = os.path.dirname(self.mock_systemctl.path) + os.pathsep + path_env
utils.systemctl('start', ['service1', 'service2'])
self.assertEquals(self.mock_systemctl.calls(), [['systemctl', 'start', '--no-block', 'service1', 'service2']])

def test_networkd_interfaces(self):
self.mock_networkctl = MockCmd('networkctl')
path_env = os.environ['PATH']
os.environ['PATH'] = os.path.dirname(self.mock_networkctl.path) + os.pathsep + path_env
self.mock_networkctl.set_output('''
1 lo loopback carrier unmanaged
2 ens3 ether routable configured
3 wlan0 wlan routable configuring
174 wwan0 wwan off linger''')
res = utils.networkd_interfaces()
self.assertEquals(self.mock_networkctl.calls(), [['networkctl', '--no-pager', '--no-legend']])
self.assertIn('wlan0', res)
self.assertIn('ens3', res)

def test_networkctl_reconfigure(self):
self.mock_networkctl = MockCmd('networkctl')
path_env = os.environ['PATH']
os.environ['PATH'] = os.path.dirname(self.mock_networkctl.path) + os.pathsep + path_env
utils.networkctl_reconfigure(['eth0', 'eth1'])
self.assertEquals(self.mock_networkctl.calls(), [
['networkctl', 'reload'],
['networkctl', 'reconfigure', 'eth0', 'eth1']
])

0 comments on commit 2b4f035

Please sign in to comment.