Statistics
| Branch: | Tag: | Revision:

root / lib / vcluster.py @ 33c730a2

History | View | Annotate | Download (7.4 kB)

1 05e733b4 Michael Hanselmann
#
2 05e733b4 Michael Hanselmann
#
3 05e733b4 Michael Hanselmann
4 05e733b4 Michael Hanselmann
# Copyright (C) 2012 Google Inc.
5 05e733b4 Michael Hanselmann
#
6 05e733b4 Michael Hanselmann
# This program is free software; you can redistribute it and/or modify
7 05e733b4 Michael Hanselmann
# it under the terms of the GNU General Public License as published by
8 05e733b4 Michael Hanselmann
# the Free Software Foundation; either version 2 of the License, or
9 05e733b4 Michael Hanselmann
# (at your option) any later version.
10 05e733b4 Michael Hanselmann
#
11 05e733b4 Michael Hanselmann
# This program is distributed in the hope that it will be useful, but
12 05e733b4 Michael Hanselmann
# WITHOUT ANY WARRANTY; without even the implied warranty of
13 05e733b4 Michael Hanselmann
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 05e733b4 Michael Hanselmann
# General Public License for more details.
15 05e733b4 Michael Hanselmann
#
16 05e733b4 Michael Hanselmann
# You should have received a copy of the GNU General Public License
17 05e733b4 Michael Hanselmann
# along with this program; if not, write to the Free Software
18 05e733b4 Michael Hanselmann
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 05e733b4 Michael Hanselmann
# 02110-1301, USA.
20 05e733b4 Michael Hanselmann
21 05e733b4 Michael Hanselmann
22 05e733b4 Michael Hanselmann
"""Module containing utilities for virtual clusters.
23 05e733b4 Michael Hanselmann

24 9340cc2b Michael Hanselmann
Most functions manipulate file system paths and are no-ops when the environment
25 9340cc2b Michael Hanselmann
variables C{GANETI_ROOTDIR} and C{GANETI_HOSTNAME} are not set. See the
26 9340cc2b Michael Hanselmann
functions' docstrings for details.
27 05e733b4 Michael Hanselmann

28 05e733b4 Michael Hanselmann
"""
29 05e733b4 Michael Hanselmann
30 05e733b4 Michael Hanselmann
import os
31 05e733b4 Michael Hanselmann
32 05e733b4 Michael Hanselmann
from ganeti import compat
33 05e733b4 Michael Hanselmann
34 05e733b4 Michael Hanselmann
35 fe4f6dca Michael Hanselmann
ETC_HOSTS = "/etc/hosts"
36 fe4f6dca Michael Hanselmann
37 05e733b4 Michael Hanselmann
_VIRT_PATH_PREFIX = "/###-VIRTUAL-PATH-###,"
38 05e733b4 Michael Hanselmann
_ROOTDIR_ENVNAME = "GANETI_ROOTDIR"
39 05e733b4 Michael Hanselmann
_HOSTNAME_ENVNAME = "GANETI_HOSTNAME"
40 05e733b4 Michael Hanselmann
41 fe4f6dca Michael Hanselmann
#: List of paths which shouldn't be virtualized
42 b8028dcf Michael Hanselmann
_VPATH_WHITELIST = compat.UniqueFrozenset([
43 fe4f6dca Michael Hanselmann
  ETC_HOSTS,
44 fe4f6dca Michael Hanselmann
  ])
45 fe4f6dca Michael Hanselmann
46 05e733b4 Michael Hanselmann
47 05e733b4 Michael Hanselmann
def _GetRootDirectory(envname):
48 05e733b4 Michael Hanselmann
  """Retrieves root directory from an environment variable.
49 05e733b4 Michael Hanselmann

50 05e733b4 Michael Hanselmann
  @type envname: string
51 05e733b4 Michael Hanselmann
  @param envname: Environment variable name
52 05e733b4 Michael Hanselmann
  @rtype: string
53 05e733b4 Michael Hanselmann
  @return: Root directory (can be empty)
54 05e733b4 Michael Hanselmann

55 05e733b4 Michael Hanselmann
  """
