All of the patches except for [3/9] are ports from master so it is the [3/9] that asks for review the most.
https://bugzilla.redhat.com/show_bug.cgi?id=1011826 [1/9], [2/9], [3/9]
https://bugzilla.redhat.com/show_bug.cgi?id=1011841 [4/9]
https://bugzilla.redhat.com/show_bug.cgi?id=1011855 [5/9]
https://bugzilla.redhat.com/show_bug.cgi?id=1011860 [6/9]
https://bugzilla.redhat.com/show_bug.cgi?id=1011866 [7/9]
https://bugzilla.redhat.com/show_bug.cgi?id=1011928 [8/9], [9/9]
From: Vratislav Podzimek vpodzime@redhat.com
NetworkManager uses IN_CLOSE_WRITE inotify event which is not triggered if a new file is moved to the place of the watched file. Also the SELinux context is wrong when a file from /tmp is used to the /etc tree.
Related: rhbz#1011826
Based on the patch from Hans de Goede hdegoede@redhat.com.
Port of commit 25b6f69b8c61df4dffeab3de87bebe1782a6b00d from master --- pyanaconda/simpleconfig.py | 36 ++++++++++++++++++++---------------- 1 file changed, 20 insertions(+), 16 deletions(-)
diff --git a/pyanaconda/simpleconfig.py b/pyanaconda/simpleconfig.py index 66b38b0..9f22d17 100644 --- a/pyanaconda/simpleconfig.py +++ b/pyanaconda/simpleconfig.py @@ -81,26 +81,31 @@ class SimpleConfigFile(object): if key: self.info[key] = value
- def write(self, filename=None): + def write(self, filename=None, use_tmp=True): """ passing filename will override the filename passed to init. """ filename = filename or self.filename if not filename: return None
- tmpf = tempfile.NamedTemporaryFile(mode="w", delete=False) - tmpf.write(str(self)) - tmpf.close() + if use_tmp: + tmpf = tempfile.NamedTemporaryFile(mode="w", delete=False) + tmpf.write(str(self)) + tmpf.close()
- # Move the temporary file (with 0600 permissions) over the top of the - # original and preserve the original's permissions - filename = os.path.realpath(filename) - if os.path.exists(filename): - m = os.stat(filename).st_mode + # Move the temporary file (with 0600 permissions) over the top of the + # original and preserve the original's permissions + filename = os.path.realpath(filename) + if os.path.exists(filename): + m = os.stat(filename).st_mode + else: + m = int('0100644', 8) + shutil.move(tmpf.name, filename) + os.chmod(filename, m) else: - m = int('0100644', 8) - shutil.move(tmpf.name, filename) - os.chmod(filename, m) + # write directly to the file + with open(filename, "w") as fobj: + fobj.write(str(self))
def set(self, *args): for key, value in args: @@ -187,8 +192,6 @@ class IfcfgFile(SimpleConfigFile): SimpleConfigFile.read(self, self.path) return len(self.info)
- # ifcfg-rh is using inotify IN_CLOSE_WRITE event - # so we don't use temporary file for new configuration. def write(self, dir=None): """ Writes values into ifcfg file. """ @@ -198,5 +201,6 @@ class IfcfgFile(SimpleConfigFile): else: path = os.path.join(dir, os.path.basename(self.path))
- SimpleConfigFile.write(self, path) - + # ifcfg-rh is using inotify IN_CLOSE_WRITE event so we don't use + # temporary file for new configuration + SimpleConfigFile.write(self, path, use_tmp=False)
Resolves: rhbz#1011826
- Remove obsolete NetworkDevice class, use just IfcfgFile instead. - More robust lookup of ifcfg files of devices - based on values instead of relying on filename.
Port of commit f14c5b62ee3d2485e50a3162fabc572c454ef227 commit 8aac0550f05efce7a11ec51f4fedafc2de0121e8 from master. --- pyanaconda/installclasses/fedora.py | 22 ++- pyanaconda/network.py | 293 ++++++++++++++++-------------------- pyanaconda/nm.py | 38 +++++ pyanaconda/simpleconfig.py | 32 ---- pyanaconda/ui/gui/spokes/network.py | 29 +--- pyanaconda/ui/tui/spokes/network.py | 11 +- 6 files changed, 199 insertions(+), 226 deletions(-)
diff --git a/pyanaconda/installclasses/fedora.py b/pyanaconda/installclasses/fedora.py index a12eb9a..e91a6db 100644 --- a/pyanaconda/installclasses/fedora.py +++ b/pyanaconda/installclasses/fedora.py @@ -50,8 +50,13 @@ class InstallClass(BaseInstallClass): def setNetworkOnbootDefault(self, ksdata): # if something's already enabled, we can just leave the config alone for devName in nm.nm_devices(): - if not nm.nm_device_type_is_wifi(devName) and \ - network.get_ifcfg_value(devName, "ONBOOT", ROOT_PATH) == "yes": + if nm.nm_device_type_is_wifi(devName): + continue + try: + onboot = nm.nm_device_setting_value(devName, "connection", "autoconnect") + except nm.DeviceSettingsNotFoundError: + continue + if not onboot == False: return
# the default otherwise: bring up the first wired netdev with link @@ -63,12 +68,15 @@ class InstallClass(BaseInstallClass): except ValueError as e: continue if link_up: - dev = network.NetworkDevice(ROOT_PATH + network.netscriptsDir, devName) - dev.loadIfcfgFile() - dev.set(('ONBOOT', 'yes')) - dev.writeIfcfgFile() + ifcfg_path = network.find_ifcfg_file_of_device(devName, root_path=ROOT_PATH) + if not ifcfg_path: + continue + ifcfg = network.IfcfgFile(ifcfg_path) + ifcfg.read() + ifcfg.set(('ONBOOT', 'yes')) + ifcfg.write() for nd in ksdata.network.network: - if nd.device == dev.iface: + if nd.device == devName: nd.onboot = True break break diff --git a/pyanaconda/network.py b/pyanaconda/network.py index bae808b..3661a16 100644 --- a/pyanaconda/network.py +++ b/pyanaconda/network.py @@ -36,8 +36,7 @@ import re import IPy from flags import flags
-from simpleconfig import IfcfgFile -import urlgrabber.grabber +from simpleconfig import SimpleConfigFile from blivet.devices import FcoeDiskDevice, iScsiDiskDevice import blivet.arch
@@ -208,82 +207,44 @@ def _ifcfg_files(directory): if name.startswith("ifcfg-"): if name == "ifcfg-lo": continue - rv.append(name) + rv.append(os.path.join(directory,name)) return rv
def logIfcfgFiles(message=""): ifcfglog.debug("content of files (%s):" % message) - for name in _ifcfg_files(netscriptsDir): - path = os.path.join(netscriptsDir, name) + for path in _ifcfg_files(netscriptsDir): with open(path, "r") as f: content = f.read() ifcfglog.debug("%s:\n%s" % (path, content))
-class NetworkDevice(IfcfgFile):
- def __init__(self, dir, iface): - IfcfgFile.__init__(self, dir, iface) - if iface.startswith('ctc'): - self.info["TYPE"] = "CTC" - self.wepkey = "" +class IfcfgFile(SimpleConfigFile): + def __init__(self, filename): + SimpleConfigFile.__init__(self, always_quote=True, filename=filename) self._dirty = False
- def clear(self): - IfcfgFile.clear(self) - if self.iface.startswith('ctc'): - self.info["TYPE"] = "CTC" - self.wepkey = "" - - def __str__(self): - s = "" - keys = self.info.keys() - if blivet.arch.isS390() and ("HWADDR" in keys): - keys.remove("HWADDR") - # make sure we include autoneg in the ethtool line - if 'ETHTOOL_OPTS' in keys: - eopts = self.get('ETHTOOL_OPTS') - if "autoneg" not in eopts: - self.set(('ETHTOOL_OPTS', "autoneg off %s" % eopts)) - - for key in keys: - if self.info[key] is not None: - s = s + key + '="' + self.info[key] + '"\n' - - return s - - def loadIfcfgFile(self): - ifcfglog.debug("loadIfcfFile %s" % self.path) - - self.clear() - IfcfgFile.read(self) + def read(self): + self.reset() + ifcfglog.debug("IfcfFile.read %s" % self.filename) + SimpleConfigFile.read(self) self._dirty = False
- def writeIfcfgFile(self): - # Write out the file only if there is a key whose - # value has been changed since last load of ifcfg file. - ifcfglog.debug("writeIfcfgFile %s to %s%s" % (self.iface, self.path, - ("" if self._dirty else " not needed"))) - if self._dirty: - ifcfglog.debug("old %s:\n%s" % (self.path, self.fileContent())) - ifcfglog.debug("writing NetworkDevice %s:\n%s" % (self.iface, self.__str__())) - IfcfgFile.write(self) + def write(self, filename=None): + if self._dirty or filename: + # ifcfg-rh is using inotify IN_CLOSE_WRITE event so we don't use + # temporary file for new configuration + ifcfglog.debug("IfcfgFile.write %s:\n%s" % self.filename, self.__str__()) + SimpleConfigFile.write(self, filename) self._dirty = False
- # We can't read the file right now racing with ifcfg-rh update - #ifcfglog.debug("%s:\n%s" % (device.path, device.fileContent())) - def set(self, *args): - # If we are changing value of a key set _dirty flag - # informing that ifcfg file needs to be synced. - s = " ".join("%s=%s" % key_val for key_val in args) - ifcfglog.debug("NetworkDevice %s set: %s" % - (self.iface, s)) for (key, data) in args: if self.get(key) != data: break else: return - IfcfgFile.set(self, *args) + ifcfglog.debug("IfcfgFile.set %s: %s" % self.filename, args) + SimpleConfigFile.set(self, *args) self._dirty = True
def unset(self, *args): @@ -293,19 +254,8 @@ class NetworkDevice(IfcfgFile): break else: return - IfcfgFile.unset(self, *args) - - @property - def keyfilePath(self): - return os.path.join(self.dir, "keys-%s" % self.iface) - - def fileContent(self): - if not os.path.exists(self.path): - return "" - f = open(self.path, 'r') - content = f.read() - f.close() - return content + ifcfglog.debug("IfcfgFile.unset %s: %s" % self.filename, args) + SimpleConfigFile.unset(self, *args)
def dumpMissingDefaultIfcfgs(): @@ -327,9 +277,12 @@ def dumpMissingDefaultIfcfgs(): if not nm.nm_device_type_is_ethernet(devname): continue
- # if there is no ifcfg file for the device - device_cfg = NetworkDevice(netscriptsDir, devname) - if os.access(device_cfg.path, os.R_OK): + # check that device has connection without ifcfg file + try: + con_uuid = nm.nm_device_setting_value(devname, "connection", "uuid") + except nm.DeviceSettingsNotFoundError: + continue + if find_ifcfg_file([("UUID", con_uuid)], root_path=""): continue
try: @@ -357,16 +310,20 @@ def dracutSetupArgs(networkStorageDevice): log.error('Unknown network interface: %s' % nic) return ""
- ifcfg = NetworkDevice(netscriptsDir, nic) - ifcfg.loadIfcfgFile() - return dracutBootArguments(ifcfg, + ifcfg_path = find_ifcfg_file_of_device(nic) + if not ifcfg_path: + log.error("dracutSetupArgs: can't find ifcfg file for %s" % nic) + return "" + ifcfg = IfcfgFile(ifcfg_path) + ifcfg.read() + return dracutBootArguments(nic, + ifcfg, networkStorageDevice.host_address, getHostname())
-def dracutBootArguments(ifcfg, storage_ipaddr, hostname=None): +def dracutBootArguments(devname, ifcfg, storage_ipaddr, hostname=None):
netargs = set() - devname = ifcfg.iface
if ifcfg.get('BOOTPROTO') == 'ibft': netargs.add("ip=ibft") @@ -422,23 +379,6 @@ def dracutBootArguments(ifcfg, storage_ipaddr, hostname=None):
return netargs
-def get_ks_network_data(devname, ifcfg_suffix=None): - retval = None - ifcfg_suffix = ifcfg_suffix or devname - - ifcfg_suffix = ifcfg_suffix.replace(' ', '_') - device_cfg = NetworkDevice(netscriptsDir, ifcfg_suffix) - try: - device_cfg.loadIfcfgFile() - except IOError as e: - log.debug("get_ks_network_data %s: %s" % (ifcfg_suffix, e)) - return None - retval = kickstartNetworkData(ifcfg=device_cfg) - if retval and devname in nm.nm_activated_devices(): - retval.activate = True - - return retval - def update_settings_with_ksdata(devname, networkdata):
new_values = [] @@ -497,27 +437,56 @@ def update_settings_with_ksdata(devname, networkdata):
nm.nm_update_settings_of_device(devname, new_values)
-def kickstartNetworkData(ifcfg=None, hostname=None): +def ksdata_from_ifcfg(devname): + + ifcfg_path = None + if nm.nm_device_type_is_ethernet(devname): + ifcfg_path = find_ifcfg_file_of_device(devname) + elif nm.nm_device_type_is_wifi(devname): + ssid = nm.nm_device_active_ssid(devname) + if ssid: + ifcfg_path = find_ifcfg_file([("ESSID", ssid)]) + elif nm.nm_device_type_is_bond(devname): + ifcfg_path = find_ifcfg_file([("DEVICE", devname)]) + elif nm.nm_device_type_is_vlan(devname): + ifcfg_path = find_ifcfg_file([("DEVICE", devname)]) + + if not ifcfg_path: + return None + + ifcfg = IfcfgFile(ifcfg_path) + ifcfg.read() + nd = ifcfg_to_ksdata(ifcfg, devname) + + if not nd: + return None + + if nm.nm_device_type_is_ethernet(devname): + nd.device = devname + elif nm.nm_device_type_is_wifi(devname): + nm.device = "" + elif nm.nm_device_type_is_bond(devname): + nd.device = devname + elif nm.nm_device_type_is_vlan(devname): + nd.device = devname.split(".")[0] + + return nd + +def ifcfg_to_ksdata(ifcfg, devname):
from pyanaconda.kickstart import AnacondaKSHandler handler = AnacondaKSHandler() kwargs = {}
- if not ifcfg and hostname: - return handler.NetworkData(hostname=hostname, bootProto="") - # no network command for bond slaves if ifcfg.get("MASTER"): return None
# ipv4 and ipv6 - if not ifcfg.get("ESSID"): - kwargs["device"] = ifcfg.iface if ifcfg.get("ONBOOT") and ifcfg.get("ONBOOT" ) == "no": kwargs["onboot"] = False if ifcfg.get('MTU') and ifcfg.get('MTU') != "0": kwargs["mtu"] = ifcfg.get('MTU') - # ipv4 if not ifcfg.get('BOOTPROTO'): kwargs["noipv4"] = True @@ -586,15 +555,11 @@ def kickstartNetworkData(ifcfg=None, hostname=None): # hostname if ifcfg.get("DHCP_HOSTNAME"): kwargs["hostname"] = ifcfg.get("DHCP_HOSTNAME") - elif ifcfg.get("BOOTPROTO").lower != "dhcp": - if (hostname and - hostname != DEFAULT_HOSTNAME): - kwargs["hostname"] = hostname
# bonding # FIXME: dracut has only BOND_OPTS if ifcfg.get("BONDING_MASTER") == "yes" or ifcfg.get("TYPE") == "Bond": - slaves = get_bond_slaves_from_ifcfgs([ifcfg.iface, ifcfg.get("UUID")]) + slaves = get_bond_slaves_from_ifcfgs([devname, ifcfg.get("UUID")]) if slaves: kwargs["bondslaves"] = ",".join(slaves) bondopts = ifcfg.get("BONDING_OPTS") @@ -611,26 +576,39 @@ def kickstartNetworkData(ifcfg=None, hostname=None):
return handler.NetworkData(**kwargs)
-def get_bond_master_ifcfg_name(devname): - """Name of ifcfg file of bond device devname""" - - for filename in _ifcfg_files(netscriptsDir): - ifcfg = NetworkDevice(netscriptsDir, filename[6:]) - ifcfg.loadIfcfgFile() - # FIXME: dracut has only BOND_OPTS - if ifcfg.get("BONDING_MASTER") == "yes" or ifcfg.get("TYPE") == "Bond": - if ifcfg.get("DEVICE") == devname: - return filename - -def get_vlan_ifcfg_name(devname): - """Name of ifcfg file of vlan device devname""" +def hostname_ksdata(hostname): + from pyanaconda.kickstart import AnacondaKSHandler + handler = AnacondaKSHandler() + kwargs = {} + return handler.NetworkData(hostname=hostname, bootProto="")
- for filename in _ifcfg_files(netscriptsDir): - ifcfg = NetworkDevice(netscriptsDir, filename[6:]) - ifcfg.loadIfcfgFile() - if ifcfg.get("VLAN") == "yes" or ifcfg.get("TYPE") == "Vlan": - if ifcfg.get("DEVICE") == devname: - return filename +def find_ifcfg_file_of_device(devname, root_path=""): + ifcfg_path = None + try: + hwaddr = nm.nm_device_hwaddress(devname) + except nm.PropertyNotFoundError: + hwaddr = None + if hwaddr: + hwaddr_check = lambda mac: mac.upper() == hwaddr.upper() + ifcfg_path = find_ifcfg_file([("HWADDR", hwaddr_check)], root_path) + if not ifcfg_path: + ifcfg_path = find_ifcfg_file([("DEVICE", devname)], root_path) + return ifcfg_path + +def find_ifcfg_file(values, root_path=""): + for filepath in _ifcfg_files(os.path.normpath(root_path+netscriptsDir)): + ifcfg = IfcfgFile(filepath) + ifcfg.read() + for key, value in values: + if callable(value): + if not value(ifcfg.get(key)): + break + else: + if ifcfg.get(key) != value: + break + else: + return filepath + return None
def get_bond_slaves_from_ifcfgs(master_specs): """List of slave device names of master specified by master_specs. @@ -640,9 +618,9 @@ def get_bond_slaves_from_ifcfgs(master_specs): """ slaves = []
- for filename in _ifcfg_files(netscriptsDir): - ifcfg = NetworkDevice(netscriptsDir, filename[6:]) - ifcfg.loadIfcfgFile() + for filepath in _ifcfg_files(netscriptsDir): + ifcfg = IfcfgFile(filepath) + ifcfg.read() master = ifcfg.get("MASTER") if master in master_specs: device = ifcfg.get("DEVICE") @@ -750,16 +728,6 @@ def get_ksdevice_name(ksspec=""):
return ksdevice
-# note that NetworkDevice.get returns "" if key is not found -def get_ifcfg_value(iface, key, root_path=""): - dev = NetworkDevice(os.path.normpath(root_path + netscriptsDir), iface) - try: - dev.loadIfcfgFile() - except IOError as e: - log.debug("get_ifcfg_value %s %s: %s" % (iface, key, e)) - return "" - return dev.get(key) - def set_hostname(hn): if flags.imageInstall: log.info("image install -- not setting hostname") @@ -794,35 +762,36 @@ def disableNMForStorageDevices(rootpath, storage): for devname in nm.nm_devices(): if (usedByFCoE(devname, storage) or usedByRootOnISCSI(devname, storage)): - dev = NetworkDevice(rootpath + netscriptsDir, devname) - if os.access(dev.path, os.R_OK): - dev.loadIfcfgFile() - dev.set(('NM_CONTROLLED', 'no')) - dev.writeIfcfgFile() - log.info("network device %s used by storage will not be " - "controlled by NM" % devname) - else: + ifcfg_path = find_ifcfg_file_of_device(devname, root_path=rootpath) + if not ifcfg_path: log.warning("disableNMForStorageDevices: ifcfg file for %s not found" % devname) + continue + ifcfg = IfcfgFile(ifcfg_path) + ifcfg.read() + ifcfg.set(('NM_CONTROLLED', 'no')) + ifcfg.write() + log.info("network device %s used by storage will not be " + "controlled by NM" % devname)
# sets ONBOOT=yes (and its mirror value in ksdata) for devices used by FCoE def autostartFCoEDevices(rootpath, storage, ksdata): for devname in nm.nm_devices(): if usedByFCoE(devname, storage): - dev = NetworkDevice(rootpath + netscriptsDir, devname) - if os.access(dev.path, os.R_OK): - dev.loadIfcfgFile() - dev.set(('ONBOOT', 'yes')) - dev.writeIfcfgFile() - log.debug("setting ONBOOT=yes for network device %s used by fcoe" - % devname) - for nd in ksdata.network.network: - if nd.device == dev.iface: - nd.onboot = True - break - else: - log.warning("autoconnectFCoEDevices: ifcfg file for %s not found" % - devname) + ifcfg_path = find_ifcfg_file_of_device(devname, root_path=rootpath) + if not ifcfg_path: + log.warning("autoconnectFCoEDevices: ifcfg file for %s not found" % devname) + continue + + ifcfg = IfcfgFile(ifcfg_path) + ifcfg.read() + ifcfg.set(('ONBOOT', 'yes')) + ifcfg.write() + log.debug("setting ONBOOT=yes for network device %s used by fcoe" % devname) + for nd in ksdata.network.network: + if nd.device == devname: + nd.onboot = True + break
def usedByFCoE(iface, storage): for d in storage.devices: @@ -912,7 +881,7 @@ def update_hostname_data(ksdata, hostname): nd.hostname = hostname hostname_found = True if not hostname_found: - nd = kickstartNetworkData(hostname=hostname) + nd = hostname_ksdata(hostname) ksdata.network.network.append(nd)
def get_device_name(devspec): diff --git a/pyanaconda/nm.py b/pyanaconda/nm.py index 0c88800..cce7aaa 100644 --- a/pyanaconda/nm.py +++ b/pyanaconda/nm.py @@ -233,6 +233,24 @@ def nm_device_type_is_ethernet(name): """ return nm_device_type(name) == NetworkManager.DeviceType.ETHERNET
+def nm_device_type_is_bond(name): + """Is the type of device bond? + + Exceptions: + UnknownDeviceError if device is not found + PropertyNotFoundError if type is not found + """ + return nm_device_type(name) == NetworkManager.DeviceType.BOND + +def nm_device_type_is_vlan(name): + """Is the type of device vlan? + + Exceptions: + UnknownDeviceError if device is not found + PropertyNotFoundError if type is not found + """ + return nm_device_type(name) == NetworkManager.DeviceType.VLAN + def nm_device_hwaddress(name): """Return device's 'HwAddress' property
@@ -277,6 +295,26 @@ def nm_device_ip_addresses(name, version=4):
return retval
+def nm_device_active_ssid(name): + """Return ssid of device's active access point. + + Exceptions: + UnknownDeviceError if device is not found + """ + + try: + aap = nm_device_property(name, "ActiveAccessPoint") + except PropertyNotFoundError: + return None + + if aap == "/": + return None + + ssid_ay = _get_property(aap, "Ssid", ".AccessPoint") + ssid = "".join(chr(b) for b in ssid_ay) + + return ssid + def nm_device_ip_config(name, version=4): """Return list of devices's IP config
diff --git a/pyanaconda/simpleconfig.py b/pyanaconda/simpleconfig.py index 9f22d17..c70df36 100644 --- a/pyanaconda/simpleconfig.py +++ b/pyanaconda/simpleconfig.py @@ -171,36 +171,4 @@ class SimpleConfigFile(object): return s
-class IfcfgFile(SimpleConfigFile): - def __init__(self, dir, iface): - SimpleConfigFile.__init__(self, always_quote=True) - self.iface = iface - self.dir = dir
- @property - def path(self): - return os.path.join(self.dir, "ifcfg-%s" % self.iface) - - def clear(self): - SimpleConfigFile.reset(self) - - def read(self): - """ Reads values from ifcfg file. - - returns: number of values read - """ - SimpleConfigFile.read(self, self.path) - return len(self.info) - - def write(self, dir=None): - """ Writes values into ifcfg file. - """ - - if not dir: - path = self.path - else: - path = os.path.join(dir, os.path.basename(self.path)) - - # ifcfg-rh is using inotify IN_CLOSE_WRITE event so we don't use - # temporary file for new configuration - SimpleConfigFile.write(self, path, use_tmp=False) diff --git a/pyanaconda/ui/gui/spokes/network.py b/pyanaconda/ui/gui/spokes/network.py index ed8014d..dc80a9e 100644 --- a/pyanaconda/ui/gui/spokes/network.py +++ b/pyanaconda/ui/gui/spokes/network.py @@ -44,7 +44,7 @@ from pyanaconda.ui.gui.utils import gtk_call_once, enlightbox from pyanaconda.ui.common import FirstbootSpokeMixIn
from pyanaconda import network -from pyanaconda.nm import nm_device_setting_value, nm_device_ip_config +from pyanaconda.nm import nm_device_setting_value, nm_device_ip_config, nm_activated_devices
# pylint: disable-msg=E0611 from gi.repository import GLib, GObject, Pango, Gio, NetworkManager, NMClient @@ -1478,29 +1478,16 @@ class NetworkStandaloneSpoke(StandaloneSpoke): def _update_network_data(data, ncb): data.network.network = [] for dev in ncb.listed_devices: - network_data = None - ifcfg_suffix = _ifcfg_suffix(dev) - if ifcfg_suffix: - network_data = network.get_ks_network_data(dev, ifcfg_suffix) - if network_data is not None: - data.network.network.append(network_data) + devname = dev.get_iface() + nd = network.ksdata_from_ifcfg(devname) + if not nd: + continue + if devname in nm_activated_devices(): + nd.activate = True + data.network.network.append(nd) hostname = ncb.hostname network.update_hostname_data(data, hostname)
-def _ifcfg_suffix(device): - retval = None - if device.get_device_type() == NetworkManager.DeviceType.ETHERNET: - retval = device.get_iface() - elif device.get_device_type() == NetworkManager.DeviceType.WIFI: - ap = device.get_active_access_point() - if ap: - retval = ap.get_ssid() - elif device.get_device_type() == NetworkManager.DeviceType.BOND: - retval = network.get_bond_master_ifcfg_name(device.get_iface())[6:] - elif device.get_device_type() == NetworkManager.DeviceType.VLAN: - retval = network.get_vlan_ifcfg_name(device.get_iface())[6:] - return retval - if __name__ == "__main__":
win = Gtk.Window() diff --git a/pyanaconda/ui/tui/spokes/network.py b/pyanaconda/ui/tui/spokes/network.py index c50f3f5..4bd0dac 100644 --- a/pyanaconda/ui/tui/spokes/network.py +++ b/pyanaconda/ui/tui/spokes/network.py @@ -186,7 +186,7 @@ class NetworkSpoke(EditTUISpoke): elif 2 <= num <= len(self.supported_devices) + 1: # configure device devname = self.supported_devices[num-2] - ndata = network.get_ks_network_data(devname) + ndata = network.ksdata_from_ifcfg(devname) newspoke = ConfigureNetworkSpoke(self.app, self.data, self.storage, self.payload, self.instclass, ndata) self.app.switch_screen_modal(newspoke) @@ -226,9 +226,12 @@ class NetworkSpoke(EditTUISpoke):
self.data.network.network = [] for name in self.supported_devices: - network_data = network.get_ks_network_data(name) - if network_data is not None: - self.data.network.network.append(network_data) + nd = network.ksdata_from_ifcfg(name) + if not nd: + continue + if name in nm_activated_devices(): + nd.activate = True + self.data.network.network.append(nd)
(valid, error) = network.sanityCheckHostname(self.hostname_dialog.value) if valid:
Resolves: rhbz#1011826 --- pyanaconda/network.py | 42 ++++++++++++++++++++++++++++++------------ pyanaconda/nm.py | 26 ++++++++++++++++++++++++++ 2 files changed, 56 insertions(+), 12 deletions(-)
diff --git a/pyanaconda/network.py b/pyanaconda/network.py index 3661a16..7f12dc5 100644 --- a/pyanaconda/network.py +++ b/pyanaconda/network.py @@ -234,7 +234,7 @@ class IfcfgFile(SimpleConfigFile): # ifcfg-rh is using inotify IN_CLOSE_WRITE event so we don't use # temporary file for new configuration ifcfglog.debug("IfcfgFile.write %s:\n%s" % self.filename, self.__str__()) - SimpleConfigFile.write(self, filename) + SimpleConfigFile.write(self, filename, use_tmp=False) self._dirty = False
def set(self, *args): @@ -439,17 +439,28 @@ def update_settings_with_ksdata(devname, networkdata):
def ksdata_from_ifcfg(devname):
+ if nm.nm_device_is_slave(devname): + return None + ifcfg_path = None - if nm.nm_device_type_is_ethernet(devname): - ifcfg_path = find_ifcfg_file_of_device(devname) - elif nm.nm_device_type_is_wifi(devname): - ssid = nm.nm_device_active_ssid(devname) - if ssid: - ifcfg_path = find_ifcfg_file([("ESSID", ssid)]) - elif nm.nm_device_type_is_bond(devname): - ifcfg_path = find_ifcfg_file([("DEVICE", devname)]) - elif nm.nm_device_type_is_vlan(devname): - ifcfg_path = find_ifcfg_file([("DEVICE", devname)]) + + # Find ifcfg file for the device. + # If the device is active, use uuid of its active connection. + uuid = nm.nm_device_active_con_uuid(devname) + if uuid: + ifcfg_path = find_ifcfg_file([("UUID", uuid)]) + else: + # If not, look it up by other values depending on its type + if nm.nm_device_type_is_ethernet(devname): + ifcfg_path = find_ifcfg_file_of_device(devname) + elif nm.nm_device_type_is_wifi(devname): + ssid = nm.nm_device_active_ssid(devname) + if ssid: + ifcfg_path = find_ifcfg_file([("ESSID", ssid)]) + elif nm.nm_device_type_is_bond(devname): + ifcfg_path = find_ifcfg_file([("DEVICE", devname)]) + elif nm.nm_device_type_is_vlan(devname): + ifcfg_path = find_ifcfg_file([("DEVICE", devname)])
if not ifcfg_path: return None @@ -584,13 +595,20 @@ def hostname_ksdata(hostname):
def find_ifcfg_file_of_device(devname, root_path=""): ifcfg_path = None + try: hwaddr = nm.nm_device_hwaddress(devname) except nm.PropertyNotFoundError: hwaddr = None if hwaddr: hwaddr_check = lambda mac: mac.upper() == hwaddr.upper() - ifcfg_path = find_ifcfg_file([("HWADDR", hwaddr_check)], root_path) + nonempty = lambda x: x + # slave configration created in GUI takes precedence + ifcfg_path = find_ifcfg_file([("HWADDR", hwaddr_check), + ("MASTER", nonempty)], + root_path) + if not ifcfg_path: + ifcfg_path = find_ifcfg_file([("HWADDR", hwaddr_check)], root_path) if not ifcfg_path: ifcfg_path = find_ifcfg_file([("DEVICE", devname)], root_path) return ifcfg_path diff --git a/pyanaconda/nm.py b/pyanaconda/nm.py index cce7aaa..57d4dc4 100644 --- a/pyanaconda/nm.py +++ b/pyanaconda/nm.py @@ -251,6 +251,19 @@ def nm_device_type_is_vlan(name): """ return nm_device_type(name) == NetworkManager.DeviceType.VLAN
+def nm_device_is_slave(name): + """Is the device a slave? + + Exceptions: + UnknownDeviceError if device is not found + """ + active_con = nm_device_property(name, 'ActiveConnection') + if active_con == "/": + return False + + master = _get_property(active_con, "Master", ".Connection.Active") + return master and master != "/" + def nm_device_hwaddress(name): """Return device's 'HwAddress' property
@@ -260,6 +273,19 @@ def nm_device_hwaddress(name): """ return nm_device_property(name, "HwAddress")
+def nm_device_active_con_uuid(name): + """Return uuid of device's active connection + + Exceptions: + UnknownDeviceError if device is not found + """ + active_con = nm_device_property(name, 'ActiveConnection') + if active_con == "/": + return None + + uuid = _get_property(active_con, "Uuid", ".Connection.Active") + return uuid + def nm_device_type(name): """Return device's 'DeviceType' property
Resolves: rhbz#1011841
Port of commit 9026aab182573431b827b781d2f2100d32c15c89 --- pyanaconda/ui/tui/spokes/network.py | 3 +++ 1 file changed, 3 insertions(+)
diff --git a/pyanaconda/ui/tui/spokes/network.py b/pyanaconda/ui/tui/spokes/network.py index 4bd0dac..7f1bf2f 100644 --- a/pyanaconda/ui/tui/spokes/network.py +++ b/pyanaconda/ui/tui/spokes/network.py @@ -53,6 +53,9 @@ class NetworkSpoke(EditTUISpoke): def initialize(self): for name in nm_devices(): if nm_device_type_is_ethernet(name): + # ignore slaves + if nm_device_setting_value(name, "connection", "slave-type"): + continue self.supported_devices.append(name)
EditTUISpoke.initialize(self)
Port of commit 93eac6daacd021cac3635f6e8cc2912083c4f54a
Resolves: rhbz#1011855
Include all devices not only devices configurable in TUI. --- pyanaconda/ui/tui/spokes/network.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/pyanaconda/ui/tui/spokes/network.py b/pyanaconda/ui/tui/spokes/network.py index 7f1bf2f..f717ac5 100644 --- a/pyanaconda/ui/tui/spokes/network.py +++ b/pyanaconda/ui/tui/spokes/network.py @@ -228,7 +228,7 @@ class NetworkSpoke(EditTUISpoke): hostname = self.data.network.hostname
self.data.network.network = [] - for name in self.supported_devices: + for name in nm_devices(): nd = network.ksdata_from_ifcfg(name) if not nd: continue
Port of commit 64833f90823ba868f396c8d5866359a6ddf1122b
Resolves: rhbz#1011860 --- dracut/parse-kickstart | 1 + 1 file changed, 1 insertion(+)
diff --git a/dracut/parse-kickstart b/dracut/parse-kickstart index 8394d37..2192e2c 100755 --- a/dracut/parse-kickstart +++ b/dracut/parse-kickstart @@ -371,6 +371,7 @@ def ksnet_to_ifcfg(net, filename=None): ifcfg.pop('HWADDR') ifcfg['TYPE'] = "Vlan" ifcfg['VLAN'] = "yes" + ifcfg['VLAN_ID'] = net.vlanid ifcfg['NAME'] = "VLAN connection %s" % interface_name ifcfg['DEVICE'] = interface_name ifcfg['PHYSDEV'] = dev
Resolves: rhbz#1011866
Port of commit 447dae925704d43ebb74b090f6eafb22f3836a1f --- pyanaconda/nm.py | 30 +++++++++++++++++++++++------- pyanaconda/ui/tui/spokes/network.py | 7 +++++-- 2 files changed, 28 insertions(+), 9 deletions(-)
diff --git a/pyanaconda/nm.py b/pyanaconda/nm.py index 57d4dc4..b65eec9 100644 --- a/pyanaconda/nm.py +++ b/pyanaconda/nm.py @@ -42,6 +42,11 @@ class UnknownDeviceError(ValueError): def __str__(self): return self.__repr__()
+class UnmanagedDeviceError(Exception): + """Device of specified name is not managed by NM or unavailable""" + def __str__(self): + return self.__repr__() + class PropertyNotFoundError(ValueError): """Property of NM object was not found""" def __str__(self): @@ -195,7 +200,7 @@ def nm_device_property(name, prop): Gio.DBusCallFlags.NONE, DEFAULT_DBUS_TIMEOUT, None) - except Exception as e: + except GLib.GError as e: if "org.freedesktop.NetworkManager.UnknownDevice" in e.message: raise UnknownDeviceError(name, e) raise @@ -543,6 +548,12 @@ def nm_device_setting_value(name, key1, key2): return value
def nm_activate_device_connection(dev_name, con_uuid): + """Activate device with specified connection. + + Exceptions: + UnknownDeviceError - device was not found + + """
proxy = _get_proxy() args = GLib.Variant('(s)', (dev_name,)) @@ -552,7 +563,7 @@ def nm_activate_device_connection(dev_name, con_uuid): Gio.DBusCallFlags.NONE, DEFAULT_DBUS_TIMEOUT, None) - except Exception as e: + except GLib.GError as e: if "org.freedesktop.NetworkManager.UnknownDevice" in e.message: raise UnknownDeviceError(dev_name, e) raise @@ -563,11 +574,16 @@ def nm_activate_device_connection(dev_name, con_uuid):
args = GLib.Variant('(ooo)', (con_path, device_path, "/")) nm_proxy = _get_proxy() - nm_proxy.call_sync("ActivateConnection", - args, - Gio.DBusCallFlags.NONE, - DEFAULT_DBUS_TIMEOUT, - None) + try: + nm_proxy.call_sync("ActivateConnection", + args, + Gio.DBusCallFlags.NONE, + DEFAULT_DBUS_TIMEOUT, + None) + except GLib.GError as e: + if "org.freedesktop.NetworkManager.UnmanagedDevice" in e.message: + raise UnmanagedDeviceError(dev_name, e) + raise
def nm_update_settings_of_device(name, new_values): """Update setting of device. diff --git a/pyanaconda/ui/tui/spokes/network.py b/pyanaconda/ui/tui/spokes/network.py index f717ac5..5589188 100644 --- a/pyanaconda/ui/tui/spokes/network.py +++ b/pyanaconda/ui/tui/spokes/network.py @@ -27,7 +27,7 @@ from pyanaconda.ui.tui.spokes import EditTUISpokeEntry as Entry from pyanaconda.ui.tui.simpleline import TextWidget, ColumnWidget from pyanaconda.i18n import _ from pyanaconda import network -from pyanaconda.nm import nm_activated_devices, nm_state, nm_devices, nm_device_type_is_ethernet, nm_device_ip_config, nm_activate_device_connection, nm_device_setting_value +from pyanaconda.nm import nm_activated_devices, nm_state, nm_devices, nm_device_type_is_ethernet, nm_device_ip_config, nm_activate_device_connection, nm_device_setting_value, UnmanagedDeviceError
# pylint: disable-msg=E0611 from gi.repository import NetworkManager @@ -213,7 +213,10 @@ class NetworkSpoke(EditTUISpoke):
if ndata._apply: uuid = nm_device_setting_value(devname, "connection", "uuid") - nm_activate_device_connection(devname, uuid) + try: + nm_activate_device_connection(devname, uuid) + except UnmanagedDeviceError: + self.errors.append(_("Can't apply configuration, device activation failed."))
self.apply() return True
Resolves: rhbz#1011928
Not only for ethernet devices. Consolidate gui and tui functions.
Port of commit 8bc777b12872ad3e34b287fb3170e8cb00b4a436 --- pyanaconda/network.py | 70 +++++++++++++++++++++++++++++++++++++ pyanaconda/ui/gui/spokes/network.py | 55 +---------------------------- pyanaconda/ui/tui/spokes/network.py | 27 +------------- 3 files changed, 72 insertions(+), 80 deletions(-)
diff --git a/pyanaconda/network.py b/pyanaconda/network.py index 7f12dc5..c0a4d6d 100644 --- a/pyanaconda/network.py +++ b/pyanaconda/network.py @@ -44,6 +44,8 @@ from pyanaconda import nm from pyanaconda.constants import NETWORK_CONNECTION_TIMEOUT from pyanaconda.i18n import _
+from gi.repository import NetworkManager + import logging log = logging.getLogger("anaconda")
@@ -1021,3 +1023,71 @@ def wait_for_connecting_NM_thread(ksdata): hostname = getHostname() update_hostname_data(ksdata, hostname) _get_ntp_servers_from_dhcp(ksdata) + +def status_message(): + """ A short string describing which devices are connected. """ + + msg = _("Unknown") + + state = nm.nm_state() + if state == NetworkManager.State.CONNECTING: + msg = _("Connecting...") + elif state == NetworkManager.State.DISCONNECTING: + msg = _("Disconnecting...") + else: + active_devs = nm.nm_activated_devices() + if active_devs: + + slaves = {} + ssids = {} + nonslaves = [] + + # first find slaves and wireless aps + for devname in active_devs: + master = nm.nm_device_setting_value(devname, "connection", "master") + if master: + if master in slaves: + slaves[master].append(devname) + else: + slaves[master] = [devname] + else: + nonslaves.append(devname) + if nm.nm_device_type_is_wifi(devname): + ssids[devname] = nm.nm_device_active_ssid(devname) or "" + + if len(nonslaves) == 1: + if nm.nm_device_type_is_ethernet(devname): + msg = _("Wired (%(interface_name)s) connected") \ + % {"interface_name": devname} + elif nm.nm_device_type_is_wifi(devname): + msg = _("Wireless connected to %(access_point)s") \ + % {"access_point" : ssids[devname]} + elif nm.nm_device_type_is_bond(devname): + msg = _("Bond %(interface_name)s (%(list_of_slaves)s) connected") \ + % {"interface_name": devname, \ + "list_of_slaves": ",".join(slaves[devname])} + elif nm.nm_device_type_is_vlan(devname): + parent = nm.nm_device_setting_value(devname, "vlan", "parent") + vlanid = nm.nm_device_setting_value(devname, "vlan", "id") + msg = _("Vlan %(interface_name)s (%(parent_device)s, ID %(vlanid)s) connected") \ + % {"interface_name": devname, "parent_device": parent, "vlanid": vlanid} + elif len(nonslaves) > 1: + devlist = [] + for devname in nonslaves: + if nm.nm_device_type_is_ethernet(devname): + devlist.append("%s" % devname) + elif nm.nm_device_type_is_wifi(devname): + devlist.append("%s" % ssids[devname]) + elif nm.nm_device_type_is_bond(devname): + devlist.append("%s (%s)" % (devname, ",".join(slaves[devname]))) + elif nm.nm_device_type_is_vlan(devname): + devlist.append("%s" % devname) + msg = _("Connected: %(list_of_interface_names)s") \ + % {"list_of_interface_names": ", ".join(devlist)} + else: + msg = _("Not connected") + + if not nm.nm_devices(): + msg = _("No network devices available") + + return msg diff --git a/pyanaconda/ui/gui/spokes/network.py b/pyanaconda/ui/gui/spokes/network.py index dc80a9e..9887130 100644 --- a/pyanaconda/ui/gui/spokes/network.py +++ b/pyanaconda/ui/gui/spokes/network.py @@ -1290,60 +1290,7 @@ class NetworkSpoke(FirstbootSpokeMixIn, NormalSpoke): @property def status(self): """ A short string describing which devices are connected. """ - msg = _("Unknown") - - state = self.network_control_box.client.get_state() - if state == NetworkManager.State.CONNECTING: - msg = _("Connecting...") - elif state == NetworkManager.State.DISCONNECTING: - msg = _("Disconnecting...") - else: - ac = self.network_control_box.activated_connections() - if ac: - # Don't show bond slaves - slaves = [] - for name, type, info in ac: - if type == NetworkManager.DeviceType.BOND: - slaves.extend(info) - if slaves: - ac = [(name, type, info) - for name, type, info in ac - if name not in slaves] - - if len(ac) == 1: - name, type, info = ac[0] - if type == NetworkManager.DeviceType.ETHERNET: - msg = _("Wired (%(interface_name)s) connected") \ - % {"interface_name": name} - elif type == NetworkManager.DeviceType.WIFI: - msg = _("Wireless connected to %(access_point)s") \ - % {"access_point" : info} - elif type == NetworkManager.DeviceType.BOND: - msg = _("Bond %(interface_name)s (%(list_of_slaves)s) connected") \ - % {"interface_name": name, "list_of_slaves": ",".join(info)} - if type == NetworkManager.DeviceType.VLAN: - msg = _("Vlan %(interface_name)s (%(parent_device)s, ID %(vlanid)s) connected") \ - % {"interface_name": name, "parent_device": info[0], "vlanid": info[1]} - else: - devlist = [] - for name, type, info in ac: - if type == NetworkManager.DeviceType.ETHERNET: - devlist.append("%s" % name) - elif type == NetworkManager.DeviceType.WIFI: - devlist.append("%s" % info) - elif type == NetworkManager.DeviceType.BOND: - devlist.append("%s (%s)" % (name, ",".join(info))) - if type == NetworkManager.DeviceType.VLAN: - devlist.append("%s" % name) - msg = _("Connected: %(list_of_interface_names)s") \ - % {"list_of_interface_names": ", ".join(devlist)} - else: - msg = _("Not connected") - - if not self.network_control_box.listed_devices: - msg = _("No network devices available") - - return msg + return network.status_message()
def initialize(self): register_secret_agent(self) diff --git a/pyanaconda/ui/tui/spokes/network.py b/pyanaconda/ui/tui/spokes/network.py index 5589188..a599a40 100644 --- a/pyanaconda/ui/tui/spokes/network.py +++ b/pyanaconda/ui/tui/spokes/network.py @@ -70,32 +70,7 @@ class NetworkSpoke(EditTUISpoke): @property def status(self): """ Short msg telling what devices are active. """ - msg = _("Unknown") - - state = nm_state() - if state == NetworkManager.State.CONNECTING: - msg = _("Connecting...") - elif state == NetworkManager.State.DISCONNECTING: - msg = _("Disconnecting...") - else: - activated_devs = nm_activated_devices() - if not activated_devs: - msg = _("Not connected") - elif len(activated_devs) == 1: - if nm_device_type_is_ethernet(activated_devs[0]): - msg = _("Wired %s connected" % activated_devs[0]) - else: - devlist = [] - for dev in activated_devs: - if nm_device_type_is_ethernet(dev): - devlist.append("%s" % dev) - msg = _("Connected: %(list_of_interface_names)s") \ - % {"list_of_interface_names": ", ".join(devlist)} - - if not nm_devices(): - msg = _("No network devices available") - - return msg + return network.status_message()
def _summary_text(self): """Devices cofiguration shown to user."""
Related: rhbz#1011928
Also do not read slaves from settings but use new 'Slaves' property to read current slaves instead.
Port of commit 45db9189550d3a986664dde9c0a99dbecbbc7918 --- pyanaconda/network.py | 14 +++++--------- pyanaconda/nm.py | 23 +++++++++++++++++++++++ 2 files changed, 28 insertions(+), 9 deletions(-)
diff --git a/pyanaconda/network.py b/pyanaconda/network.py index c0a4d6d..e73ea68 100644 --- a/pyanaconda/network.py +++ b/pyanaconda/network.py @@ -35,6 +35,7 @@ import simpleconfig import re import IPy from flags import flags +import itertools
from simpleconfig import SimpleConfigFile from blivet.devices import FcoeDiskDevice, iScsiDiskDevice @@ -1040,21 +1041,16 @@ def status_message():
slaves = {} ssids = {} - nonslaves = []
# first find slaves and wireless aps for devname in active_devs: - master = nm.nm_device_setting_value(devname, "connection", "master") - if master: - if master in slaves: - slaves[master].append(devname) - else: - slaves[master] = [devname] - else: - nonslaves.append(devname) + slaves[devname] = nm.nm_device_slaves(devname) or [] if nm.nm_device_type_is_wifi(devname): ssids[devname] = nm.nm_device_active_ssid(devname) or ""
+ all_slaves = set(itertools.chain.from_iterable(slaves.values())) + nonslaves = [dev for dev in active_devs if dev not in all_slaves] + if len(nonslaves) == 1: if nm.nm_device_type_is_ethernet(devname): msg = _("Wired (%(interface_name)s) connected") \ diff --git a/pyanaconda/nm.py b/pyanaconda/nm.py index b65eec9..d2c6a6f 100644 --- a/pyanaconda/nm.py +++ b/pyanaconda/nm.py @@ -415,6 +415,29 @@ def nm_device_ip_config(name, version=4):
return [addr_list, ns_list]
+def nm_device_slaves(name): + """Return slaves of device. + + :param name: name of device + :type name: str + :return: names of slaves of device or None if device has no 'Slaves' property + :rtype: list of strings or None + :raise UnknownDeviceError: if device is not found + """ + + try: + slaves = nm_device_property(name, "Slaves") + except PropertyNotFoundError: + return None + + slave_ifaces = [] + for slave in slaves: + iface = _get_property(slave, "Interface", ".Device") + slave_ifaces.append(iface) + + return slave_ifaces + + def nm_ntp_servers_from_dhcp(): """Return a list of NTP servers that were specified the reply of the DHCP server or empty list if no NTP servers were returned.
anaconda-patches@lists.fedorahosted.org