root / lib / vcluster.py @ 5d94c034
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 |