#
# network.py - network configuration install data
#
# Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006, 2007 Red Hat, Inc.
# 2008, 2009
#
# 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; either version 2 of the License, or
# (at your option) any later version.
#
# 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 .
#
# Author(s): Matt Wilson
# Erik Troan
# Mike Fulbright
# Brent Fox
# David Cantrell
#
import string
import shutil
import isys
import iutil
import socket
import struct
import os
import time
import dbus
from flags import flags
from simpleconfig import SimpleConfigFile
import gettext
_ = lambda x: gettext.ldgettext("anaconda", x)
import logging
log = logging.getLogger("anaconda")
class IPError(Exception):
pass
class IPMissing(Exception):
pass
def sanityCheckHostname(hostname):
if len(hostname) < 1:
return None
if len(hostname) > 255:
return _("Hostname must be 255 or fewer characters in length.")
validStart = string.ascii_letters + string.digits
validAll = validStart + ".-"
if string.find(validStart, hostname[0]) == -1:
return _("Hostname must start with a valid character in the ranges "
"'a-z', 'A-Z', or '0-9'")
for i in range(1, len(hostname)):
if string.find(validAll, hostname[i]) == -1:
return _("Hostnames can only contain the characters 'a-z', 'A-Z', '0-9', '-', or '.'")
return None
# Try to determine what the hostname should be for this system
def getDefaultHostname(anaconda):
isys.resetResolv()
hn = None
bus = dbus.SystemBus()
nm = bus.get_object(isys.NM_SERVICE, isys.NM_MANAGER_PATH)
nm_props_iface = dbus.Interface(nm, isys.DBUS_PROPS_IFACE)
active_connections = nm_props_iface.Get(isys.NM_MANAGER_IFACE, "ActiveConnections")
# XXX: account for Ip6Config objects when NetworkManager supports them
for connection in active_connections:
active_connection = bus.get_object(isys.NM_SERVICE, connection)
active_connection_props_iface = dbus.Interface(active_connection, isys.DBUS_PROPS_IFACE)
devices = active_connection_props_iface.Get(isys.NM_ACTIVE_CONNECTION_IFACE, 'Devices')
for device_path in devices:
device = bus.get_object(isys.NM_SERVICE, device_path)
device_props_iface = dbus.Interface(device, isys.DBUS_PROPS_IFACE)
ip4_config_path = device_props_iface.Get(isys.NM_DEVICE_IFACE, 'Ip4Config')
ip4_config_obj = bus.get_object(isys.NM_SERVICE, ip4_config_path)
ip4_config_props = dbus.Interface(ip4_config_obj, isys.DBUS_PROPS_IFACE)
# addresses (3-element list: ipaddr, netmask, gateway)
try:
addrs = ip4_config_props.Get(isys.NM_IP4CONFIG_IFACE, "Addresses")[0]
except:
# DBusException
continue
try:
tmp = struct.pack('I', addrs[0])
ipaddr = socket.inet_ntop(socket.AF_INET, tmp)
hinfo = socket.gethostbyaddr(ipaddr)
if len(hinfo) == 3:
hn = hinfo[0]
else:
continue
except:
continue
if hn and hn != 'localhost' and hn != 'localhost.localdomain':
return hn
try:
hn = anaconda.network.hostname
except:
hn = None
if not hn or hn == '(none)' or hn == 'localhost' or hn == 'localhost.localdomain':
hn = socket.gethostname()
if not hn or hn == '(none)' or hn == 'localhost':
hn = 'localhost.localdomain'
return hn
# return if the device is of a type that requires a ptpaddr to be specified
def isPtpDev(devname):
if devname.startswith("ctc"):
return True
return False
def _anyUsing(method):
# method names that NetworkManager might use
if method == 'auto':
methods = (method, 'dhcp')
else:
methods = (method)
try:
bus = dbus.SystemBus()
nm = bus.get_object(isys.NM_SERVICE, isys.NM_MANAGER_PATH)
nm_props_iface = dbus.Interface(nm, isys.DBUS_PROPS_IFACE)
active_connections = nm_props_iface.Get(isys.NM_MANAGER_IFACE, "ActiveConnections")
for path in active_connections:
active = bus.get_object(isys.NM_SERVICE, path)
active_props_iface = dbus.Interface(active, isys.DBUS_PROPS_IFACE)
active_service_name = active_props_iface.Get(isys.NM_ACTIVE_CONNECTION_IFACE, "ServiceName")
active_path = active_props_iface.Get(isys.NM_ACTIVE_CONNECTION_IFACE, "Connection")
connection = bus.get_object(active_service_name, active_path)
connection_iface = dbus.Interface(connection, isys.NM_CONNECTION_IFACE)
settings = connection_iface.GetSettings()
# XXX: add support for Ip6Config when it appears
ip4_setting = settings['ipv4']
if not ip4_setting or not ip4_setting['method'] or ip4_setting['method'] in methods:
return True
return False
except:
return False
# determine whether any active at boot devices are using dhcp or dhcpv6
def anyUsingDHCP():
return _anyUsing('auto')
# determine whether any active at boot devices are using static IP config
def anyUsingStatic():
return _anyUsing('manual')
# sanity check an IP string.
def sanityCheckIPString(ip_string):
if ip_string.strip() == "":
raise IPMissing, _("IP address is missing.")
if ip_string.find(':') == -1 and ip_string.find('.') > 0:
family = socket.AF_INET
errstr = _("IPv4 addresses must contain four numbers between 0 and 255, separated by periods.")
elif ip_string.find(':') > 0 and ip_string.find('.') == -1:
family = socket.AF_INET6
errstr = _("'%s' is not a valid IPv6 address.") % ip_string
else:
raise IPError, _("'%s' is an invalid IP address.") % ip_string
try:
socket.inet_pton(family, ip_string)
except socket.error:
raise IPError, errstr
def hasActiveNetDev():
try:
bus = dbus.SystemBus()
nm = bus.get_object(isys.NM_SERVICE, isys.NM_MANAGER_PATH)
props = dbus.Interface(nm, isys.DBUS_PROPS_IFACE)
state = props.Get(isys.NM_SERVICE, "State")
if int(state) == isys.NM_STATE_CONNECTED:
return True
else:
return False
except:
return False
# Return a list of device names (e.g., eth0) for all active devices.
# Returning a list here even though we will almost always have one
# device. NM uses lists throughout its D-Bus communication, so trying
# to follow suit here. Also, if this uses a list now, we can think
# about multihomed hosts during installation later.
def getActiveNetDevs():
active_devs = set()
bus = dbus.SystemBus()
nm = bus.get_object(isys.NM_SERVICE, isys.NM_MANAGER_PATH)
nm_props_iface = dbus.Interface(nm, isys.DBUS_PROPS_IFACE)
active_connections = nm_props_iface.Get(isys.NM_MANAGER_IFACE, "ActiveConnections")
for connection in active_connections:
active_connection = bus.get_object(isys.NM_SERVICE, connection)
active_connection_props_iface = dbus.Interface(active_connection, isys.DBUS_PROPS_IFACE)
devices = active_connection_props_iface.Get(isys.NM_ACTIVE_CONNECTION_IFACE, 'Devices')
for device_path in devices:
device = bus.get_object(isys.NM_SERVICE, device_path)
device_props_iface = dbus.Interface(device, isys.DBUS_PROPS_IFACE)
interface_name = device_props_iface.Get(isys.NM_DEVICE_IFACE, 'Interface')
active_devs.add(interface_name)
ret = list(active_devs)
ret.sort()
return ret
class NetworkDevice(SimpleConfigFile):
def __str__(self):
s = ""
s = s + "DEVICE=" + self.info["DEVICE"] + "\n"
keys = self.info.keys()
keys.sort()
keys.remove("DEVICE")
if "DESC" in keys:
keys.remove("DESC")
if "KEY" in keys:
keys.remove("KEY")
if iutil.isS390() and ("OPTIONS" in keys) and ("HWADDR" in keys) and \
(self.info["OPTIONS"].find("layer2=1") != -1):
keys.remove("HWADDR")
for key in keys:
if (key == 'NAME') or \
(key == 'NM_CONTROLLED' and not flags.livecdInstall):
continue
# make sure we include autoneg in the ethtool line
elif key == 'ETHTOOL_OPTS' and self.info[key].find("autoneg")== -1:
s = s + key + """="autoneg off %s"\n""" % (self.info[key])
elif self.info[key] is not None:
s = s + key + "=" + self.info[key] + "\n"
return s
def __init__(self, dev):
self.info = { "DEVICE" : dev }
if dev.startswith('ctc'):
self.info["TYPE"] = "CTC"
class Network:
def __init__(self):
self.netdevices = {}
self.ksdevice = None
self.domains = []
self.hostname = socket.gethostname()
self.overrideDHCPhostname = False
# populate self.netdevices
devhash = isys.getDeviceProperties(dev=None)
for dev in devhash.keys():
self.netdevices[dev] = NetworkDevice(dev)
ifcfg_contents = self.readIfcfgContents(dev)
# if NM_CONTROLLED is set to yes, we read in settings from
# NetworkManager first, then fill in the gaps with the data
# from the ifcfg file
useNetworkManager = False
if ifcfg_contents.has_key('NM_CONTROLLED') and \
not ifcfg_contents['NM_CONTROLLED'].lower() == 'no':
useNetworkManager = True
# this interface is managed by NetworkManager, so read from
# NetworkManager first
if useNetworkManager:
props = devhash[dev]
if isys.isDeviceDHCP(dev):
self.netdevices[dev].set(('BOOTPROTO', 'dhcp'))
else:
self.netdevices[dev].unset('BOOTPROTO')
bus = dbus.SystemBus()
config_path = props.Get(isys.NM_DEVICE_IFACE, 'Ip4Config')
config = bus.get_object(isys.NM_SERVICE, config_path)
config_props = dbus.Interface(config, isys.DBUS_PROPS_IFACE)
# addresses (3-element list: ipaddr, netmask, gateway)
addrs = config_props.Get(isys.NM_IP4CONFIG_IFACE, 'Addresses')[0]
try:
tmp = struct.pack('I', addrs[0])
ipaddr = socket.inet_ntop(socket.AF_INET, tmp)
self.netdevices[dev].set(('IPADDR', ipaddr))
except:
pass
try:
tmp = struct.pack('I', addrs[1])
netmask = socket.inet_ntop(socket.AF_INET, tmp)
self.netdevices[dev].set(('NETMASK', netmask))
except:
pass
try:
tmp = struct.pack('I', addrs[2])
gateway = socket.inet_ntop(socket.AF_INET, tmp)
self.netdevices[dev].set(('GATEWAY', gateway))
except:
pass
self.hostname = socket.gethostname()
# read in remaining settings from ifcfg file
for key in ifcfg_contents.keys():
if key == 'GATEWAY':
self.netdevices[dev].set((key, ifcfg_contents[key]))
elif key == 'DOMAIN':
self.domains.append(ifcfg_contents[key])
elif key == 'HOSTNAME':
self.hostname = ifcfg_contents[key]
elif self.netdevices[dev].get(key) == '':
self.netdevices[dev].set((key, ifcfg_contents[key]))
# now initialize remaining devices
# XXX we just throw return away, the method initialize a
# object member so we dont need to
available_devices = self.available()
if len(available_devices) > 0:
# set first device to start up onboot
oneactive = 0
for dev in available_devices.keys():
try:
if available_devices[dev].get("ONBOOT") == "yes":
oneactive = 1
break
except:
continue
def readIfcfgContents(self, dev):
ifcfg = "/etc/sysconfig/network-scripts/ifcfg-%s" % (dev,)
contents = {}
try:
f = open(ifcfg, "r")
lines = f.readlines()
f.close()
for line in lines:
line = line.strip()
if line.startswith('#') or line == '':
continue
var = string.splitfields(line, '=', 1)
if len(var) == 2:
var[1] = var[1].replace('"', '')
contents[var[0]] = string.strip(var[1])
except:
return {}
return contents
def getDevice(self, device):
return self.netdevices[device]
def available(self):
ksdevice = None
if flags.cmdline.has_key('ksdevice'):
ksdevice = flags.cmdline['ksdevice']
for dev in isys.getDeviceProperties().keys():
if not self.netdevices.has_key(dev):
self.netdevices[dev] = NetworkDevice(dev)
hwaddr = isys.getMacAddress(dev)
if hwaddr is None:
# not a valid device
log.warning("invalid hwaddr for: %s" % (dev,))
continue
self.netdevices[dev].set(('HWADDR', hwaddr))
self.netdevices[dev].set(('DESC', isys.getNetDevDesc(dev)))
if not ksdevice:
continue
if ksdevice == 'link' and isys.getLinkStatus(dev):
self.ksdevice = dev
elif ksdevice == dev:
self.ksdevice = dev
elif ksdevice.find(':') != -1:
if ksdevice.upper() == hwaddr:
self.ksdevice = dev
return self.netdevices
def getKSDevice(self):
if self.ksdevice is None:
return None
try:
return self.netdevices[self.ksdevice]
except:
return None
def setHostname(self, hn):
self.hostname = hn
def setDNS(self, ns, device):
dns = ns.split(',')
i = 1
for addr in dns:
addr = addr.strip()
dnslabel = "DNS%d" % (i,)
self.netdevices[device].set((dnslabel, addr))
i += 1
def setGateway(self, gw, device):
self.netdevices[device].set(('GATEWAY', gw))
def lookupHostname(self):
# can't look things up if they don't exist!
if not self.hostname or self.hostname == "localhost.localdomain":
return None
if not hasActiveNetDev():
log.warning("no network devices were available to look up host name")
return None
try:
(family, socktype, proto, canonname, sockaddr) = \
socket.getaddrinfo(self.hostname, None, socket.AF_INET)[0]
(ip, port) = sockaddr
except:
try:
(family, socktype, proto, canonname, sockaddr) = \
socket.getaddrinfo(self.hostname, None, socket.AF_INET6)[0]
(ip, port, flowinfo, scopeid) = sockaddr
except:
return None
return ip
def writeKS(self, f):
devNames = self.netdevices.keys()
devNames.sort()
if len(devNames) == 0:
return
for devName in devNames:
dev = self.netdevices[devName]
if dev.get('bootproto').lower() == 'dhcp' or dev.get('ipaddr'):
f.write("network --device %s" % dev.get('device'))
if dev.get('MTU') and dev.get('MTU') != 0:
f.write(" --mtu=%s" % dev.get('MTU'))
onboot = dev.get("onboot")
if onboot and onboot == "no":
f.write(" --onboot no")
if dev.get('bootproto').lower() == 'dhcp':
f.write(" --bootproto dhcp")
if dev.get('dhcpclass'):
f.write(" --dhcpclass %s" % dev.get('dhcpclass'))
if self.overrideDHCPhostname:
if (self.hostname and
self.hostname != "localhost.localdomain"):
f.write(" --hostname %s" % self.hostname)
else:
f.write(" --bootproto static --ip %s" % dev.get('ipaddr'))
if dev.get('netmask'):
f.write(" --netmask %s" % dev.get('netmask'))
if dev.get('GATEWAY'):
f.write(" --gateway %s" % (dev.get('GATEWAY'),))
dnsline = ''
for key in dev.info.keys():
if key.upper().startswith('DNS'):
if dnsline == '':
dnsline = dev.get(key)
else:
dnsline += "," + dev.get(key)
if dnsline != '':
f.write(" --nameserver %s" % (dnsline,))
if (self.hostname and
self.hostname != "localhost.localdomain"):
f.write(" --hostname %s" % self.hostname)
f.write("\n")
def hasNameServers(self, hash):
if hash.keys() == []:
return False
for key in hash.keys():
if key.upper().startswith('DNS'):
return True
return False
def write(self, instPath='', anaconda=None, devices=None):
# If the hostname was not looked up, but typed in by the user,
# domain might not be computed, so do it now.
domainname = None
if "." in self.hostname:
fqdn = self.hostname
else:
fqdn = socket.getfqdn(self.hostname)
if fqdn in [ "localhost.localdomain", "localhost",
"localhost6.localdomain6", "localhost6",
self.hostname ] or "." not in fqdn:
fqdn = None
if fqdn:
domainname = fqdn.split('.', 1)[1]
if domainname in [ "localdomain", "localdomain6" ]:
domainname = None
else:
domainname = None
if self.domains == ["localdomain"] or not self.domains:
if domainname:
self.domains = [domainname]
# /etc/udev/rules.d/70-persistent-net.rules
rules = "/etc/udev/rules.d/70-persistent-net.rules"
destRules = instPath + rules
if (not instPath) or (not os.path.isfile(destRules)) or \
flags.livecdInstall:
if not os.path.isdir("%s/etc/udev/rules.d" %(instPath,)):
iutil.mkdirChain("%s/etc/udev/rules.d" %(instPath,))
if os.path.isfile(rules) and rules != destRules:
shutil.copy(rules, destRules)
else:
f = open(destRules, "w")
f.write("""
# This file was automatically generated by the /lib/udev/write_net_rules
# program run by the persistent-net-generator.rules rules file.
#
# You can modify it, as long as you keep each rule on a single line.
""")
for dev in self.netdevices.values():
addr = dev.get("HWADDR")
if not addr:
continue
devname = dev.get("DEVICE")
basename = devname
while basename != "" and basename[-1] in string.digits:
basename = basename[:-1]
# rules are case senstive for address. Lame.
addr = addr.lower()
s = ""
if len(dev.get("DESC")) > 0:
s = "# %s (rule written by anaconda)\n" % (dev.get("DESC"),)
else:
s = "# %s (rule written by anaconda)\n" % (devname,)
s = s + 'SUBSYSTEM==\"net\", ACTION==\"add\", DRIVERS=="?*", ATTR{address}=="%s", ATTR{type}=="1", KERNEL=="%s*", NAME="%s"\n' % (addr, basename, devname,)
f.write(s)
f.close()
net_conf = instPath+"/etc/conf.d/net"
net_conf_dir = os.path.dirname(net_conf)
if os.path.isfile(net_conf):
f = open(net_conf, "aw")
else:
if not os.path.isdir(net_conf_dir):
os.makedirs(net_conf_dir)
f = open (net_conf, "w")
for dev in self.netdevices.values():
device = dev.get("DEVICE")
net_conf = []
ipaddr = dev.get('IPADDR')
netmask = dev.get('NETMASK')
gateway = dev.get('GATEWAY')
is_dhcp_boot = dev.get('BOOTPROTO') == "dhcp"
if is_dhcp_boot or (not ipaddr):
net_conf.append('dhcp_%s="nosendhost"\n' % (device,))
else:
net_conf.append('config_%s="%s netmask %s"\n' % (
device,
ipaddr,
netmask,
)
)
if gateway:
net_conf.append('routes_%s="default via %s"\n' % (
device,
gateway,
)
)
# add new dns_domain_device nis_domain_device management
net_conf.append('dns_domain_%s="localdomain"\n' % (
device,
)
)
net_conf.append('nis_domain_%s="localdomain"\n' % (
device,
)
)
for line in net_conf:
f.write(line)
f.flush()
f.flush()
f.close()
# hostname
f = open(instPath+"/etc/conf.d/hostname","w")
if not self.hostname:
self.hostname = "gentoo"
f.write("hostname=\""+self.hostname + "\"\n")
f.flush()
f.close()
# samba
smb_cfg = instPath+"/etc/samba/smb.conf"
if os.path.isfile(smb_cfg):
g = open(smb_cfg, "r")
smb_conf = g.readlines()
g = open(smb_cfg, "w")
for line in smb_conf:
if (line.find("netbios name = ") != -1) and (not line.strip().startswith("#")):
line = " netbios name = %s\n" % (self.hostname,)
g.write(line)
g.flush()
g.close()
# /etc/hosts
host_file = instPath + "/etc/hosts"
host_data = []
if os.path.isfile(host_file) and os.access(host_file,os.R_OK):
f = open(host_file, "r")
host_data = [x.strip() for x in f.readlines()]
f.close()
f = open(host_file, "w")
found = False
for line in host_data:
if line.startswith("127.0.0.1"):
if self.hostname not in line.split():
line += " %s" % (self.hostname,)
found = True
f.write(line+"\n")
if not found:
f.write("127.0.0.1\t\t%s\n" % (self.hostname,))
f.flush()
f.close()
log.info("hostname set to = %s" % (self.hostname,))
domain_conf = instPath+"/etc/conf.d/domainname"
if os.path.isfile(domain_conf):
f = open(domain_conf,"r")
domainname_cont = f.readlines()
f.close()
f = open(domain_conf, "w")
for line in domainname_cont:
if line.startswith("DNSDOMAIN="):
line = 'DNSDOMAIN="localdomain"\n'
elif line.startswith("NISDOMAIN="):
line = 'NISDOMAIN="localdomain"\n'
f.write(line)
f.flush()
f.close()
# dhclient.conf -> force NetworkManager to not change hostname
dh_conf = instPath + "/etc/dhcp/dhclient.conf"
if os.path.isfile(dh_conf):
f = open(dh_conf, "w")
f.write('send host-name "'+self.hostname+'";\n')
f.write('supersede host-name "'+self.hostname+'";\n')
f.flush()
f.close()
# write out current configuration state and wait for NetworkManager
# to bring the device up, watch NM state and return to the caller
# once we have a state
def bringUp(self, devices=None):
self.write(devices=devices)
bus = dbus.SystemBus()
nm = bus.get_object(isys.NM_SERVICE, isys.NM_MANAGER_PATH)
props = dbus.Interface(nm, isys.DBUS_PROPS_IFACE)
i = 0
while i < 45:
state = props.Get(isys.NM_SERVICE, "State")
if int(state) == isys.NM_STATE_CONNECTED:
isys.resetResolv()
return True
i += 1
time.sleep(1)
state = props.Get(isys.NM_SERVICE, "State")
if int(state) == isys.NM_STATE_CONNECTED:
isys.resetResolv()
return True
return False
# get a kernel cmdline string for dracut needed for access to host host
def dracutSetupString(self, networkStorageDevice):
netargs=""
if networkStorageDevice.nic:
# Storage bound to a specific nic (ie FCoE)
nic = networkStorageDevice.nic
else:
# Storage bound through ip, find out which interface leads to host
host = networkStorageDevice.host_address
route = iutil.execWithCapture("ip", [ "route", "get", "to", host ])
if not route:
log.error("Could net get interface for route to %s" % host)
return ""
routeInfo = route.split()
if routeInfo[0] != host or len(routeInfo) < 5 or \
"dev" not in routeInfo or routeInfo.index("dev") > 3:
log.error('Unexpected "ip route get to %s" reply: %s' %
(host, routeInfo))
return ""
nic = routeInfo[routeInfo.index("dev") + 1]
if nic not in self.netdevices.keys():
log.error('Unknown network interface: %s' % nic)
return ""
dev = self.netdevices[nic]
if networkStorageDevice.host_address:
if dev.get('bootproto').lower() == 'dhcp':
netargs += "ip=%s:dhcp" % nic
else:
if dev.get('GATEWAY'):
gateway = dev.get('GATEWAY')
else:
gateway = ""
if self.hostname:
hostname = self.hostname
else:
hostname = ""
netargs += "ip=%s::%s:%s:%s:%s:none" % (dev.get('ipaddr'),
gateway, dev.get('netmask'), hostname, nic)
hwaddr = dev.get("HWADDR")
if hwaddr:
if netargs != "":
netargs += " "
netargs += "ifname=%s:%s" % (nic, hwaddr.lower())
nettype = dev.get("NETTYPE")
subchannels = dev.get("SUBCHANNELS")
if iutil.isS390() and nettype and subchannels:
if netargs != "":
netargs += " "
netargs += "rd_CCW=%s,%s" % (nettype, subchannels)
options = dev.get("OPTIONS")
if options:
options = filter(lambda x: x != '', options.split(' '))
netargs += ",%s" % (','.join(options))
return netargs