Statistics
| Branch: | Tag: | Revision:

root / lib / vcluster.py @ 63a3d8f7

History | View | Annotate | Download (7.2 kB)

1
#
2
#
3

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

    
21

    
22
"""Module containing utilities for virtual clusters.
23

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

28
"""
29

    
30
import os
31

    
32
from ganeti import compat
33

    
34

    
35
_VIRT_PATH_PREFIX = "/###-VIRTUAL-PATH-###,"
36
_ROOTDIR_ENVNAME = "GANETI_ROOTDIR"
37
_HOSTNAME_ENVNAME = "GANETI_HOSTNAME"
38

    
39

    
40
def _GetRootDirectory(envname):
41
  """Retrieves root directory from an environment variable.
42

43
  @type envname: string
44
  @param envname: Environment variable name
45
  @rtype: string
46
  @return: Root directory (can be empty)
47

48
  """
49
  path = os.getenv(envname)
50

    
51
  if path:
52
    if not os.path.isabs(path):
53
      raise RuntimeError("Root directory in '%s' must be absolute: %s" %
54
                         (envname, path))
55
    return os.path.normpath(path)
56

    
57
  return ""
58

    
59

    
60
def _GetHostname(envname):
61
  """Retrieves virtual hostname from an environment variable.
62

63
  @type envname: string
64
  @param envname: Environment variable name
65
  @rtype: string
66
  @return: Host name (can be empty)
67

68
  """
69
  return os.getenv(envname, default="")
70

    
71

    
72
def _CheckHostname(hostname):
73
  """Very basic check for hostnames.
74

75
  @type hostname: string
76
  @param hostname: Hostname
77

78
  """
79
  if os.path.basename(hostname) != hostname:
80
    raise RuntimeError("Hostname '%s' can not be used for a file system"
81
                       " path" % hostname)
82

    
83

    
84
def _PreparePaths(rootdir, hostname):
85
  """Checks if the root directory and hostname are acceptable.
86

87
  The (node-specific) root directory must have the hostname as its last
88
  component. The parent directory then becomes the cluster-wide root directory.
89
  This is necessary as some components must be able to predict the root path on
90
  a remote node (e.g. copying files via scp).
91

92
  @type rootdir: string
93
  @param rootdir: Root directory (from environment)
94
  @type hostname: string
95
  @param hostname: Hostname (from environment)
96
  @rtype: tuple; (string, string, string or None)
97
  @return: Tuple containing cluster-global root directory, node root directory
98
    and virtual hostname
99

100
  """
101
  if bool(rootdir) ^ bool(hostname):
102
    raise RuntimeError("Both root directory and hostname must be specified"
103
                       " using the environment variables %s and %s" %
104
                       (_ROOTDIR_ENVNAME, _HOSTNAME_ENVNAME))
105

    
106
  if rootdir:
107
    assert rootdir == os.path.normpath(rootdir)
108

    
109
    _CheckHostname(hostname)
110

    
111
    if os.path.basename(rootdir) != hostname:
112
      raise RuntimeError("Last component of root directory ('%s') must match"
113
                         " hostname ('%s')" % (rootdir, hostname))
114

    
115
    return (os.path.dirname(rootdir), rootdir, hostname)
116
  else:
117
    return ("", "", None)
118

    
119

    
120
(_VIRT_BASEDIR, _VIRT_NODEROOT, _VIRT_HOSTNAME) = \
121
  _PreparePaths(_GetRootDirectory(_ROOTDIR_ENVNAME),
122
                _GetHostname(_HOSTNAME_ENVNAME))
123

    
124

    
125
assert (compat.all([_VIRT_BASEDIR, _VIRT_NODEROOT, _VIRT_HOSTNAME]) or
126
        not compat.any([_VIRT_BASEDIR, _VIRT_NODEROOT, _VIRT_HOSTNAME]))
127

    
128

    
129
def GetVirtualHostname():
130
  """Returns the virtual hostname.
131

132
  @rtype: string or L{None}
133

134
  """
135
  return _VIRT_HOSTNAME
136

    
137

    
138
def _MakeNodeRoot(base, node_name):
139
  """Appends a node name to the base directory.
140

141
  """
142
  _CheckHostname(node_name)
143
  return os.path.normpath("%s/%s" % (base, node_name))
