# vim:ts=4:sw=4:et:ai:sts=4 import errno, os, os.path, socket, subprocess, sys, syslog from syslog import LOG_ERR, LOG_WARNING, LOG_NOTICE, LOG_INFO, LOG_DEBUG __all__ = ["ip_path", "tc_path", "brctl_path", "sysctl_path", "hz"] __all__ += ["tcpdump_path", "netperf_path", "xauth_path", "xdpyinfo_path"] __all__ += ["execute", "backticks", "eintr_wrapper"] __all__ += ["find_listen_port"] __all__ += ["LOG_ERR", "LOG_WARNING", "LOG_NOTICE", "LOG_INFO", "LOG_DEBUG"] __all__ += ["set_log_level", "logger"] __all__ += ["error", "warning", "notice", "info", "debug"] def find_bin(name, extra_path = None): search = [] if "PATH" in os.environ: search += os.environ["PATH"].split(":") for pref in ("/", "/usr/", "/usr/local/"): for d in ("bin", "sbin"): search.append(pref + d) if extra_path: search += extra_path for d in search: try: os.stat(d + "/" + name) return d + "/" + name except OSError, e: if e.errno != os.errno.ENOENT: raise return None def find_bin_or_die(name, extra_path = None): r = find_bin(name) if not r: raise RuntimeError(("Cannot find `%s' command, impossible to " + "continue.") % name) return r ip_path = find_bin_or_die("ip") tc_path = find_bin_or_die("tc") brctl_path = find_bin_or_die("brctl") sysctl_path = find_bin_or_die("sysctl") # Optional tools tcpdump_path = find_bin("tcpdump") netperf_path = find_bin("netperf") xauth_path = find_bin("xauth") xdpyinfo_path = find_bin("xdpyinfo") # Seems this is completely bogus. At least, we can assume that the internal HZ # is bigger than this. hz = os.sysconf("SC_CLK_TCK") try: os.stat("/sys/class/net") except: raise RuntimeError("Sysfs does not seem to be mounted, impossible to " + "continue.") def execute(cmd): debug("execute(%s)" % cmd) null = open("/dev/null", "r+") p = subprocess.Popen(cmd, stdout = null, stderr = subprocess.PIPE) out, err = p.communicate() if p.returncode != 0: raise RuntimeError("Error executing `%s': %s" % (" ".join(cmd), err)) def backticks(cmd): debug("backticks(%s)" % cmd) p = subprocess.Popen(cmd, stdout = subprocess.PIPE, stderr = subprocess.PIPE) out, err = p.communicate() if p.returncode != 0: raise RuntimeError("Error executing `%s': %s" % (" ".join(cmd), err)) return out def eintr_wrapper(f, *args): "Wraps some callable with a loop that retries on EINTR." while True: try: return f(*args) except OSError, e: # pragma: no cover if e.errno == errno.EINTR: continue raise except IOError, e: # pragma: no cover if e.errno == errno.EINTR: continue raise def find_listen_port(family = socket.AF_INET, type = socket.SOCK_STREAM, proto = 0, addr = "127.0.0.1", min_port = 1, max_port = 65535): s = socket.socket(family, type, proto) for p in range(min_port, max_port + 1): try: s.bind((addr, p)) return s, p except socket.error: pass raise RuntimeError("Cannot find an usable port in the range specified") # Logging _log_level = LOG_WARNING _log_use_syslog = False _log_stream = sys.stderr _log_syslog_opts = () _log_pid = os.getpid() def set_log_level(level): "Sets the log level for console messages, does not affect syslog logging." global _log_level assert level > LOG_ERR and level <= LOG_DEBUG _log_level = level def set_log_output(file): "Redirect console messages to the provided stream." global _log_stream assert hasattr(file, "write") and hasattr(file, "flush") _log_stream = file def log_use_syslog(use = True, ident = None, logopt = 0, facility = syslog.LOG_USER): "Enable or disable the use of syslog for logging messages." global _log_use_syslog, _log_syslog_opts if not use: if _log_use_syslog: syslog.closelog() _log_use_syslog = False return if not ident: #ident = os.path.basename(sys.argv[0]) ident = "netns" syslog.openlog("%s[%d]" % (ident, os.getpid()), logopt, facility) _log_syslog_opts = (ident, logopt, facility) _log_use_syslog = True info("Syslog logging started") def logger(priority, message): "Print a log message in syslog, console or both." global _log_use_syslog, _log_stream if _log_use_syslog: if os.getpid() != _log_pid: # Re-init logging log_use_syslog(_log_use_syslog, *_log_syslog_opts) syslog.syslog(priority, message) return if priority > _log_level: return eintr_wrapper(_log_stream.write, "[%d] %s\n" % (os.getpid(), message.rstrip())) _log_stream.flush() def error(message): logger(LOG_ERR, message) def warning(message): logger(LOG_WARNING, message) def notice(message): logger(LOG_NOTICE, message) def info(message): logger(LOG_INFO, message) def debug(message): logger(LOG_DEBUG, message) # Used to print extra info in nested exceptions def _custom_hook(t, v, tb): # pragma: no cover if hasattr(v, "child_traceback"): sys.stderr.write("Nested exception, original traceback " + "(most recent call last):\n") sys.stderr.write(v.child_traceback + ("-" * 70) + "\n") sys.__excepthook__(t, v, tb) # XXX: somebody kill me, I deserve it :) sys.excepthook = _custom_hook