Added utils.get_relative_path() to compute the path of an absolute path
as relative to a given directory path and used it when creating the
test results sysinfo/current_reboot symlink to create a relative symlink
that would still be valid when relocated.

Added a "preserve_symlinks" default False keyword argument to
AbstractSSHHost.get_file/send_file to tell rsync to try to preserve the
original symlink and not transform them into the referenced
file/directory when copied (only for rsync as scp has no way to do
this). Used the new flag when fetching the test result directory to
preserve symlinks. Along with the previous change this should fix an
issue of >100k sysinfo bloat because of duplicated content instead of
preserving the client symlink.

Signed-off-by: Mihai Rusu <dizzy@google.com>


git-svn-id: http://test.kernel.org/svn/autotest/trunk@3104 592f7852-d20e-0410-864c-8624ca9c26a4
diff --git a/client/common_lib/utils.py b/client/common_lib/utils.py
index 2a13c4e..711ad42 100644
--- a/client/common_lib/utils.py
+++ b/client/common_lib/utils.py
@@ -915,3 +915,39 @@
     if pidf:
       pidf.write("%s\n" % os.getpid())
       pidf.close()
+
+
+def get_relative_path(path, reference):
+    """Given 2 absolute paths "path" and "reference", compute the path of
+    "path" as relative to the directory "reference".
+
+    @param path the absolute path to convert to a relative path
+    @param reference an absolute directory path to which the relative
+        path will be computed
+    """
+    # normalize the paths (remove double slashes, etc)
+    assert(os.path.isabs(path))
+    assert(os.path.isabs(reference))
+
+    path = os.path.normpath(path)
+    reference = os.path.normpath(reference)
+
+    # we could use os.path.split() but it splits from the end
+    path_list = path.split(os.path.sep)[1:]
+    ref_list = reference.split(os.path.sep)[1:]
+
+    # find the longest leading common path
+    for i in xrange(min(len(path_list), len(ref_list))):
+        if path_list[i] != ref_list[i]:
+            # decrement i so when exiting this loop either by no match or by
+            # end of range we are one step behind
+            i -= 1
+            break
+    i += 1
+    # drop the common part of the paths, not interested in that anymore
+    del path_list[:i]
+
+    # for each uncommon component in the reference prepend a ".."
+    path_list[:0] = ['..'] * (len(ref_list) - i)
+
+    return os.path.join(*path_list)