56 05e733b4 Michael Hanselmann
  path = os.getenv(envname)
57 05e733b4 Michael Hanselmann
58 05e733b4 Michael Hanselmann
  if path:
59 05e733b4 Michael Hanselmann
    if not os.path.isabs(path):
60 05e733b4 Michael Hanselmann
      raise RuntimeError("Root directory in '%s' must be absolute: %s" %
61 05e733b4 Michael Hanselmann
                         (envname, path))
62 05e733b4 Michael Hanselmann
    return os.path.normpath(path)
63 05e733b4 Michael Hanselmann
64 05e733b4 Michael Hanselmann
  return ""
65 05e733b4 Michael Hanselmann
66 05e733b4 Michael Hanselmann
67 05e733b4 Michael Hanselmann
def _GetHostname(envname):
68 05e733b4 Michael Hanselmann
  """Retrieves virtual hostname from an environment variable.
69 05e733b4 Michael Hanselmann

70 05e733b4 Michael Hanselmann
  @type envname: string
71 05e733b4 Michael Hanselmann
  @param envname: Environment variable name
72 05e733b4 Michael Hanselmann
  @rtype: string
73 05e733b4 Michael Hanselmann
  @return: Host name (can be empty)
74 05e733b4 Michael Hanselmann

75 05e733b4 Michael Hanselmann
  """
76 05e733b4 Michael Hanselmann
  return os.getenv(envname, default="")
77 05e733b4 Michael Hanselmann
78 05e733b4 Michael Hanselmann
79 05e733b4 Michael Hanselmann
def _CheckHostname(hostname):
80 05e733b4 Michael Hanselmann
  """Very basic check for hostnames.
81 05e733b4 Michael Hanselmann

82 05e733b4 Michael Hanselmann
  @type hostname: string
83 05e733b4 Michael Hanselmann
  @param hostname: Hostname
84 05e733b4 Michael Hanselmann

85 05e733b4 Michael Hanselmann
  """
86 05e733b4 Michael Hanselmann
  if os.path.basename(hostname) != hostname:
87 05e733b4 Michael Hanselmann
    raise RuntimeError("Hostname '%s' can not be used for a file system"
88 05e733b4 Michael Hanselmann
                       " path" % hostname)
89 05e733b4 Michael Hanselmann
90 05e733b4 Michael Hanselmann
91 05e733b4 Michael Hanselmann
def _PreparePaths(rootdir, hostname):
92 05e733b4 Michael Hanselmann
  """Checks if the root directory and hostname are acceptable.
93 05e733b4 Michael Hanselmann

94 9340cc2b Michael Hanselmann
  The (node-specific) root directory must have the hostname as its last
95 9340cc2b Michael Hanselmann
  component. The parent directory then becomes the cluster-wide root directory.
96 9340cc2b Michael Hanselmann
  This is necessary as some components must be able to predict the root path on
97 9340cc2b Michael Hanselmann
  a remote node (e.g. copying files via scp).
98 9340cc2b Michael Hanselmann

99 05e733b4 Michael Hanselmann
  @type rootdir: string
100 05e733b4 Michael Hanselmann
  @param rootdir: Root directory (from environment)
101 05e733b4 Michael Hanselmann
  @type hostname: string
102 05e733b4 Michael Hanselmann
  @param hostname: Hostname (from environment)
103 05e733b4 Michael Hanselmann
  @rtype: tuple; (string, string, string or None)
104 05e733b4 Michael Hanselmann
  @return: Tuple containing cluster-global root directory, node root directory
105 05e733b4 Michael Hanselmann
    and virtual hostname
106 05e733b4 Michael Hanselmann

107 05e733b4 Michael Hanselmann
  """
108 05e733b4 Michael Hanselmann
  if bool(rootdir) ^ bool(hostname):
109 05e733b4 Michael Hanselmann
    raise RuntimeError("Both root directory and hostname must be specified"
110 05e733b4 Michael Hanselmann
                       " using the environment variables %s and %s" %
111 05e733b4 Michael Hanselmann
                       (_ROOTDIR_ENVNAME, _HOSTNAME_ENVNAME))
