[PATCH rhel7-branch] network kickstart: add support for devices configured in %pre (#1019796)
Radek Vykydal
rvykydal at redhat.com
Fri Oct 18 11:45:44 UTC 2013
Resolves: rhbz#1019796
Eg %include configuration creted dynamically in %pre
---
pyanaconda/network.py | 264 +++++++++++++++++++++++++++++++++++++++-----------
pyanaconda/nm.py | 114 +++++++++++++++++++---
2 files changed, 309 insertions(+), 69 deletions(-)
diff --git a/pyanaconda/network.py b/pyanaconda/network.py
index 79f609f..ef50a27 100644
--- a/pyanaconda/network.py
+++ b/pyanaconda/network.py
@@ -34,6 +34,7 @@ import tempfile
import simpleconfig
import re
import IPy
+import uuid
from flags import flags
import itertools
@@ -216,10 +217,11 @@ def _ifcfg_files(directory):
def logIfcfgFiles(message=""):
ifcfglog.debug("content of files (%s):" % message)
for path in _ifcfg_files(netscriptsDir):
+ ifcfglog.debug("%s:" % path)
with open(path, "r") as f:
- content = f.read()
- ifcfglog.debug("%s:\n%s" % (path, content))
-
+ for line in f.readlines():
+ ifcfglog.debug(" %s" % line.strip())
+ ifcfglog.debug("all settings: %s" % nm.nm_get_all_settings())
class IfcfgFile(SimpleConfigFile):
def __init__(self, filename):
@@ -269,10 +271,10 @@ def dumpMissingDefaultIfcfgs():
and dump its ifcfg file. (For server, default auto connections will
be turned off in NetworkManager.conf.)
The connection id (and consequently ifcfg file) is set to device name.
- Returns True if any ifcfg file was dumped.
+ Returns list of devices for which ifcfg file was dumped.
"""
- rv = False
+ rv = []
for devname in nm.nm_devices():
# for each ethernet device
@@ -285,7 +287,7 @@ def dumpMissingDefaultIfcfgs():
con_uuid = nm.nm_device_setting_value(devname, "connection", "uuid")
except nm.SettingsNotFoundError:
continue
- if find_ifcfg_file([("UUID", con_uuid)], root_path=""):
+ if find_ifcfg_file_of_device(devname):
continue
try:
@@ -295,7 +297,7 @@ def dumpMissingDefaultIfcfgs():
log.debug("network: setting autoconnect of %s to False" % devname)
except nm.SettingsNotFoundError:
log.debug("network: no ifcfg file for %s" % devname)
- rv = True
+ rv.append(devname)
return rv
@@ -382,33 +384,34 @@ def dracutBootArguments(devname, ifcfg, storage_ipaddr, hostname=None):
return netargs
-def update_settings_with_ksdata(devname, networkdata):
-
- new_values = []
+def _get_ip_setting_values_from_ksdata(networkdata):
+ values = []
# ipv4 settings
method4 = "auto"
if networkdata.bootProto == "static":
method4 = "manual"
- new_values.append(["ipv4", "method", method4, "s"])
+ values.append(["ipv4", "method", method4, "s"])
if method4 == "manual":
addr4 = nm.nm_ipv4_to_dbus_int(networkdata.ip)
gateway4 = nm.nm_ipv4_to_dbus_int(networkdata.gateway)
prefix4 = netmask2prefix(networkdata.netmask)
- new_values.append(["ipv4", "addresses", [[addr4, prefix4, gateway4]], "aau"])
+ values.append(["ipv4", "addresses", [[addr4, prefix4, gateway4]], "aau"])
# ipv6 settings
if networkdata.noipv6:
method6 = "ignore"
else:
- if networkdata.ipv6 == "auto":
+ if not networkdata.ipv6:
+ method6 = "auto"
+ elif networkdata.ipv6 == "auto":
method6 = "auto"
elif networkdata.ipv6 == "dhcp":
method6 = "dhcp"
else:
method6 = "manual"
- new_values.append(["ipv6", "method", method6, "s"])
+ values.append(["ipv6", "method", method6, "s"])
if method6 == "manual":
addr6, _slash, prefix6 = networkdata.ipv6.partition("/")
@@ -421,7 +424,7 @@ def update_settings_with_ksdata(devname, networkdata):
gateway6 = nm.nm_ipv6_to_dbus_ay(networkdata.ipv6gateway)
else:
gateway6 = [0] * 16
- new_values.append(["ipv6", "addresses", [(addr6, prefix6, gateway6)], "a(ayuay)"])
+ values.append(["ipv6", "addresses", [(addr6, prefix6, gateway6)], "a(ayuay)"])
# nameservers
nss4 = []
@@ -432,13 +435,91 @@ def update_settings_with_ksdata(devname, networkdata):
nss6.append(nm.nm_ipv6_to_dbus_ay(ns))
else:
nss4.append(nm.nm_ipv4_to_dbus_int(ns))
- new_values.append(["ipv4", "dns", nss4, "au"])
- new_values.append(["ipv6", "dns", nss6, "aay"])
+ values.append(["ipv4", "dns", nss4, "au"])
+ values.append(["ipv6", "dns", nss6, "aay"])
- # onboot
- new_values.append(['connection', 'autoconnect', networkdata.onboot, None])
+ return values
+def update_settings_with_ksdata(devname, networkdata):
+ new_values = _get_ip_setting_values_from_ksdata(networkdata)
+ new_values.append(['connection', 'autoconnect', networkdata.onboot, None])
+ uuid = nm.nm_device_setting_value(devname, "connection", "uuid")
nm.nm_update_settings_of_device(devname, new_values)
+ return uuid
+
+def bond_options_ksdata_to_dbus(opts_str):
+ retval = {}
+ for option in opts_str.split(";" if ';' in opts_str else ","):
+ key, _sep, value = option.partition("=")
+ retval[key] = value
+ return retval
+
+def add_connection_for_ksdata(networkdata, devname):
+
+ added_connections = []
+ con_uuid = str(uuid.uuid4())
+ values = _get_ip_setting_values_from_ksdata(networkdata)
+ # HACK preventing NM to autoactivate the connection
+ #values.append(['connection', 'autoconnect', networkdata.onboot, 'b'])
+ values.append(['connection', 'autoconnect', False, 'b'])
+ values.append(['connection', 'uuid', con_uuid, 's'])
+
+ # type "bond"
+ if networkdata.bondslaves:
+ # bond connection is autoactivated
+ values.append(['connection', 'type', 'bond', 's'])
+ values.append(['connection', 'id', devname, 's'])
+ values.append(['bond', 'interface-name', devname, 's'])
+ options = bond_options_ksdata_to_dbus(networkdata.bondopts)
+ values.append(['bond', 'options', options, 'a{ss}'])
+ for i, slave in enumerate(networkdata.bondslaves.split(","), 1):
+
+ #slave_name = "%s slave %d" % (devname, i)
+ slave_name = slave
+
+ svalues = []
+ suuid = str(uuid.uuid4())
+ svalues.append(['connection', 'uuid', suuid, 's'])
+ svalues.append(['connection', 'id', slave_name, 's'])
+ svalues.append(['connection', 'slave-type', 'bond', 's'])
+ svalues.append(['connection', 'master', devname, 's'])
+ svalues.append(['connection', 'type', '802-3-ethernet', 's'])
+ mac = nm.nm_device_hwaddress(slave)
+ mac = [int(b, 16) for b in mac.split(":")]
+ svalues.append(['802-3-ethernet', 'mac-address', mac, 'ay'])
+
+ # disconnect slaves
+ if networkdata.activate:
+ nm.nm_disconnect_device(slave)
+ # remove ifcfg file
+ ifcfg_path = find_ifcfg_file_of_device(slave)
+ if ifcfg_path and os.access(ifcfg_path, os.R_OK):
+ os.unlink(ifcfg_path)
+
+ nm.nm_add_connection(svalues)
+ added_connections.append((suuid, slave))
+ dev_spec = None
+ # type "vlan"
+ elif networkdata.vlanid:
+ parent, _sep, _vlanid = devname.partition(".")
+ values.append(['vlan', 'parent', parent, 's'])
+ values.append(['connection', 'type', 'vlan', 's'])
+ values.append(['connection', 'id', devname, 's'])
+ values.append(['vlan', 'interface-name', devname, 's'])
+ values.append(['vlan', 'id', int(networkdata.vlanid), 'u'])
+ dev_spec = None
+ # type "802-3-ethernet"
+ else:
+ values.append(['connection', 'type', '802-3-ethernet', 's'])
+ values.append(['connection', 'id', devname, 's'])
+ mac = nm.nm_device_hwaddress(devname)
+ mac = [int(b, 16) for b in mac.split(":")]
+ values.append(['802-3-ethernet', 'mac-address', mac, 'ay'])
+ dev_spec = devname
+
+ nm.nm_add_connection(values)
+ added_connections.insert(0, (con_uuid, dev_spec))
+ return added_connections
def ksdata_from_ifcfg(devname):
@@ -454,16 +535,7 @@ def ksdata_from_ifcfg(devname):
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)])
+ ifcfg_path = find_ifcfg_file_of_device(devname)
if not ifcfg_path:
return None
@@ -599,21 +671,34 @@ 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()
- nonempty = lambda x: x
- # slave configration created in GUI takes precedence
- ifcfg_path = find_ifcfg_file([("HWADDR", hwaddr_check),
- ("MASTER", nonempty)],
- root_path)
+ if devname not in nm.nm_devices():
+ return None
+
+ if 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)])
+ elif nm.nm_device_type_is_ethernet(devname):
+ try:
+ hwaddr = nm.nm_device_hwaddress(devname)
+ except nm.PropertyNotFoundError:
+ hwaddr = None
+ if hwaddr:
+ hwaddr_check = lambda mac: mac.upper() == hwaddr.upper()
+ 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([("HWADDR", hwaddr_check)], root_path)
- if not ifcfg_path:
- ifcfg_path = find_ifcfg_file([("DEVICE", devname)], root_path)
+ ifcfg_path = find_ifcfg_file([("DEVICE", devname)], root_path)
+
return ifcfg_path
def find_ifcfg_file(values, root_path=""):
@@ -720,7 +805,7 @@ def copyDhclientConfFiles(destPath):
dhclientfile = os.path.join("/etc/dhcp/dhclient-%s.conf" % devName)
copyFileToPath(dhclientfile, destPath)
-def get_ksdevice_name(ksspec=""):
+def ks_spec_to_device_name(ksspec=""):
if not ksspec:
ksspec = flags.cmdline.get('ksdevice', "")
@@ -738,7 +823,7 @@ def get_ksdevice_name(ksspec=""):
try:
link_up = nm.nm_device_carrier(dev)
except ValueError as e:
- log.debug("get_ksdevice_name: %s" % e)
+ log.debug("ks_spec_to_device_name: %s" % e)
continue
if link_up:
ksdevice = dev
@@ -748,7 +833,7 @@ def get_ksdevice_name(ksspec=""):
try:
hwaddr = nm.nm_device_hwaddress(dev)
except ValueError as e:
- log.debug("get_ksdevice_name: %s" % e)
+ log.debug("ks_spec_to_device_name: %s" % e)
continue
if ksdevice.lower() == hwaddr.lower():
ksdevice = dev
@@ -758,7 +843,7 @@ def get_ksdevice_name(ksspec=""):
try:
hwaddr = nm.nm_device_hwaddress(dev)
except ValueError as e:
- log.debug("get_ksdevice_name: %s" % e)
+ log.debug("ks_spec_to_device_name: %s" % e)
continue
if bootif_mac.lower() == hwaddr.lower():
ksdevice = dev
@@ -930,7 +1015,7 @@ def get_device_name(devspec):
if not devspec:
if "ksdevice" in flags.cmdline:
msg = "ksdevice boot parameter"
- devname = get_ksdevice_name(flags.cmdline["ksdevice"])
+ devname = ks_spec_to_device_name(flags.cmdline["ksdevice"])
elif nm.nm_is_connected():
# device activated in stage 1 by network kickstart command
msg = "first active device"
@@ -983,18 +1068,71 @@ def get_device_name(devspec):
return devname
def setOnboot(ksdata):
+ updated_devices = []
for network_data in ksdata.network.network:
- devname = get_device_name(network_data.device)
- if not devname:
- log.error("Kickstart: The provided network interface %s does not exist" % network_data.device)
- continue
+ # vlan
+ if network_data.vlanid:
+ physdev = get_device_name(network_data.device) or network_data.device
+ devname = "%s.%s" % (physdev, network_data.vlanid)
+ # bond
+ elif network_data.bondslaves:
+ devname = network_data.device
+ # ethernet
+ else:
+ devname = get_device_name(network_data.device)
+ if not devname:
+ log.warning("Kickstart: The provided network interface %s does not exist" % network_data.device)
+ continue
+ updated_devices.append(devname)
try:
nm.nm_update_settings_of_device(devname, [['connection', 'autoconnect', network_data.onboot, None]])
- ifcfglog.debug("setting autoconnect (ONBOOT) of %s to %s" % (devname, network_data.onboot))
- except nm.SettingsNotFoundError as e:
+ except (nm.SettingsNotFoundError, nm.UnknownDeviceError) as e:
log.debug("setOnboot: %s" % e)
+ return updated_devices
+
+def apply_kickstart_from_pre_section(ksdata):
+ applied_devices = []
+
+ for network_data in ksdata.network.network:
+
+ # TODO: wireless not supported yet
+ if network_data.essid:
+ continue
+
+ # get device name for device specified by MAC, link, bootif, etc...
+ dev_name = ks_spec_to_device_name(network_data.device)
+ if not dev_name:
+ log.error("Kickstart: The provided network interface %s does not exist" % network_data.device)
+ continue
+
+ if network_data.vlanid:
+ dev_name = "%s.%s" % (dev_name, network_data.vlanid)
+
+ ifcfg_path = find_ifcfg_file_of_device(dev_name)
+ # if the device was already configured in intramfs by kickstart ignore it
+ if ifcfg_path:
+ with open(ifcfg_path, 'r') as f:
+ if "parse-kickstart" in f.read():
+ continue
+
+ applied_devices.append(dev_name)
+ if ifcfg_path:
+ # if the device was already configured in initramfs update the settings
+ log.debug("network: pre kickstart - updating settings of device %s" % dev_name)
+ con_uuid = update_settings_with_ksdata(dev_name, network_data)
+ added_connections = [(con_uuid, dev_name)]
+ else:
+ log.debug("network: pre kickstart - adding connection for %s" % dev_name)
+ # Virtual devices (eg vlan, bond) return dev_name == None
+ added_connections = add_connection_for_ksdata(network_data, dev_name)
+
+ if network_data.activate:
+ for con_uuid, dev_name in added_connections:
+ nm.nm_activate_device_connection(dev_name, con_uuid)
+
+ return applied_devices
def networkInitialize(ksdata):
@@ -1002,13 +1140,25 @@ def networkInitialize(ksdata):
logIfcfgFiles("network initialization")
if not flags.imageInstall:
- if dumpMissingDefaultIfcfgs():
- logIfcfgFiles("ifcfgs created")
+ devnames = apply_kickstart_from_pre_section(ksdata)
+ if devnames:
+ msg = "kickstart pre section applied for devices %s" % devnames
+ log.debug("network: %s" % msg)
+ logIfcfgFiles(msg)
+ devnames = dumpMissingDefaultIfcfgs()
+ if devnames:
+ msg = "missing ifcfgs created for devices %s" % devnames
+ log.debug("network: %s" % msg)
+ logIfcfgFiles(msg)
# For kickstart network --activate option we set ONBOOT=yes
# in dracut to get devices activated by NM. The real network --onboot
# value is set here.
- setOnboot(ksdata)
+ devnames = setOnboot(ksdata)
+ if devnames:
+ msg = "setting real kickstart ONBOOT value for devices %s" % devnames
+ log.debug("network: %s" % msg)
+ logIfcfgFiles(msg)
if ksdata.network.hostname is None:
hostname = getHostname()
diff --git a/pyanaconda/nm.py b/pyanaconda/nm.py
index b08d4ca..9325339 100644
--- a/pyanaconda/nm.py
+++ b/pyanaconda/nm.py
@@ -357,8 +357,6 @@ def nm_device_ip_config(name, version=4):
UnknownDeviceError if device is not found
PropertyNotFoundError if ip configuration is not found
"""
- retval = []
-
state = nm_device_property(name, "State")
if state != NetworkManager.DeviceState.ACTIVATED:
return []
@@ -535,6 +533,31 @@ def _find_settings(value, key1, key2, format_value=lambda x:x):
return retval
+def nm_get_all_settings():
+ """Return all settings for logging."""
+ retval = []
+
+ proxy = _get_proxy(object_path="/org/freedesktop/NetworkManager/Settings", interface_name="org.freedesktop.NetworkManager.Settings")
+
+ args = None
+ connections = proxy.call_sync("ListConnections",
+ args,
+ Gio.DBusCallFlags.NONE,
+ DEFAULT_DBUS_TIMEOUT,
+ None)
+
+ for con in connections.unpack()[0]:
+ proxy = _get_proxy(object_path=con, interface_name="org.freedesktop.NetworkManager.Settings.Connection")
+ args = None
+ settings = proxy.call_sync("GetSettings",
+ args,
+ Gio.DBusCallFlags.NONE,
+ DEFAULT_DBUS_TIMEOUT,
+ None)
+ retval.append(settings)
+
+ return retval
+
def nm_device_setting_value(name, key1, key2):
"""Return value of device's setting specified by key1 and key2.
@@ -599,17 +622,13 @@ def nm_ap_setting_value(ssid, key1, key2):
value = None
return value
+def nm_disconnect_device(name):
+ """Disconnect the device.
-def nm_activate_device_connection(dev_name, con_uuid):
- """Activate device with specified connection.
-
- Exceptions:
- UnknownDeviceError - device was not found
-
+ :raise UnknownDeviceError: if device is not found
"""
-
proxy = _get_proxy()
- args = GLib.Variant('(s)', (dev_name,))
+ args = GLib.Variant('(s)', (name,))
try:
device = proxy.call_sync("GetDeviceByIpIface",
args,
@@ -618,10 +637,48 @@ def nm_activate_device_connection(dev_name, con_uuid):
None)
except GLib.GError as e:
if "org.freedesktop.NetworkManager.UnknownDevice" in e.message:
- raise UnknownDeviceError(dev_name, e)
+ raise UnknownDeviceError(name, e)
raise
- device_path = device.unpack()[0]
+ device = device.unpack()[0]
+ device_proxy = _get_proxy(object_path=device, interface_name="org.freedesktop.NetworkManager.Device")
+ args = None
+ device_proxy.call_sync("Disconnect",
+ args,
+ Gio.DBusCallFlags.NONE,
+ DEFAULT_DBUS_TIMEOUT,
+ None)
+
+def nm_activate_device_connection(dev_name, con_uuid):
+ """Activate device with specified connection.
+
+ :param dev_name: name of device or None for virtual devices
+ :type dev_name: str or None
+ :param con_uuid: uuid of connection to be activated on device
+ :type con_uuid: str
+ :raise UnknownDeviceError: if device is not found
+ :raise UnmanagedDeviceError: if device is not managed by NM
+ or unavailable
+ """
+
+ if dev_name is None:
+ # virtual devices (eg bond, vlan)
+ device_path = "/"
+ else:
+ proxy = _get_proxy()
+ args = GLib.Variant('(s)', (dev_name,))
+ try:
+ device = proxy.call_sync("GetDeviceByIpIface",
+ args,
+ Gio.DBusCallFlags.NONE,
+ DEFAULT_DBUS_TIMEOUT,
+ None)
+ except Exception as e:
+ if "org.freedesktop.NetworkManager.UnknownDevice" in e.message:
+ raise UnknownDeviceError(dev_name, e)
+ raise
+
+ device_path = device.unpack()[0]
con_path = _find_settings(con_uuid, 'connection', 'uuid')
@@ -638,6 +695,39 @@ def nm_activate_device_connection(dev_name, con_uuid):
raise UnmanagedDeviceError(dev_name, e)
raise
+def nm_add_connection(values):
+ """Add new connection specified by values.
+
+ :param values: list of settings with new values and its types
+ [[key1, key2, value, type_str], ...]
+ key1: first-level key of setting (eg "connection")
+ key2: second-level key of setting (eg "uuid")
+ value: new value
+ type_str: dbus type of new value (eg "ay")
+ :type values: [[key1, key2, value, type_str], ...]
+ key1: str
+ key2: str
+ value: object
+ type_str: str
+ """
+
+ settings = {}
+ for key1, key2, value, type_str in values:
+ gvalue = GLib.Variant(type_str, value)
+ if key1 not in settings:
+ settings[key1] = {}
+ settings[key1][key2] = gvalue
+ gsettings = GLib.Variant("(a{sa{sv}})", (settings,))
+
+ proxy = _get_proxy(object_path="/org/freedesktop/NetworkManager/Settings",
+ interface_name="org.freedesktop.NetworkManager.Settings")
+ connection = proxy.call_sync("AddConnection",
+ gsettings,
+ Gio.DBusCallFlags.NONE,
+ DEFAULT_DBUS_TIMEOUT,
+ None)
+ return connection.unpack()
+
def nm_update_settings_of_device(name, new_values):
"""Update setting of device.
--
1.7.11.7
More information about the anaconda-patches
mailing list