X-Git-Url: https://code.grnet.gr/git/ganeti-local/blobdiff_plain/339be5a8385dcdb58e85cd1385102a80f4829a11..95f84636f431ffaf777de917dabbb12bc548a678:/lib/utils.py diff --git a/lib/utils.py b/lib/utils.py index d0d07bc..1d0750e 100644 --- a/lib/utils.py +++ b/lib/utils.py @@ -46,6 +46,8 @@ import signal import datetime import calendar import collections +import struct +import IN from cStringIO import StringIO @@ -69,6 +71,18 @@ no_fork = False _RANDOM_UUID_FILE = "/proc/sys/kernel/random/uuid" +# Structure definition for getsockopt(SOL_SOCKET, SO_PEERCRED, ...): +# struct ucred { pid_t pid; uid_t uid; gid_t gid; }; +# +# The GNU C Library defines gid_t and uid_t to be "unsigned int" and +# pid_t to "int". +# +# IEEE Std 1003.1-2008: +# "nlink_t, uid_t, gid_t, and id_t shall be integer types" +# "blksize_t, pid_t, and ssize_t shall be signed integer types" +_STRUCT_UCRED = "iII" +_STRUCT_UCRED_SIZE = struct.calcsize(_STRUCT_UCRED) + class RunResult(object): """Holds the result of running external programs. @@ -328,6 +342,19 @@ def RunParts(dir_name, env=None, reset_env=False): return rr +def GetSocketCredentials(sock): + """Returns the credentials of the foreign process connected to a socket. + + @param sock: Unix socket + @rtype: tuple; (number, number, number) + @return: The PID, UID and GID of the connected foreign process. + + """ + peercred = sock.getsockopt(socket.SOL_SOCKET, IN.SO_PEERCRED, + _STRUCT_UCRED_SIZE) + return struct.unpack(_STRUCT_UCRED, peercred) + + def RemoveFile(filename): """Remove a file ignoring some errors. @@ -539,16 +566,28 @@ def IsProcessAlive(pid): @return: True if the process exists """ + def _TryStat(name): + try: + os.stat(name) + return True + except EnvironmentError, err: + if err.errno in (errno.ENOENT, errno.ENOTDIR): + return False + elif err.errno == errno.EINVAL: + raise RetryAgain(err) + raise + + assert isinstance(pid, int), "pid must be an integer" if pid <= 0: return False + proc_entry = "/proc/%d/status" % pid + # /proc in a multiprocessor environment can have strange behaviors. + # Retry the os.stat a few times until we get a good result. try: - os.stat("/proc/%d/status" % pid) - return True - except EnvironmentError, err: - if err.errno in (errno.ENOENT, errno.ENOTDIR): - return False - raise + return Retry(_TryStat, (0.01, 1.5, 0.1), 0.5, args=[proc_entry]) + except RetryTimeout, err: + err.RaiseInner() def ReadPidFile(pidfile): @@ -1487,20 +1526,6 @@ def FirstFree(seq, base=0): return None -def all(seq, pred=bool): # pylint: disable-msg=W0622 - "Returns True if pred(x) is True for every element in the iterable" - for _ in itertools.ifilterfalse(pred, seq): - return False - return True - - -def any(seq, pred=bool): # pylint: disable-msg=W0622 - "Returns True if pred(x) is True for at least one element in the iterable" - for _ in itertools.ifilter(pred, seq): - return True - return False - - def SingleWaitForFdCondition(fdobj, event, timeout): """Waits for a condition to occur on the socket. @@ -1592,12 +1617,6 @@ def WaitForFdCondition(fdobj, event, timeout): return result -def partition(seq, pred=bool): # # pylint: disable-msg=W0622 - "Partition a list in two, based on the given predicate" - return (list(itertools.ifilter(pred, seq)), - list(itertools.ifilterfalse(pred, seq))) - - def UniqueSequence(seq): """Returns a list with unique elements. @@ -2491,12 +2510,25 @@ def ReadWatcherPauseFile(filename, now=None, remove_after=3600): class RetryTimeout(Exception): """Retry loop timed out. + Any arguments which was passed by the retried function to RetryAgain will be + preserved in RetryTimeout, if it is raised. If such argument was an exception + the RaiseInner helper method will reraise it. + """ + def RaiseInner(self): + if self.args and isinstance(self.args[0], Exception): + raise self.args[0] + else: + raise RetryTimeout(*self.args) class RetryAgain(Exception): """Retry again. + Any arguments passed to RetryAgain will be preserved, if a timeout occurs, as + arguments to RetryTimeout. If an exception is passed, the RaiseInner() method + of the RetryTimeout() method can be used to reraise it. + """ @@ -2605,11 +2637,12 @@ def Retry(fn, delay, timeout, args=None, wait_fn=time.sleep, assert calc_delay is None or callable(calc_delay) while True: + retry_args = [] try: # pylint: disable-msg=W0142 return fn(*args) - except RetryAgain: - pass + except RetryAgain, err: + retry_args = err.args except RetryTimeout: raise errors.ProgrammerError("Nested retry loop detected that didn't" " handle RetryTimeout") @@ -2617,7 +2650,8 @@ def Retry(fn, delay, timeout, args=None, wait_fn=time.sleep, remaining_time = end_time - _time_fn() if remaining_time < 0.0: - raise RetryTimeout() + # pylint: disable-msg=W0142 + raise RetryTimeout(*retry_args) assert remaining_time >= 0.0