112 05e733b4 Michael Hanselmann
113 05e733b4 Michael Hanselmann
  if rootdir:
114 05e733b4 Michael Hanselmann
    assert rootdir == os.path.normpath(rootdir)
115 05e733b4 Michael Hanselmann
116 05e733b4 Michael Hanselmann
    _CheckHostname(hostname)
117 05e733b4 Michael Hanselmann
118 05e733b4 Michael Hanselmann
    if os.path.basename(rootdir) != hostname:
119 05e733b4 Michael Hanselmann
      raise RuntimeError("Last component of root directory ('%s') must match"
120 05e733b4 Michael Hanselmann
                         " hostname ('%s')" % (rootdir, hostname))
121 05e733b4 Michael Hanselmann
122 05e733b4 Michael Hanselmann
    return (os.path.dirname(rootdir), rootdir, hostname)
123 05e733b4 Michael Hanselmann
  else:
124 05e733b4 Michael Hanselmann
    return ("", "", None)
125 05e733b4 Michael Hanselmann
126 05e733b4 Michael Hanselmann
127 05e733b4 Michael Hanselmann
(_VIRT_BASEDIR, _VIRT_NODEROOT, _VIRT_HOSTNAME) = \
128 05e733b4 Michael Hanselmann
  _PreparePaths(_GetRootDirectory(_ROOTDIR_ENVNAME),
129 05e733b4 Michael Hanselmann
                _GetHostname(_HOSTNAME_ENVNAME))
130 05e733b4 Michael Hanselmann
131 05e733b4 Michael Hanselmann
132 05e733b4 Michael Hanselmann
assert (compat.all([_VIRT_BASEDIR, _VIRT_NODEROOT, _VIRT_HOSTNAME]) or
133 05e733b4 Michael Hanselmann
        not compat.any([_VIRT_BASEDIR, _VIRT_NODEROOT, _VIRT_HOSTNAME]))
134 05e733b4 Michael Hanselmann
135 05e733b4 Michael Hanselmann
136 05e733b4 Michael Hanselmann
def GetVirtualHostname():
137 05e733b4 Michael Hanselmann
  """Returns the virtual hostname.
138 05e733b4 Michael Hanselmann

139 05e733b4 Michael Hanselmann
  @rtype: string or L{None}
140 05e733b4 Michael Hanselmann

141 05e733b4 Michael Hanselmann
  """
142 05e733b4 Michael Hanselmann
  return _VIRT_HOSTNAME
143 05e733b4 Michael Hanselmann
144 05e733b4 Michael Hanselmann
145 595149d5 Michael Hanselmann
def MakeNodeRoot(base, node_name):
146 05e733b4 Michael Hanselmann
  """Appends a node name to the base directory.
147 05e733b4 Michael Hanselmann

148 05e733b4 Michael Hanselmann
  """
149 05e733b4 Michael Hanselmann
  _CheckHostname(node_name)
150 05e733b4 Michael Hanselmann
  return os.path.normpath("%s/%s" % (base, node_name))
151 05e733b4 Michael Hanselmann
152 05e733b4 Michael Hanselmann
153 05e733b4 Michael Hanselmann
def ExchangeNodeRoot(node_name, filename,
154 05e733b4 Michael Hanselmann
                     _basedir=_VIRT_BASEDIR, _noderoot=_VIRT_NODEROOT):
155 05e733b4 Michael Hanselmann
  """Replaces the node-specific root directory in a path.
156 05e733b4 Michael Hanselmann

157 9340cc2b Michael Hanselmann
  Replaces it with the root directory for another node. Assuming
158 9340cc2b Michael Hanselmann
  C{/tmp/vcluster/node1} is the root directory for C{node1}, the result will be
159 9340cc2b Michael Hanselmann
  C{/tmp/vcluster/node3} for C{node3} (as long as a root directory is specified
160 9340cc2b Michael Hanselmann
  in the environment).
161 05e733b4 Michael Hanselmann

162 05e733b4 Michael Hanselmann
  """
