From f6cbcc0623478ad75fd535fd1939b7b47d40449a Mon Sep 17 00:00:00 2001
From: Michael Hanselmann <hansmi@google.com>
Date: Tue, 30 Aug 2011 13:44:59 +0200
Subject: [PATCH] Add check for standalone modules' imports

This check will abort if one of the standalone modules (currently
lib/rapi/client.py and tools/ganeti-listrunner) imports anything from
the Ganeti source directory.

Signed-off-by: Michael Hanselmann <hansmi@google.com>
Reviewed-by: Iustin Pop <iustin@google.com>
---
 Makefile.am              | 12 +++++-
 autotools/check-imports  | 92 ++++++++++++++++++++++++++++++++++++++++
 autotools/run-in-tempdir |  1 +
 3 files changed, 104 insertions(+), 1 deletion(-)
 create mode 100755 autotools/check-imports

diff --git a/Makefile.am b/Makefile.am
index 444b53e26..0a9b95edd 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -24,6 +24,7 @@ CHECK_PYTHON_CODE = $(top_srcdir)/autotools/check-python-code
 CHECK_MAN = $(top_srcdir)/autotools/check-man
 CHECK_VERSION = $(top_srcdir)/autotools/check-version
 CHECK_NEWS = $(top_srcdir)/autotools/check-news
+CHECK_IMPORTS = $(top_srcdir)/autotools/check-imports
 DOCPP = $(top_srcdir)/autotools/docpp
 REPLACE_VARS_SED = autotools/replace_vars.sed
 CONVERT_CONSTANTS = $(top_srcdir)/autotools/convert-constants
@@ -540,6 +541,7 @@ EXTRA_DIST = \
 	pylintrc \
 	autotools/build-bash-completion \
 	autotools/check-python-code \
+	autotools/check-imports \
 	autotools/check-man \
 	autotools/check-news \
 	autotools/check-tar \
@@ -752,6 +754,7 @@ srclink_files = \
 
 check_python_code = \
 	$(BUILD_BASH_COMPLETION) \
+	$(CHECK_IMPORTS) \
 	$(DOCPP) \
 	$(all_python_code)
 
@@ -762,9 +765,14 @@ lint_python_code = \
 	$(dist_tools_PYTHON) \
 	$(pkglib_python_scripts) \
 	$(BUILD_BASH_COMPLETION) \
+	$(CHECK_IMPORTS) \
 	$(DOCPP) \
 	$(PYTHON_BOOTSTRAP)
 
+standalone_python_modules = \
+	lib/rapi/client.py \
+	tools/ganeti-listrunner
+
 test/daemon-util_unittest.bash: daemons/daemon-util
 
 test/ganeti-cleaner_unittest.bash: daemons/ganeti-cleaner
@@ -1048,10 +1056,12 @@ check-dirs: $(BUILT_SOURCES)
 		if test -n "$$error"; then exit 1; else exit 0; fi; \
 	}
 
-check-local: check-dirs
+.PHONY: check-local
+check-local: check-dirs $(BUILT_SOURCES)
 	$(CHECK_PYTHON_CODE) $(check_python_code)
 	$(CHECK_VERSION) $(VERSION) $(top_srcdir)/NEWS
 	$(CHECK_NEWS) < $(top_srcdir)/NEWS
+	PYTHONPATH=. $(RUN_IN_TEMPDIR) $(CURDIR)/$(CHECK_IMPORTS) $(CURDIR) $(standalone_python_modules)
 	expver=$(VERSION_MAJOR).$(VERSION_MINOR); \
 	if test "`head -n 1 $(top_srcdir)/README`" != "Ganeti $$expver"; then \
 		echo "Incorrect version in README, expected $$expver"; \
diff --git a/autotools/check-imports b/autotools/check-imports
new file mode 100755
index 000000000..d50cb31ee
--- /dev/null
+++ b/autotools/check-imports
@@ -0,0 +1,92 @@
+#!/usr/bin/python
+#
+
+# Copyright (C) 2011 Google Inc.
+#
+# 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, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301, USA.
+
+
+"""Script to check module imports.
+
+"""
+
+# pylint: disable=C0103
+# C0103: Invalid name
+
+import sys
+
+# All modules imported after this line are removed from the global list before
+# importing a module to be checked
+_STANDARD_MODULES = sys.modules.keys()
+
+import os.path
+
+from ganeti import build
+
+
+def main():
+  args = sys.argv[1:]
+
+  # Get references to functions used later on
+  load_module = build.LoadModule
+  abspath = os.path.abspath
+  commonprefix = os.path.commonprefix
+  normpath = os.path.normpath
+
+  script_path = abspath(__file__)
+  srcdir = normpath(abspath(args.pop(0)))
+
+  assert "ganeti" in sys.modules
+
+  for filename in args:
+    # Reset global state
+    for name in sys.modules.keys():
+      if name not in _STANDARD_MODULES:
+        sys.modules.pop(name, None)
+
+    assert "ganeti" not in sys.modules
+
+    # Load module (this might import other modules)
+    module = load_module(filename)
+
+    result = []
+
+    for (name, checkmod) in sorted(sys.modules.items()):
+      if checkmod is None or checkmod == module:
+        continue
+
+      try:
+        checkmodpath = getattr(checkmod, "__file__")
+      except AttributeError:
+        # Built-in module
+        pass
+      else:
+        abscheckmodpath = os.path.abspath(checkmodpath)
+
+        if abscheckmodpath == script_path:
+          # Ignore check script
+          continue
+
+        if commonprefix([abscheckmodpath, srcdir]) == srcdir:
+          result.append(name)
+
+    if result:
+      raise Exception("Module '%s' has illegal imports: %s" %
+                      (filename, ", ".join(result)))
+
+
+if __name__ == "__main__":
+  main()
diff --git a/autotools/run-in-tempdir b/autotools/run-in-tempdir
index e32f86339..48a166ac7 100755
--- a/autotools/run-in-tempdir
+++ b/autotools/run-in-tempdir
@@ -10,6 +10,7 @@ trap "rm -rf $tmpdir" EXIT
 
 cp -r autotools daemons scripts lib tools test $tmpdir
 mv $tmpdir/lib $tmpdir/ganeti
+ln -T -s $tmpdir/ganeti $tmpdir/lib
 mkdir -p $tmpdir/htools
 if [ -e htools/test ]; then
   cp -p htools/test $tmpdir/htools/
-- 
GitLab