From cffbbae79a8cb49c412a6dde2df9c2c81491ab54 Mon Sep 17 00:00:00 2001
From: Michael Hanselmann <hansmi@google.com>
Date: Thu, 20 Sep 2012 18:52:31 +0200
Subject: [PATCH] Implement virtual cluster support in Python code
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

- pathutils: Prepend node-specific prefix path
- RPC: Use virtual paths (see vcluster.py)
- SSH: Pass environment variables, use destination's node directory when
  copying files using scp, use GANETI_HOSTNAME to determine hostname

Signed-off-by: Michael Hanselmann <hansmi@google.com>
Reviewed-by: RenΓ© Nussbaumer <rn@google.com>
---
 lib/backend.py           |  8 ++++++++
 lib/hypervisor/hv_xen.py |  7 ++++---
 lib/jqueue.py            | 15 ++++++++++++---
 lib/pathutils.py         | 20 ++++++++++++--------
 lib/rpc.py               |  5 ++++-
 lib/ssh.py               | 22 +++++++++++++++++++---
 6 files changed, 59 insertions(+), 18 deletions(-)

diff --git a/lib/backend.py b/lib/backend.py
index 1e76c9941..0f102dd46 100644
--- a/lib/backend.py
+++ b/lib/backend.py
@@ -63,6 +63,7 @@ from ganeti import runtime
 from ganeti import mcpu
 from ganeti import compat
 from ganeti import pathutils
+from ganeti import vcluster
 
 
 _BOOT_ID_PATH = "/proc/sys/kernel/random/boot_id"
@@ -2072,6 +2073,8 @@ def UploadFile(file_name, data, mode, uid, gid, atime, mtime):
   @rtype: None
 
   """
+  file_name = vcluster.LocalizeVirtualPath(file_name)
+
   if not os.path.isabs(file_name):
     _Fail("Filename passed to UploadFile is not absolute: '%s'", file_name)
 
@@ -2821,6 +2824,8 @@ def JobQueueUpdate(file_name, content):
   @return: the success of the operation
 
   """
+  file_name = vcluster.LocalizeVirtualPath(file_name)
+
   _EnsureJobQueueFile(file_name)
   getents = runtime.GetEnts()
 
@@ -2842,6 +2847,9 @@ def JobQueueRename(old, new):
   @return: the success of the operation and payload
 
   """
+  old = vcluster.LocalizeVirtualPath(old)
+  new = vcluster.LocalizeVirtualPath(new)
+
   _EnsureJobQueueFile(old)
   _EnsureJobQueueFile(new)
 
diff --git a/lib/hypervisor/hv_xen.py b/lib/hypervisor/hv_xen.py
index 72742405b..2ed84f30a 100644
--- a/lib/hypervisor/hv_xen.py
+++ b/lib/hypervisor/hv_xen.py
@@ -33,11 +33,12 @@ from ganeti.hypervisor import hv_base
 from ganeti import netutils
 from ganeti import objects
 from ganeti import pathutils
+from ganeti import vcluster
 
 
-XEND_CONFIG_FILE = "/etc/xen/xend-config.sxp"
-XL_CONFIG_FILE = "/etc/xen/xl.conf"
-VIF_BRIDGE_SCRIPT = "/etc/xen/scripts/vif-bridge"
+XEND_CONFIG_FILE = vcluster.AddNodePrefix("/etc/xen/xend-config.sxp")
+XL_CONFIG_FILE = vcluster.AddNodePrefix("/etc/xen/xl.conf")
+VIF_BRIDGE_SCRIPT = vcluster.AddNodePrefix("/etc/xen/scripts/vif-bridge")
 _DOM0_NAME = "Domain-0"
 
 
diff --git a/lib/jqueue.py b/lib/jqueue.py
index f4d58f4e7..96c29a137 100644
--- a/lib/jqueue.py
+++ b/lib/jqueue.py
@@ -60,6 +60,7 @@ from ganeti import ht
 from ganeti import query
 from ganeti import qlang
 from ganeti import pathutils
+from ganeti import vcluster
 
 
 JOBQUEUE_THREADS = 25
@@ -85,6 +86,14 @@ def TimeStampNow():
   return utils.SplitTime(time.time())
 
 
+def _CallJqUpdate(runner, names, file_name, content):
+  """Updates job queue file after virtualizing filename.
+
+  """
+  virt_file_name = vcluster.MakeVirtualPath(file_name)
+  return runner.call_jobqueue_update(names, virt_file_name, content)
+
+
 class _SimpleJobQuery:
   """Wrapper for job queries.
 
@@ -1690,8 +1699,8 @@ class JobQueue(object):
       # Read file content
       content = utils.ReadFile(file_name)
 
