Beispiel #1
0
def dhcp_discovery(dhclient_cmd_path, interface, cleandir):
    """Run dhclient on the interface without scripts or filesystem artifacts.

    @param dhclient_cmd_path: Full path to the dhclient used.
    @param interface: Name of the network inteface on which to dhclient.
    @param cleandir: The directory from which to run dhclient as well as store
        dhcp leases.

    @return: A list of dicts of representing the dhcp leases parsed from the
        dhcp.leases file or empty list.
    """
    LOG.debug('Performing a dhcp discovery on %s', interface)

    # XXX We copy dhclient out of /sbin/dhclient to avoid dealing with strict
    # app armor profiles which disallow running dhclient -sf <our-script-file>.
    # We want to avoid running /sbin/dhclient-script because of side-effects in
    # /etc/resolv.conf any any other vendor specific scripts in
    # /etc/dhcp/dhclient*hooks.d.
    sandbox_dhclient_cmd = os.path.join(cleandir, 'dhclient')
    util.copy(dhclient_cmd_path, sandbox_dhclient_cmd)
    pid_file = os.path.join(cleandir, 'dhclient.pid')
    lease_file = os.path.join(cleandir, 'dhcp.leases')

    # ISC dhclient needs the interface up to send initial discovery packets.
    # Generally dhclient relies on dhclient-script PREINIT action to bring the
    # link up before attempting discovery. Since we are using -sf /bin/true,
    # we need to do that "link up" ourselves first.
    util.subp(['ip', 'link', 'set', 'dev', interface, 'up'], capture=True)
    cmd = [sandbox_dhclient_cmd, '-1', '-v', '-lf', lease_file,
           '-pf', pid_file, interface, '-sf', '/bin/true']
    util.subp(cmd, capture=True)

    # Wait for pid file and lease file to appear, and for the process
    # named by the pid file to daemonize (have pid 1 as its parent). If we
    # try to read the lease file before daemonization happens, we might try
    # to read it before the dhclient has actually written it. We also have
    # to wait until the dhclient has become a daemon so we can be sure to
    # kill the correct process, thus freeing cleandir to be deleted back
    # up the callstack.
    missing = util.wait_for_files(
        [pid_file, lease_file], maxwait=5, naplen=0.01)
    if missing:
        LOG.warning("dhclient did not produce expected files: %s",
                    ', '.join(os.path.basename(f) for f in missing))
        return []

    ppid = 'unknown'
    for _ in range(0, 1000):
        pid_content = util.load_file(pid_file).strip()
        try:
            pid = int(pid_content)
        except ValueError:
            pass
        else:
            ppid = util.get_proc_ppid(pid)
            if ppid == 1:
                LOG.debug('killing dhclient with pid=%s', pid)
                os.kill(pid, signal.SIGKILL)
                return parse_dhcp_lease_file(lease_file)
        time.sleep(0.01)

    LOG.error(
        'dhclient(pid=%s, parentpid=%s) failed to daemonize after %s seconds',
        pid_content, ppid, 0.01 * 1000
    )
    return parse_dhcp_lease_file(lease_file)
Beispiel #2
0
 def test_get_proc_ppid(self):
     """get_proc_ppid returns correct parent pid value."""
     my_pid = os.getpid()
     my_ppid = os.getppid()
     self.assertEqual(my_ppid, util.get_proc_ppid(my_pid))
Beispiel #3
0
 def test_get_proc_ppid(self):
     """get_proc_ppid returns correct parent pid value."""
     my_pid = os.getpid()
     my_ppid = os.getppid()
     self.assertEqual(my_ppid, util.get_proc_ppid(my_pid))