144

    
145

    
146
def ExchangeNodeRoot(node_name, filename,
147
                     _basedir=_VIRT_BASEDIR, _noderoot=_VIRT_NODEROOT):
148
  """Replaces the node-specific root directory in a path.
149

150
  Replaces it with the root directory for another node. Assuming
151
  C{/tmp/vcluster/node1} is the root directory for C{node1}, the result will be
152
  C{/tmp/vcluster/node3} for C{node3} (as long as a root directory is specified
153
  in the environment).
154

155
  """
156
  if _basedir:
157
    pure = _RemoveNodePrefix(filename, _noderoot=_noderoot)
158
    result = "%s/%s" % (_MakeNodeRoot(_basedir, node_name), pure)
159
  else:
160
    result = filename
161

    
162
  return os.path.normpath(result)
163

    
164

    
165
def EnvironmentForHost(hostname, _basedir=_VIRT_BASEDIR):
166
  """Returns the environment variables for a host.
167

168
  """
169
  if _basedir:
170
    return {
171
      _ROOTDIR_ENVNAME: _MakeNodeRoot(_basedir, hostname),
172
      _HOSTNAME_ENVNAME: hostname,
173
      }
174
  else:
175
    return {}
176

    
177

    
178
def AddNodePrefix(path, _noderoot=_VIRT_NODEROOT):
179
  """Adds a node-specific prefix to a path in a virtual cluster.
180

181
  Returned path includes user-specified root directory if specified in
182
  environment. As an example, the path C{/var/lib/ganeti} becomes
183
  C{/tmp/vcluster/node1/var/lib/ganeti} if C{/tmp/vcluster/node1} is the root
184
  directory specified in the environment.
185

186
  """
187
  assert os.path.isabs(path)
188

    
189
  if _noderoot:
190
    result = "%s/%s" % (_noderoot, path)
191
  else:
192
    result = path
193

    
194
  assert os.path.isabs(result)
195

    
196
  return os.path.normpath(result)
197

    
198

    
199
def _RemoveNodePrefix(path, _noderoot=_VIRT_NODEROOT):
200
  """Removes the node-specific prefix from a path.
201

202
  This is the opposite of L{AddNodePrefix} and removes a node-local prefix
203
  path.
204

205
  """
206
  assert os.path.isabs(path)
207

    
208
  norm_path = os.path.normpath(path)
209

    
210
  if _noderoot:
211
    # Make sure path is actually below node root
212
    norm_root = os.path.normpath(_noderoot)
213
    root_with_sep = "%s%s" % (norm_root, os.sep)
214
    prefix = os.path.commonprefix([root_with_sep, norm_path])
215

    
216
    if prefix == root_with_sep:
217
      result = norm_path[len(norm_root):]
218
    else:
219
      raise RuntimeError("Path '%s' is not below node root '%s'" %
220
                         (path, _noderoot))
221
  else:
222
    result = norm_path
223

    
224
  assert os.path.isabs(result)
225

    
226
  return result
227

    
228

    
229
def MakeVirtualPath(path, _noderoot=_VIRT_NODEROOT):
230
  """Virtualizes a path.
231

232
  A path is "virtualized" by stripping it of its node-specific directory and
233
  prepending a prefix (L{_VIRT_PATH_PREFIX}). Use L{LocalizeVirtualPath} to
234
  undo the process. Virtual paths are meant to be transported via RPC.
235

236
  """
237
  assert os.path.isabs(path)
238

    
239
  if _noderoot:
240
    return _VIRT_PATH_PREFIX + _RemoveNodePrefix(path, _noderoot=_noderoot)
241
  else:
242
    return path
243

    
244

    
245
def LocalizeVirtualPath(path, _noderoot=_VIRT_NODEROOT):
246
  """Localizes a virtual path.
247

248
  A "virtualized" path consists of a prefix (L{LocalizeVirtualPath}) and a
249
  local path. This function adds the node-specific directory to the local path.
250
  Virtual paths are meant to be transported via RPC.
251

252
  """
253
  assert os.path.isabs(path)
254

    
255
  if _noderoot:
256
    if path.startswith(_VIRT_PATH_PREFIX):
257
      return AddNodePrefix(path[len(_VIRT_PATH_PREFIX):], _noderoot=_noderoot)
258
    else:
259
      raise RuntimeError("Path '%s' is not a virtual path" % path)
260
  else:
261
    return path