-      result = self._GetRpc(addrs).call_jobqueue_update([node_name], file_name,
-                                                        content)
+      result = _CallJqUpdate(self._GetRpc(addrs), [node_name],
+                             file_name, content)
       msg = result[node_name].fail_msg
       if msg:
         logging.error("Failed to upload file %s to node %s: %s",
@@ -1775,7 +1784,7 @@ class JobQueue(object):
 
     if replicate:
       names, addrs = self._GetNodeIp()
-      result = self._GetRpc(addrs).call_jobqueue_update(names, file_name, data)
+      result = _CallJqUpdate(self._GetRpc(addrs), names, file_name, data)
       self._CheckRpcResult(result, self._nodes, "Updating %s" % file_name)
 
   def _RenameFilesUnlocked(self, rename):
diff --git a/lib/pathutils.py b/lib/pathutils.py
index 738c66196..e60a7a468 100644
--- a/lib/pathutils.py
+++ b/lib/pathutils.py
@@ -24,15 +24,19 @@
 """
 
 from ganeti import _autoconf
+from ganeti import vcluster
+
 
 # Build-time constants
-DEFAULT_FILE_STORAGE_DIR = _autoconf.FILE_STORAGE_DIR
-DEFAULT_SHARED_FILE_STORAGE_DIR = _autoconf.SHARED_FILE_STORAGE_DIR
-EXPORT_DIR = _autoconf.EXPORT_DIR
+DEFAULT_FILE_STORAGE_DIR = vcluster.AddNodePrefix(_autoconf.FILE_STORAGE_DIR)
+DEFAULT_SHARED_FILE_STORAGE_DIR = \
+  vcluster.AddNodePrefix(_autoconf.SHARED_FILE_STORAGE_DIR)
+EXPORT_DIR = vcluster.AddNodePrefix(_autoconf.EXPORT_DIR)
 OS_SEARCH_PATH = _autoconf.OS_SEARCH_PATH
 SSH_CONFIG_DIR = _autoconf.SSH_CONFIG_DIR
-SYSCONFDIR = _autoconf.SYSCONFDIR
+SYSCONFDIR = vcluster.AddNodePrefix(_autoconf.SYSCONFDIR)
 TOOLSDIR = _autoconf.TOOLSDIR
+LOCALSTATEDIR = vcluster.AddNodePrefix(_autoconf.LOCALSTATEDIR)
 
 # Paths which don't change for a virtual cluster
 DAEMON_UTIL = _autoconf.PKGLIBDIR + "/daemon-util"
@@ -43,10 +47,10 @@ SETUP_SSH = _autoconf.TOOLSDIR + "/setup-ssh"
 XM_CONSOLE_WRAPPER = _autoconf.PKGLIBDIR + "/tools/xm-console-wrapper"
 
 # Top-level paths
-DATA_DIR = _autoconf.LOCALSTATEDIR + "/lib/ganeti"
-LOCK_DIR = _autoconf.LOCALSTATEDIR + "/lock"
-LOG_DIR = _autoconf.LOCALSTATEDIR + "/log/ganeti"
-RUN_DIR = _autoconf.LOCALSTATEDIR + "/run/ganeti"
+DATA_DIR = LOCALSTATEDIR + "/lib/ganeti"
+LOCK_DIR = LOCALSTATEDIR + "/lock"
+LOG_DIR = LOCALSTATEDIR + "/log/ganeti"
+RUN_DIR = LOCALSTATEDIR + "/run/ganeti"
 
 #: Script to configure master IP address
 DEFAULT_MASTER_SETUP_SCRIPT = TOOLSDIR + "/master-ip-setup"
diff --git a/lib/rpc.py b/lib/rpc.py
index 2d7d28b76..7959b8c57 100644
--- a/lib/rpc.py
+++ b/lib/rpc.py
@@ -48,6 +48,7 @@ from ganeti import runtime
 from ganeti import compat
 from ganeti import rpc_defs
 from ganeti import pathutils
+from ganeti import vcluster
 
 # Special module generated at build time
 from ganeti import _generated_rpc
@@ -524,7 +525,9 @@ def _PrepareFileUpload(getents_fn, filename):
 
   getents = getents_fn()
 
-  return [filename, data, st.st_mode, getents.LookupUid(st.st_uid),
+  virt_filename = vcluster.MakeVirtualPath(filename)
+
+  return [virt_filename, data, st.st_mode, getents.LookupUid(st.st_uid),
           getents.LookupGid(st.st_gid), st.st_atime, st.st_mtime]
 
 
diff --git a/lib/ssh.py b/lib/ssh.py
index ad42f95b4..1ecff4507 100644
--- a/lib/ssh.py
+++ b/lib/ssh.py
@@ -33,6 +33,7 @@ from ganeti import errors
 from ganeti import constants
 from ganeti import netutils
 from ganeti import pathutils
+from ganeti import vcluster
 
 
 def FormatParamikoFingerprint(fingerprint):
@@ -184,7 +185,17 @@ class SshRunner:
                                       quiet=quiet))
     if tty:
       argv.extend(["-t", "-t"])
-    argv.extend(["%s@%s" % (user, hostname), command])
+
+    argv.append("%s@%s" % (user, hostname))
+
+    # Insert variables for virtual nodes
+    argv.extend("export %s=%s;" %
+                (utils.ShellQuote(name), utils.ShellQuote(value))
+                for (name, value) in
+                  vcluster.EnvironmentForHost(hostname).items())
+
+    argv.append(command)
+
     return argv
 
   def Run(self, *args, **kwargs):
@@ -225,7 +236,7 @@ class SshRunner:
     if netutils.IP6Address.IsValid(node):
       node = netutils.FormatAddress((node, None))
 
-    command.append("%s:%s" % (node, filename))
+    command.append("%s:%s" % (node, vcluster.ExchangeNodeRoot(node, filename)))
 
     result = utils.RunCmd(command)
 
@@ -255,7 +266,12 @@ class SshRunner:
         - detail: string with details
 
     """
-    retval = self.Run(node, "root", "hostname --fqdn", quiet=False)
+    cmd = ("if test -z \"$GANETI_HOSTNAME\"; then"
+           "  hostname --fqdn;"
+           "else"
+           "  echo \"$GANETI_HOSTNAME\";"
+           "fi")
+    retval = self.Run(node, "root", cmd, quiet=False)
 
     if retval.failed:
       msg = "ssh problem"
-- 
GitLab