#
# 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 <http://www.gnu.org/licenses/>.
#
# Author(s): Matt Wilson <ewt@redhat.com>
#            Erik Troan <ewt@redhat.com>
#            Mike Fulbright <msf@redhat.com>
#            Brent Fox <bfox@redhat.com>
#            David Cantrell <dcantrell@redhat.com>
#

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_MANAGER_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_MANAGER_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)
            addrs = ip4_config_props.Get(isys.NM_MANAGER_IFACE, "Addresses")[0]
            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_MANAGER_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_MANAGER_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_MANAGER_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_MANAGER_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)

            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):

        sysconfig = "%s/etc/sysconfig" % (instPath,)
        netscripts = "%s/network-scripts" % (sysconfig,)
        destnetwork = "%s/network" % (sysconfig,)

        # /etc/sysconfig/network
        if (not instPath) or (not os.path.isfile(destnetwork)) or flags.livecdInstall:
            newnetwork = "%s.new" % (destnetwork,)

            # Qubes specific - see ticket #145
            f = open(newnetwork, "w")
            f.write("NETWORKING=no\n")
            f.write("HOSTNAME=dom0")

            f.close()
            shutil.move(newnetwork, destnetwork)

        if devices is None:
            devices = self.netdevices.values()

        if len(devices) == 0:
            return

        if not os.path.isdir(netscripts):
            iutil.mkdirChain(netscripts)

        # /etc/sysconfig/network-scripts/ifcfg-*
        for dev in devices:
            device = dev.get('DEVICE')

            cfgfile = "%s/ifcfg-%s" % (netscripts, device,)
            if (instPath) and (os.path.isfile(cfgfile)):
                continue

            bootproto = dev.get('BOOTPROTO').lower()
            ipv6addr = dev.get('IPV6ADDR').lower()
            ipv6prefix = dev.get('IPV6PREFIX').lower()
            ipv6autoconf = dev.get('IPV6_AUTOCONF').lower()
            dhcpv6c = dev.get('DHCPV6C').lower()

            newifcfg = "/tmp/ifcfg-%s.new" % (device,)
            f = open(newifcfg, "w")
            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':
                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'))
                dev.set(('DHCPV6C', '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('MTU') and dev.get('MTU') != 0:
                f.write("MTU=%s\n" % dev.get('MTU'))

            # tell NetworkManager not to touch any interfaces used during
            # installation when / is on a network backed device.
            if anaconda is not None:
                import storage
                rootdev = anaconda.storage.rootDevice
                # FIXME: use d.host_address to only add "NM_CONTROLLED=no"
                # for interfaces actually used enroute to the device
                for d in anaconda.storage.devices:
                    if isinstance(d, storage.devices.NetworkStorageDevice) and\
                       (rootdev.dependsOn(d) or d.nic == device):
                        f.write("NM_CONTROLLED=no\n")
                        break

            f.close()
            os.chmod(newifcfg, 0644)

            # move the new ifcfg in place
            destcfg = "%s/ifcfg-%s" % (netscripts, device,)
            try:
                os.remove(destcfg)
            except OSError as e:
                if e.errno != 2:
                    raise
            shutil.move(newifcfg, destcfg)

            # XXX: is this necessary with NetworkManager?
            # handle the keys* files if we have those
            if dev.get("KEY"):
                cfgfile = "%s/keys-%s" % (netscripts, device,)
                if not instPath == '' and os.path.isfile(cfgfile):
                    continue

                newkey = "%s/keys-%s.new" % (netscripts, device,)
                f = open(newkey, "w")
                f.write("KEY=%s\n" % (dev.get('KEY'),))
                f.close()
                os.chmod(newkey, 0600)

                destkey = "%s/keys-%s" % (netscripts, device,)
                shutil.move(newkey, destkey)

            # /etc/dhclient-DEVICE.conf
            dhclientconf = '/etc/dhclient-' + device + '.conf'
            if os.path.isfile(dhclientconf):
                destdhclientconf = '%s%s' % (instPath, dhclientconf,)
                try:
                    shutil.copy(dhclientconf, destdhclientconf)
                except:
                    log.warning("unable to copy %s to target system" % (dhclientconf,))

        # 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/resolv.conf
        if (not instPath) or (not os.path.isfile(instPath + '/etc/resolv.conf')) or flags.livecdInstall:
            if os.path.isfile('/etc/resolv.conf') and instPath != '':
                destresolv = "%s/etc/resolv.conf" % (instPath,)
                shutil.copy('/etc/resolv.conf', destresolv)
            elif (self.domains != ['localdomain'] and self.domains) or \
                self.hasNameServers(dev.info):
                resolv = "%s/etc/resolv.conf" % (instPath,)

                f = open(resolv, "w")

                if self.domains != ['localdomain'] and self.domains:
                    f.write("search %s\n" % (string.joinfields(self.domains, ' '),))

                for key in dev.info.keys():
                    if key.upper().startswith('DNS'):
                        f.write("nameserver %s\n" % (dev.get(key),))

                f.close()

        # /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()

    # 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:
                log.error('Unexpected "ip route get to %s" reply: %s' %
                          (host, routeInfo))
                return ""

            nic = routeInfo[2]

        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())

        return netargs