#
# network.py - network configuration install data
#
# Copyright (C) 2001, 2002, 2003 Red Hat, Inc. All rights reserved.
#
# 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
#
import string
import isys
import iutil
import socket
import os
import minihal
import rhpl
from flags import flags
from rhpl.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
# XXX: POSIX says this limit is 255, but Linux also defines HOST_NAME_MAX
# as 64, so I don't know which we should believe. --dcantrell
if len(hostname) > 64:
return _("Hostname must be 64 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
def networkDeviceCheck(anaconda):
devs = anaconda.id.network.available()
if not devs:
anaconda.dispatch.skipStep("network")
# return if the device is of a type that requires a ptpaddr to be specified
def isPtpDev(devname):
if (devname.startswith("ctc") or devname.startswith("iucv")):
return True
return False
def _anyUsing(devices, method):
if method != "dhcp" and method != "static":
return False
for dev in devices.keys():
onboot = devices[dev].get("onboot").lower()
if onboot == "no":
continue
bootproto = devices[dev].get("bootproto").lower()
ipv6addr = devices[dev].get("ipv6addr").lower()
ipv6ac = devices[dev].get("ipv6_autoconf").lower()
if method == "dhcp":
if bootproto == "dhcp" or ipv6addr == "dhcp" or ipv6ac == "yes":
return True
elif method == "static":
if bootproto == "static" or ipv6addr != "dhcp" and ipv6ac != "yes":
return True
return False
# determine whether any active at boot devices are using dhcp or dhcpv6
def anyUsingDHCP(devices):
return _anyUsing(devices, "dhcp")
# determine whether any active at boot devices are using static IP config
def anyUsingStatic(devices):
return _anyUsing(devices, "static")
# 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 to load /tmp/netinfo and see if we can sniff out network info
netinfo = Network()
for dev in netinfo.netdevices.keys():
try:
ip = isys.getIPAddress(dev)
except Exception, e:
log.error("Got an exception trying to get the ip addr of %s: "
"%s" %(dev, e))
continue
if ip == '127.0.0.1' or ip is None:
continue
if isys.getLinkStatus(dev):
return True
return False
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")
# Don't let onboot be turned on unless we have config information
# to go along with it
proto = self.get('bootproto') or ""
if proto.lower() != 'dhcp' and not self.get('ipaddr'):
forceOffOnBoot = 1
else:
forceOffOnBoot = 0
onBootWritten = 0
for key in keys:
if key in ("USEIPV4", "USEIPV6"): # XXX: these are per-device, but not written out
continue
if key == 'ONBOOT' and forceOffOnBoot:
s = s + key + "=" + 'no' + "\n"
# 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"
if key == 'ONBOOT':
onBootWritten = 1
if not onBootWritten:
s = s + 'ONBOOT=no\n'
return s
def __init__(self, dev):
self.info = { "DEVICE" : dev,
"ONBOOT": "no" }
if dev.startswith('ctc'):
self.info["TYPE"] = "CTC"
elif dev.startswith('iucv'):
self.info["TYPE"] = "IUCV"
class Network:
def __init__(self):
self.firstnetdevice = None
self.netdevices = {}
self.gateway = ""
self.primaryNS = ""
self.secondaryNS = ""
self.domains = []
self.isConfigured = 0
self.hostname = "localhost.localdomain"
# if we specify a hostname and are using dhcp, do an override
# originally used by the gui but overloaded now
# we also test in places if the hostname is localhost.localdomain
# to see if its been override. Need some consolidation in future.
#
# force users to set a manual hostname by default (dcantrell, #408921)
self.overrideDHCPhostname = True
if flags.rootpath:
self.isConfigured = 1
try:
f = open("/tmp/netinfo", "r")
except:
pass
else:
lines = f.readlines()
f.close()
info = {}
self.isConfigured = 1
for line in lines:
netinf = string.splitfields(line, '=')
if len(netinf) >= 2:
info [netinf[0]] = string.strip(netinf[1])
self.netdevices [info["DEVICE"]] = NetworkDevice(info["DEVICE"])
self.firstnetdevice = info["DEVICE"]
for key in ("IPADDR", "NETMASK", "BOOTPROTO", "ONBOOT", "MTU",
"NETTYPE", "SUBCHANNELS", "PORTNAME", "CTCPROT",
"PEERID", "ESSID", "KEY", "IPV6ADDR", "IPV6_AUTOCONF"):
if info.has_key(key):
self.netdevices [info["DEVICE"]].set((key, info[key]))
self.netdevices [info["DEVICE"]].set(('useIPv4', flags.useIPv4))
self.netdevices [info["DEVICE"]].set(('useIPv6', flags.useIPv6))
if info.has_key("GATEWAY"):
self.gateway = info["GATEWAY"]
if info.has_key("DOMAIN"):
self.domains.append(info["DOMAIN"])
if info.has_key("HOSTNAME"):
self.hostname = info["HOSTNAME"]
if not info.has_key("BOOTPROTO"):
if not info.has_key("IPADDR"):
self.netdevices [info["DEVICE"]].set(('useIPv4', False))
if not (info.has_key("IPV6ADDR") and info.has_key("IPV6_AUTOCONF")):
self.netdevices [info["DEVICE"]].set(('useIPv6', False))
try:
f = open("/etc/resolv.conf", "r")
except:
pass
else:
lines = f.readlines()
f.close()
for line in lines:
resolv = string.split(line)
if resolv and resolv[0] == 'nameserver':
if self.primaryNS == "":
self.primaryNS = resolv[1]
elif self.secondaryNS == "":
self.secondaryNS = resolv[1]
# 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
if not oneactive:
self.netdevices[self.firstnetdevice].set(("onboot", "yes"))
def getDevice(self, device):
return self.netdevices[device]
def getFirstDeviceName(self):
return self.firstnetdevice
def available(self):
for device in minihal.get_devices_by_type("net"):
if device.has_key('net.arp_proto_hw_id'):
if device['net.arp_proto_hw_id'] == 1:
dev = device['device']
if not self.netdevices.has_key(dev):
self.netdevices[dev] = NetworkDevice(dev);
if self.firstnetdevice is None:
self.firstnetdevice = dev
self.netdevices[dev].set(('hwaddr', device['net.address']))
self.netdevices[dev].set(('desc', device['description']))
ksdevice = None
if flags.cmdline.has_key("ksdevice"):
ksdevice = flags.cmdline["ksdevice"]
if ksdevice and self.netdevices.has_key(ksdevice):
self.firstnetdevice = ksdevice
return self.netdevices
def setHostname(self, hn):
self.hostname = hn
def setDNS(self, ns):
dns = ns.split(',')
if len(dns) >= 1:
self.primaryNS = dns[0]
if len(dns) >= 2:
self.secondaryNS = dns[1]
def setGateway(self, gw):
self.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 self.primaryNS:
return
myns = self.primaryNS
if not self.isConfigured:
for dev in self.netdevices.values():
if (dev.get('bootproto').lower() == "dhcp" and
dev.get('onboot') == "yes"):
ret = isys.dhcpNetDevice(dev)
if ret is None:
continue
myns = ret
self.isConfigured = 1
break
elif (dev.get('ipaddr') and dev.get('netmask') and
self.gateway is not None and dev.get('onboot') == "yes"):
try:
isys.configNetDevice(dev, self.gateway)
self.isConfigured = 1
break
except SystemError:
log.error("failed to configure network device %s when "
"looking up host name", dev.get('device'))
if self.isConfigured and not flags.rootpath:
f = open("/etc/resolv.conf", "w")
f.write("nameserver %s\n" % myns)
f.close()
isys.resetResolv()
isys.setResolvRetry(1)
if not self.isConfigured:
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 nameservers(self):
return (self.primaryNS, self.secondaryNS)
def dnsString(self):
str = ""
for ns in self.nameservers():
if not ns:
continue
if str: str = str + ","
str = str + ns
return str
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(" --class %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 --netmask %s" %
(dev.get('ipaddr'), dev.get('netmask')))
if self.gateway is not None:
f.write(" --gateway %s" % (self.gateway,))
if self.dnsString():
f.write(" --nameserver %s" % (self.dnsString(),))
if (self.hostname and
self.hostname != "localhost.localdomain"):
f.write(" --hostname %s" % self.hostname)
f.write("\n");
def write(self, instPath):
# make sure the directory exists
if not os.path.isdir("%s/etc/sysconfig/network-scripts" %(instPath,)):
iutil.mkdirChain("%s/etc/sysconfig/network-scripts" %(instPath,))
# /etc/sysconfig/network-scripts/ifcfg-*
for dev in self.netdevices.values():
device = dev.get("device")
bootproto = dev.get('BOOTPROTO').lower()
ipv6addr = dev.get('IPV6ADDR').lower()
ipv6prefix = dev.get('IPV6PREFIX').lower()
ipv6autoconf = dev.get('IPV6_AUTOCONF').lower()
fn = "%s/etc/sysconfig/network-scripts/ifcfg-%s" % (instPath,
device)
f = open(fn, "w")
os.chmod(fn, 0644)
if len(dev.get("DESC")) > 0:
f.write("# %s\n" % (dev.get("DESC"),))
# if bootproto is dhcp, unset any static settings (#218489)
# *but* don't unset if either IPv4 or IPv6 is manual (#433290)
if bootproto == 'dhcp' and \
(ipv6addr == 'dhcp' or ipv6autoconf == 'yes'):
dev.unset('IPADDR')
dev.unset('NETMASK')
dev.unset('GATEWAY')
# handle IPv6 settings correctly for the ifcfg file
dev.unset('IPV6ADDR')
dev.unset('IPV6PREFIX')
if ipv6addr == 'dhcp':
dev.set(('IPV6INIT', 'yes'))
elif ipv6addr != '' and ipv6addr is not None:
dev.set(('IPV6INIT', 'yes'))
if ipv6prefix != '' and ipv6prefix is not None:
dev.set(('IPV6ADDR', ipv6addr + '/' + ipv6prefix))
else:
dev.set(('IPV6ADDR', ipv6addr))
if dev.get('IPV6_AUTOCONF').lower() == 'yes':
dev.set(('IPV6INIT', 'yes'))
f.write(str(dev))
# write out the hostname as DHCP_HOSTNAME if given (#81613)
if (bootproto == 'dhcp' and self.hostname and
self.overrideDHCPhostname):
f.write("DHCP_HOSTNAME=%s\n" %(self.hostname,))
if dev.get('dhcpclass'):
f.write("DHCP_CLASSID=%s\n" % dev.get('dhcpclass'))
if dev.get('MTU') and dev.get('MTU') != 0:
f.write("MTU=%s\n" % dev.get('MTU'))
# write per-interface DNS information for NetworkManager (#443244)
dnsIndex = 1
for ns in self.nameservers():
if ns:
f.write("DNS%d=%s\n" % (dnsIndex, ns,))
dnsIndex += 1
if self.domains != ['localdomain'] and self.domains:
searchLine = string.joinfields(self.domains, ' ')
f.write("SEARCH=\"%s\"\n" % (searchLine,))
# XXX: write NM_CONTROLLED= for now, until the UI is updated
f.write("NM_CONTROLLED=\n")
f.close()
if dev.get("key"):
fn = "%s/etc/sysconfig/network-scripts/keys-%s" % (instPath,
device)
f = open(fn, "w")
os.chmod(fn, 0600)
f.write("KEY=%s\n" % dev.get('key'))
f.close()
# /etc/sysconfig/network
f = open(instPath + "/etc/sysconfig/network", "w")
f.write("NETWORKING=yes\n")
f.write("HOSTNAME=")
# use instclass hostname if set(kickstart) to override
if self.hostname:
f.write(self.hostname + "\n")
else:
f.write("localhost.localdomain\n")
if self.gateway:
if self.gateway.find('.') != -1:
f.write("GATEWAY=%s\n" % (self.gateway,))
f.write("IPV6_DEFAULTGW=\n")
elif self.gateway.find(':') != -1:
f.write("GATEWAY=\n")
f.write("IPV6_DEFAULTGW=%s\n" % (self.gateway,))
f.close()
# /etc/hosts
f = open(instPath + "/etc/hosts", "w")
localline = ""
log.info("self.hostname = %s", self.hostname)
ip = self.lookupHostname()
l = string.split(self.hostname, ".")
# If the hostname is not resolvable, tie it to 127.0.0.1
if not ip and self.hostname != "localhost.localdomain":
localline += self.hostname + " "
if len(l) > 1:
localline += l[0] + " "
# always add the short hostname to 127.0.0.1 (#253979)
localline += "localhost.localdomain localhost"
if len(l) > 1:
localline += " " + l[0]
f.write("# Do not remove the following line, or various programs\n")
f.write("# that require network functionality will fail.\n")
f.write("127.0.0.1\t\t" + localline + "\n")
f.write("::1\t\tlocalhost6.localdomain6 localhost6\n")
if ip:
nameline = "%s\t\t%s" % (ip, self.hostname)
n = string.split(self.hostname, ".")
if len(n) > 1:
nameline = nameline + " " + n[0]
f.write("%s\n" %(nameline,))
# If the hostname was not looked up, but typed in by the user,
# domain might not be computed, so do it now.
if self.domains == ["localdomain"] or not self.domains:
if '.' in self.hostname:
# chop off everything before the leading '.'
domain = self.hostname[(string.find(self.hostname, '.') + 1):]
self.domains = [domain]
# /etc/resolv.conf
f = open(instPath + "/etc/resolv.conf", "w")
if self.domains != ['localdomain'] and self.domains:
f.write("search %s\n" % (string.joinfields(self.domains, ' '),))
for ns in self.nameservers():
if ns:
f.write("nameserver %s\n" % (ns,))
f.close()
# /lib/udev/rules.d/70-persistent-net.rules
if not os.path.isdir("%s/lib/udev/rules.d" %(instPath,)):
iutil.mkdirChain("%s/lib/udev/rules.d" %(instPath,))
f = open(instPath + "/lib/udev/rules.d/70-persistent-net.rules", "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 is not "" 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()