| Server IP : 165.22.154.248 / Your IP : 216.73.217.54 [ Web Server : Apache/2.4.29 (Ubuntu) System : Linux droplet-integra 4.15.0-197-generic #208-Ubuntu SMP Tue Nov 1 17:23:37 UTC 2022 x86_64 User : www-data ( 33) PHP Version : 7.2.24-0ubuntu0.18.04.15 Disable Function : pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_get_handler,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,pcntl_async_signals, Domains : 1 Domains MySQL : OFF | cURL : ON | WGET : ON | Perl : ON | Python : OFF | Sudo : ON | Pkexec : ON Directory : /usr/share/netplan/netplan/cli/commands/ |
Upload File : |
#!/usr/bin/python3
#
# Copyright (C) 2018 Canonical, Ltd.
# Author: Mathieu Trudel-Lapierre <mathieu.trudel-lapierre@canonical.com>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; version 3.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
'''netplan apply command line'''
import logging
import os
import sys
import glob
import subprocess
import netplan.cli.utils as utils
from netplan.configmanager import ConfigManager, ConfigurationError
import netifaces
class NetplanApply(utils.NetplanCommand):
def __init__(self):
super().__init__(command_id='apply',
description='Apply current netplan config to running system',
leaf=True)
def run(self): # pragma: nocover (covered in autopkgtest)
self.func = NetplanApply.command_apply
self.parse_args()
self.run_command()
@staticmethod
def command_apply(run_generate=True, sync=False, exit_on_error=True): # pragma: nocover (covered in autopkgtest)
old_files_networkd = bool(glob.glob('/run/systemd/network/*netplan-*'))
old_files_nm = bool(glob.glob('/run/NetworkManager/system-connections/netplan-*'))
if run_generate and subprocess.call([utils.get_generator_path()]) != 0:
if exit_on_error:
sys.exit(os.EX_CONFIG)
else:
raise ConfigurationError("the configuration could not be generated")
config_manager = ConfigManager()
devices = netifaces.interfaces()
# Re-start service when
# 1. We have configuration files for it
# 2. Previously we had config files for it but not anymore
# Ideally we should compare the content of the *netplan-* files before and
# after generation to minimize the number of re-starts, but the conditions
# above works too.
restart_networkd = bool(glob.glob('/run/systemd/network/*netplan-*'))
if not restart_networkd and old_files_networkd:
restart_networkd = True
restart_nm = bool(glob.glob('/run/NetworkManager/system-connections/netplan-*'))
if not restart_nm and old_files_nm:
restart_nm = True
# stop backends
if restart_networkd:
logging.debug('netplan generated networkd configuration changed, restarting networkd')
utils.systemctl_networkd('stop', sync=sync, extra_services=['netplan-wpa@*.service'])
else:
logging.debug('no netplan generated networkd configuration exists')
if restart_nm:
logging.debug('netplan generated NM configuration changed, restarting NM')
if utils.nm_running():
# restarting NM does not cause new config to be applied, need to shut down devices first
for device in devices:
# ignore failures here -- some/many devices might not be managed by NM
try:
utils.nmcli(['device', 'disconnect', device])
except subprocess.CalledProcessError:
pass
utils.systemctl_network_manager('stop', sync=sync)
else:
logging.debug('no netplan generated NM configuration exists')
# evaluate config for extra steps we need to take (like renaming)
# for now, only applies to non-virtual (real) devices.
config_manager.parse()
changes = NetplanApply.process_link_changes(devices, config_manager)
# if the interface is up, we can still apply some .link file changes
for device in devices:
logging.debug('netplan triggering .link rules for %s', device)
subprocess.check_call(['udevadm', 'test-builtin',
'net_setup_link',
'/sys/class/net/' + device],
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL)
# apply renames to "down" devices
for iface, settings in changes.items():
if settings.get('name'):
subprocess.check_call(['ip', 'link', 'set',
'dev', iface,
'name', settings.get('name')],
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL)
subprocess.check_call(['udevadm', 'settle'])
# (re)start backends
if restart_networkd:
netplan_wpa = [os.path.basename(f) for f in glob.glob('/run/systemd/system/*.wants/netplan-wpa@*.service')]
utils.systemctl_networkd('start', sync=sync, extra_services=netplan_wpa)
if restart_nm:
utils.systemctl_network_manager('start', sync=sync)
@staticmethod
def is_composite_member(composites, phy): # pragma: nocover (covered in autopkgtest)
"""
Is this physical interface a member of a 'composite' virtual
interface? (bond, bridge)
"""
for composite in composites:
for id, settings in composite.items():
members = settings.get('interfaces', [])
for iface in members:
if iface == phy:
return True
return False
@staticmethod
def process_link_changes(interfaces, config_manager): # pragma: nocover (covered in autopkgtest)
"""
Go through the pending changes and pick what needs special
handling. Only applies to "down" interfaces which can be safely
updated.
"""
changes = {}
phys = dict(config_manager.physical_interfaces)
composite_interfaces = [config_manager.bridges, config_manager.bonds]
# TODO (cyphermox): factor out some of this matching code (and make it
# pretty) in its own module.
matches = {'by-driver': {},
'by-mac': {},
}
for phy, settings in phys.items():
if not settings:
continue
if phy == 'renderer':
continue
newname = settings.get('set-name')
if not newname:
continue
match = settings.get('match')
if not match:
continue
driver = match.get('driver')
mac = match.get('macaddress')
if driver:
matches['by-driver'][driver] = newname
if mac:
matches['by-mac'][mac] = newname
# /sys/class/net/ens3/device -> ../../../virtio0
# /sys/class/net/ens3/device/driver -> ../../../../bus/virtio/drivers/virtio_net
for interface in interfaces:
if interface not in phys:
# do not rename virtual devices
logging.debug('Skipping non-physical interface: %s', interface)
continue
if NetplanApply.is_composite_member(composite_interfaces, interface):
logging.debug('Skipping composite member %s', interface)
# do not rename members of virtual devices. MAC addresses
# may be the same for all interface members.
continue
# try to get the device's driver for matching.
devdir = os.path.join('/sys/class/net', interface)
try:
with open(os.path.join(devdir, 'operstate')) as f:
state = f.read().strip()
if state != 'down':
logging.debug('device %s operstate is %s, not changing', interface, state)
continue
except IOError as e:
logging.error('Cannot determine operstate of %s: %s', interface, str(e))
continue
try:
driver = os.path.realpath(os.path.join(devdir, 'device', 'driver'))
driver_name = os.path.basename(driver)
except IOError as e:
logging.debug('Cannot replug %s: cannot read link %s/device: %s', interface, devdir, str(e))
driver_name = None
pass
link = netifaces.ifaddresses(interface)[netifaces.AF_LINK][0]
macaddress = link.get('addr')
if driver_name in matches['by-driver']:
new_name = matches['by-driver'][driver_name]
logging.debug(new_name)
logging.debug(interface)
if new_name != interface:
changes.update({interface: {'name': new_name}})
if macaddress in matches['by-mac']:
new_name = matches['by-mac'][macaddress]
if new_name != interface:
changes.update({interface: {'name': new_name}})
logging.debug(changes)
return changes