Beispiel #4
0
def dhcp_discovery(dhclient_cmd_path, interface, cleandir, dhcp_log_func=None):
    """Run dhclient on the interface without scripts or filesystem artifacts.

    @param dhclient_cmd_path: Full path to the dhclient used.
    @param interface: Name of the network inteface on which to dhclient.
    @param cleandir: The directory from which to run dhclient as well as store
        dhcp leases.
    @param dhcp_log_func: A callable accepting the dhclient output and error
        streams.

    @return: A list of dicts of representing the dhcp leases parsed from the
        dhcp.leases file or empty list.
    """
    LOG.debug("Performing a dhcp discovery on %s", interface)

    # XXX We copy dhclient out of /sbin/dhclient to avoid dealing with strict
    # app armor profiles which disallow running dhclient -sf <our-script-file>.
    # We want to avoid running /sbin/dhclient-script because of side-effects in
    # /etc/resolv.conf any any other vendor specific scripts in
    # /etc/dhcp/dhclient*hooks.d.
    sandbox_dhclient_cmd = os.path.join(cleandir, "dhclient")
    util.copy(dhclient_cmd_path, sandbox_dhclient_cmd)
    pid_file = os.path.join(cleandir, "dhclient.pid")
    lease_file = os.path.join(cleandir, "dhcp.leases")

    # In some cases files in /var/tmp may not be executable, launching dhclient
    # from there will certainly raise 'Permission denied' error. Try launching
    # the original dhclient instead.
    if not os.access(sandbox_dhclient_cmd, os.X_OK):
        sandbox_dhclient_cmd = dhclient_cmd_path

    # ISC dhclient needs the interface up to send initial discovery packets.
    # Generally dhclient relies on dhclient-script PREINIT action to bring the
    # link up before attempting discovery. Since we are using -sf /bin/true,
    # we need to do that "link up" ourselves first.
    subp.subp(["ip", "link", "set", "dev", interface, "up"], capture=True)
    cmd = [
        sandbox_dhclient_cmd,
        "-1",
        "-v",
        "-lf",
        lease_file,
        "-pf",
        pid_file,
        interface,
        "-sf",
        "/bin/true",
    ]
    out, err = subp.subp(cmd, capture=True)

    # Wait for pid file and lease file to appear, and for the process
    # named by the pid file to daemonize (have pid 1 as its parent). If we
    # try to read the lease file before daemonization happens, we might try
    # to read it before the dhclient has actually written it. We also have
    # to wait until the dhclient has become a daemon so we can be sure to
    # kill the correct process, thus freeing cleandir to be deleted back
    # up the callstack.
    missing = util.wait_for_files([pid_file, lease_file],
                                  maxwait=5,
                                  naplen=0.01)
    if missing:
        LOG.warning(
            "dhclient did not produce expected files: %s",
            ", ".join(os.path.basename(f) for f in missing),
        )
        return []

    ppid = "unknown"
    daemonized = False
    for _ in range(0, 1000):
        pid_content = util.load_file(pid_file).strip()
        try:
            pid = int(pid_content)
        except ValueError:
            pass
        else:
            ppid = util.get_proc_ppid(pid)
            if ppid == 1:
                LOG.debug("killing dhclient with pid=%s", pid)
                os.kill(pid, signal.SIGKILL)
                daemonized = True
                break
        time.sleep(0.01)

    if not daemonized:
        LOG.error(
            "dhclient(pid=%s, parentpid=%s) failed to daemonize after %s "
            "seconds",
            pid_content,
            ppid,
            0.01 * 1000,
        )
    if dhcp_log_func is not None:
        dhcp_log_func(out, err)
    return parse_dhcp_lease_file(lease_file)
Beispiel #5
0
def dhcp_discovery(dhclient_cmd_path, interface, cleandir):
    """Run dhclient on the interface without scripts or filesystem artifacts.

    @param dhclient_cmd_path: Full path to the dhclient used.
    @param interface: Name of the network inteface on which to dhclient.
    @param cleandir: The directory from which to run dhclient as well as store
        dhcp leases.

    @return: A list of dicts of representing the dhcp leases parsed from the
        dhcp.leases file or empty list.
    """
    LOG.debug('Performing a dhcp discovery on %s', interface)

    # XXX We copy dhclient out of /sbin/dhclient to avoid dealing with strict
    # app armor profiles which disallow running dhclient -sf <our-script-file>.
    # We want to avoid running /sbin/dhclient-script because of side-effects in
    # /etc/resolv.conf any any other vendor specific scripts in
    # /etc/dhcp/dhclient*hooks.d.
    sandbox_dhclient_cmd = os.path.join(cleandir, 'dhclient')
    util.copy(dhclient_cmd_path, sandbox_dhclient_cmd)
    pid_file = os.path.join(cleandir, 'dhclient.pid')
    lease_file = os.path.join(cleandir, 'dhcp.leases')

    # ISC dhclient needs the interface up to send initial discovery packets.
    # Generally dhclient relies on dhclient-script PREINIT action to bring the
    # link up before attempting discovery. Since we are using -sf /bin/true,
    # we need to do that "link up" ourselves first.
    util.subp(['ip', 'link', 'set', 'dev', interface, 'up'], capture=True)
    cmd = [sandbox_dhclient_cmd, '-1', '-v', '-lf', lease_file,
           '-pf', pid_file, interface, '-sf', '/bin/true']
    util.subp(cmd, capture=True)

    # Wait for pid file and lease file to appear, and for the process
    # named by the pid file to daemonize (have pid 1 as its parent). If we
    # try to read the lease file before daemonization happens, we might try
    # to read it before the dhclient has actually written it. We also have
    # to wait until the dhclient has become a daemon so we can be sure to
    # kill the correct process, thus freeing cleandir to be deleted back
    # up the callstack.
    missing = util.wait_for_files(
        [pid_file, lease_file], maxwait=5, naplen=0.01)
    if missing:
        LOG.warning("dhclient did not produce expected files: %s",
                    ', '.join(os.path.basename(f) for f in missing))
        return []

    ppid = 'unknown'
    for _ in range(0, 1000):
        pid_content = util.load_file(pid_file).strip()
        try:
            pid = int(pid_content)
        except ValueError:
            pass
        else:
            ppid = util.get_proc_ppid(pid)
            if ppid == 1:
                LOG.debug('killing dhclient with pid=%s', pid)
                os.kill(pid, signal.SIGKILL)
                return parse_dhcp_lease_file(lease_file)
        time.sleep(0.01)

    LOG.error(
        'dhclient(pid=%s, parentpid=%s) failed to daemonize after %s seconds',
        pid_content, ppid, 0.01 * 1000
    )
    return parse_dhcp_lease_file(lease_file)