163 05e733b4 Michael Hanselmann
  if _basedir:
164 05e733b4 Michael Hanselmann
    pure = _RemoveNodePrefix(filename, _noderoot=_noderoot)
165 595149d5 Michael Hanselmann
    result = "%s/%s" % (MakeNodeRoot(_basedir, node_name), pure)
166 05e733b4 Michael Hanselmann
  else:
167 05e733b4 Michael Hanselmann
    result = filename
168 05e733b4 Michael Hanselmann
169 05e733b4 Michael Hanselmann
  return os.path.normpath(result)
170 05e733b4 Michael Hanselmann
171 05e733b4 Michael Hanselmann
172 05e733b4 Michael Hanselmann
def EnvironmentForHost(hostname, _basedir=_VIRT_BASEDIR):
173 05e733b4 Michael Hanselmann
  """Returns the environment variables for a host.
174 05e733b4 Michael Hanselmann

175 05e733b4 Michael Hanselmann
  """
176 05e733b4 Michael Hanselmann
  if _basedir:
177 05e733b4 Michael Hanselmann
    return {
178 595149d5 Michael Hanselmann
      _ROOTDIR_ENVNAME: MakeNodeRoot(_basedir, hostname),
179 05e733b4 Michael Hanselmann
      _HOSTNAME_ENVNAME: hostname,
180 05e733b4 Michael Hanselmann
      }
181 05e733b4 Michael Hanselmann
  else:
182 05e733b4 Michael Hanselmann
    return {}
183 05e733b4 Michael Hanselmann
184 05e733b4 Michael Hanselmann
185 05e733b4 Michael Hanselmann
def AddNodePrefix(path, _noderoot=_VIRT_NODEROOT):
186 05e733b4 Michael Hanselmann
  """Adds a node-specific prefix to a path in a virtual cluster.
187 05e733b4 Michael Hanselmann

188 05e733b4 Michael Hanselmann
  Returned path includes user-specified root directory if specified in
189 9340cc2b Michael Hanselmann
  environment. As an example, the path C{/var/lib/ganeti} becomes
190 9340cc2b Michael Hanselmann
  C{/tmp/vcluster/node1/var/lib/ganeti} if C{/tmp/vcluster/node1} is the root
191 9340cc2b Michael Hanselmann
  directory specified in the environment.
192 05e733b4 Michael Hanselmann

193 05e733b4 Michael Hanselmann
  """
194 05e733b4 Michael Hanselmann
  assert os.path.isabs(path)
195 05e733b4 Michael Hanselmann
196 05e733b4 Michael Hanselmann
  if _noderoot:
197 05e733b4 Michael Hanselmann
    result = "%s/%s" % (_noderoot, path)
198 05e733b4 Michael Hanselmann
  else:
199 05e733b4 Michael Hanselmann
    result = path
200 05e733b4 Michael Hanselmann
201 05e733b4 Michael Hanselmann
  assert os.path.isabs(result)
202 05e733b4 Michael Hanselmann
203 05e733b4 Michael Hanselmann
  return os.path.normpath(result)
204 05e733b4 Michael Hanselmann
205 05e733b4 Michael Hanselmann
206 05e733b4 Michael Hanselmann
def _RemoveNodePrefix(path, _noderoot=_VIRT_NODEROOT):
207 05e733b4 Michael Hanselmann
  """Removes the node-specific prefix from a path.
208 05e733b4 Michael Hanselmann

209 9340cc2b Michael Hanselmann
  This is the opposite of L{AddNodePrefix} and removes a node-local prefix
210 9340cc2b Michael Hanselmann
  path.
211 9340cc2b Michael Hanselmann

212 05e733b4 Michael Hanselmann
  """
213 05e733b4 Michael Hanselmann
  assert os.path.isabs(path)
214 05e733b4 Michael Hanselmann
215 05e733b4 Michael Hanselmann
  norm_path = os.path.normpath(path)
216 05e733b4 Michael Hanselmann
217 05e733b4 Michael Hanselmann
  if _noderoot:
218 05e733b4 Michael Hanselmann
    # Make sure path is actually below node root
219 05e733b4 Michael Hanselmann
    norm_root = os.path.normpath(_noderoot)
220 05e733b4 Michael Hanselmann
    root_with_sep = "%s%s" % (norm_root, os.sep)
221 05e733b4 Michael Hanselmann
    prefix = os.path.commonprefix([root_with_sep, norm_path])
222 05e733b4 Michael Hanselmann
223 05e733b4 Michael Hanselmann
    if prefix == root_with_sep:
224 05e733b4 Michael Hanselmann
      result = norm_path[len(norm_root):]
225 05e733b4 Michael Hanselmann
    else:
226 05e733b4 Michael Hanselmann
      raise RuntimeError("Path '%s' is not below node root '%s'" %
227 05e733b4 Michael Hanselmann
                         (path, _noderoot))
228 05e733b4 Michael Hanselmann
  else:
229 05e733b4 Michael Hanselmann
    result = norm_path
230 05e733b4 Michael Hanselmann
231 05e733b4 Michael Hanselmann
  assert os.path.isabs(result)
232 05e733b4 Michael Hanselmann
233 05e733b4 Michael Hanselmann
  return result
234 05e733b4 Michael Hanselmann
235 05e733b4 Michael Hanselmann
236 05e733b4 Michael Hanselmann
def MakeVirtualPath(path, _noderoot=_VIRT_NODEROOT):
237 05e733b4 Michael Hanselmann
  """Virtualizes a path.
238 05e733b4 Michael Hanselmann

239 05e733b4 Michael Hanselmann
  A path is "virtualized" by stripping it of its node-specific directory and
240 05e733b4 Michael Hanselmann
  prepending a prefix (L{_VIRT_PATH_PREFIX}). Use L{LocalizeVirtualPath} to
241 9340cc2b Michael Hanselmann
  undo the process. Virtual paths are meant to be transported via RPC.
242 05e733b4 Michael Hanselmann

243 05e733b4 Michael Hanselmann
  """
244 05e733b4 Michael Hanselmann
  assert os.path.isabs(path)
245 05e733b4 Michael Hanselmann
246 fe4f6dca Michael Hanselmann
  if _noderoot and path not in _VPATH_WHITELIST:
247 05e733b4 Michael Hanselmann
    return _VIRT_PATH_PREFIX + _RemoveNodePrefix(path, _noderoot=_noderoot)
248 05e733b4 Michael Hanselmann
  else:
249 05e733b4 Michael Hanselmann
    return path
250 05e733b4 Michael Hanselmann
251 05e733b4 Michael Hanselmann
252 05e733b4 Michael Hanselmann
def LocalizeVirtualPath(path, _noderoot=_VIRT_NODEROOT):
253 05e733b4 Michael Hanselmann
  """Localizes a virtual path.
254 05e733b4 Michael Hanselmann

255 05e733b4 Michael Hanselmann
  A "virtualized" path consists of a prefix (L{LocalizeVirtualPath}) and a
256 05e733b4 Michael Hanselmann
  local path. This function adds the node-specific directory to the local path.
257 9340cc2b Michael Hanselmann
  Virtual paths are meant to be transported via RPC.
258 05e733b4 Michael Hanselmann

259 05e733b4 Michael Hanselmann
  """
260 05e733b4 Michael Hanselmann
  assert os.path.isabs(path)
261 05e733b4 Michael Hanselmann
262 fe4f6dca Michael Hanselmann
  if _noderoot and path not in _VPATH_WHITELIST:
263 05e733b4 Michael Hanselmann
    if path.startswith(_VIRT_PATH_PREFIX):
264 05e733b4 Michael Hanselmann
      return AddNodePrefix(path[len(_VIRT_PATH_PREFIX):], _noderoot=_noderoot)
265 05e733b4 Michael Hanselmann
    else:
266 05e733b4 Michael Hanselmann
      raise RuntimeError("Path '%s' is not a virtual path" % path)
267 05e733b4 Michael Hanselmann
  else:
268 05e733b4 Michael Hanselmann
    return path