Statistics
| Branch: | Tag: | Revision:

root / lib / backend.py @ 196ec587

History | View | Annotate | Download (88.1 kB)

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

24 360b0dc2 Iustin Pop
@var _ALLOWED_UPLOAD_FILES: denotes which files are accepted in
25 360b0dc2 Iustin Pop
     the L{UploadFile} function
26 714ea7ca Iustin Pop
@var _ALLOWED_CLEAN_DIRS: denotes which directories are accepted
27 714ea7ca Iustin Pop
     in the L{_CleanDirectory} function
28 360b0dc2 Iustin Pop

29 360b0dc2 Iustin Pop
"""
30 a8083063 Iustin Pop
31 6c881c52 Iustin Pop
# pylint: disable-msg=E1103
32 6c881c52 Iustin Pop
33 6c881c52 Iustin Pop
# E1103: %s %r has no %r member (but some types could not be
34 6c881c52 Iustin Pop
# inferred), because the _TryOSFromDisk returns either (True, os_obj)
35 6c881c52 Iustin Pop
# or (False, "string") which confuses pylint
36 6c881c52 Iustin Pop
37 a8083063 Iustin Pop
38 a8083063 Iustin Pop
import os
39 a8083063 Iustin Pop
import os.path
40 a8083063 Iustin Pop
import shutil
41 a8083063 Iustin Pop
import time
42 a8083063 Iustin Pop
import stat
43 a8083063 Iustin Pop
import errno
44 a8083063 Iustin Pop
import re
45 b544cfe0 Iustin Pop
import random
46 18682bca Iustin Pop
import logging
47 3b9e6a30 Iustin Pop
import tempfile
48 12bce260 Michael Hanselmann
import zlib
49 12bce260 Michael Hanselmann
import base64
50 a8083063 Iustin Pop
51 a8083063 Iustin Pop
from ganeti import errors
52 a8083063 Iustin Pop
from ganeti import utils
53 a8083063 Iustin Pop
from ganeti import ssh
54 a8083063 Iustin Pop
from ganeti import hypervisor
55 a8083063 Iustin Pop
from ganeti import constants
56 a8083063 Iustin Pop
from ganeti import bdev
57 a8083063 Iustin Pop
from ganeti import objects
58 880478f8 Iustin Pop
from ganeti import ssconf
59 a8083063 Iustin Pop
60 a8083063 Iustin Pop
61 13998ef2 Michael Hanselmann
_BOOT_ID_PATH = "/proc/sys/kernel/random/boot_id"
62 714ea7ca Iustin Pop
_ALLOWED_CLEAN_DIRS = frozenset([
63 714ea7ca Iustin Pop
  constants.DATA_DIR,
64 714ea7ca Iustin Pop
  constants.JOB_QUEUE_ARCHIVE_DIR,
65 714ea7ca Iustin Pop
  constants.QUEUE_DIR,
66 714ea7ca Iustin Pop
  ])
67 13998ef2 Michael Hanselmann
68 13998ef2 Michael Hanselmann
69 2cc6781a Iustin Pop
class RPCFail(Exception):
70 2cc6781a Iustin Pop
  """Class denoting RPC failure.
71 2cc6781a Iustin Pop

72 2cc6781a Iustin Pop
  Its argument is the error message.
73 2cc6781a Iustin Pop

74 2cc6781a Iustin Pop
  """
75 2cc6781a Iustin Pop
76 13998ef2 Michael Hanselmann
77 2cc6781a Iustin Pop
def _Fail(msg, *args, **kwargs):
78 2cc6781a Iustin Pop
  """Log an error and the raise an RPCFail exception.
79 2cc6781a Iustin Pop

80 2cc6781a Iustin Pop
  This exception is then handled specially in the ganeti daemon and
81 2cc6781a Iustin Pop
  turned into a 'failed' return type. As such, this function is a
82 2cc6781a Iustin Pop
  useful shortcut for logging the error and returning it to the master
83 2cc6781a Iustin Pop
  daemon.
84 2cc6781a Iustin Pop

85 2cc6781a Iustin Pop
  @type msg: string
86 2cc6781a Iustin Pop
  @param msg: the text of the exception
87 2cc6781a Iustin Pop
  @raise RPCFail
88 2cc6781a Iustin Pop

89 2cc6781a Iustin Pop
  """
90 2cc6781a Iustin Pop
  if args:
91 2cc6781a Iustin Pop
    msg = msg % args
92 afdc3985 Iustin Pop
  if "log" not in kwargs or kwargs["log"]: # if we should log this error
93 afdc3985 Iustin Pop
    if "exc" in kwargs and kwargs["exc"]:
94 afdc3985 Iustin Pop
      logging.exception(msg)
95 afdc3985 Iustin Pop
    else:
96 afdc3985 Iustin Pop
      logging.error(msg)
97 2cc6781a Iustin Pop
  raise RPCFail(msg)
98 2cc6781a Iustin Pop
99 2cc6781a Iustin Pop
100 c657dcc9 Michael Hanselmann
def _GetConfig():
101 93384844 Iustin Pop
  """Simple wrapper to return a SimpleStore.
102 10c2650b Iustin Pop

103 93384844 Iustin Pop
  @rtype: L{ssconf.SimpleStore}
104 93384844 Iustin Pop
  @return: a SimpleStore instance
105 10c2650b Iustin Pop

106 10c2650b Iustin Pop
  """
107 93384844 Iustin Pop
  return ssconf.SimpleStore()
108 c657dcc9 Michael Hanselmann
109 c657dcc9 Michael Hanselmann
110 62c9ec92 Iustin Pop
def _GetSshRunner(cluster_name):
111 10c2650b Iustin Pop
  """Simple wrapper to return an SshRunner.
112 10c2650b Iustin Pop

113 10c2650b Iustin Pop
  @type cluster_name: str
114 10c2650b Iustin Pop
  @param cluster_name: the cluster name, which is needed
115 10c2650b Iustin Pop
      by the SshRunner constructor
116 10c2650b Iustin Pop
  @rtype: L{ssh.SshRunner}
117 10c2650b Iustin Pop
  @return: an SshRunner instance
118 10c2650b Iustin Pop

119 10c2650b Iustin Pop
  """
120 62c9ec92 Iustin Pop
  return ssh.SshRunner(cluster_name)
121 c92b310a Michael Hanselmann
122 c92b310a Michael Hanselmann
123 12bce260 Michael Hanselmann
def _Decompress(data):
124 12bce260 Michael Hanselmann
  """Unpacks data compressed by the RPC client.
125 12bce260 Michael Hanselmann

126 12bce260 Michael Hanselmann
  @type data: list or tuple
127 12bce260 Michael Hanselmann
  @param data: Data sent by RPC client
128 12bce260 Michael Hanselmann
  @rtype: str
129 12bce260 Michael Hanselmann
  @return: Decompressed data
130 12bce260 Michael Hanselmann

131 12bce260 Michael Hanselmann
  """
132 52e2f66e Michael Hanselmann
  assert isinstance(data, (list, tuple))
133 12bce260 Michael Hanselmann
  assert len(data) == 2
134 12bce260 Michael Hanselmann
  (encoding, content) = data
135 12bce260 Michael Hanselmann
  if encoding == constants.RPC_ENCODING_NONE:
136 12bce260 Michael Hanselmann
    return content
137 12bce260 Michael Hanselmann
  elif encoding == constants.RPC_ENCODING_ZLIB_BASE64:
138 12bce260 Michael Hanselmann
    return zlib.decompress(base64.b64decode(content))
139 12bce260 Michael Hanselmann
  else:
140 12bce260 Michael Hanselmann
    raise AssertionError("Unknown data encoding")
141 12bce260 Michael Hanselmann
142 12bce260 Michael Hanselmann
143 3bc6be5c Iustin Pop
def _CleanDirectory(path, exclude=None):
144 76ab5558 Michael Hanselmann
  """Removes all regular files in a directory.
145 76ab5558 Michael Hanselmann

146 10c2650b Iustin Pop
  @type path: str
147 10c2650b Iustin Pop
  @param path: the directory to clean
148 76ab5558 Michael Hanselmann
  @type exclude: list
149 10c2650b Iustin Pop
  @param exclude: list of files to be excluded, defaults
150 10c2650b Iustin Pop
      to the empty list
151 76ab5558 Michael Hanselmann

152 76ab5558 Michael Hanselmann
  """
153 714ea7ca Iustin Pop
  if path not in _ALLOWED_CLEAN_DIRS:
154 714ea7ca Iustin Pop
    _Fail("Path passed to _CleanDirectory not in allowed clean targets: '%s'",
155 714ea7ca Iustin Pop
          path)
156 714ea7ca Iustin Pop
157 3956cee1 Michael Hanselmann
  if not os.path.isdir(path):
158 3956cee1 Michael Hanselmann
    return
159 3bc6be5c Iustin Pop
  if exclude is None:
160 3bc6be5c Iustin Pop
    exclude = []
161 3bc6be5c Iustin Pop
  else:
162 3bc6be5c Iustin Pop
    # Normalize excluded paths
163 3bc6be5c Iustin Pop
    exclude = [os.path.normpath(i) for i in exclude]
164 76ab5558 Michael Hanselmann
165 3956cee1 Michael Hanselmann
  for rel_name in utils.ListVisibleFiles(path):
166 c4feafe8 Iustin Pop
    full_name = utils.PathJoin(path, rel_name)
167 76ab5558 Michael Hanselmann
    if full_name in exclude:
168 76ab5558 Michael Hanselmann
      continue
169 3956cee1 Michael Hanselmann
    if os.path.isfile(full_name) and not os.path.islink(full_name):
170 3956cee1 Michael Hanselmann
      utils.RemoveFile(full_name)
171 3956cee1 Michael Hanselmann
172 3956cee1 Michael Hanselmann
173 360b0dc2 Iustin Pop
def _BuildUploadFileList():
174 360b0dc2 Iustin Pop
  """Build the list of allowed upload files.
175 360b0dc2 Iustin Pop

176 360b0dc2 Iustin Pop
  This is abstracted so that it's built only once at module import time.
177 360b0dc2 Iustin Pop

178 360b0dc2 Iustin Pop
  """
179 b397a7d2 Iustin Pop
  allowed_files = set([
180 b397a7d2 Iustin Pop
    constants.CLUSTER_CONF_FILE,
181 b397a7d2 Iustin Pop
    constants.ETC_HOSTS,
182 b397a7d2 Iustin Pop
    constants.SSH_KNOWN_HOSTS_FILE,
183 b397a7d2 Iustin Pop
    constants.VNC_PASSWORD_FILE,
184 b397a7d2 Iustin Pop
    constants.RAPI_CERT_FILE,
185 b397a7d2 Iustin Pop
    constants.RAPI_USERS_FILE,
186 6b7d5878 Michael Hanselmann
    constants.CONFD_HMAC_KEY,
187 b397a7d2 Iustin Pop
    ])
188 b397a7d2 Iustin Pop
189 b397a7d2 Iustin Pop
  for hv_name in constants.HYPER_TYPES:
190 e5a45a16 Iustin Pop
    hv_class = hypervisor.GetHypervisorClass(hv_name)
191 b397a7d2 Iustin Pop
    allowed_files.update(hv_class.GetAncillaryFiles())
192 b397a7d2 Iustin Pop
193 b397a7d2 Iustin Pop
  return frozenset(allowed_files)
194 360b0dc2 Iustin Pop
195 360b0dc2 Iustin Pop
196 360b0dc2 Iustin Pop
_ALLOWED_UPLOAD_FILES = _BuildUploadFileList()
197 360b0dc2 Iustin Pop
198 360b0dc2 Iustin Pop
199 1bc59f76 Michael Hanselmann
def JobQueuePurge():
200 10c2650b Iustin Pop
  """Removes job queue files and archived jobs.
201 10c2650b Iustin Pop

202 c8457ce7 Iustin Pop
  @rtype: tuple
203 c8457ce7 Iustin Pop
  @return: True, None
204 24fc781f Michael Hanselmann

205 24fc781f Michael Hanselmann
  """
206 1bc59f76 Michael Hanselmann
  _CleanDirectory(constants.QUEUE_DIR, exclude=[constants.JOB_QUEUE_LOCK_FILE])
207 24fc781f Michael Hanselmann
  _CleanDirectory(constants.JOB_QUEUE_ARCHIVE_DIR)
208 24fc781f Michael Hanselmann
209 24fc781f Michael Hanselmann
210 bd1e4562 Iustin Pop
def GetMasterInfo():
211 bd1e4562 Iustin Pop
  """Returns master information.
212 bd1e4562 Iustin Pop

213 bd1e4562 Iustin Pop
  This is an utility function to compute master information, either
214 bd1e4562 Iustin Pop
  for consumption here or from the node daemon.
215 bd1e4562 Iustin Pop

216 bd1e4562 Iustin Pop
  @rtype: tuple
217 c26a6bd2 Iustin Pop
  @return: master_netdev, master_ip, master_name
218 2a52a064 Iustin Pop
  @raise RPCFail: in case of errors
219 b1b6ea87 Iustin Pop

220 b1b6ea87 Iustin Pop
  """
221 b1b6ea87 Iustin Pop
  try:
222 c657dcc9 Michael Hanselmann
    cfg = _GetConfig()
223 c657dcc9 Michael Hanselmann
    master_netdev = cfg.GetMasterNetdev()
224 c657dcc9 Michael Hanselmann
    master_ip = cfg.GetMasterIP()
225 c657dcc9 Michael Hanselmann
    master_node = cfg.GetMasterNode()
226 b1b6ea87 Iustin Pop
  except errors.ConfigurationError, err:
227 29921401 Iustin Pop
    _Fail("Cluster configuration incomplete: %s", err, exc=True)
228 bd1e4562 Iustin Pop
  return (master_netdev, master_ip, master_node)
229 b1b6ea87 Iustin Pop
230 b1b6ea87 Iustin Pop
231 3583908a Guido Trotter
def StartMaster(start_daemons, no_voting):
232 a8083063 Iustin Pop
  """Activate local node as master node.
233 a8083063 Iustin Pop

234 1c65840b Iustin Pop
  The function will always try activate the IP address of the master
235 10c2650b Iustin Pop
  (unless someone else has it). It will also start the master daemons,
236 10c2650b Iustin Pop
  based on the start_daemons parameter.
237 10c2650b Iustin Pop

238 10c2650b Iustin Pop
  @type start_daemons: boolean
239 c26a6bd2 Iustin Pop
  @param start_daemons: whether to also start the master
240 10c2650b Iustin Pop
      daemons (ganeti-masterd and ganeti-rapi)
241 3583908a Guido Trotter
  @type no_voting: boolean
242 3583908a Guido Trotter
  @param no_voting: whether to start ganeti-masterd without a node vote
243 3583908a Guido Trotter
      (if start_daemons is True), but still non-interactively
244 10c2650b Iustin Pop
  @rtype: None
245 a8083063 Iustin Pop

246 a8083063 Iustin Pop
  """
247 2a52a064 Iustin Pop
  # GetMasterInfo will raise an exception if not able to return data
248 541741d3 Guido Trotter
  master_netdev, master_ip, _ = GetMasterInfo()
249 a8083063 Iustin Pop
250 396b5733 Iustin Pop
  err_msgs = []
251 b1b6ea87 Iustin Pop
  if utils.TcpPing(master_ip, constants.DEFAULT_NODED_PORT):
252 caad16e2 Iustin Pop
    if utils.OwnIpAddress(master_ip):
253 b1b6ea87 Iustin Pop
      # we already have the ip:
254 b726aff0 Iustin Pop
      logging.debug("Master IP already configured, doing nothing")
255 b1b6ea87 Iustin Pop
    else:
256 b726aff0 Iustin Pop
      msg = "Someone else has the master ip, not activating"
257 b726aff0 Iustin Pop
      logging.error(msg)
258 396b5733 Iustin Pop
      err_msgs.append(msg)
259 b1b6ea87 Iustin Pop
  else:
260 b1b6ea87 Iustin Pop
    result = utils.RunCmd(["ip", "address", "add", "%s/32" % master_ip,
261 b1b6ea87 Iustin Pop
                           "dev", master_netdev, "label",
262 b1b6ea87 Iustin Pop
                           "%s:0" % master_netdev])
263 b1b6ea87 Iustin Pop
    if result.failed:
264 b726aff0 Iustin Pop
      msg = "Can't activate master IP: %s" % result.output
265 b726aff0 Iustin Pop
      logging.error(msg)
266 396b5733 Iustin Pop
      err_msgs.append(msg)
267 b1b6ea87 Iustin Pop
268 b1b6ea87 Iustin Pop
    result = utils.RunCmd(["arping", "-q", "-U", "-c 3", "-I", master_netdev,
269 b1b6ea87 Iustin Pop
                           "-s", master_ip, master_ip])
270 b1b6ea87 Iustin Pop
    # we'll ignore the exit code of arping
271 b1b6ea87 Iustin Pop
272 b1b6ea87 Iustin Pop
  # and now start the master and rapi daemons
273 b1b6ea87 Iustin Pop
  if start_daemons:
274 3583908a Guido Trotter
    if no_voting:
275 f154a7a3 Michael Hanselmann
      masterd_args = "--no-voting --yes-do-it"
276 f154a7a3 Michael Hanselmann
    else:
277 f154a7a3 Michael Hanselmann
      masterd_args = ""
278 f154a7a3 Michael Hanselmann
279 f154a7a3 Michael Hanselmann
    env = {
280 f154a7a3 Michael Hanselmann
      "EXTRA_MASTERD_ARGS": masterd_args,
281 f154a7a3 Michael Hanselmann
      }
282 f154a7a3 Michael Hanselmann
283 f154a7a3 Michael Hanselmann
    result = utils.RunCmd([constants.DAEMON_UTIL, "start-master"], env=env)
284 f154a7a3 Michael Hanselmann
    if result.failed:
285 f154a7a3 Michael Hanselmann
      msg = "Can't start Ganeti master: %s" % result.output
286 f154a7a3 Michael Hanselmann
      logging.error(msg)
287 f154a7a3 Michael Hanselmann
      err_msgs.append(msg)
288 b726aff0 Iustin Pop
289 396b5733 Iustin Pop
  if err_msgs:
290 396b5733 Iustin Pop
    _Fail("; ".join(err_msgs))
291 afdc3985 Iustin Pop
292 a8083063 Iustin Pop
293 1c65840b Iustin Pop
def StopMaster(stop_daemons):
294 a8083063 Iustin Pop
  """Deactivate this node as master.
295 a8083063 Iustin Pop

296 1c65840b Iustin Pop
  The function will always try to deactivate the IP address of the
297 10c2650b Iustin Pop
  master. It will also stop the master daemons depending on the
298 10c2650b Iustin Pop
  stop_daemons parameter.
299 10c2650b Iustin Pop

300 10c2650b Iustin Pop
  @type stop_daemons: boolean
301 10c2650b Iustin Pop
  @param stop_daemons: whether to also stop the master daemons
302 10c2650b Iustin Pop
      (ganeti-masterd and ganeti-rapi)
303 10c2650b Iustin Pop
  @rtype: None
304 a8083063 Iustin Pop

305 a8083063 Iustin Pop
  """
306 6c00d19a Iustin Pop
  # TODO: log and report back to the caller the error failures; we
307 6c00d19a Iustin Pop
  # need to decide in which case we fail the RPC for this
308 2a52a064 Iustin Pop
309 2a52a064 Iustin Pop
  # GetMasterInfo will raise an exception if not able to return data
310 541741d3 Guido Trotter
  master_netdev, master_ip, _ = GetMasterInfo()
311 a8083063 Iustin Pop
312 b1b6ea87 Iustin Pop
  result = utils.RunCmd(["ip", "address", "del", "%s/32" % master_ip,
313 b1b6ea87 Iustin Pop
                         "dev", master_netdev])
314 a8083063 Iustin Pop
  if result.failed:
315 3b9e6a30 Iustin Pop
    logging.error("Can't remove the master IP, error: %s", result.output)
316 b1b6ea87 Iustin Pop
    # but otherwise ignore the failure
317 b1b6ea87 Iustin Pop
318 b1b6ea87 Iustin Pop
  if stop_daemons:
319 f154a7a3 Michael Hanselmann
    result = utils.RunCmd([constants.DAEMON_UTIL, "stop-master"])
320 f154a7a3 Michael Hanselmann
    if result.failed:
321 f154a7a3 Michael Hanselmann
      logging.error("Could not stop Ganeti master, command %s had exitcode %s"
322 f154a7a3 Michael Hanselmann
                    " and error %s",
323 f154a7a3 Michael Hanselmann
                    result.cmd, result.exit_code, result.output)
324 a8083063 Iustin Pop
325 a8083063 Iustin Pop
326 9716fdce Iustin Pop
def AddNode(dsa, dsapub, rsa, rsapub, sshkey, sshpub):
327 7900ed01 Iustin Pop
  """Joins this node to the cluster.
328 a8083063 Iustin Pop

329 7900ed01 Iustin Pop
  This does the following:
330 7900ed01 Iustin Pop
      - updates the hostkeys of the machine (rsa and dsa)
331 7900ed01 Iustin Pop
      - adds the ssh private key to the user
332 7900ed01 Iustin Pop
      - adds the ssh public key to the users' authorized_keys file
333 a8083063 Iustin Pop

334 10c2650b Iustin Pop
  @type dsa: str
335 10c2650b Iustin Pop
  @param dsa: the DSA private key to write
336 10c2650b Iustin Pop
  @type dsapub: str
337 10c2650b Iustin Pop
  @param dsapub: the DSA public key to write
338 10c2650b Iustin Pop
  @type rsa: str
339 10c2650b Iustin Pop
  @param rsa: the RSA private key to write
340 10c2650b Iustin Pop
  @type rsapub: str
341 10c2650b Iustin Pop
  @param rsapub: the RSA public key to write
342 10c2650b Iustin Pop
  @type sshkey: str
343 10c2650b Iustin Pop
  @param sshkey: the SSH private key to write
344 10c2650b Iustin Pop
  @type sshpub: str
345 10c2650b Iustin Pop
  @param sshpub: the SSH public key to write
346 10c2650b Iustin Pop
  @rtype: boolean
347 10c2650b Iustin Pop
  @return: the success of the operation
348 10c2650b Iustin Pop

349 7900ed01 Iustin Pop
  """
350 70d9e3d8 Iustin Pop
  sshd_keys =  [(constants.SSH_HOST_RSA_PRIV, rsa, 0600),
351 70d9e3d8 Iustin Pop
                (constants.SSH_HOST_RSA_PUB, rsapub, 0644),
352 70d9e3d8 Iustin Pop
                (constants.SSH_HOST_DSA_PRIV, dsa, 0600),
353 70d9e3d8 Iustin Pop
                (constants.SSH_HOST_DSA_PUB, dsapub, 0644)]
354 7900ed01 Iustin Pop
  for name, content, mode in sshd_keys:
355 70d9e3d8 Iustin Pop
    utils.WriteFile(name, data=content, mode=mode)
356 a8083063 Iustin Pop
357 70d9e3d8 Iustin Pop
  try:
358 70d9e3d8 Iustin Pop
    priv_key, pub_key, auth_keys = ssh.GetUserFiles(constants.GANETI_RUNAS,
359 70d9e3d8 Iustin Pop
                                                    mkdir=True)
360 70d9e3d8 Iustin Pop
  except errors.OpExecError, err:
361 2cc6781a Iustin Pop
    _Fail("Error while processing user ssh files: %s", err, exc=True)
362 a8083063 Iustin Pop
363 70d9e3d8 Iustin Pop
  for name, content in [(priv_key, sshkey), (pub_key, sshpub)]:
364 70d9e3d8 Iustin Pop
    utils.WriteFile(name, data=content, mode=0600)
365 a8083063 Iustin Pop
366 70d9e3d8 Iustin Pop
  utils.AddAuthorizedKey(auth_keys, sshpub)
367 a8083063 Iustin Pop
368 7e1fac25 Michael Hanselmann
  result = utils.RunCmd([constants.DAEMON_UTIL, "reload-ssh-keys"])
369 7e1fac25 Michael Hanselmann
  if result.failed:
370 7e1fac25 Michael Hanselmann
    _Fail("Unable to reload SSH keys (command %r, exit code %s, output %r)",
371 7e1fac25 Michael Hanselmann
          result.cmd, result.exit_code, result.output)
372 a8083063 Iustin Pop
373 a8083063 Iustin Pop
374 b989b9d9 Ken Wehr
def LeaveCluster(modify_ssh_setup):
375 10c2650b Iustin Pop
  """Cleans up and remove the current node.
376 10c2650b Iustin Pop

377 10c2650b Iustin Pop
  This function cleans up and prepares the current node to be removed
378 10c2650b Iustin Pop
  from the cluster.
379 10c2650b Iustin Pop

380 10c2650b Iustin Pop
  If processing is successful, then it raises an
381 c41eea6e Iustin Pop
  L{errors.QuitGanetiException} which is used as a special case to
382 10c2650b Iustin Pop
  shutdown the node daemon.
383 a8083063 Iustin Pop

384 b989b9d9 Ken Wehr
  @param modify_ssh_setup: boolean
385 b989b9d9 Ken Wehr

386 a8083063 Iustin Pop
  """
387 f78346f5 Michael Hanselmann
  _CleanDirectory(constants.DATA_DIR)
388 1bc59f76 Michael Hanselmann
  JobQueuePurge()
389 f78346f5 Michael Hanselmann
390 b989b9d9 Ken Wehr
  if modify_ssh_setup:
391 b989b9d9 Ken Wehr
    try:
392 b989b9d9 Ken Wehr
      priv_key, pub_key, auth_keys = ssh.GetUserFiles(constants.GANETI_RUNAS)
393 7900ed01 Iustin Pop
394 b989b9d9 Ken Wehr
      utils.RemoveAuthorizedKey(auth_keys, utils.ReadFile(pub_key))
395 a8083063 Iustin Pop
396 b989b9d9 Ken Wehr
      utils.RemoveFile(priv_key)
397 b989b9d9 Ken Wehr
      utils.RemoveFile(pub_key)
398 b989b9d9 Ken Wehr
    except errors.OpExecError:
399 b989b9d9 Ken Wehr
      logging.exception("Error while processing ssh files")
400 a8083063 Iustin Pop
401 ed008420 Guido Trotter
  try:
402 6b7d5878 Michael Hanselmann
    utils.RemoveFile(constants.CONFD_HMAC_KEY)
403 ed008420 Guido Trotter
    utils.RemoveFile(constants.RAPI_CERT_FILE)
404 168c1de2 Michael Hanselmann
    utils.RemoveFile(constants.NODED_CERT_FILE)
405 7260cfbe Iustin Pop
  except: # pylint: disable-msg=W0702
406 ed008420 Guido Trotter
    logging.exception("Error while removing cluster secrets")
407 ed008420 Guido Trotter
408 f154a7a3 Michael Hanselmann
  result = utils.RunCmd([constants.DAEMON_UTIL, "stop", constants.CONFD])
409 f154a7a3 Michael Hanselmann
  if result.failed:
410 f154a7a3 Michael Hanselmann
    logging.error("Command %s failed with exitcode %s and error %s",
411 f154a7a3 Michael Hanselmann
                  result.cmd, result.exit_code, result.output)
412 ed008420 Guido Trotter
413 0623d351 Iustin Pop
  # Raise a custom exception (handled in ganeti-noded)
414 0623d351 Iustin Pop
  raise errors.QuitGanetiException(True, 'Shutdown scheduled')
415 6d8b6238 Guido Trotter
416 a8083063 Iustin Pop
417 e69d05fd Iustin Pop
def GetNodeInfo(vgname, hypervisor_type):
418 5bbd3f7f Michael Hanselmann
  """Gives back a hash with different information about the node.
419 a8083063 Iustin Pop

420 e69d05fd Iustin Pop
  @type vgname: C{string}
421 e69d05fd Iustin Pop
  @param vgname: the name of the volume group to ask for disk space information
422 e69d05fd Iustin Pop
  @type hypervisor_type: C{str}
423 e69d05fd Iustin Pop
  @param hypervisor_type: the name of the hypervisor to ask for
424 e69d05fd Iustin Pop
      memory information
425 e69d05fd Iustin Pop
  @rtype: C{dict}
426 e69d05fd Iustin Pop
  @return: dictionary with the following keys:
427 e69d05fd Iustin Pop
      - vg_size is the size of the configured volume group in MiB
428 e69d05fd Iustin Pop
      - vg_free is the free size of the volume group in MiB
429 e69d05fd Iustin Pop
      - memory_dom0 is the memory allocated for domain0 in MiB
430 e69d05fd Iustin Pop
      - memory_free is the currently available (free) ram in MiB
431 e69d05fd Iustin Pop
      - memory_total is the total number of ram in MiB
432 a8083063 Iustin Pop

433 098c0958 Michael Hanselmann
  """
434 a8083063 Iustin Pop
  outputarray = {}
435 a8083063 Iustin Pop
  vginfo = _GetVGInfo(vgname)
436 a8083063 Iustin Pop
  outputarray['vg_size'] = vginfo['vg_size']
437 a8083063 Iustin Pop
  outputarray['vg_free'] = vginfo['vg_free']
438 a8083063 Iustin Pop
439 e69d05fd Iustin Pop
  hyper = hypervisor.GetHypervisor(hypervisor_type)
440 a8083063 Iustin Pop
  hyp_info = hyper.GetNodeInfo()
441 a8083063 Iustin Pop
  if hyp_info is not None:
442 a8083063 Iustin Pop
    outputarray.update(hyp_info)
443 a8083063 Iustin Pop
444 13998ef2 Michael Hanselmann
  outputarray["bootid"] = utils.ReadFile(_BOOT_ID_PATH, size=128).rstrip("\n")
445 3ef10550 Michael Hanselmann
446 c26a6bd2 Iustin Pop
  return outputarray
447 a8083063 Iustin Pop
448 a8083063 Iustin Pop
449 62c9ec92 Iustin Pop
def VerifyNode(what, cluster_name):
450 a8083063 Iustin Pop
  """Verify the status of the local node.
451 a8083063 Iustin Pop

452 e69d05fd Iustin Pop
  Based on the input L{what} parameter, various checks are done on the
453 e69d05fd Iustin Pop
  local node.
454 e69d05fd Iustin Pop

455 e69d05fd Iustin Pop
  If the I{filelist} key is present, this list of
456 e69d05fd Iustin Pop
  files is checksummed and the file/checksum pairs are returned.
457 e69d05fd Iustin Pop

458 e69d05fd Iustin Pop
  If the I{nodelist} key is present, we check that we have
459 e69d05fd Iustin Pop
  connectivity via ssh with the target nodes (and check the hostname
460 e69d05fd Iustin Pop
  report).
461 a8083063 Iustin Pop

462 e69d05fd Iustin Pop
  If the I{node-net-test} key is present, we check that we have
463 e69d05fd Iustin Pop
  connectivity to the given nodes via both primary IP and, if
464 e69d05fd Iustin Pop
  applicable, secondary IPs.
465 e69d05fd Iustin Pop

466 e69d05fd Iustin Pop
  @type what: C{dict}
467 e69d05fd Iustin Pop
  @param what: a dictionary of things to check:
468 e69d05fd Iustin Pop
      - filelist: list of files for which to compute checksums
469 e69d05fd Iustin Pop
      - nodelist: list of nodes we should check ssh communication with
470 e69d05fd Iustin Pop
      - node-net-test: list of nodes we should check node daemon port
471 e69d05fd Iustin Pop
        connectivity with
472 e69d05fd Iustin Pop
      - hypervisor: list with hypervisors to run the verify for
473 10c2650b Iustin Pop
  @rtype: dict
474 10c2650b Iustin Pop
  @return: a dictionary with the same keys as the input dict, and
475 10c2650b Iustin Pop
      values representing the result of the checks
476 a8083063 Iustin Pop

477 a8083063 Iustin Pop
  """
478 a8083063 Iustin Pop
  result = {}
479 a3a5f850 Iustin Pop
  my_name = utils.HostInfo().name
480 a3a5f850 Iustin Pop
  port = utils.GetDaemonPort(constants.NODED)
481 a8083063 Iustin Pop
482 25361b9a Iustin Pop
  if constants.NV_HYPERVISOR in what:
483 25361b9a Iustin Pop
    result[constants.NV_HYPERVISOR] = tmp = {}
484 25361b9a Iustin Pop
    for hv_name in what[constants.NV_HYPERVISOR]:
485 0cf5e7f5 Iustin Pop
      try:
486 0cf5e7f5 Iustin Pop
        val = hypervisor.GetHypervisor(hv_name).Verify()
487 0cf5e7f5 Iustin Pop
      except errors.HypervisorError, err:
488 0cf5e7f5 Iustin Pop
        val = "Error while checking hypervisor: %s" % str(err)
489 0cf5e7f5 Iustin Pop
      tmp[hv_name] = val
490 25361b9a Iustin Pop
491 25361b9a Iustin Pop
  if constants.NV_FILELIST in what:
492 25361b9a Iustin Pop
    result[constants.NV_FILELIST] = utils.FingerprintFiles(
493 25361b9a Iustin Pop
      what[constants.NV_FILELIST])
494 25361b9a Iustin Pop
495 25361b9a Iustin Pop
  if constants.NV_NODELIST in what:
496 25361b9a Iustin Pop
    result[constants.NV_NODELIST] = tmp = {}
497 25361b9a Iustin Pop
    random.shuffle(what[constants.NV_NODELIST])
498 25361b9a Iustin Pop
    for node in what[constants.NV_NODELIST]:
499 62c9ec92 Iustin Pop
      success, message = _GetSshRunner(cluster_name).VerifyNodeHostname(node)
500 a8083063 Iustin Pop
      if not success:
501 25361b9a Iustin Pop
        tmp[node] = message
502 25361b9a Iustin Pop
503 25361b9a Iustin Pop
  if constants.NV_NODENETTEST in what:
504 25361b9a Iustin Pop
    result[constants.NV_NODENETTEST] = tmp = {}
505 9d4bfc96 Iustin Pop
    my_pip = my_sip = None
506 25361b9a Iustin Pop
    for name, pip, sip in what[constants.NV_NODENETTEST]:
507 9d4bfc96 Iustin Pop
      if name == my_name:
508 9d4bfc96 Iustin Pop
        my_pip = pip
509 9d4bfc96 Iustin Pop
        my_sip = sip
510 9d4bfc96 Iustin Pop
        break
511 9d4bfc96 Iustin Pop
    if not my_pip:
512 25361b9a Iustin Pop
      tmp[my_name] = ("Can't find my own primary/secondary IP"
513 25361b9a Iustin Pop
                      " in the node list")
514 9d4bfc96 Iustin Pop
    else:
515 25361b9a Iustin Pop
      for name, pip, sip in what[constants.NV_NODENETTEST]:
516 9d4bfc96 Iustin Pop
        fail = []
517 9d4bfc96 Iustin Pop
        if not utils.TcpPing(pip, port, source=my_pip):
518 9d4bfc96 Iustin Pop
          fail.append("primary")
519 9d4bfc96 Iustin Pop
        if sip != pip:
520 9d4bfc96 Iustin Pop
          if not utils.TcpPing(sip, port, source=my_sip):
521 9d4bfc96 Iustin Pop
            fail.append("secondary")
522 9d4bfc96 Iustin Pop
        if fail:
523 25361b9a Iustin Pop
          tmp[name] = ("failure using the %s interface(s)" %
524 25361b9a Iustin Pop
                       " and ".join(fail))
525 25361b9a Iustin Pop
526 a3a5f850 Iustin Pop
  if constants.NV_MASTERIP in what:
527 a3a5f850 Iustin Pop
    # FIXME: add checks on incoming data structures (here and in the
528 a3a5f850 Iustin Pop
    # rest of the function)
529 a3a5f850 Iustin Pop
    master_name, master_ip = what[constants.NV_MASTERIP]
530 a3a5f850 Iustin Pop
    if master_name == my_name:
531 a3a5f850 Iustin Pop
      source = constants.LOCALHOST_IP_ADDRESS
532 a3a5f850 Iustin Pop
    else:
533 a3a5f850 Iustin Pop
      source = None
534 a3a5f850 Iustin Pop
    result[constants.NV_MASTERIP] = utils.TcpPing(master_ip, port,
535 a3a5f850 Iustin Pop
                                                  source=source)
536 a3a5f850 Iustin Pop
537 25361b9a Iustin Pop
  if constants.NV_LVLIST in what:
538 ed904904 Iustin Pop
    try:
539 ed904904 Iustin Pop
      val = GetVolumeList(what[constants.NV_LVLIST])
540 ed904904 Iustin Pop
    except RPCFail, err:
541 ed904904 Iustin Pop
      val = str(err)
542 ed904904 Iustin Pop
    result[constants.NV_LVLIST] = val
543 25361b9a Iustin Pop
544 25361b9a Iustin Pop
  if constants.NV_INSTANCELIST in what:
545 0cf5e7f5 Iustin Pop
    # GetInstanceList can fail
546 0cf5e7f5 Iustin Pop
    try:
547 0cf5e7f5 Iustin Pop
      val = GetInstanceList(what[constants.NV_INSTANCELIST])
548 0cf5e7f5 Iustin Pop
    except RPCFail, err:
549 0cf5e7f5 Iustin Pop
      val = str(err)
550 0cf5e7f5 Iustin Pop
    result[constants.NV_INSTANCELIST] = val
551 25361b9a Iustin Pop
552 25361b9a Iustin Pop
  if constants.NV_VGLIST in what:
553 e480923b Iustin Pop
    result[constants.NV_VGLIST] = utils.ListVolumeGroups()
554 25361b9a Iustin Pop
555 d091393e Iustin Pop
  if constants.NV_PVLIST in what:
556 d091393e Iustin Pop
    result[constants.NV_PVLIST] = \
557 d091393e Iustin Pop
      bdev.LogicalVolume.GetPVInfo(what[constants.NV_PVLIST],
558 d091393e Iustin Pop
                                   filter_allocatable=False)
559 d091393e Iustin Pop
560 25361b9a Iustin Pop
  if constants.NV_VERSION in what:
561 e9ce0a64 Iustin Pop
    result[constants.NV_VERSION] = (constants.PROTOCOL_VERSION,
562 e9ce0a64 Iustin Pop
                                    constants.RELEASE_VERSION)
563 25361b9a Iustin Pop
564 25361b9a Iustin Pop
  if constants.NV_HVINFO in what:
565 25361b9a Iustin Pop
    hyper = hypervisor.GetHypervisor(what[constants.NV_HVINFO])
566 25361b9a Iustin Pop
    result[constants.NV_HVINFO] = hyper.GetNodeInfo()
567 9d4bfc96 Iustin Pop
568 6d2e83d5 Iustin Pop
  if constants.NV_DRBDLIST in what:
569 6d2e83d5 Iustin Pop
    try:
570 6d2e83d5 Iustin Pop
      used_minors = bdev.DRBD8.GetUsedDevs().keys()
571 f6eaed12 Iustin Pop
    except errors.BlockDeviceError, err:
572 6d2e83d5 Iustin Pop
      logging.warning("Can't get used minors list", exc_info=True)
573 f6eaed12 Iustin Pop
      used_minors = str(err)
574 6d2e83d5 Iustin Pop
    result[constants.NV_DRBDLIST] = used_minors
575 6d2e83d5 Iustin Pop
576 7c0aa8e9 Iustin Pop
  if constants.NV_NODESETUP in what:
577 7c0aa8e9 Iustin Pop
    result[constants.NV_NODESETUP] = tmpr = []
578 7c0aa8e9 Iustin Pop
    if not os.path.isdir("/sys/block") or not os.path.isdir("/sys/class/net"):
579 7c0aa8e9 Iustin Pop
      tmpr.append("The sysfs filesytem doesn't seem to be mounted"
580 7c0aa8e9 Iustin Pop
                  " under /sys, missing required directories /sys/block"
581 7c0aa8e9 Iustin Pop
                  " and /sys/class/net")
582 7c0aa8e9 Iustin Pop
    if (not os.path.isdir("/proc/sys") or
583 7c0aa8e9 Iustin Pop
        not os.path.isfile("/proc/sysrq-trigger")):
584 7c0aa8e9 Iustin Pop
      tmpr.append("The procfs filesystem doesn't seem to be mounted"
585 7c0aa8e9 Iustin Pop
                  " under /proc, missing required directory /proc/sys and"
586 7c0aa8e9 Iustin Pop
                  " the file /proc/sysrq-trigger")
587 313b2dd4 Michael Hanselmann
588 313b2dd4 Michael Hanselmann
  if constants.NV_TIME in what:
589 313b2dd4 Michael Hanselmann
    result[constants.NV_TIME] = utils.SplitTime(time.time())
590 313b2dd4 Michael Hanselmann
591 c26a6bd2 Iustin Pop
  return result
592 a8083063 Iustin Pop
593 a8083063 Iustin Pop
594 a8083063 Iustin Pop
def GetVolumeList(vg_name):
595 a8083063 Iustin Pop
  """Compute list of logical volumes and their size.
596 a8083063 Iustin Pop

597 10c2650b Iustin Pop
  @type vg_name: str
598 10c2650b Iustin Pop
  @param vg_name: the volume group whose LVs we should list
599 10c2650b Iustin Pop
  @rtype: dict
600 10c2650b Iustin Pop
  @return:
601 10c2650b Iustin Pop
      dictionary of all partions (key) with value being a tuple of
602 10c2650b Iustin Pop
      their size (in MiB), inactive and online status::
603 10c2650b Iustin Pop

604 10c2650b Iustin Pop
        {'test1': ('20.06', True, True)}
605 10c2650b Iustin Pop

606 10c2650b Iustin Pop
      in case of errors, a string is returned with the error
607 10c2650b Iustin Pop
      details.
608 a8083063 Iustin Pop

609 a8083063 Iustin Pop
  """
610 cb2037a2 Iustin Pop
  lvs = {}
611 cb2037a2 Iustin Pop
  sep = '|'
612 cb2037a2 Iustin Pop
  result = utils.RunCmd(["lvs", "--noheadings", "--units=m", "--nosuffix",
613 cb2037a2 Iustin Pop
                         "--separator=%s" % sep,
614 cb2037a2 Iustin Pop
                         "-olv_name,lv_size,lv_attr", vg_name])
615 a8083063 Iustin Pop
  if result.failed:
616 29d376ec Iustin Pop
    _Fail("Failed to list logical volumes, lvs output: %s", result.output)
617 cb2037a2 Iustin Pop
618 df4c2628 Iustin Pop
  valid_line_re = re.compile("^ *([^|]+)\|([0-9.]+)\|([^|]{6})\|?$")
619 cb2037a2 Iustin Pop
  for line in result.stdout.splitlines():
620 df4c2628 Iustin Pop
    line = line.strip()
621 df4c2628 Iustin Pop
    match = valid_line_re.match(line)
622 df4c2628 Iustin Pop
    if not match:
623 18682bca Iustin Pop
      logging.error("Invalid line returned from lvs output: '%s'", line)
624 df4c2628 Iustin Pop
      continue
625 df4c2628 Iustin Pop
    name, size, attr = match.groups()
626 cb2037a2 Iustin Pop
    inactive = attr[4] == '-'
627 cb2037a2 Iustin Pop
    online = attr[5] == 'o'
628 33f2a81a Iustin Pop
    virtual = attr[0] == 'v'
629 33f2a81a Iustin Pop
    if virtual:
630 33f2a81a Iustin Pop
      # we don't want to report such volumes as existing, since they
631 33f2a81a Iustin Pop
      # don't really hold data
632 33f2a81a Iustin Pop
      continue
633 cb2037a2 Iustin Pop
    lvs[name] = (size, inactive, online)
634 cb2037a2 Iustin Pop
635 cb2037a2 Iustin Pop
  return lvs
636 a8083063 Iustin Pop
637 a8083063 Iustin Pop
638 a8083063 Iustin Pop
def ListVolumeGroups():
639 2f8598a5 Alexander Schreiber
  """List the volume groups and their size.
640 a8083063 Iustin Pop

641 10c2650b Iustin Pop
  @rtype: dict
642 10c2650b Iustin Pop
  @return: dictionary with keys volume name and values the
643 10c2650b Iustin Pop
      size of the volume
644 a8083063 Iustin Pop

645 a8083063 Iustin Pop
  """
646 c26a6bd2 Iustin Pop
  return utils.ListVolumeGroups()
647 a8083063 Iustin Pop
648 a8083063 Iustin Pop
649 dcb93971 Michael Hanselmann
def NodeVolumes():
650 dcb93971 Michael Hanselmann
  """List all volumes on this node.
651 dcb93971 Michael Hanselmann

652 10c2650b Iustin Pop
  @rtype: list
653 10c2650b Iustin Pop
  @return:
654 10c2650b Iustin Pop
    A list of dictionaries, each having four keys:
655 10c2650b Iustin Pop
      - name: the logical volume name,
656 10c2650b Iustin Pop
      - size: the size of the logical volume
657 10c2650b Iustin Pop
      - dev: the physical device on which the LV lives
658 10c2650b Iustin Pop
      - vg: the volume group to which it belongs
659 10c2650b Iustin Pop

660 10c2650b Iustin Pop
    In case of errors, we return an empty list and log the
661 10c2650b Iustin Pop
    error.
662 10c2650b Iustin Pop

663 10c2650b Iustin Pop
    Note that since a logical volume can live on multiple physical
664 10c2650b Iustin Pop
    volumes, the resulting list might include a logical volume
665 10c2650b Iustin Pop
    multiple times.
666 10c2650b Iustin Pop

667 dcb93971 Michael Hanselmann
  """
668 dcb93971 Michael Hanselmann
  result = utils.RunCmd(["lvs", "--noheadings", "--units=m", "--nosuffix",
669 dcb93971 Michael Hanselmann
                         "--separator=|",
670 dcb93971 Michael Hanselmann
                         "--options=lv_name,lv_size,devices,vg_name"])
671 dcb93971 Michael Hanselmann
  if result.failed:
672 10bfe6cb Iustin Pop
    _Fail("Failed to list logical volumes, lvs output: %s",
673 10bfe6cb Iustin Pop
          result.output)
674 dcb93971 Michael Hanselmann
675 dcb93971 Michael Hanselmann
  def parse_dev(dev):
676 89e5ab02 Iustin Pop
    return dev.split('(')[0]
677 89e5ab02 Iustin Pop
678 89e5ab02 Iustin Pop
  def handle_dev(dev):
679 89e5ab02 Iustin Pop
    return [parse_dev(x) for x in dev.split(",")]
680 dcb93971 Michael Hanselmann
681 dcb93971 Michael Hanselmann
  def map_line(line):
682 89e5ab02 Iustin Pop
    line = [v.strip() for v in line]
683 89e5ab02 Iustin Pop
    return [{'name': line[0], 'size': line[1],
684 89e5ab02 Iustin Pop
             'dev': dev, 'vg': line[3]} for dev in handle_dev(line[2])]
685 89e5ab02 Iustin Pop
686 89e5ab02 Iustin Pop
  all_devs = []
687 89e5ab02 Iustin Pop
  for line in result.stdout.splitlines():
688 89e5ab02 Iustin Pop
    if line.count('|') >= 3:
689 89e5ab02 Iustin Pop
      all_devs.extend(map_line(line.split('|')))
690 89e5ab02 Iustin Pop
    else:
691 89e5ab02 Iustin Pop
      logging.warning("Strange line in the output from lvs: '%s'", line)
692 89e5ab02 Iustin Pop
  return all_devs
693 dcb93971 Michael Hanselmann
694 dcb93971 Michael Hanselmann
695 a8083063 Iustin Pop
def BridgesExist(bridges_list):
696 2f8598a5 Alexander Schreiber
  """Check if a list of bridges exist on the current node.
697 a8083063 Iustin Pop

698 b1206984 Iustin Pop
  @rtype: boolean
699 b1206984 Iustin Pop
  @return: C{True} if all of them exist, C{False} otherwise
700 a8083063 Iustin Pop

701 a8083063 Iustin Pop
  """
702 35c0c8da Iustin Pop
  missing = []
703 a8083063 Iustin Pop
  for bridge in bridges_list:
704 a8083063 Iustin Pop
    if not utils.BridgeExists(bridge):
705 35c0c8da Iustin Pop
      missing.append(bridge)
706 a8083063 Iustin Pop
707 35c0c8da Iustin Pop
  if missing:
708 1f864b60 Iustin Pop
    _Fail("Missing bridges %s", utils.CommaJoin(missing))
709 35c0c8da Iustin Pop
710 a8083063 Iustin Pop
711 e69d05fd Iustin Pop
def GetInstanceList(hypervisor_list):
712 2f8598a5 Alexander Schreiber
  """Provides a list of instances.
713 a8083063 Iustin Pop

714 e69d05fd Iustin Pop
  @type hypervisor_list: list
715 e69d05fd Iustin Pop
  @param hypervisor_list: the list of hypervisors to query information
716 e69d05fd Iustin Pop

717 e69d05fd Iustin Pop
  @rtype: list
718 e69d05fd Iustin Pop
  @return: a list of all running instances on the current node
719 10c2650b Iustin Pop
    - instance1.example.com
720 10c2650b Iustin Pop
    - instance2.example.com
721 a8083063 Iustin Pop

722 098c0958 Michael Hanselmann
  """
723 e69d05fd Iustin Pop
  results = []
724 e69d05fd Iustin Pop
  for hname in hypervisor_list:
725 e69d05fd Iustin Pop
    try:
726 e69d05fd Iustin Pop
      names = hypervisor.GetHypervisor(hname).ListInstances()
727 e69d05fd Iustin Pop
      results.extend(names)
728 e69d05fd Iustin Pop
    except errors.HypervisorError, err:
729 aca13712 Iustin Pop
      _Fail("Error enumerating instances (hypervisor %s): %s",
730 aca13712 Iustin Pop
            hname, err, exc=True)
731 a8083063 Iustin Pop
732 e69d05fd Iustin Pop
  return results
733 a8083063 Iustin Pop
734 a8083063 Iustin Pop
735 e69d05fd Iustin Pop
def GetInstanceInfo(instance, hname):
736 5bbd3f7f Michael Hanselmann
  """Gives back the information about an instance as a dictionary.
737 a8083063 Iustin Pop

738 e69d05fd Iustin Pop
  @type instance: string
739 e69d05fd Iustin Pop
  @param instance: the instance name
740 e69d05fd Iustin Pop
  @type hname: string
741 e69d05fd Iustin Pop
  @param hname: the hypervisor type of the instance
742 a8083063 Iustin Pop

743 e69d05fd Iustin Pop
  @rtype: dict
744 e69d05fd Iustin Pop
  @return: dictionary with the following keys:
745 e69d05fd Iustin Pop
      - memory: memory size of instance (int)
746 e69d05fd Iustin Pop
      - state: xen state of instance (string)
747 e69d05fd Iustin Pop
      - time: cpu time of instance (float)
748 a8083063 Iustin Pop

749 098c0958 Michael Hanselmann
  """
750 a8083063 Iustin Pop
  output = {}
751 a8083063 Iustin Pop
752 e69d05fd Iustin Pop
  iinfo = hypervisor.GetHypervisor(hname).GetInstanceInfo(instance)
753 a8083063 Iustin Pop
  if iinfo is not None:
754 a8083063 Iustin Pop
    output['memory'] = iinfo[2]
755 a8083063 Iustin Pop
    output['state'] = iinfo[4]
756 a8083063 Iustin Pop
    output['time'] = iinfo[5]
757 a8083063 Iustin Pop
758 c26a6bd2 Iustin Pop
  return output
759 a8083063 Iustin Pop
760 a8083063 Iustin Pop
761 56e7640c Iustin Pop
def GetInstanceMigratable(instance):
762 56e7640c Iustin Pop
  """Gives whether an instance can be migrated.
763 56e7640c Iustin Pop

764 56e7640c Iustin Pop
  @type instance: L{objects.Instance}
765 56e7640c Iustin Pop
  @param instance: object representing the instance to be checked.
766 56e7640c Iustin Pop

767 56e7640c Iustin Pop
  @rtype: tuple
768 56e7640c Iustin Pop
  @return: tuple of (result, description) where:
769 56e7640c Iustin Pop
      - result: whether the instance can be migrated or not
770 56e7640c Iustin Pop
      - description: a description of the issue, if relevant
771 56e7640c Iustin Pop

772 56e7640c Iustin Pop
  """
773 56e7640c Iustin Pop
  hyper = hypervisor.GetHypervisor(instance.hypervisor)
774 afdc3985 Iustin Pop
  iname = instance.name
775 afdc3985 Iustin Pop
  if iname not in hyper.ListInstances():
776 afdc3985 Iustin Pop
    _Fail("Instance %s is not running", iname)
777 56e7640c Iustin Pop
778 56e7640c Iustin Pop
  for idx in range(len(instance.disks)):
779 afdc3985 Iustin Pop
    link_name = _GetBlockDevSymlinkPath(iname, idx)
780 56e7640c Iustin Pop
    if not os.path.islink(link_name):
781 afdc3985 Iustin Pop
      _Fail("Instance %s was not restarted since ganeti 1.2.5", iname)
782 56e7640c Iustin Pop
783 56e7640c Iustin Pop
784 e69d05fd Iustin Pop
def GetAllInstancesInfo(hypervisor_list):
785 a8083063 Iustin Pop
  """Gather data about all instances.
786 a8083063 Iustin Pop

787 10c2650b Iustin Pop
  This is the equivalent of L{GetInstanceInfo}, except that it
788 a8083063 Iustin Pop
  computes data for all instances at once, thus being faster if one
789 a8083063 Iustin Pop
  needs data about more than one instance.
790 a8083063 Iustin Pop

791 e69d05fd Iustin Pop
  @type hypervisor_list: list
792 e69d05fd Iustin Pop
  @param hypervisor_list: list of hypervisors to query for instance data
793 e69d05fd Iustin Pop

794 955db481 Guido Trotter
  @rtype: dict
795 e69d05fd Iustin Pop
  @return: dictionary of instance: data, with data having the following keys:
796 e69d05fd Iustin Pop
      - memory: memory size of instance (int)
797 e69d05fd Iustin Pop
      - state: xen state of instance (string)
798 e69d05fd Iustin Pop
      - time: cpu time of instance (float)
799 10c2650b Iustin Pop
      - vcpus: the number of vcpus
800 a8083063 Iustin Pop

801 098c0958 Michael Hanselmann
  """
802 a8083063 Iustin Pop
  output = {}
803 a8083063 Iustin Pop
804 e69d05fd Iustin Pop
  for hname in hypervisor_list:
805 e69d05fd Iustin Pop
    iinfo = hypervisor.GetHypervisor(hname).GetAllInstancesInfo()
806 e69d05fd Iustin Pop
    if iinfo:
807 29921401 Iustin Pop
      for name, _, memory, vcpus, state, times in iinfo:
808 f23b5ae8 Iustin Pop
        value = {
809 e69d05fd Iustin Pop
          'memory': memory,
810 e69d05fd Iustin Pop
          'vcpus': vcpus,
811 e69d05fd Iustin Pop
          'state': state,
812 e69d05fd Iustin Pop
          'time': times,
813 e69d05fd Iustin Pop
          }
814 b33b6f55 Iustin Pop
        if name in output:
815 b33b6f55 Iustin Pop
          # we only check static parameters, like memory and vcpus,
816 b33b6f55 Iustin Pop
          # and not state and time which can change between the
817 b33b6f55 Iustin Pop
          # invocations of the different hypervisors
818 b33b6f55 Iustin Pop
          for key in 'memory', 'vcpus':
819 b33b6f55 Iustin Pop
            if value[key] != output[name][key]:
820 2fa74ef4 Iustin Pop
              _Fail("Instance %s is running twice"
821 2fa74ef4 Iustin Pop
                    " with different parameters", name)
822 f23b5ae8 Iustin Pop
        output[name] = value
823 a8083063 Iustin Pop
824 c26a6bd2 Iustin Pop
  return output
825 a8083063 Iustin Pop
826 a8083063 Iustin Pop
827 81a3406c Iustin Pop
def _InstanceLogName(kind, os_name, instance):
828 81a3406c Iustin Pop
  """Compute the OS log filename for a given instance and operation.
829 81a3406c Iustin Pop

830 81a3406c Iustin Pop
  The instance name and os name are passed in as strings since not all
831 81a3406c Iustin Pop
  operations have these as part of an instance object.
832 81a3406c Iustin Pop

833 81a3406c Iustin Pop
  @type kind: string
834 81a3406c Iustin Pop
  @param kind: the operation type (e.g. add, import, etc.)
835 81a3406c Iustin Pop
  @type os_name: string
836 81a3406c Iustin Pop
  @param os_name: the os name
837 81a3406c Iustin Pop
  @type instance: string
838 81a3406c Iustin Pop
  @param instance: the name of the instance being imported/added/etc.
839 81a3406c Iustin Pop

840 81a3406c Iustin Pop
  """
841 1d466a4f Michael Hanselmann
  base = ("%s-%s-%s-%s.log" %
842 1d466a4f Michael Hanselmann
          (kind, os_name, instance, utils.TimestampForFilename()))
843 81a3406c Iustin Pop
  return utils.PathJoin(constants.LOG_OS_DIR, base)
844 81a3406c Iustin Pop
845 81a3406c Iustin Pop
846 4a0e011f Iustin Pop
def InstanceOsAdd(instance, reinstall, debug):
847 2f8598a5 Alexander Schreiber
  """Add an OS to an instance.
848 a8083063 Iustin Pop

849 d15a9ad3 Guido Trotter
  @type instance: L{objects.Instance}
850 d15a9ad3 Guido Trotter
  @param instance: Instance whose OS is to be installed
851 e557bae9 Guido Trotter
  @type reinstall: boolean
852 e557bae9 Guido Trotter
  @param reinstall: whether this is an instance reinstall
853 4a0e011f Iustin Pop
  @type debug: integer
854 4a0e011f Iustin Pop
  @param debug: debug level, passed to the OS scripts
855 c26a6bd2 Iustin Pop
  @rtype: None
856 a8083063 Iustin Pop

857 a8083063 Iustin Pop
  """
858 255dcebd Iustin Pop
  inst_os = OSFromDisk(instance.os)
859 255dcebd Iustin Pop
860 4a0e011f Iustin Pop
  create_env = OSEnvironment(instance, inst_os, debug)
861 e557bae9 Guido Trotter
  if reinstall:
862 e557bae9 Guido Trotter
    create_env['INSTANCE_REINSTALL'] = "1"
863 a8083063 Iustin Pop
864 81a3406c Iustin Pop
  logfile = _InstanceLogName("add", instance.os, instance.name)
865 decd5f45 Iustin Pop
866 d868edb4 Iustin Pop
  result = utils.RunCmd([inst_os.create_script], env=create_env,
867 d868edb4 Iustin Pop
                        cwd=inst_os.path, output=logfile,)
868 decd5f45 Iustin Pop
  if result.failed:
869 18682bca Iustin Pop
    logging.error("os create command '%s' returned error: %s, logfile: %s,"
870 d868edb4 Iustin Pop
                  " output: %s", result.cmd, result.fail_reason, logfile,
871 18682bca Iustin Pop
                  result.output)
872 26f15862 Iustin Pop
    lines = [utils.SafeEncode(val)
873 20e01edd Iustin Pop
             for val in utils.TailFile(logfile, lines=20)]
874 afdc3985 Iustin Pop
    _Fail("OS create script failed (%s), last lines in the"
875 afdc3985 Iustin Pop
          " log file:\n%s", result.fail_reason, "\n".join(lines), log=False)
876 decd5f45 Iustin Pop
877 decd5f45 Iustin Pop
878 4a0e011f Iustin Pop
def RunRenameInstance(instance, old_name, debug):
879 decd5f45 Iustin Pop
  """Run the OS rename script for an instance.
880 decd5f45 Iustin Pop

881 b1206984 Iustin Pop
  @type instance: L{objects.Instance}
882 d15a9ad3 Guido Trotter
  @param instance: Instance whose OS is to be installed
883 d15a9ad3 Guido Trotter
  @type old_name: string
884 d15a9ad3 Guido Trotter
  @param old_name: previous instance name
885 4a0e011f Iustin Pop
  @type debug: integer
886 4a0e011f Iustin Pop
  @param debug: debug level, passed to the OS scripts
887 10c2650b Iustin Pop
  @rtype: boolean
888 10c2650b Iustin Pop
  @return: the success of the operation
889 decd5f45 Iustin Pop

890 decd5f45 Iustin Pop
  """
891 decd5f45 Iustin Pop
  inst_os = OSFromDisk(instance.os)
892 decd5f45 Iustin Pop
893 4a0e011f Iustin Pop
  rename_env = OSEnvironment(instance, inst_os, debug)
894 ff38b6c0 Guido Trotter
  rename_env['OLD_INSTANCE_NAME'] = old_name
895 decd5f45 Iustin Pop
896 81a3406c Iustin Pop
  logfile = _InstanceLogName("rename", instance.os,
897 81a3406c Iustin Pop
                             "%s-%s" % (old_name, instance.name))
898 a8083063 Iustin Pop
899 d868edb4 Iustin Pop
  result = utils.RunCmd([inst_os.rename_script], env=rename_env,
900 d868edb4 Iustin Pop
                        cwd=inst_os.path, output=logfile)
901 a8083063 Iustin Pop
902 a8083063 Iustin Pop
  if result.failed:
903 18682bca Iustin Pop
    logging.error("os create command '%s' returned error: %s output: %s",
904 d868edb4 Iustin Pop
                  result.cmd, result.fail_reason, result.output)
905 26f15862 Iustin Pop
    lines = [utils.SafeEncode(val)
906 96841384 Iustin Pop
             for val in utils.TailFile(logfile, lines=20)]
907 afdc3985 Iustin Pop
    _Fail("OS rename script failed (%s), last lines in the"
908 afdc3985 Iustin Pop
          " log file:\n%s", result.fail_reason, "\n".join(lines), log=False)
909 a8083063 Iustin Pop
910 a8083063 Iustin Pop
911 a8083063 Iustin Pop
def _GetVGInfo(vg_name):
912 5bbd3f7f Michael Hanselmann
  """Get information about the volume group.
913 a8083063 Iustin Pop

914 10c2650b Iustin Pop
  @type vg_name: str
915 10c2650b Iustin Pop
  @param vg_name: the volume group which we query
916 10c2650b Iustin Pop
  @rtype: dict
917 10c2650b Iustin Pop
  @return:
918 10c2650b Iustin Pop
    A dictionary with the following keys:
919 10c2650b Iustin Pop
      - C{vg_size} is the total size of the volume group in MiB
920 10c2650b Iustin Pop
      - C{vg_free} is the free size of the volume group in MiB
921 10c2650b Iustin Pop
      - C{pv_count} are the number of physical disks in that VG
922 a8083063 Iustin Pop

923 10c2650b Iustin Pop
    If an error occurs during gathering of data, we return the same dict
924 10c2650b Iustin Pop
    with keys all set to None.
925 f4d377e7 Iustin Pop

926 a8083063 Iustin Pop
  """
927 f4d377e7 Iustin Pop
  retdic = dict.fromkeys(["vg_size", "vg_free", "pv_count"])
928 f4d377e7 Iustin Pop
929 a8083063 Iustin Pop
  retval = utils.RunCmd(["vgs", "-ovg_size,vg_free,pv_count", "--noheadings",
930 a8083063 Iustin Pop
                         "--nosuffix", "--units=m", "--separator=:", vg_name])
931 a8083063 Iustin Pop
932 a8083063 Iustin Pop
  if retval.failed:
933 18682bca Iustin Pop
    logging.error("volume group %s not present", vg_name)
934 f4d377e7 Iustin Pop
    return retdic
935 d87ae7d2 Iustin Pop
  valarr = retval.stdout.strip().rstrip(':').split(':')
936 f4d377e7 Iustin Pop
  if len(valarr) == 3:
937 f4d377e7 Iustin Pop
    try:
938 f4d377e7 Iustin Pop
      retdic = {
939 f4d377e7 Iustin Pop
        "vg_size": int(round(float(valarr[0]), 0)),
940 f4d377e7 Iustin Pop
        "vg_free": int(round(float(valarr[1]), 0)),
941 f4d377e7 Iustin Pop
        "pv_count": int(valarr[2]),
942 f4d377e7 Iustin Pop
        }
943 691744c4 Iustin Pop
    except (TypeError, ValueError), err:
944 29921401 Iustin Pop
      logging.exception("Fail to parse vgs output: %s", err)
945 f4d377e7 Iustin Pop
  else:
946 18682bca Iustin Pop
    logging.error("vgs output has the wrong number of fields (expected"
947 18682bca Iustin Pop
                  " three): %s", str(valarr))
948 a8083063 Iustin Pop
  return retdic
949 a8083063 Iustin Pop
950 a8083063 Iustin Pop
951 5282084b Iustin Pop
def _GetBlockDevSymlinkPath(instance_name, idx):
952 c4feafe8 Iustin Pop
  return utils.PathJoin(constants.DISK_LINKS_DIR,
953 c4feafe8 Iustin Pop
                        "%s:%d" % (instance_name, idx))
954 5282084b Iustin Pop
955 5282084b Iustin Pop
956 5282084b Iustin Pop
def _SymlinkBlockDev(instance_name, device_path, idx):
957 9332fd8a Iustin Pop
  """Set up symlinks to a instance's block device.
958 9332fd8a Iustin Pop

959 9332fd8a Iustin Pop
  This is an auxiliary function run when an instance is start (on the primary
960 9332fd8a Iustin Pop
  node) or when an instance is migrated (on the target node).
961 9332fd8a Iustin Pop

962 9332fd8a Iustin Pop

963 5282084b Iustin Pop
  @param instance_name: the name of the target instance
964 5282084b Iustin Pop
  @param device_path: path of the physical block device, on the node
965 5282084b Iustin Pop
  @param idx: the disk index
966 5282084b Iustin Pop
  @return: absolute path to the disk's symlink
967 9332fd8a Iustin Pop

968 9332fd8a Iustin Pop
  """
969 5282084b Iustin Pop
  link_name = _GetBlockDevSymlinkPath(instance_name, idx)
970 9332fd8a Iustin Pop
  try:
971 9332fd8a Iustin Pop
    os.symlink(device_path, link_name)
972 5282084b Iustin Pop
  except OSError, err:
973 5282084b Iustin Pop
    if err.errno == errno.EEXIST:
974 9332fd8a Iustin Pop
      if (not os.path.islink(link_name) or
975 9332fd8a Iustin Pop
          os.readlink(link_name) != device_path):
976 9332fd8a Iustin Pop
        os.remove(link_name)
977 9332fd8a Iustin Pop
        os.symlink(device_path, link_name)
978 9332fd8a Iustin Pop
    else:
979 9332fd8a Iustin Pop
      raise
980 9332fd8a Iustin Pop
981 9332fd8a Iustin Pop
  return link_name
982 9332fd8a Iustin Pop
983 9332fd8a Iustin Pop
984 5282084b Iustin Pop
def _RemoveBlockDevLinks(instance_name, disks):
985 3c9c571d Iustin Pop
  """Remove the block device symlinks belonging to the given instance.
986 3c9c571d Iustin Pop

987 3c9c571d Iustin Pop
  """
988 29921401 Iustin Pop
  for idx, _ in enumerate(disks):
989 5282084b Iustin Pop
    link_name = _GetBlockDevSymlinkPath(instance_name, idx)
990 5282084b Iustin Pop
    if os.path.islink(link_name):
991 3c9c571d Iustin Pop
      try:
992 03dfa658 Iustin Pop
        os.remove(link_name)
993 03dfa658 Iustin Pop
      except OSError:
994 03dfa658 Iustin Pop
        logging.exception("Can't remove symlink '%s'", link_name)
995 3c9c571d Iustin Pop
996 3c9c571d Iustin Pop
997 9332fd8a Iustin Pop
def _GatherAndLinkBlockDevs(instance):
998 a8083063 Iustin Pop
  """Set up an instance's block device(s).
999 a8083063 Iustin Pop

1000 a8083063 Iustin Pop
  This is run on the primary node at instance startup. The block
1001 a8083063 Iustin Pop
  devices must be already assembled.
1002 a8083063 Iustin Pop

1003 10c2650b Iustin Pop
  @type instance: L{objects.Instance}
1004 10c2650b Iustin Pop
  @param instance: the instance whose disks we shoul assemble
1005 069cfbf1 Iustin Pop
  @rtype: list
1006 069cfbf1 Iustin Pop
  @return: list of (disk_object, device_path)
1007 10c2650b Iustin Pop

1008 a8083063 Iustin Pop
  """
1009 a8083063 Iustin Pop
  block_devices = []
1010 9332fd8a Iustin Pop
  for idx, disk in enumerate(instance.disks):
1011 a8083063 Iustin Pop
    device = _RecursiveFindBD(disk)
1012 a8083063 Iustin Pop
    if device is None:
1013 a8083063 Iustin Pop
      raise errors.BlockDeviceError("Block device '%s' is not set up." %
1014 a8083063 Iustin Pop
                                    str(disk))
1015 a8083063 Iustin Pop
    device.Open()
1016 9332fd8a Iustin Pop
    try:
1017 5282084b Iustin Pop
      link_name = _SymlinkBlockDev(instance.name, device.dev_path, idx)
1018 9332fd8a Iustin Pop
    except OSError, e:
1019 9332fd8a Iustin Pop
      raise errors.BlockDeviceError("Cannot create block device symlink: %s" %
1020 9332fd8a Iustin Pop
                                    e.strerror)
1021 9332fd8a Iustin Pop
1022 9332fd8a Iustin Pop
    block_devices.append((disk, link_name))
1023 9332fd8a Iustin Pop
1024 a8083063 Iustin Pop
  return block_devices
1025 a8083063 Iustin Pop
1026 a8083063 Iustin Pop
1027 07813a9e Iustin Pop
def StartInstance(instance):
1028 a8083063 Iustin Pop
  """Start an instance.
1029 a8083063 Iustin Pop

1030 10c2650b Iustin Pop
  @type instance: L{objects.Instance}
1031 e69d05fd Iustin Pop
  @param instance: the instance object
1032 c26a6bd2 Iustin Pop
  @rtype: None
1033 a8083063 Iustin Pop

1034 098c0958 Michael Hanselmann
  """
1035 e69d05fd Iustin Pop
  running_instances = GetInstanceList([instance.hypervisor])
1036 a8083063 Iustin Pop
1037 a8083063 Iustin Pop
  if instance.name in running_instances:
1038 c26a6bd2 Iustin Pop
    logging.info("Instance %s already running, not starting", instance.name)
1039 c26a6bd2 Iustin Pop
    return
1040 a8083063 Iustin Pop
1041 a8083063 Iustin Pop
  try:
1042 ec596c24 Iustin Pop
    block_devices = _GatherAndLinkBlockDevs(instance)
1043 ec596c24 Iustin Pop
    hyper = hypervisor.GetHypervisor(instance.hypervisor)
1044 07813a9e Iustin Pop
    hyper.StartInstance(instance, block_devices)
1045 ec596c24 Iustin Pop
  except errors.BlockDeviceError, err:
1046 2cc6781a Iustin Pop
    _Fail("Block device error: %s", err, exc=True)
1047 a8083063 Iustin Pop
  except errors.HypervisorError, err:
1048 5282084b Iustin Pop
    _RemoveBlockDevLinks(instance.name, instance.disks)
1049 2cc6781a Iustin Pop
    _Fail("Hypervisor error: %s", err, exc=True)
1050 a8083063 Iustin Pop
1051 a8083063 Iustin Pop
1052 6263189c Guido Trotter
def InstanceShutdown(instance, timeout):
1053 a8083063 Iustin Pop
  """Shut an instance down.
1054 a8083063 Iustin Pop

1055 10c2650b Iustin Pop
  @note: this functions uses polling with a hardcoded timeout.
1056 10c2650b Iustin Pop

1057 10c2650b Iustin Pop
  @type instance: L{objects.Instance}
1058 e69d05fd Iustin Pop
  @param instance: the instance object
1059 6263189c Guido Trotter
  @type timeout: integer
1060 6263189c Guido Trotter
  @param timeout: maximum timeout for soft shutdown
1061 c26a6bd2 Iustin Pop
  @rtype: None
1062 a8083063 Iustin Pop

1063 098c0958 Michael Hanselmann
  """
1064 e69d05fd Iustin Pop
  hv_name = instance.hypervisor
1065 e4e9b806 Guido Trotter
  hyper = hypervisor.GetHypervisor(hv_name)
1066 c26a6bd2 Iustin Pop
  iname = instance.name
1067 a8083063 Iustin Pop
1068 3c0cdc83 Michael Hanselmann
  if instance.name not in hyper.ListInstances():
1069 c26a6bd2 Iustin Pop
    logging.info("Instance %s not running, doing nothing", iname)
1070 c26a6bd2 Iustin Pop
    return
1071 a8083063 Iustin Pop
1072 3c0cdc83 Michael Hanselmann
  class _TryShutdown:
1073 3c0cdc83 Michael Hanselmann
    def __init__(self):
1074 3c0cdc83 Michael Hanselmann
      self.tried_once = False
1075 a8083063 Iustin Pop
1076 3c0cdc83 Michael Hanselmann
    def __call__(self):
1077 3c0cdc83 Michael Hanselmann
      if iname not in hyper.ListInstances():
1078 3c0cdc83 Michael Hanselmann
        return
1079 3c0cdc83 Michael Hanselmann
1080 3c0cdc83 Michael Hanselmann
      try:
1081 3c0cdc83 Michael Hanselmann
        hyper.StopInstance(instance, retry=self.tried_once)
1082 3c0cdc83 Michael Hanselmann
      except errors.HypervisorError, err:
1083 3c0cdc83 Michael Hanselmann
        if iname not in hyper.ListInstances():
1084 3c0cdc83 Michael Hanselmann
          # if the instance is no longer existing, consider this a
1085 3c0cdc83 Michael Hanselmann
          # success and go to cleanup
1086 3c0cdc83 Michael Hanselmann
          return
1087 3c0cdc83 Michael Hanselmann
1088 3c0cdc83 Michael Hanselmann
        _Fail("Failed to stop instance %s: %s", iname, err)
1089 3c0cdc83 Michael Hanselmann
1090 3c0cdc83 Michael Hanselmann
      self.tried_once = True
1091 3c0cdc83 Michael Hanselmann
1092 3c0cdc83 Michael Hanselmann
      raise utils.RetryAgain()
1093 3c0cdc83 Michael Hanselmann
1094 3c0cdc83 Michael Hanselmann
  try:
1095 3c0cdc83 Michael Hanselmann
    utils.Retry(_TryShutdown(), 5, timeout)
1096 3c0cdc83 Michael Hanselmann
  except utils.RetryTimeout:
1097 a8083063 Iustin Pop
    # the shutdown did not succeed
1098 e4e9b806 Guido Trotter
    logging.error("Shutdown of '%s' unsuccessful, forcing", iname)
1099 a8083063 Iustin Pop
1100 a8083063 Iustin Pop
    try:
1101 a8083063 Iustin Pop
      hyper.StopInstance(instance, force=True)
1102 a8083063 Iustin Pop
    except errors.HypervisorError, err:
1103 3c0cdc83 Michael Hanselmann
      if iname in hyper.ListInstances():
1104 3782acd7 Iustin Pop
        # only raise an error if the instance still exists, otherwise
1105 3782acd7 Iustin Pop
        # the error could simply be "instance ... unknown"!
1106 3782acd7 Iustin Pop
        _Fail("Failed to force stop instance %s: %s", iname, err)
1107 a8083063 Iustin Pop
1108 a8083063 Iustin Pop
    time.sleep(1)
1109 3c0cdc83 Michael Hanselmann
1110 3c0cdc83 Michael Hanselmann
    if iname in hyper.ListInstances():
1111 c26a6bd2 Iustin Pop
      _Fail("Could not shutdown instance %s even by destroy", iname)
1112 3c9c571d Iustin Pop
1113 f28ec899 Guido Trotter
  try:
1114 f28ec899 Guido Trotter
    hyper.CleanupInstance(instance.name)
1115 f28ec899 Guido Trotter
  except errors.HypervisorError, err:
1116 f28ec899 Guido Trotter
    logging.warning("Failed to execute post-shutdown cleanup step: %s", err)
1117 f28ec899 Guido Trotter
1118 c26a6bd2 Iustin Pop
  _RemoveBlockDevLinks(iname, instance.disks)
1119 a8083063 Iustin Pop
1120 a8083063 Iustin Pop
1121 17c3f802 Guido Trotter
def InstanceReboot(instance, reboot_type, shutdown_timeout):
1122 007a2f3e Alexander Schreiber
  """Reboot an instance.
1123 007a2f3e Alexander Schreiber

1124 10c2650b Iustin Pop
  @type instance: L{objects.Instance}
1125 10c2650b Iustin Pop
  @param instance: the instance object to reboot
1126 10c2650b Iustin Pop
  @type reboot_type: str
1127 10c2650b Iustin Pop
  @param reboot_type: the type of reboot, one the following
1128 10c2650b Iustin Pop
    constants:
1129 10c2650b Iustin Pop
      - L{constants.INSTANCE_REBOOT_SOFT}: only reboot the
1130 10c2650b Iustin Pop
        instance OS, do not recreate the VM
1131 10c2650b Iustin Pop
      - L{constants.INSTANCE_REBOOT_HARD}: tear down and
1132 10c2650b Iustin Pop
        restart the VM (at the hypervisor level)
1133 73e5a4f4 Iustin Pop
      - the other reboot type (L{constants.INSTANCE_REBOOT_FULL}) is
1134 73e5a4f4 Iustin Pop
        not accepted here, since that mode is handled differently, in
1135 73e5a4f4 Iustin Pop
        cmdlib, and translates into full stop and start of the
1136 73e5a4f4 Iustin Pop
        instance (instead of a call_instance_reboot RPC)
1137 23057d29 Michael Hanselmann
  @type shutdown_timeout: integer
1138 23057d29 Michael Hanselmann
  @param shutdown_timeout: maximum timeout for soft shutdown
1139 c26a6bd2 Iustin Pop
  @rtype: None
1140 007a2f3e Alexander Schreiber

1141 007a2f3e Alexander Schreiber
  """
1142 e69d05fd Iustin Pop
  running_instances = GetInstanceList([instance.hypervisor])
1143 007a2f3e Alexander Schreiber
1144 007a2f3e Alexander Schreiber
  if instance.name not in running_instances:
1145 2cc6781a Iustin Pop
    _Fail("Cannot reboot instance %s that is not running", instance.name)
1146 007a2f3e Alexander Schreiber
1147 e69d05fd Iustin Pop
  hyper = hypervisor.GetHypervisor(instance.hypervisor)
1148 007a2f3e Alexander Schreiber
  if reboot_type == constants.INSTANCE_REBOOT_SOFT:
1149 007a2f3e Alexander Schreiber
    try:
1150 007a2f3e Alexander Schreiber
      hyper.RebootInstance(instance)
1151 007a2f3e Alexander Schreiber
    except errors.HypervisorError, err:
1152 2cc6781a Iustin Pop
      _Fail("Failed to soft reboot instance %s: %s", instance.name, err)
1153 007a2f3e Alexander Schreiber
  elif reboot_type == constants.INSTANCE_REBOOT_HARD:
1154 007a2f3e Alexander Schreiber
    try:
1155 17c3f802 Guido Trotter
      InstanceShutdown(instance, shutdown_timeout)
1156 07813a9e Iustin Pop
      return StartInstance(instance)
1157 007a2f3e Alexander Schreiber
    except errors.HypervisorError, err:
1158 2cc6781a Iustin Pop
      _Fail("Failed to hard reboot instance %s: %s", instance.name, err)
1159 007a2f3e Alexander Schreiber
  else:
1160 2cc6781a Iustin Pop
    _Fail("Invalid reboot_type received: %s", reboot_type)
1161 007a2f3e Alexander Schreiber
1162 007a2f3e Alexander Schreiber
1163 6906a9d8 Guido Trotter
def MigrationInfo(instance):
1164 6906a9d8 Guido Trotter
  """Gather information about an instance to be migrated.
1165 6906a9d8 Guido Trotter

1166 6906a9d8 Guido Trotter
  @type instance: L{objects.Instance}
1167 6906a9d8 Guido Trotter
  @param instance: the instance definition
1168 6906a9d8 Guido Trotter

1169 6906a9d8 Guido Trotter
  """
1170 cd42d0ad Guido Trotter
  hyper = hypervisor.GetHypervisor(instance.hypervisor)
1171 cd42d0ad Guido Trotter
  try:
1172 cd42d0ad Guido Trotter
    info = hyper.MigrationInfo(instance)
1173 cd42d0ad Guido Trotter
  except errors.HypervisorError, err:
1174 2cc6781a Iustin Pop
    _Fail("Failed to fetch migration information: %s", err, exc=True)
1175 c26a6bd2 Iustin Pop
  return info
1176 6906a9d8 Guido Trotter
1177 6906a9d8 Guido Trotter
1178 6906a9d8 Guido Trotter
def AcceptInstance(instance, info, target):
1179 6906a9d8 Guido Trotter
  """Prepare the node to accept an instance.
1180 6906a9d8 Guido Trotter

1181 6906a9d8 Guido Trotter
  @type instance: L{objects.Instance}
1182 6906a9d8 Guido Trotter
  @param instance: the instance definition
1183 6906a9d8 Guido Trotter
  @type info: string/data (opaque)
1184 6906a9d8 Guido Trotter
  @param info: migration information, from the source node
1185 6906a9d8 Guido Trotter
  @type target: string
1186 6906a9d8 Guido Trotter
  @param target: target host (usually ip), on this node
1187 6906a9d8 Guido Trotter

1188 6906a9d8 Guido Trotter
  """
1189 cd42d0ad Guido Trotter
  hyper = hypervisor.GetHypervisor(instance.hypervisor)
1190 cd42d0ad Guido Trotter
  try:
1191 cd42d0ad Guido Trotter
    hyper.AcceptInstance(instance, info, target)
1192 cd42d0ad Guido Trotter
  except errors.HypervisorError, err:
1193 2cc6781a Iustin Pop
    _Fail("Failed to accept instance: %s", err, exc=True)
1194 6906a9d8 Guido Trotter
1195 6906a9d8 Guido Trotter
1196 6906a9d8 Guido Trotter
def FinalizeMigration(instance, info, success):
1197 6906a9d8 Guido Trotter
  """Finalize any preparation to accept an instance.
1198 6906a9d8 Guido Trotter

1199 6906a9d8 Guido Trotter
  @type instance: L{objects.Instance}
1200 6906a9d8 Guido Trotter
  @param instance: the instance definition
1201 6906a9d8 Guido Trotter
  @type info: string/data (opaque)
1202 6906a9d8 Guido Trotter
  @param info: migration information, from the source node
1203 6906a9d8 Guido Trotter
  @type success: boolean
1204 6906a9d8 Guido Trotter
  @param success: whether the migration was a success or a failure
1205 6906a9d8 Guido Trotter

1206 6906a9d8 Guido Trotter
  """
1207 cd42d0ad Guido Trotter
  hyper = hypervisor.GetHypervisor(instance.hypervisor)
1208 cd42d0ad Guido Trotter
  try:
1209 cd42d0ad Guido Trotter
    hyper.FinalizeMigration(instance, info, success)
1210 cd42d0ad Guido Trotter
  except errors.HypervisorError, err:
1211 2cc6781a Iustin Pop
    _Fail("Failed to finalize migration: %s", err, exc=True)
1212 6906a9d8 Guido Trotter
1213 6906a9d8 Guido Trotter
1214 2a10865c Iustin Pop
def MigrateInstance(instance, target, live):
1215 2a10865c Iustin Pop
  """Migrates an instance to another node.
1216 2a10865c Iustin Pop

1217 b1206984 Iustin Pop
  @type instance: L{objects.Instance}
1218 9f0e6b37 Iustin Pop
  @param instance: the instance definition
1219 9f0e6b37 Iustin Pop
  @type target: string
1220 9f0e6b37 Iustin Pop
  @param target: the target node name
1221 9f0e6b37 Iustin Pop
  @type live: boolean
1222 9f0e6b37 Iustin Pop
  @param live: whether the migration should be done live or not (the
1223 9f0e6b37 Iustin Pop
      interpretation of this parameter is left to the hypervisor)
1224 9f0e6b37 Iustin Pop
  @rtype: tuple
1225 9f0e6b37 Iustin Pop
  @return: a tuple of (success, msg) where:
1226 9f0e6b37 Iustin Pop
      - succes is a boolean denoting the success/failure of the operation
1227 9f0e6b37 Iustin Pop
      - msg is a string with details in case of failure
1228 9f0e6b37 Iustin Pop

1229 2a10865c Iustin Pop
  """
1230 53c776b5 Iustin Pop
  hyper = hypervisor.GetHypervisor(instance.hypervisor)
1231 2a10865c Iustin Pop
1232 2a10865c Iustin Pop
  try:
1233 58d38b02 Iustin Pop
    hyper.MigrateInstance(instance, target, live)
1234 2a10865c Iustin Pop
  except errors.HypervisorError, err:
1235 2cc6781a Iustin Pop
    _Fail("Failed to migrate instance: %s", err, exc=True)
1236 2a10865c Iustin Pop
1237 2a10865c Iustin Pop
1238 821d1bd1 Iustin Pop
def BlockdevCreate(disk, size, owner, on_primary, info):
1239 a8083063 Iustin Pop
  """Creates a block device for an instance.
1240 a8083063 Iustin Pop

1241 b1206984 Iustin Pop
  @type disk: L{objects.Disk}
1242 b1206984 Iustin Pop
  @param disk: the object describing the disk we should create
1243 b1206984 Iustin Pop
  @type size: int
1244 b1206984 Iustin Pop
  @param size: the size of the physical underlying device, in MiB
1245 b1206984 Iustin Pop
  @type owner: str
1246 b1206984 Iustin Pop
  @param owner: the name of the instance for which disk is created,
1247 b1206984 Iustin Pop
      used for device cache data
1248 b1206984 Iustin Pop
  @type on_primary: boolean
1249 b1206984 Iustin Pop
  @param on_primary:  indicates if it is the primary node or not
1250 b1206984 Iustin Pop
  @type info: string
1251 b1206984 Iustin Pop
  @param info: string that will be sent to the physical device
1252 b1206984 Iustin Pop
      creation, used for example to set (LVM) tags on LVs
1253 b1206984 Iustin Pop

1254 b1206984 Iustin Pop
  @return: the new unique_id of the device (this can sometime be
1255 b1206984 Iustin Pop
      computed only after creation), or None. On secondary nodes,
1256 b1206984 Iustin Pop
      it's not required to return anything.
1257 a8083063 Iustin Pop

1258 a8083063 Iustin Pop
  """
1259 7260cfbe Iustin Pop
  # TODO: remove the obsolete 'size' argument
1260 7260cfbe Iustin Pop
  # pylint: disable-msg=W0613
1261 a8083063 Iustin Pop
  clist = []
1262 a8083063 Iustin Pop
  if disk.children:
1263 a8083063 Iustin Pop
    for child in disk.children:
1264 1063abd1 Iustin Pop
      try:
1265 1063abd1 Iustin Pop
        crdev = _RecursiveAssembleBD(child, owner, on_primary)
1266 1063abd1 Iustin Pop
      except errors.BlockDeviceError, err:
1267 2cc6781a Iustin Pop
        _Fail("Can't assemble device %s: %s", child, err)
1268 a8083063 Iustin Pop
      if on_primary or disk.AssembleOnSecondary():
1269 a8083063 Iustin Pop
        # we need the children open in case the device itself has to
1270 a8083063 Iustin Pop
        # be assembled
1271 1063abd1 Iustin Pop
        try:
1272 fe267188 Iustin Pop
          # pylint: disable-msg=E1103
1273 1063abd1 Iustin Pop
          crdev.Open()
1274 1063abd1 Iustin Pop
        except errors.BlockDeviceError, err:
1275 2cc6781a Iustin Pop
          _Fail("Can't make child '%s' read-write: %s", child, err)
1276 a8083063 Iustin Pop
      clist.append(crdev)
1277 a8083063 Iustin Pop
1278 dab69e97 Iustin Pop
  try:
1279 464f8daf Iustin Pop
    device = bdev.Create(disk.dev_type, disk.physical_id, clist, disk.size)
1280 1063abd1 Iustin Pop
  except errors.BlockDeviceError, err:
1281 2cc6781a Iustin Pop
    _Fail("Can't create block device: %s", err)
1282 6c626518 Iustin Pop
1283 a8083063 Iustin Pop
  if on_primary or disk.AssembleOnSecondary():
1284 1063abd1 Iustin Pop
    try:
1285 1063abd1 Iustin Pop
      device.Assemble()
1286 1063abd1 Iustin Pop
    except errors.BlockDeviceError, err:
1287 2cc6781a Iustin Pop
      _Fail("Can't assemble device after creation, unusual event: %s", err)
1288 e31c43f7 Michael Hanselmann
    device.SetSyncSpeed(constants.SYNC_SPEED)
1289 a8083063 Iustin Pop
    if on_primary or disk.OpenOnSecondary():
1290 1063abd1 Iustin Pop
      try:
1291 1063abd1 Iustin Pop
        device.Open(force=True)
1292 1063abd1 Iustin Pop
      except errors.BlockDeviceError, err:
1293 2cc6781a Iustin Pop
        _Fail("Can't make device r/w after creation, unusual event: %s", err)
1294 3f78eef2 Iustin Pop
    DevCacheManager.UpdateCache(device.dev_path, owner,
1295 3f78eef2 Iustin Pop
                                on_primary, disk.iv_name)
1296 a0c3fea1 Michael Hanselmann
1297 a0c3fea1 Michael Hanselmann
  device.SetInfo(info)
1298 a0c3fea1 Michael Hanselmann
1299 c26a6bd2 Iustin Pop
  return device.unique_id
1300 a8083063 Iustin Pop
1301 a8083063 Iustin Pop
1302 821d1bd1 Iustin Pop
def BlockdevRemove(disk):
1303 a8083063 Iustin Pop
  """Remove a block device.
1304 a8083063 Iustin Pop

1305 10c2650b Iustin Pop
  @note: This is intended to be called recursively.
1306 10c2650b Iustin Pop

1307 c41eea6e Iustin Pop
  @type disk: L{objects.Disk}
1308 10c2650b Iustin Pop
  @param disk: the disk object we should remove
1309 10c2650b Iustin Pop
  @rtype: boolean
1310 10c2650b Iustin Pop
  @return: the success of the operation
1311 a8083063 Iustin Pop

1312 a8083063 Iustin Pop
  """
1313 e1bc0878 Iustin Pop
  msgs = []
1314 a8083063 Iustin Pop
  try:
1315 bca2e7f4 Iustin Pop
    rdev = _RecursiveFindBD(disk)
1316 a8083063 Iustin Pop
  except errors.BlockDeviceError, err:
1317 a8083063 Iustin Pop
    # probably can't attach
1318 18682bca Iustin Pop
    logging.info("Can't attach to device %s in remove", disk)
1319 a8083063 Iustin Pop
    rdev = None
1320 a8083063 Iustin Pop
  if rdev is not None:
1321 3f78eef2 Iustin Pop
    r_path = rdev.dev_path
1322 e1bc0878 Iustin Pop
    try:
1323 0c6c04ec Iustin Pop
      rdev.Remove()
1324 e1bc0878 Iustin Pop
    except errors.BlockDeviceError, err:
1325 e1bc0878 Iustin Pop
      msgs.append(str(err))
1326 c26a6bd2 Iustin Pop
    if not msgs:
1327 3f78eef2 Iustin Pop
      DevCacheManager.RemoveCache(r_path)
1328 e1bc0878 Iustin Pop
1329 a8083063 Iustin Pop
  if disk.children:
1330 a8083063 Iustin Pop
    for child in disk.children:
1331 c26a6bd2 Iustin Pop
      try:
1332 c26a6bd2 Iustin Pop
        BlockdevRemove(child)
1333 c26a6bd2 Iustin Pop
      except RPCFail, err:
1334 c26a6bd2 Iustin Pop
        msgs.append(str(err))
1335 e1bc0878 Iustin Pop
1336 c26a6bd2 Iustin Pop
  if msgs:
1337 afdc3985 Iustin Pop
    _Fail("; ".join(msgs))
1338 afdc3985 Iustin Pop
1339 a8083063 Iustin Pop
1340 3f78eef2 Iustin Pop
def _RecursiveAssembleBD(disk, owner, as_primary):
1341 a8083063 Iustin Pop
  """Activate a block device for an instance.
1342 a8083063 Iustin Pop

1343 a8083063 Iustin Pop
  This is run on the primary and secondary nodes for an instance.
1344 a8083063 Iustin Pop

1345 10c2650b Iustin Pop
  @note: this function is called recursively.
1346 a8083063 Iustin Pop

1347 10c2650b Iustin Pop
  @type disk: L{objects.Disk}
1348 10c2650b Iustin Pop
  @param disk: the disk we try to assemble
1349 10c2650b Iustin Pop
  @type owner: str
1350 10c2650b Iustin Pop
  @param owner: the name of the instance which owns the disk
1351 10c2650b Iustin Pop
  @type as_primary: boolean
1352 10c2650b Iustin Pop
  @param as_primary: if we should make the block device
1353 10c2650b Iustin Pop
      read/write
1354 a8083063 Iustin Pop

1355 10c2650b Iustin Pop
  @return: the assembled device or None (in case no device
1356 10c2650b Iustin Pop
      was assembled)
1357 10c2650b Iustin Pop
  @raise errors.BlockDeviceError: in case there is an error
1358 10c2650b Iustin Pop
      during the activation of the children or the device
1359 10c2650b Iustin Pop
      itself
1360 a8083063 Iustin Pop

1361 a8083063 Iustin Pop
  """
1362 a8083063 Iustin Pop
  children = []
1363 a8083063 Iustin Pop
  if disk.children:
1364 fc1dc9d7 Iustin Pop
    mcn = disk.ChildrenNeeded()
1365 fc1dc9d7 Iustin Pop
    if mcn == -1:
1366 fc1dc9d7 Iustin Pop
      mcn = 0 # max number of Nones allowed
1367 fc1dc9d7 Iustin Pop
    else:
1368 fc1dc9d7 Iustin Pop
      mcn = len(disk.children) - mcn # max number of Nones
1369 a8083063 Iustin Pop
    for chld_disk in disk.children:
1370 fc1dc9d7 Iustin Pop
      try:
1371 fc1dc9d7 Iustin Pop
        cdev = _RecursiveAssembleBD(chld_disk, owner, as_primary)
1372 fc1dc9d7 Iustin Pop
      except errors.BlockDeviceError, err:
1373 7803d4d3 Iustin Pop
        if children.count(None) >= mcn:
1374 fc1dc9d7 Iustin Pop
          raise
1375 fc1dc9d7 Iustin Pop
        cdev = None
1376 1063abd1 Iustin Pop
        logging.error("Error in child activation (but continuing): %s",
1377 1063abd1 Iustin Pop
                      str(err))
1378 fc1dc9d7 Iustin Pop
      children.append(cdev)
1379 a8083063 Iustin Pop
1380 a8083063 Iustin Pop
  if as_primary or disk.AssembleOnSecondary():
1381 464f8daf Iustin Pop
    r_dev = bdev.Assemble(disk.dev_type, disk.physical_id, children, disk.size)
1382 e31c43f7 Michael Hanselmann
    r_dev.SetSyncSpeed(constants.SYNC_SPEED)
1383 a8083063 Iustin Pop
    result = r_dev
1384 a8083063 Iustin Pop
    if as_primary or disk.OpenOnSecondary():
1385 a8083063 Iustin Pop
      r_dev.Open()
1386 3f78eef2 Iustin Pop
    DevCacheManager.UpdateCache(r_dev.dev_path, owner,
1387 3f78eef2 Iustin Pop
                                as_primary, disk.iv_name)
1388 3f78eef2 Iustin Pop
1389 a8083063 Iustin Pop
  else:
1390 a8083063 Iustin Pop
    result = True
1391 a8083063 Iustin Pop
  return result
1392 a8083063 Iustin Pop
1393 a8083063 Iustin Pop
1394 821d1bd1 Iustin Pop
def BlockdevAssemble(disk, owner, as_primary):
1395 a8083063 Iustin Pop
  """Activate a block device for an instance.
1396 a8083063 Iustin Pop

1397 a8083063 Iustin Pop
  This is a wrapper over _RecursiveAssembleBD.
1398 a8083063 Iustin Pop

1399 b1206984 Iustin Pop
  @rtype: str or boolean
1400 b1206984 Iustin Pop
  @return: a C{/dev/...} path for primary nodes, and
1401 b1206984 Iustin Pop
      C{True} for secondary nodes
1402 a8083063 Iustin Pop

1403 a8083063 Iustin Pop
  """
1404 53c14ef1 Iustin Pop
  try:
1405 53c14ef1 Iustin Pop
    result = _RecursiveAssembleBD(disk, owner, as_primary)
1406 53c14ef1 Iustin Pop
    if isinstance(result, bdev.BlockDev):
1407 fe267188 Iustin Pop
      # pylint: disable-msg=E1103
1408 53c14ef1 Iustin Pop
      result = result.dev_path
1409 53c14ef1 Iustin Pop
  except errors.BlockDeviceError, err:
1410 afdc3985 Iustin Pop
    _Fail("Error while assembling disk: %s", err, exc=True)
1411 afdc3985 Iustin Pop
1412 c26a6bd2 Iustin Pop
  return result
1413 a8083063 Iustin Pop
1414 a8083063 Iustin Pop
1415 821d1bd1 Iustin Pop
def BlockdevShutdown(disk):
1416 a8083063 Iustin Pop
  """Shut down a block device.
1417 a8083063 Iustin Pop

1418 5bbd3f7f Michael Hanselmann
  First, if the device is assembled (Attach() is successful), then
1419 c41eea6e Iustin Pop
  the device is shutdown. Then the children of the device are
1420 c41eea6e Iustin Pop
  shutdown.
1421 a8083063 Iustin Pop

1422 a8083063 Iustin Pop
  This function is called recursively. Note that we don't cache the
1423 a8083063 Iustin Pop
  children or such, as oppossed to assemble, shutdown of different
1424 a8083063 Iustin Pop
  devices doesn't require that the upper device was active.
1425 a8083063 Iustin Pop

1426 10c2650b Iustin Pop
  @type disk: L{objects.Disk}
1427 10c2650b Iustin Pop
  @param disk: the description of the disk we should
1428 10c2650b Iustin Pop
      shutdown
1429 c26a6bd2 Iustin Pop
  @rtype: None
1430 10c2650b Iustin Pop

1431 a8083063 Iustin Pop
  """
1432 cacfd1fd Iustin Pop
  msgs = []
1433 a8083063 Iustin Pop
  r_dev = _RecursiveFindBD(disk)
1434 a8083063 Iustin Pop
  if r_dev is not None:
1435 3f78eef2 Iustin Pop
    r_path = r_dev.dev_path
1436 cacfd1fd Iustin Pop
    try:
1437 746f7476 Iustin Pop
      r_dev.Shutdown()
1438 746f7476 Iustin Pop
      DevCacheManager.RemoveCache(r_path)
1439 cacfd1fd Iustin Pop
    except errors.BlockDeviceError, err:
1440 cacfd1fd Iustin Pop
      msgs.append(str(err))
1441 746f7476 Iustin Pop
1442 a8083063 Iustin Pop
  if disk.children:
1443 a8083063 Iustin Pop
    for child in disk.children:
1444 c26a6bd2 Iustin Pop
      try:
1445 c26a6bd2 Iustin Pop
        BlockdevShutdown(child)
1446 c26a6bd2 Iustin Pop
      except RPCFail, err:
1447 c26a6bd2 Iustin Pop
        msgs.append(str(err))
1448 746f7476 Iustin Pop
1449 c26a6bd2 Iustin Pop
  if msgs:
1450 afdc3985 Iustin Pop
    _Fail("; ".join(msgs))
1451 a8083063 Iustin Pop
1452 a8083063 Iustin Pop
1453 821d1bd1 Iustin Pop
def BlockdevAddchildren(parent_cdev, new_cdevs):
1454 153d9724 Iustin Pop
  """Extend a mirrored block device.
1455 a8083063 Iustin Pop

1456 10c2650b Iustin Pop
  @type parent_cdev: L{objects.Disk}
1457 10c2650b Iustin Pop
  @param parent_cdev: the disk to which we should add children
1458 10c2650b Iustin Pop
  @type new_cdevs: list of L{objects.Disk}
1459 10c2650b Iustin Pop
  @param new_cdevs: the list of children which we should add
1460 c26a6bd2 Iustin Pop
  @rtype: None
1461 10c2650b Iustin Pop

1462 a8083063 Iustin Pop
  """
1463 bca2e7f4 Iustin Pop
  parent_bdev = _RecursiveFindBD(parent_cdev)
1464 153d9724 Iustin Pop
  if parent_bdev is None:
1465 2cc6781a Iustin Pop
    _Fail("Can't find parent device '%s' in add children", parent_cdev)
1466 153d9724 Iustin Pop
  new_bdevs = [_RecursiveFindBD(disk) for disk in new_cdevs]
1467 153d9724 Iustin Pop
  if new_bdevs.count(None) > 0:
1468 2cc6781a Iustin Pop
    _Fail("Can't find new device(s) to add: %s:%s", new_bdevs, new_cdevs)
1469 153d9724 Iustin Pop
  parent_bdev.AddChildren(new_bdevs)
1470 a8083063 Iustin Pop
1471 a8083063 Iustin Pop
1472 821d1bd1 Iustin Pop
def BlockdevRemovechildren(parent_cdev, new_cdevs):
1473 153d9724 Iustin Pop
  """Shrink a mirrored block device.
1474 a8083063 Iustin Pop

1475 10c2650b Iustin Pop
  @type parent_cdev: L{objects.Disk}
1476 10c2650b Iustin Pop
  @param parent_cdev: the disk from which we should remove children
1477 10c2650b Iustin Pop
  @type new_cdevs: list of L{objects.Disk}
1478 10c2650b Iustin Pop
  @param new_cdevs: the list of children which we should remove
1479 c26a6bd2 Iustin Pop
  @rtype: None
1480 10c2650b Iustin Pop

1481 a8083063 Iustin Pop
  """
1482 153d9724 Iustin Pop
  parent_bdev = _RecursiveFindBD(parent_cdev)
1483 153d9724 Iustin Pop
  if parent_bdev is None:
1484 2cc6781a Iustin Pop
    _Fail("Can't find parent device '%s' in remove children", parent_cdev)
1485 e739bd57 Iustin Pop
  devs = []
1486 e739bd57 Iustin Pop
  for disk in new_cdevs:
1487 e739bd57 Iustin Pop
    rpath = disk.StaticDevPath()
1488 e739bd57 Iustin Pop
    if rpath is None:
1489 e739bd57 Iustin Pop
      bd = _RecursiveFindBD(disk)
1490 e739bd57 Iustin Pop
      if bd is None:
1491 2cc6781a Iustin Pop
        _Fail("Can't find device %s while removing children", disk)
1492 e739bd57 Iustin Pop
      else:
1493 e739bd57 Iustin Pop
        devs.append(bd.dev_path)
1494 e739bd57 Iustin Pop
    else:
1495 e51db2a6 Iustin Pop
      if not utils.IsNormAbsPath(rpath):
1496 e51db2a6 Iustin Pop
        _Fail("Strange path returned from StaticDevPath: '%s'", rpath)
1497 e739bd57 Iustin Pop
      devs.append(rpath)
1498 e739bd57 Iustin Pop
  parent_bdev.RemoveChildren(devs)
1499 a8083063 Iustin Pop
1500 a8083063 Iustin Pop
1501 821d1bd1 Iustin Pop
def BlockdevGetmirrorstatus(disks):
1502 a8083063 Iustin Pop
  """Get the mirroring status of a list of devices.
1503 a8083063 Iustin Pop

1504 10c2650b Iustin Pop
  @type disks: list of L{objects.Disk}
1505 10c2650b Iustin Pop
  @param disks: the list of disks which we should query
1506 10c2650b Iustin Pop
  @rtype: disk
1507 10c2650b Iustin Pop
  @return:
1508 10c2650b Iustin Pop
      a list of (mirror_done, estimated_time) tuples, which
1509 c41eea6e Iustin Pop
      are the result of L{bdev.BlockDev.CombinedSyncStatus}
1510 10c2650b Iustin Pop
  @raise errors.BlockDeviceError: if any of the disks cannot be
1511 10c2650b Iustin Pop
      found
1512 a8083063 Iustin Pop

1513 a8083063 Iustin Pop
  """
1514 a8083063 Iustin Pop
  stats = []
1515 a8083063 Iustin Pop
  for dsk in disks:
1516 a8083063 Iustin Pop
    rbd = _RecursiveFindBD(dsk)
1517 a8083063 Iustin Pop
    if rbd is None:
1518 3efa9051 Iustin Pop
      _Fail("Can't find device %s", dsk)
1519 96acbc09 Michael Hanselmann
1520 36145b12 Michael Hanselmann
    stats.append(rbd.CombinedSyncStatus())
1521 96acbc09 Michael Hanselmann
1522 c26a6bd2 Iustin Pop
  return stats
1523 a8083063 Iustin Pop
1524 a8083063 Iustin Pop
1525 bca2e7f4 Iustin Pop
def _RecursiveFindBD(disk):
1526 a8083063 Iustin Pop
  """Check if a device is activated.
1527 a8083063 Iustin Pop

1528 5bbd3f7f Michael Hanselmann
  If so, return information about the real device.
1529 a8083063 Iustin Pop

1530 10c2650b Iustin Pop
  @type disk: L{objects.Disk}
1531 10c2650b Iustin Pop
  @param disk: the disk object we need to find
1532 a8083063 Iustin Pop

1533 10c2650b Iustin Pop
  @return: None if the device can't be found,
1534 10c2650b Iustin Pop
      otherwise the device instance
1535 a8083063 Iustin Pop

1536 a8083063 Iustin Pop
  """
1537 a8083063 Iustin Pop
  children = []
1538 a8083063 Iustin Pop
  if disk.children:
1539 a8083063 Iustin Pop
    for chdisk in disk.children:
1540 a8083063 Iustin Pop
      children.append(_RecursiveFindBD(chdisk))
1541 a8083063 Iustin Pop
1542 464f8daf Iustin Pop
  return bdev.FindDevice(disk.dev_type, disk.physical_id, children, disk.size)
1543 a8083063 Iustin Pop
1544 a8083063 Iustin Pop
1545 f2e07bb4 Michael Hanselmann
def _OpenRealBD(disk):
1546 f2e07bb4 Michael Hanselmann
  """Opens the underlying block device of a disk.
1547 f2e07bb4 Michael Hanselmann

1548 f2e07bb4 Michael Hanselmann
  @type disk: L{objects.Disk}
1549 f2e07bb4 Michael Hanselmann
  @param disk: the disk object we want to open
1550 f2e07bb4 Michael Hanselmann

1551 f2e07bb4 Michael Hanselmann
  """
1552 f2e07bb4 Michael Hanselmann
  real_disk = _RecursiveFindBD(disk)
1553 f2e07bb4 Michael Hanselmann
  if real_disk is None:
1554 f2e07bb4 Michael Hanselmann
    _Fail("Block device '%s' is not set up", disk)
1555 f2e07bb4 Michael Hanselmann
1556 f2e07bb4 Michael Hanselmann
  real_disk.Open()
1557 f2e07bb4 Michael Hanselmann
1558 f2e07bb4 Michael Hanselmann
  return real_disk
1559 f2e07bb4 Michael Hanselmann
1560 f2e07bb4 Michael Hanselmann
1561 821d1bd1 Iustin Pop
def BlockdevFind(disk):
1562 a8083063 Iustin Pop
  """Check if a device is activated.
1563 a8083063 Iustin Pop

1564 5bbd3f7f Michael Hanselmann
  If it is, return information about the real device.
1565 a8083063 Iustin Pop

1566 10c2650b Iustin Pop
  @type disk: L{objects.Disk}
1567 10c2650b Iustin Pop
  @param disk: the disk to find
1568 96acbc09 Michael Hanselmann
  @rtype: None or objects.BlockDevStatus
1569 96acbc09 Michael Hanselmann
  @return: None if the disk cannot be found, otherwise a the current
1570 96acbc09 Michael Hanselmann
           information
1571 a8083063 Iustin Pop

1572 a8083063 Iustin Pop
  """
1573 23829f6f Iustin Pop
  try:
1574 23829f6f Iustin Pop
    rbd = _RecursiveFindBD(disk)
1575 23829f6f Iustin Pop
  except errors.BlockDeviceError, err:
1576 2cc6781a Iustin Pop
    _Fail("Failed to find device: %s", err, exc=True)
1577 96acbc09 Michael Hanselmann
1578 a8083063 Iustin Pop
  if rbd is None:
1579 c26a6bd2 Iustin Pop
    return None
1580 96acbc09 Michael Hanselmann
1581 96acbc09 Michael Hanselmann
  return rbd.GetSyncStatus()
1582 a8083063 Iustin Pop
1583 a8083063 Iustin Pop
1584 968a7623 Iustin Pop
def BlockdevGetsize(disks):
1585 968a7623 Iustin Pop
  """Computes the size of the given disks.
1586 968a7623 Iustin Pop

1587 968a7623 Iustin Pop
  If a disk is not found, returns None instead.
1588 968a7623 Iustin Pop

1589 968a7623 Iustin Pop
  @type disks: list of L{objects.Disk}
1590 968a7623 Iustin Pop
  @param disks: the list of disk to compute the size for
1591 968a7623 Iustin Pop
  @rtype: list
1592 968a7623 Iustin Pop
  @return: list with elements None if the disk cannot be found,
1593 968a7623 Iustin Pop
      otherwise the size
1594 968a7623 Iustin Pop

1595 968a7623 Iustin Pop
  """
1596 968a7623 Iustin Pop
  result = []
1597 968a7623 Iustin Pop
  for cf in disks:
1598 968a7623 Iustin Pop
    try:
1599 968a7623 Iustin Pop
      rbd = _RecursiveFindBD(cf)
1600 1122eb25 Iustin Pop
    except errors.BlockDeviceError:
1601 968a7623 Iustin Pop
      result.append(None)
1602 968a7623 Iustin Pop
      continue
1603 968a7623 Iustin Pop
    if rbd is None:
1604 968a7623 Iustin Pop
      result.append(None)
1605 968a7623 Iustin Pop
    else:
1606 968a7623 Iustin Pop
      result.append(rbd.GetActualSize())
1607 968a7623 Iustin Pop
  return result
1608 968a7623 Iustin Pop
1609 968a7623 Iustin Pop
1610 858f3d18 Iustin Pop
def BlockdevExport(disk, dest_node, dest_path, cluster_name):
1611 858f3d18 Iustin Pop
  """Export a block device to a remote node.
1612 858f3d18 Iustin Pop

1613 858f3d18 Iustin Pop
  @type disk: L{objects.Disk}
1614 858f3d18 Iustin Pop
  @param disk: the description of the disk to export
1615 858f3d18 Iustin Pop
  @type dest_node: str
1616 858f3d18 Iustin Pop
  @param dest_node: the destination node to export to
1617 858f3d18 Iustin Pop
  @type dest_path: str
1618 858f3d18 Iustin Pop
  @param dest_path: the destination path on the target node
1619 858f3d18 Iustin Pop
  @type cluster_name: str
1620 858f3d18 Iustin Pop
  @param cluster_name: the cluster name, needed for SSH hostalias
1621 858f3d18 Iustin Pop
  @rtype: None
1622 858f3d18 Iustin Pop

1623 858f3d18 Iustin Pop
  """
1624 f2e07bb4 Michael Hanselmann
  real_disk = _OpenRealBD(disk)
1625 858f3d18 Iustin Pop
1626 858f3d18 Iustin Pop
  # the block size on the read dd is 1MiB to match our units
1627 858f3d18 Iustin Pop
  expcmd = utils.BuildShellCmd("set -e; set -o pipefail; "
1628 858f3d18 Iustin Pop
                               "dd if=%s bs=1048576 count=%s",
1629 858f3d18 Iustin Pop
                               real_disk.dev_path, str(disk.size))
1630 858f3d18 Iustin Pop
1631 858f3d18 Iustin Pop
  # we set here a smaller block size as, due to ssh buffering, more
1632 858f3d18 Iustin Pop
  # than 64-128k will mostly ignored; we use nocreat to fail if the
1633 858f3d18 Iustin Pop
  # device is not already there or we pass a wrong path; we use
1634 858f3d18 Iustin Pop
  # notrunc to no attempt truncate on an LV device; we use oflag=dsync
1635 858f3d18 Iustin Pop
  # to not buffer too much memory; this means that at best, we flush
1636 858f3d18 Iustin Pop
  # every 64k, which will not be very fast
1637 858f3d18 Iustin Pop
  destcmd = utils.BuildShellCmd("dd of=%s conv=nocreat,notrunc bs=65536"
1638 858f3d18 Iustin Pop
                                " oflag=dsync", dest_path)
1639 858f3d18 Iustin Pop
1640 858f3d18 Iustin Pop
  remotecmd = _GetSshRunner(cluster_name).BuildCmd(dest_node,
1641 858f3d18 Iustin Pop
                                                   constants.GANETI_RUNAS,
1642 858f3d18 Iustin Pop
                                                   destcmd)
1643 858f3d18 Iustin Pop
1644 858f3d18 Iustin Pop
  # all commands have been checked, so we're safe to combine them
1645 858f3d18 Iustin Pop
  command = '|'.join([expcmd, utils.ShellQuoteArgs(remotecmd)])
1646 858f3d18 Iustin Pop
1647 858f3d18 Iustin Pop
  result = utils.RunCmd(["bash", "-c", command])
1648 858f3d18 Iustin Pop
1649 858f3d18 Iustin Pop
  if result.failed:
1650 858f3d18 Iustin Pop
    _Fail("Disk copy command '%s' returned error: %s"
1651 858f3d18 Iustin Pop
          " output: %s", command, result.fail_reason, result.output)
1652 858f3d18 Iustin Pop
1653 858f3d18 Iustin Pop
1654 a8083063 Iustin Pop
def UploadFile(file_name, data, mode, uid, gid, atime, mtime):
1655 a8083063 Iustin Pop
  """Write a file to the filesystem.
1656 a8083063 Iustin Pop

1657 a8083063 Iustin Pop
  This allows the master to overwrite(!) a file. It will only perform
1658 a8083063 Iustin Pop
  the operation if the file belongs to a list of configuration files.
1659 a8083063 Iustin Pop

1660 10c2650b Iustin Pop
  @type file_name: str
1661 10c2650b Iustin Pop
  @param file_name: the target file name
1662 10c2650b Iustin Pop
  @type data: str
1663 10c2650b Iustin Pop
  @param data: the new contents of the file
1664 10c2650b Iustin Pop
  @type mode: int
1665 10c2650b Iustin Pop
  @param mode: the mode to give the file (can be None)
1666 10c2650b Iustin Pop
  @type uid: int
1667 10c2650b Iustin Pop
  @param uid: the owner of the file (can be -1 for default)
1668 10c2650b Iustin Pop
  @type gid: int
1669 10c2650b Iustin Pop
  @param gid: the group of the file (can be -1 for default)
1670 10c2650b Iustin Pop
  @type atime: float
1671 10c2650b Iustin Pop
  @param atime: the atime to set on the file (can be None)
1672 10c2650b Iustin Pop
  @type mtime: float
1673 10c2650b Iustin Pop
  @param mtime: the mtime to set on the file (can be None)
1674 c26a6bd2 Iustin Pop
  @rtype: None
1675 10c2650b Iustin Pop

1676 a8083063 Iustin Pop
  """
1677 a8083063 Iustin Pop
  if not os.path.isabs(file_name):
1678 2cc6781a Iustin Pop
    _Fail("Filename passed to UploadFile is not absolute: '%s'", file_name)
1679 a8083063 Iustin Pop
1680 360b0dc2 Iustin Pop
  if file_name not in _ALLOWED_UPLOAD_FILES:
1681 2cc6781a Iustin Pop
    _Fail("Filename passed to UploadFile not in allowed upload targets: '%s'",
1682 2cc6781a Iustin Pop
          file_name)
1683 a8083063 Iustin Pop
1684 12bce260 Michael Hanselmann
  raw_data = _Decompress(data)
1685 12bce260 Michael Hanselmann
1686 12bce260 Michael Hanselmann
  utils.WriteFile(file_name, data=raw_data, mode=mode, uid=uid, gid=gid,
1687 41a57aab Michael Hanselmann
                  atime=atime, mtime=mtime)
1688 a8083063 Iustin Pop
1689 386b57af Iustin Pop
1690 03d1dba2 Michael Hanselmann
def WriteSsconfFiles(values):
1691 89b14f05 Iustin Pop
  """Update all ssconf files.
1692 89b14f05 Iustin Pop

1693 89b14f05 Iustin Pop
  Wrapper around the SimpleStore.WriteFiles.
1694 89b14f05 Iustin Pop

1695 89b14f05 Iustin Pop
  """
1696 89b14f05 Iustin Pop
  ssconf.SimpleStore().WriteFiles(values)
1697 6ddc95ec Michael Hanselmann
1698 6ddc95ec Michael Hanselmann
1699 a8083063 Iustin Pop
def _ErrnoOrStr(err):
1700 a8083063 Iustin Pop
  """Format an EnvironmentError exception.
1701 a8083063 Iustin Pop

1702 10c2650b Iustin Pop
  If the L{err} argument has an errno attribute, it will be looked up
1703 10c2650b Iustin Pop
  and converted into a textual C{E...} description. Otherwise the
1704 10c2650b Iustin Pop
  string representation of the error will be returned.
1705 10c2650b Iustin Pop

1706 10c2650b Iustin Pop
  @type err: L{EnvironmentError}
1707 10c2650b Iustin Pop
  @param err: the exception to format
1708 a8083063 Iustin Pop

1709 a8083063 Iustin Pop
  """
1710 a8083063 Iustin Pop
  if hasattr(err, 'errno'):
1711 a8083063 Iustin Pop
    detail = errno.errorcode[err.errno]
1712 a8083063 Iustin Pop
  else:
1713 a8083063 Iustin Pop
    detail = str(err)
1714 a8083063 Iustin Pop
  return detail
1715 a8083063 Iustin Pop
1716 5d0fe286 Iustin Pop
1717 c19f9810 Iustin Pop
def _OSOndiskAPIVersion(os_dir):
1718 2f8598a5 Alexander Schreiber
  """Compute and return the API version of a given OS.
1719 a8083063 Iustin Pop

1720 c19f9810 Iustin Pop
  This function will try to read the API version of the OS residing in
1721 c19f9810 Iustin Pop
  the 'os_dir' directory.
1722 7c3d51d4 Guido Trotter

1723 10c2650b Iustin Pop
  @type os_dir: str
1724 c19f9810 Iustin Pop
  @param os_dir: the directory in which we should look for the OS
1725 8e70b181 Iustin Pop
  @rtype: tuple
1726 8e70b181 Iustin Pop
  @return: tuple (status, data) with status denoting the validity and
1727 8e70b181 Iustin Pop
      data holding either the vaid versions or an error message
1728 a8083063 Iustin Pop

1729 a8083063 Iustin Pop
  """
1730 e02b9114 Iustin Pop
  api_file = utils.PathJoin(os_dir, constants.OS_API_FILE)
1731 a8083063 Iustin Pop
1732 a8083063 Iustin Pop
  try:
1733 a8083063 Iustin Pop
    st = os.stat(api_file)
1734 a8083063 Iustin Pop
  except EnvironmentError, err:
1735 b6b45e0d Guido Trotter
    return False, ("Required file '%s' not found under path %s: %s" %
1736 b6b45e0d Guido Trotter
                   (constants.OS_API_FILE, os_dir, _ErrnoOrStr(err)))
1737 a8083063 Iustin Pop
1738 a8083063 Iustin Pop
  if not stat.S_ISREG(stat.S_IFMT(st.st_mode)):
1739 b6b45e0d Guido Trotter
    return False, ("File '%s' in %s is not a regular file" %
1740 b6b45e0d Guido Trotter
                   (constants.OS_API_FILE, os_dir))
1741 a8083063 Iustin Pop
1742 a8083063 Iustin Pop
  try:
1743 3374afa9 Guido Trotter
    api_versions = utils.ReadFile(api_file).splitlines()
1744 a8083063 Iustin Pop
  except EnvironmentError, err:
1745 255dcebd Iustin Pop
    return False, ("Error while reading the API version file at %s: %s" %
1746 255dcebd Iustin Pop
                   (api_file, _ErrnoOrStr(err)))
1747 a8083063 Iustin Pop
1748 a8083063 Iustin Pop
  try:
1749 63b9b186 Guido Trotter
    api_versions = [int(version.strip()) for version in api_versions]
1750 a8083063 Iustin Pop
  except (TypeError, ValueError), err:
1751 255dcebd Iustin Pop
    return False, ("API version(s) can't be converted to integer: %s" %
1752 255dcebd Iustin Pop
                   str(err))
1753 a8083063 Iustin Pop
1754 255dcebd Iustin Pop
  return True, api_versions
1755 a8083063 Iustin Pop
1756 386b57af Iustin Pop
1757 7c3d51d4 Guido Trotter
def DiagnoseOS(top_dirs=None):
1758 a8083063 Iustin Pop
  """Compute the validity for all OSes.
1759 a8083063 Iustin Pop

1760 10c2650b Iustin Pop
  @type top_dirs: list
1761 10c2650b Iustin Pop
  @param top_dirs: the list of directories in which to
1762 10c2650b Iustin Pop
      search (if not given defaults to
1763 10c2650b Iustin Pop
      L{constants.OS_SEARCH_PATH})
1764 10c2650b Iustin Pop
  @rtype: list of L{objects.OS}
1765 ba00557a Guido Trotter
  @return: a list of tuples (name, path, status, diagnose, variants)
1766 255dcebd Iustin Pop
      for all (potential) OSes under all search paths, where:
1767 255dcebd Iustin Pop
          - name is the (potential) OS name
1768 255dcebd Iustin Pop
          - path is the full path to the OS
1769 255dcebd Iustin Pop
          - status True/False is the validity of the OS
1770 255dcebd Iustin Pop
          - diagnose is the error message for an invalid OS, otherwise empty
1771 ba00557a Guido Trotter
          - variants is a list of supported OS variants, if any
1772 a8083063 Iustin Pop

1773 a8083063 Iustin Pop
  """
1774 7c3d51d4 Guido Trotter
  if top_dirs is None:
1775 7c3d51d4 Guido Trotter
    top_dirs = constants.OS_SEARCH_PATH
1776 a8083063 Iustin Pop
1777 a8083063 Iustin Pop
  result = []
1778 65fe4693 Iustin Pop
  for dir_name in top_dirs:
1779 65fe4693 Iustin Pop
    if os.path.isdir(dir_name):
1780 7c3d51d4 Guido Trotter
      try:
1781 65fe4693 Iustin Pop
        f_names = utils.ListVisibleFiles(dir_name)
1782 7c3d51d4 Guido Trotter
      except EnvironmentError, err:
1783 29921401 Iustin Pop
        logging.exception("Can't list the OS directory %s: %s", dir_name, err)
1784 7c3d51d4 Guido Trotter
        break
1785 7c3d51d4 Guido Trotter
      for name in f_names:
1786 e02b9114 Iustin Pop
        os_path = utils.PathJoin(dir_name, name)
1787 255dcebd Iustin Pop
        status, os_inst = _TryOSFromDisk(name, base_dir=dir_name)
1788 255dcebd Iustin Pop
        if status:
1789 255dcebd Iustin Pop
          diagnose = ""
1790 ba00557a Guido Trotter
          variants = os_inst.supported_variants
1791 255dcebd Iustin Pop
        else:
1792 255dcebd Iustin Pop
          diagnose = os_inst
1793 ba00557a Guido Trotter
          variants = []
1794 ba00557a Guido Trotter
        result.append((name, os_path, status, diagnose, variants))
1795 a8083063 Iustin Pop
1796 c26a6bd2 Iustin Pop
  return result
1797 a8083063 Iustin Pop
1798 a8083063 Iustin Pop
1799 255dcebd Iustin Pop
def _TryOSFromDisk(name, base_dir=None):
1800 a8083063 Iustin Pop
  """Create an OS instance from disk.
1801 a8083063 Iustin Pop

1802 a8083063 Iustin Pop
  This function will return an OS instance if the given name is a
1803 8e70b181 Iustin Pop
  valid OS name.
1804 a8083063 Iustin Pop

1805 8ee4dc80 Guido Trotter
  @type base_dir: string
1806 8ee4dc80 Guido Trotter
  @keyword base_dir: Base directory containing OS installations.
1807 8ee4dc80 Guido Trotter
                     Defaults to a search in all the OS_SEARCH_PATH dirs.
1808 255dcebd Iustin Pop
  @rtype: tuple
1809 255dcebd Iustin Pop
  @return: success and either the OS instance if we find a valid one,
1810 255dcebd Iustin Pop
      or error message
1811 7c3d51d4 Guido Trotter

1812 a8083063 Iustin Pop
  """
1813 56bcd3f4 Guido Trotter
  if base_dir is None:
1814 57c177af Iustin Pop
    os_dir = utils.FindFile(name, constants.OS_SEARCH_PATH, os.path.isdir)
1815 c34c0cfd Iustin Pop
  else:
1816 f95c81bf Iustin Pop
    os_dir = utils.FindFile(name, [base_dir], os.path.isdir)
1817 f95c81bf Iustin Pop
1818 f95c81bf Iustin Pop
  if os_dir is None:
1819 5c0433d6 Iustin Pop
    return False, "Directory for OS %s not found in search path" % name
1820 a8083063 Iustin Pop
1821 c19f9810 Iustin Pop
  status, api_versions = _OSOndiskAPIVersion(os_dir)
1822 255dcebd Iustin Pop
  if not status:
1823 255dcebd Iustin Pop
    # push the error up
1824 255dcebd Iustin Pop
    return status, api_versions
1825 a8083063 Iustin Pop
1826 d1a7d66f Guido Trotter
  if not constants.OS_API_VERSIONS.intersection(api_versions):
1827 255dcebd Iustin Pop
    return False, ("API version mismatch for path '%s': found %s, want %s." %
1828 d1a7d66f Guido Trotter
                   (os_dir, api_versions, constants.OS_API_VERSIONS))
1829 a8083063 Iustin Pop
1830 41ba4061 Guido Trotter
  # OS Files dictionary, we will populate it with the absolute path names
1831 41ba4061 Guido Trotter
  os_files = dict.fromkeys(constants.OS_SCRIPTS)
1832 a8083063 Iustin Pop
1833 95075fba Guido Trotter
  if max(api_versions) >= constants.OS_API_V15:
1834 95075fba Guido Trotter
    os_files[constants.OS_VARIANTS_FILE] = ''
1835 95075fba Guido Trotter
1836 ea79fc15 Michael Hanselmann
  for filename in os_files:
1837 e02b9114 Iustin Pop
    os_files[filename] = utils.PathJoin(os_dir, filename)
1838 a8083063 Iustin Pop
1839 a8083063 Iustin Pop
    try:
1840 ea79fc15 Michael Hanselmann
      st = os.stat(os_files[filename])
1841 a8083063 Iustin Pop
    except EnvironmentError, err:
1842 41ba4061 Guido Trotter
      return False, ("File '%s' under path '%s' is missing (%s)" %
1843 ea79fc15 Michael Hanselmann
                     (filename, os_dir, _ErrnoOrStr(err)))
1844 a8083063 Iustin Pop
1845 a8083063 Iustin Pop
    if not stat.S_ISREG(stat.S_IFMT(st.st_mode)):
1846 41ba4061 Guido Trotter
      return False, ("File '%s' under path '%s' is not a regular file" %
1847 ea79fc15 Michael Hanselmann
                     (filename, os_dir))
1848 255dcebd Iustin Pop
1849 ea79fc15 Michael Hanselmann
    if filename in constants.OS_SCRIPTS:
1850 0757c107 Guido Trotter
      if stat.S_IMODE(st.st_mode) & stat.S_IXUSR != stat.S_IXUSR:
1851 0757c107 Guido Trotter
        return False, ("File '%s' under path '%s' is not executable" %
1852 ea79fc15 Michael Hanselmann
                       (filename, os_dir))
1853 0757c107 Guido Trotter
1854 95075fba Guido Trotter
  variants = None
1855 95075fba Guido Trotter
  if constants.OS_VARIANTS_FILE in os_files:
1856 95075fba Guido Trotter
    variants_file = os_files[constants.OS_VARIANTS_FILE]
1857 95075fba Guido Trotter
    try:
1858 95075fba Guido Trotter
      variants = utils.ReadFile(variants_file).splitlines()
1859 95075fba Guido Trotter
    except EnvironmentError, err:
1860 95075fba Guido Trotter
      return False, ("Error while reading the OS variants file at %s: %s" %
1861 95075fba Guido Trotter
                     (variants_file, _ErrnoOrStr(err)))
1862 95075fba Guido Trotter
    if not variants:
1863 95075fba Guido Trotter
      return False, ("No supported os variant found")
1864 0757c107 Guido Trotter
1865 8e70b181 Iustin Pop
  os_obj = objects.OS(name=name, path=os_dir,
1866 41ba4061 Guido Trotter
                      create_script=os_files[constants.OS_SCRIPT_CREATE],
1867 41ba4061 Guido Trotter
                      export_script=os_files[constants.OS_SCRIPT_EXPORT],
1868 41ba4061 Guido Trotter
                      import_script=os_files[constants.OS_SCRIPT_IMPORT],
1869 41ba4061 Guido Trotter
                      rename_script=os_files[constants.OS_SCRIPT_RENAME],
1870 95075fba Guido Trotter
                      supported_variants=variants,
1871 255dcebd Iustin Pop
                      api_versions=api_versions)
1872 255dcebd Iustin Pop
  return True, os_obj
1873 255dcebd Iustin Pop
1874 255dcebd Iustin Pop
1875 255dcebd Iustin Pop
def OSFromDisk(name, base_dir=None):
1876 255dcebd Iustin Pop
  """Create an OS instance from disk.
1877 255dcebd Iustin Pop

1878 255dcebd Iustin Pop
  This function will return an OS instance if the given name is a
1879 255dcebd Iustin Pop
  valid OS name. Otherwise, it will raise an appropriate
1880 255dcebd Iustin Pop
  L{RPCFail} exception, detailing why this is not a valid OS.
1881 255dcebd Iustin Pop

1882 255dcebd Iustin Pop
  This is just a wrapper over L{_TryOSFromDisk}, which doesn't raise
1883 255dcebd Iustin Pop
  an exception but returns true/false status data.
1884 255dcebd Iustin Pop

1885 255dcebd Iustin Pop
  @type base_dir: string
1886 255dcebd Iustin Pop
  @keyword base_dir: Base directory containing OS installations.
1887 255dcebd Iustin Pop
                     Defaults to a search in all the OS_SEARCH_PATH dirs.
1888 255dcebd Iustin Pop
  @rtype: L{objects.OS}
1889 255dcebd Iustin Pop
  @return: the OS instance if we find a valid one
1890 255dcebd Iustin Pop
  @raise RPCFail: if we don't find a valid OS
1891 255dcebd Iustin Pop

1892 255dcebd Iustin Pop
  """
1893 69b99987 Michael Hanselmann
  name_only = name.split("+", 1)[0]
1894 6ee7102a Guido Trotter
  status, payload = _TryOSFromDisk(name_only, base_dir)
1895 255dcebd Iustin Pop
1896 255dcebd Iustin Pop
  if not status:
1897 255dcebd Iustin Pop
    _Fail(payload)
1898 a8083063 Iustin Pop
1899 255dcebd Iustin Pop
  return payload
1900 a8083063 Iustin Pop
1901 a8083063 Iustin Pop
1902 099c52ad Iustin Pop
def OSEnvironment(instance, inst_os, debug=0):
1903 2266edb2 Guido Trotter
  """Calculate the environment for an os script.
1904 2266edb2 Guido Trotter

1905 10c2650b Iustin Pop
  @type instance: L{objects.Instance}
1906 2266edb2 Guido Trotter
  @param instance: target instance for the os script run
1907 099c52ad Iustin Pop
  @type inst_os: L{objects.OS}
1908 099c52ad Iustin Pop
  @param inst_os: operating system for which the environment is being built
1909 2266edb2 Guido Trotter
  @type debug: integer
1910 10c2650b Iustin Pop
  @param debug: debug level (0 or 1, for OS Api 10)
1911 2266edb2 Guido Trotter
  @rtype: dict
1912 2266edb2 Guido Trotter
  @return: dict of environment variables
1913 10c2650b Iustin Pop
  @raise errors.BlockDeviceError: if the block device
1914 10c2650b Iustin Pop
      cannot be found
1915 2266edb2 Guido Trotter

1916 2266edb2 Guido Trotter
  """
1917 2266edb2 Guido Trotter
  result = {}
1918 099c52ad Iustin Pop
  api_version = \
1919 099c52ad Iustin Pop
    max(constants.OS_API_VERSIONS.intersection(inst_os.api_versions))
1920 d1a7d66f Guido Trotter
  result['OS_API_VERSION'] = '%d' % api_version
1921 2266edb2 Guido Trotter
  result['INSTANCE_NAME'] = instance.name
1922 15552312 Iustin Pop
  result['INSTANCE_OS'] = instance.os
1923 2266edb2 Guido Trotter
  result['HYPERVISOR'] = instance.hypervisor
1924 2266edb2 Guido Trotter
  result['DISK_COUNT'] = '%d' % len(instance.disks)
1925 2266edb2 Guido Trotter
  result['NIC_COUNT'] = '%d' % len(instance.nics)
1926 2266edb2 Guido Trotter
  result['DEBUG_LEVEL'] = '%d' % debug
1927 f11280b5 Guido Trotter
  if api_version >= constants.OS_API_V15:
1928 f11280b5 Guido Trotter
    try:
1929 f11280b5 Guido Trotter
      variant = instance.os.split('+', 1)[1]
1930 f11280b5 Guido Trotter
    except IndexError:
1931 099c52ad Iustin Pop
      variant = inst_os.supported_variants[0]
1932 f11280b5 Guido Trotter
    result['OS_VARIANT'] = variant
1933 2266edb2 Guido Trotter
  for idx, disk in enumerate(instance.disks):
1934 f2e07bb4 Michael Hanselmann
    real_disk = _OpenRealBD(disk)
1935 2266edb2 Guido Trotter
    result['DISK_%d_PATH' % idx] = real_disk.dev_path
1936 15552312 Iustin Pop
    result['DISK_%d_ACCESS' % idx] = disk.mode
1937 2266edb2 Guido Trotter
    if constants.HV_DISK_TYPE in instance.hvparams:
1938 2266edb2 Guido Trotter
      result['DISK_%d_FRONTEND_TYPE' % idx] = \
1939 2266edb2 Guido Trotter
        instance.hvparams[constants.HV_DISK_TYPE]
1940 2266edb2 Guido Trotter
    if disk.dev_type in constants.LDS_BLOCK:
1941 2266edb2 Guido Trotter
      result['DISK_%d_BACKEND_TYPE' % idx] = 'block'
1942 2266edb2 Guido Trotter
    elif disk.dev_type == constants.LD_FILE:
1943 2266edb2 Guido Trotter
      result['DISK_%d_BACKEND_TYPE' % idx] = \
1944 2266edb2 Guido Trotter
        'file:%s' % disk.physical_id[0]
1945 2266edb2 Guido Trotter
  for idx, nic in enumerate(instance.nics):
1946 2266edb2 Guido Trotter
    result['NIC_%d_MAC' % idx] = nic.mac
1947 2266edb2 Guido Trotter
    if nic.ip:
1948 2266edb2 Guido Trotter
      result['NIC_%d_IP' % idx] = nic.ip
1949 1ba9227f Guido Trotter
    result['NIC_%d_MODE' % idx] = nic.nicparams[constants.NIC_MODE]
1950 1ba9227f Guido Trotter
    if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
1951 1ba9227f Guido Trotter
      result['NIC_%d_BRIDGE' % idx] = nic.nicparams[constants.NIC_LINK]
1952 1ba9227f Guido Trotter
    if nic.nicparams[constants.NIC_LINK]:
1953 1ba9227f Guido Trotter
      result['NIC_%d_LINK' % idx] = nic.nicparams[constants.NIC_LINK]
1954 2266edb2 Guido Trotter
    if constants.HV_NIC_TYPE in instance.hvparams:
1955 2266edb2 Guido Trotter
      result['NIC_%d_FRONTEND_TYPE' % idx] = \
1956 2266edb2 Guido Trotter
        instance.hvparams[constants.HV_NIC_TYPE]
1957 2266edb2 Guido Trotter
1958 67fc3042 Iustin Pop
  for source, kind in [(instance.beparams, "BE"), (instance.hvparams, "HV")]:
1959 67fc3042 Iustin Pop
    for key, value in source.items():
1960 030b218a Iustin Pop
      result["INSTANCE_%s_%s" % (kind, key)] = str(value)
1961 67fc3042 Iustin Pop
1962 2266edb2 Guido Trotter
  return result
1963 a8083063 Iustin Pop
1964 f2e07bb4 Michael Hanselmann
1965 821d1bd1 Iustin Pop
def BlockdevGrow(disk, amount):
1966 594609c0 Iustin Pop
  """Grow a stack of block devices.
1967 594609c0 Iustin Pop

1968 594609c0 Iustin Pop
  This function is called recursively, with the childrens being the
1969 10c2650b Iustin Pop
  first ones to resize.
1970 594609c0 Iustin Pop

1971 10c2650b Iustin Pop
  @type disk: L{objects.Disk}
1972 10c2650b Iustin Pop
  @param disk: the disk to be grown
1973 10c2650b Iustin Pop
  @rtype: (status, result)
1974 10c2650b Iustin Pop
  @return: a tuple with the status of the operation
1975 10c2650b Iustin Pop
      (True/False), and the errors message if status
1976 10c2650b Iustin Pop
      is False
1977 594609c0 Iustin Pop

1978 594609c0 Iustin Pop
  """
1979 594609c0 Iustin Pop
  r_dev = _RecursiveFindBD(disk)
1980 594609c0 Iustin Pop
  if r_dev is None:
1981 afdc3985 Iustin Pop
    _Fail("Cannot find block device %s", disk)
1982 594609c0 Iustin Pop
1983 594609c0 Iustin Pop
  try:
1984 594609c0 Iustin Pop
    r_dev.Grow(amount)
1985 594609c0 Iustin Pop
  except errors.BlockDeviceError, err:
1986 2cc6781a Iustin Pop
    _Fail("Failed to grow block device: %s", err, exc=True)
1987 594609c0 Iustin Pop
1988 594609c0 Iustin Pop
1989 821d1bd1 Iustin Pop
def BlockdevSnapshot(disk):
1990 a8083063 Iustin Pop
  """Create a snapshot copy of a block device.
1991 a8083063 Iustin Pop

1992 a8083063 Iustin Pop
  This function is called recursively, and the snapshot is actually created
1993 a8083063 Iustin Pop
  just for the leaf lvm backend device.
1994 a8083063 Iustin Pop

1995 e9e9263d Guido Trotter
  @type disk: L{objects.Disk}
1996 e9e9263d Guido Trotter
  @param disk: the disk to be snapshotted
1997 e9e9263d Guido Trotter
  @rtype: string
1998 e9e9263d Guido Trotter
  @return: snapshot disk path
1999 a8083063 Iustin Pop

2000 098c0958 Michael Hanselmann
  """
2001 433c63aa Iustin Pop
  if disk.dev_type == constants.LD_DRBD8:
2002 433c63aa Iustin Pop
    if not disk.children:
2003 433c63aa Iustin Pop
      _Fail("DRBD device '%s' without backing storage cannot be snapshotted",
2004 433c63aa Iustin Pop
            disk.unique_id)
2005 433c63aa Iustin Pop
    return BlockdevSnapshot(disk.children[0])
2006 fe96220b Iustin Pop
  elif disk.dev_type == constants.LD_LV:
2007 a8083063 Iustin Pop
    r_dev = _RecursiveFindBD(disk)
2008 a8083063 Iustin Pop
    if r_dev is not None:
2009 433c63aa Iustin Pop
      # FIXME: choose a saner value for the snapshot size
2010 a8083063 Iustin Pop
      # let's stay on the safe side and ask for the full size, for now
2011 c26a6bd2 Iustin Pop
      return r_dev.Snapshot(disk.size)
2012 a8083063 Iustin Pop
    else:
2013 87812fd3 Iustin Pop
      _Fail("Cannot find block device %s", disk)
2014 a8083063 Iustin Pop
  else:
2015 87812fd3 Iustin Pop
    _Fail("Cannot snapshot non-lvm block device '%s' of type '%s'",
2016 87812fd3 Iustin Pop
          disk.unique_id, disk.dev_type)
2017 a8083063 Iustin Pop
2018 a8083063 Iustin Pop
2019 4a0e011f Iustin Pop
def ExportSnapshot(disk, dest_node, instance, cluster_name, idx, debug):
2020 a8083063 Iustin Pop
  """Export a block device snapshot to a remote node.
2021 a8083063 Iustin Pop

2022 74c47259 Iustin Pop
  @type disk: L{objects.Disk}
2023 74c47259 Iustin Pop
  @param disk: the description of the disk to export
2024 74c47259 Iustin Pop
  @type dest_node: str
2025 74c47259 Iustin Pop
  @param dest_node: the destination node to export to
2026 74c47259 Iustin Pop
  @type instance: L{objects.Instance}
2027 74c47259 Iustin Pop
  @param instance: the instance object to whom the disk belongs
2028 74c47259 Iustin Pop
  @type cluster_name: str
2029 74c47259 Iustin Pop
  @param cluster_name: the cluster name, needed for SSH hostalias
2030 74c47259 Iustin Pop
  @type idx: int
2031 74c47259 Iustin Pop
  @param idx: the index of the disk in the instance's disk list,
2032 74c47259 Iustin Pop
      used to export to the OS scripts environment
2033 4a0e011f Iustin Pop
  @type debug: integer
2034 4a0e011f Iustin Pop
  @param debug: debug level, passed to the OS scripts
2035 c26a6bd2 Iustin Pop
  @rtype: None
2036 a8083063 Iustin Pop

2037 098c0958 Michael Hanselmann
  """
2038 a8083063 Iustin Pop
  inst_os = OSFromDisk(instance.os)
2039 4a0e011f Iustin Pop
  export_env = OSEnvironment(instance, inst_os, debug)
2040 d1a7d66f Guido Trotter
2041 a8083063 Iustin Pop
  export_script = inst_os.export_script
2042 a8083063 Iustin Pop
2043 81a3406c Iustin Pop
  logfile = _InstanceLogName("export", inst_os.name, instance.name)
2044 ba55d062 Iustin Pop
2045 f2e07bb4 Michael Hanselmann
  real_disk = _OpenRealBD(disk)
2046 0607699d Guido Trotter
2047 0607699d Guido Trotter
  export_env['EXPORT_DEVICE'] = real_disk.dev_path
2048 74c47259 Iustin Pop
  export_env['EXPORT_INDEX'] = str(idx)
2049 a8083063 Iustin Pop
2050 c4feafe8 Iustin Pop
  destdir = utils.PathJoin(constants.EXPORT_DIR, instance.name + ".new")
2051 a8083063 Iustin Pop
  destfile = disk.physical_id[1]
2052 a8083063 Iustin Pop
2053 a8083063 Iustin Pop
  # the target command is built out of three individual commands,
2054 a8083063 Iustin Pop
  # which are joined by pipes; we check each individual command for
2055 a8083063 Iustin Pop
  # valid parameters
2056 a48b08bf Iustin Pop
  expcmd = utils.BuildShellCmd("set -e; set -o pipefail; cd %s; %s 2>%s",
2057 a48b08bf Iustin Pop
                               inst_os.path, export_script, logfile)
2058 a8083063 Iustin Pop
2059 a8083063 Iustin Pop
  comprcmd = "gzip"
2060 a8083063 Iustin Pop
2061 0411c011 Iustin Pop
  destcmd = utils.BuildShellCmd("mkdir -p %s && cat > %s",
2062 0411c011 Iustin Pop
                                destdir, utils.PathJoin(destdir, destfile))
2063 62c9ec92 Iustin Pop
  remotecmd = _GetSshRunner(cluster_name).BuildCmd(dest_node,
2064 62c9ec92 Iustin Pop
                                                   constants.GANETI_RUNAS,
2065 62c9ec92 Iustin Pop
                                                   destcmd)
2066 a8083063 Iustin Pop
2067 a8083063 Iustin Pop
  # all commands have been checked, so we're safe to combine them
2068 72f0f7fd Iustin Pop
  command = '|'.join([expcmd, comprcmd, utils.ShellQuoteArgs(remotecmd)])
2069 a8083063 Iustin Pop
2070 a48b08bf Iustin Pop
  result = utils.RunCmd(["bash", "-c", command], env=export_env)
2071 a8083063 Iustin Pop
2072 a8083063 Iustin Pop
  if result.failed:
2073 ba55d062 Iustin Pop
    _Fail("OS snapshot export command '%s' returned error: %s"
2074 ba55d062 Iustin Pop
          " output: %s", command, result.fail_reason, result.output)
2075 a8083063 Iustin Pop
2076 a8083063 Iustin Pop
2077 a8083063 Iustin Pop
def FinalizeExport(instance, snap_disks):
2078 a8083063 Iustin Pop
  """Write out the export configuration information.
2079 a8083063 Iustin Pop

2080 10c2650b Iustin Pop
  @type instance: L{objects.Instance}
2081 10c2650b Iustin Pop
  @param instance: the instance which we export, used for
2082 10c2650b Iustin Pop
      saving configuration
2083 10c2650b Iustin Pop
  @type snap_disks: list of L{objects.Disk}
2084 10c2650b Iustin Pop
  @param snap_disks: list of snapshot block devices, which
2085 10c2650b Iustin Pop
      will be used to get the actual name of the dump file
2086 a8083063 Iustin Pop

2087 c26a6bd2 Iustin Pop
  @rtype: None
2088 a8083063 Iustin Pop

2089 098c0958 Michael Hanselmann
  """
2090 c4feafe8 Iustin Pop
  destdir = utils.PathJoin(constants.EXPORT_DIR, instance.name + ".new")
2091 c4feafe8 Iustin Pop
  finaldestdir = utils.PathJoin(constants.EXPORT_DIR, instance.name)
2092 a8083063 Iustin Pop
2093 a8083063 Iustin Pop
  config = objects.SerializableConfigParser()
2094 a8083063 Iustin Pop
2095 a8083063 Iustin Pop
  config.add_section(constants.INISECT_EXP)
2096 a8083063 Iustin Pop
  config.set(constants.INISECT_EXP, 'version', '0')
2097 a8083063 Iustin Pop
  config.set(constants.INISECT_EXP, 'timestamp', '%d' % int(time.time()))
2098 a8083063 Iustin Pop
  config.set(constants.INISECT_EXP, 'source', instance.primary_node)
2099 a8083063 Iustin Pop
  config.set(constants.INISECT_EXP, 'os', instance.os)
2100 a8083063 Iustin Pop
  config.set(constants.INISECT_EXP, 'compression', 'gzip')
2101 a8083063 Iustin Pop
2102 a8083063 Iustin Pop
  config.add_section(constants.INISECT_INS)
2103 a8083063 Iustin Pop
  config.set(constants.INISECT_INS, 'name', instance.name)
2104 51de46bf Iustin Pop
  config.set(constants.INISECT_INS, 'memory', '%d' %
2105 51de46bf Iustin Pop
             instance.beparams[constants.BE_MEMORY])
2106 51de46bf Iustin Pop
  config.set(constants.INISECT_INS, 'vcpus', '%d' %
2107 51de46bf Iustin Pop
             instance.beparams[constants.BE_VCPUS])
2108 a8083063 Iustin Pop
  config.set(constants.INISECT_INS, 'disk_template', instance.disk_template)
2109 3c8954ad Iustin Pop
  config.set(constants.INISECT_INS, 'hypervisor', instance.hypervisor)
2110 66f93869 Manuel Franceschini
2111 95268cc3 Iustin Pop
  nic_total = 0
2112 a8083063 Iustin Pop
  for nic_count, nic in enumerate(instance.nics):
2113 95268cc3 Iustin Pop
    nic_total += 1
2114 a8083063 Iustin Pop
    config.set(constants.INISECT_INS, 'nic%d_mac' %
2115 a8083063 Iustin Pop
               nic_count, '%s' % nic.mac)
2116 a8083063 Iustin Pop
    config.set(constants.INISECT_INS, 'nic%d_ip' % nic_count, '%s' % nic.ip)
2117 6801eb5c Iustin Pop
    for param in constants.NICS_PARAMETER_TYPES:
2118 6801eb5c Iustin Pop
      config.set(constants.INISECT_INS, 'nic%d_%s' % (nic_count, param),
2119 6801eb5c Iustin Pop
                 '%s' % nic.nicparams.get(param, None))
2120 a8083063 Iustin Pop
  # TODO: redundant: on load can read nics until it doesn't exist
2121 95268cc3 Iustin Pop
  config.set(constants.INISECT_INS, 'nic_count' , '%d' % nic_total)
2122 a8083063 Iustin Pop
2123 726d7d68 Iustin Pop
  disk_total = 0
2124 a8083063 Iustin Pop
  for disk_count, disk in enumerate(snap_disks):
2125 19d7f90a Guido Trotter
    if disk:
2126 726d7d68 Iustin Pop
      disk_total += 1
2127 19d7f90a Guido Trotter
      config.set(constants.INISECT_INS, 'disk%d_ivname' % disk_count,
2128 19d7f90a Guido Trotter
                 ('%s' % disk.iv_name))
2129 19d7f90a Guido Trotter
      config.set(constants.INISECT_INS, 'disk%d_dump' % disk_count,
2130 19d7f90a Guido Trotter
                 ('%s' % disk.physical_id[1]))
2131 19d7f90a Guido Trotter
      config.set(constants.INISECT_INS, 'disk%d_size' % disk_count,
2132 19d7f90a Guido Trotter
                 ('%d' % disk.size))
2133 a8083063 Iustin Pop
2134 726d7d68 Iustin Pop
  config.set(constants.INISECT_INS, 'disk_count' , '%d' % disk_total)
2135 a8083063 Iustin Pop
2136 3c8954ad Iustin Pop
  # New-style hypervisor/backend parameters
2137 3c8954ad Iustin Pop
2138 3c8954ad Iustin Pop
  config.add_section(constants.INISECT_HYP)
2139 3c8954ad Iustin Pop
  for name, value in instance.hvparams.items():
2140 3c8954ad Iustin Pop
    if name not in constants.HVC_GLOBALS:
2141 3c8954ad Iustin Pop
      config.set(constants.INISECT_HYP, name, str(value))
2142 3c8954ad Iustin Pop
2143 3c8954ad Iustin Pop
  config.add_section(constants.INISECT_BEP)
2144 3c8954ad Iustin Pop
  for name, value in instance.beparams.items():
2145 3c8954ad Iustin Pop
    config.set(constants.INISECT_BEP, name, str(value))
2146 3c8954ad Iustin Pop
2147 c4feafe8 Iustin Pop
  utils.WriteFile(utils.PathJoin(destdir, constants.EXPORT_CONF_FILE),
2148 726d7d68 Iustin Pop
                  data=config.Dumps())
2149 56569f4e Michael Hanselmann
  shutil.rmtree(finaldestdir, ignore_errors=True)
2150 a8083063 Iustin Pop
  shutil.move(destdir, finaldestdir)
2151 a8083063 Iustin Pop
2152 a8083063 Iustin Pop
2153 a8083063 Iustin Pop
def ExportInfo(dest):
2154 a8083063 Iustin Pop
  """Get export configuration information.
2155 a8083063 Iustin Pop

2156 10c2650b Iustin Pop
  @type dest: str
2157 10c2650b Iustin Pop
  @param dest: directory containing the export
2158 a8083063 Iustin Pop

2159 10c2650b Iustin Pop
  @rtype: L{objects.SerializableConfigParser}
2160 10c2650b Iustin Pop
  @return: a serializable config file containing the
2161 10c2650b Iustin Pop
      export info
2162 a8083063 Iustin Pop

2163 a8083063 Iustin Pop
  """
2164 c4feafe8 Iustin Pop
  cff = utils.PathJoin(dest, constants.EXPORT_CONF_FILE)
2165 a8083063 Iustin Pop
2166 a8083063 Iustin Pop
  config = objects.SerializableConfigParser()
2167 a8083063 Iustin Pop
  config.read(cff)
2168 a8083063 Iustin Pop
2169 a8083063 Iustin Pop
  if (not config.has_section(constants.INISECT_EXP) or
2170 a8083063 Iustin Pop
      not config.has_section(constants.INISECT_INS)):
2171 3eccac06 Iustin Pop
    _Fail("Export info file doesn't have the required fields")
2172 a8083063 Iustin Pop
2173 c26a6bd2 Iustin Pop
  return config.Dumps()
2174 a8083063 Iustin Pop
2175 a8083063 Iustin Pop
2176 4a0e011f Iustin Pop
def ImportOSIntoInstance(instance, src_node, src_images, cluster_name, debug):
2177 a8083063 Iustin Pop
  """Import an os image into an instance.
2178 a8083063 Iustin Pop

2179 b1206984 Iustin Pop
  @type instance: L{objects.Instance}
2180 6c0af70e Guido Trotter
  @param instance: instance to import the disks into
2181 6c0af70e Guido Trotter
  @type src_node: string
2182 6c0af70e Guido Trotter
  @param src_node: source node for the disk images
2183 6c0af70e Guido Trotter
  @type src_images: list of string
2184 6c0af70e Guido Trotter
  @param src_images: absolute paths of the disk images
2185 4a0e011f Iustin Pop
  @type debug: integer
2186 4a0e011f Iustin Pop
  @param debug: debug level, passed to the OS scripts
2187 6c0af70e Guido Trotter
  @rtype: list of boolean
2188 6c0af70e Guido Trotter
  @return: each boolean represent the success of importing the n-th disk
2189 a8083063 Iustin Pop

2190 a8083063 Iustin Pop
  """
2191 a8083063 Iustin Pop
  inst_os = OSFromDisk(instance.os)
2192 4a0e011f Iustin Pop
  import_env = OSEnvironment(instance, inst_os, debug)
2193 a8083063 Iustin Pop
  import_script = inst_os.import_script
2194 a8083063 Iustin Pop
2195 81a3406c Iustin Pop
  logfile = _InstanceLogName("import", instance.os, instance.name)
2196 a8083063 Iustin Pop
2197 a8083063 Iustin Pop
  comprcmd = "gunzip"
2198 d868edb4 Iustin Pop
  impcmd = utils.BuildShellCmd("(cd %s; %s >%s 2>&1)", inst_os.path,
2199 d868edb4 Iustin Pop
                               import_script, logfile)
2200 a8083063 Iustin Pop
2201 6c0af70e Guido Trotter
  final_result = []
2202 6c0af70e Guido Trotter
  for idx, image in enumerate(src_images):
2203 6c0af70e Guido Trotter
    if image:
2204 6c0af70e Guido Trotter
      destcmd = utils.BuildShellCmd('cat %s', image)
2205 6c0af70e Guido Trotter
      remotecmd = _GetSshRunner(cluster_name).BuildCmd(src_node,
2206 6c0af70e Guido Trotter
                                                       constants.GANETI_RUNAS,
2207 6c0af70e Guido Trotter
                                                       destcmd)
2208 6c0af70e Guido Trotter
      command = '|'.join([utils.ShellQuoteArgs(remotecmd), comprcmd, impcmd])
2209 6c0af70e Guido Trotter
      import_env['IMPORT_DEVICE'] = import_env['DISK_%d_PATH' % idx]
2210 74c47259 Iustin Pop
      import_env['IMPORT_INDEX'] = str(idx)
2211 6c0af70e Guido Trotter
      result = utils.RunCmd(command, env=import_env)
2212 6c0af70e Guido Trotter
      if result.failed:
2213 726d7d68 Iustin Pop
        logging.error("Disk import command '%s' returned error: %s"
2214 726d7d68 Iustin Pop
                      " output: %s", command, result.fail_reason,
2215 726d7d68 Iustin Pop
                      result.output)
2216 944bf548 Iustin Pop
        final_result.append("error importing disk %d: %s, %s" %
2217 944bf548 Iustin Pop
                            (idx, result.fail_reason, result.output[-100]))
2218 a8083063 Iustin Pop
2219 944bf548 Iustin Pop
  if final_result:
2220 afdc3985 Iustin Pop
    _Fail("; ".join(final_result), log=False)
2221 a8083063 Iustin Pop
2222 a8083063 Iustin Pop
2223 a8083063 Iustin Pop
def ListExports():
2224 a8083063 Iustin Pop
  """Return a list of exports currently available on this machine.
2225 098c0958 Michael Hanselmann

2226 10c2650b Iustin Pop
  @rtype: list
2227 10c2650b Iustin Pop
  @return: list of the exports
2228 10c2650b Iustin Pop

2229 a8083063 Iustin Pop
  """
2230 a8083063 Iustin Pop
  if os.path.isdir(constants.EXPORT_DIR):
2231 c26a6bd2 Iustin Pop
    return utils.ListVisibleFiles(constants.EXPORT_DIR)
2232 a8083063 Iustin Pop
  else:
2233 afdc3985 Iustin Pop
    _Fail("No exports directory")
2234 a8083063 Iustin Pop
2235 a8083063 Iustin Pop
2236 a8083063 Iustin Pop
def RemoveExport(export):
2237 a8083063 Iustin Pop
  """Remove an existing export from the node.
2238 a8083063 Iustin Pop

2239 10c2650b Iustin Pop
  @type export: str
2240 10c2650b Iustin Pop
  @param export: the name of the export to remove
2241 c26a6bd2 Iustin Pop
  @rtype: None
2242 a8083063 Iustin Pop

2243 098c0958 Michael Hanselmann
  """
2244 c4feafe8 Iustin Pop
  target = utils.PathJoin(constants.EXPORT_DIR, export)
2245 a8083063 Iustin Pop
2246 35fbcd11 Iustin Pop
  try:
2247 35fbcd11 Iustin Pop
    shutil.rmtree(target)
2248 35fbcd11 Iustin Pop
  except EnvironmentError, err:
2249 35fbcd11 Iustin Pop
    _Fail("Error while removing the export: %s", err, exc=True)
2250 a8083063 Iustin Pop
2251 a8083063 Iustin Pop
2252 821d1bd1 Iustin Pop
def BlockdevRename(devlist):
2253 f3e513ad Iustin Pop
  """Rename a list of block devices.
2254 f3e513ad Iustin Pop

2255 10c2650b Iustin Pop
  @type devlist: list of tuples
2256 10c2650b Iustin Pop
  @param devlist: list of tuples of the form  (disk,
2257 10c2650b Iustin Pop
      new_logical_id, new_physical_id); disk is an
2258 10c2650b Iustin Pop
      L{objects.Disk} object describing the current disk,
2259 10c2650b Iustin Pop
      and new logical_id/physical_id is the name we
2260 10c2650b Iustin Pop
      rename it to
2261 10c2650b Iustin Pop
  @rtype: boolean
2262 10c2650b Iustin Pop
  @return: True if all renames succeeded, False otherwise
2263 f3e513ad Iustin Pop

2264 f3e513ad Iustin Pop
  """
2265 6b5e3f70 Iustin Pop
  msgs = []
2266 f3e513ad Iustin Pop
  result = True
2267 f3e513ad Iustin Pop
  for disk, unique_id in devlist:
2268 f3e513ad Iustin Pop
    dev = _RecursiveFindBD(disk)
2269 f3e513ad Iustin Pop
    if dev is None:
2270 6b5e3f70 Iustin Pop
      msgs.append("Can't find device %s in rename" % str(disk))
2271 f3e513ad Iustin Pop
      result = False
2272 f3e513ad Iustin Pop
      continue
2273 f3e513ad Iustin Pop
    try:
2274 3f78eef2 Iustin Pop
      old_rpath = dev.dev_path
2275 f3e513ad Iustin Pop
      dev.Rename(unique_id)
2276 3f78eef2 Iustin Pop
      new_rpath = dev.dev_path
2277 3f78eef2 Iustin Pop
      if old_rpath != new_rpath:
2278 3f78eef2 Iustin Pop
        DevCacheManager.RemoveCache(old_rpath)
2279 3f78eef2 Iustin Pop
        # FIXME: we should add the new cache information here, like:
2280 3f78eef2 Iustin Pop
        # DevCacheManager.UpdateCache(new_rpath, owner, ...)
2281 3f78eef2 Iustin Pop
        # but we don't have the owner here - maybe parse from existing
2282 3f78eef2 Iustin Pop
        # cache? for now, we only lose lvm data when we rename, which
2283 3f78eef2 Iustin Pop
        # is less critical than DRBD or MD
2284 f3e513ad Iustin Pop
    except errors.BlockDeviceError, err:
2285 6b5e3f70 Iustin Pop
      msgs.append("Can't rename device '%s' to '%s': %s" %
2286 6b5e3f70 Iustin Pop
                  (dev, unique_id, err))
2287 18682bca Iustin Pop
      logging.exception("Can't rename device '%s' to '%s'", dev, unique_id)
2288 f3e513ad Iustin Pop
      result = False
2289 afdc3985 Iustin Pop
  if not result:
2290 afdc3985 Iustin Pop
    _Fail("; ".join(msgs))
2291 f3e513ad Iustin Pop
2292 f3e513ad Iustin Pop
2293 778b75bb Manuel Franceschini
def _TransformFileStorageDir(file_storage_dir):
2294 778b75bb Manuel Franceschini
  """Checks whether given file_storage_dir is valid.
2295 778b75bb Manuel Franceschini

2296 778b75bb Manuel Franceschini
  Checks wheter the given file_storage_dir is within the cluster-wide
2297 778b75bb Manuel Franceschini
  default file_storage_dir stored in SimpleStore. Only paths under that
2298 778b75bb Manuel Franceschini
  directory are allowed.
2299 778b75bb Manuel Franceschini

2300 b1206984 Iustin Pop
  @type file_storage_dir: str
2301 b1206984 Iustin Pop
  @param file_storage_dir: the path to check
2302 d61cbe76 Iustin Pop

2303 b1206984 Iustin Pop
  @return: the normalized path if valid, None otherwise
2304 778b75bb Manuel Franceschini

2305 778b75bb Manuel Franceschini
  """
2306 cb7c0198 Iustin Pop
  if not constants.ENABLE_FILE_STORAGE:
2307 cb7c0198 Iustin Pop
    _Fail("File storage disabled at configure time")
2308 c657dcc9 Michael Hanselmann
  cfg = _GetConfig()
2309 778b75bb Manuel Franceschini
  file_storage_dir = os.path.normpath(file_storage_dir)
2310 c657dcc9 Michael Hanselmann
  base_file_storage_dir = cfg.GetFileStorageDir()
2311 56569f4e Michael Hanselmann
  if (os.path.commonprefix([file_storage_dir, base_file_storage_dir]) !=
2312 778b75bb Manuel Franceschini
      base_file_storage_dir):
2313 b2b8bcce Iustin Pop
    _Fail("File storage directory '%s' is not under base file"
2314 b2b8bcce Iustin Pop
          " storage directory '%s'", file_storage_dir, base_file_storage_dir)
2315 778b75bb Manuel Franceschini
  return file_storage_dir
2316 778b75bb Manuel Franceschini
2317 778b75bb Manuel Franceschini
2318 778b75bb Manuel Franceschini
def CreateFileStorageDir(file_storage_dir):
2319 778b75bb Manuel Franceschini
  """Create file storage directory.
2320 778b75bb Manuel Franceschini

2321 b1206984 Iustin Pop
  @type file_storage_dir: str
2322 b1206984 Iustin Pop
  @param file_storage_dir: directory to create
2323 778b75bb Manuel Franceschini

2324 b1206984 Iustin Pop
  @rtype: tuple
2325 b1206984 Iustin Pop
  @return: tuple with first element a boolean indicating wheter dir
2326 b1206984 Iustin Pop
      creation was successful or not
2327 778b75bb Manuel Franceschini

2328 778b75bb Manuel Franceschini
  """
2329 778b75bb Manuel Franceschini
  file_storage_dir = _TransformFileStorageDir(file_storage_dir)
2330 b2b8bcce Iustin Pop
  if os.path.exists(file_storage_dir):
2331 b2b8bcce Iustin Pop
    if not os.path.isdir(file_storage_dir):
2332 b2b8bcce Iustin Pop
      _Fail("Specified storage dir '%s' is not a directory",
2333 b2b8bcce Iustin Pop
            file_storage_dir)
2334 778b75bb Manuel Franceschini
  else:
2335 b2b8bcce Iustin Pop
    try:
2336 b2b8bcce Iustin Pop
      os.makedirs(file_storage_dir, 0750)
2337 b2b8bcce Iustin Pop
    except OSError, err:
2338 b2b8bcce Iustin Pop
      _Fail("Cannot create file storage directory '%s': %s",
2339 b2b8bcce Iustin Pop
            file_storage_dir, err, exc=True)
2340 778b75bb Manuel Franceschini
2341 778b75bb Manuel Franceschini
2342 778b75bb Manuel Franceschini
def RemoveFileStorageDir(file_storage_dir):
2343 778b75bb Manuel Franceschini
  """Remove file storage directory.
2344 778b75bb Manuel Franceschini

2345 778b75bb Manuel Franceschini
  Remove it only if it's empty. If not log an error and return.
2346 778b75bb Manuel Franceschini

2347 10c2650b Iustin Pop
  @type file_storage_dir: str
2348 10c2650b Iustin Pop
  @param file_storage_dir: the directory we should cleanup
2349 10c2650b Iustin Pop
  @rtype: tuple (success,)
2350 10c2650b Iustin Pop
  @return: tuple of one element, C{success}, denoting
2351 5bbd3f7f Michael Hanselmann
      whether the operation was successful
2352 778b75bb Manuel Franceschini

2353 778b75bb Manuel Franceschini
  """
2354 778b75bb Manuel Franceschini
  file_storage_dir = _TransformFileStorageDir(file_storage_dir)
2355 b2b8bcce Iustin Pop
  if os.path.exists(file_storage_dir):
2356 b2b8bcce Iustin Pop
    if not os.path.isdir(file_storage_dir):
2357 b2b8bcce Iustin Pop
      _Fail("Specified Storage directory '%s' is not a directory",
2358 b2b8bcce Iustin Pop
            file_storage_dir)
2359 afdc3985 Iustin Pop
    # deletes dir only if empty, otherwise we want to fail the rpc call
2360 b2b8bcce Iustin Pop
    try:
2361 b2b8bcce Iustin Pop
      os.rmdir(file_storage_dir)
2362 b2b8bcce Iustin Pop
    except OSError, err:
2363 b2b8bcce Iustin Pop
      _Fail("Cannot remove file storage directory '%s': %s",
2364 b2b8bcce Iustin Pop
            file_storage_dir, err)
2365 b2b8bcce Iustin Pop
2366 778b75bb Manuel Franceschini
2367 778b75bb Manuel Franceschini
def RenameFileStorageDir(old_file_storage_dir, new_file_storage_dir):
2368 778b75bb Manuel Franceschini
  """Rename the file storage directory.
2369 778b75bb Manuel Franceschini

2370 10c2650b Iustin Pop
  @type old_file_storage_dir: str
2371 10c2650b Iustin Pop
  @param old_file_storage_dir: the current path
2372 10c2650b Iustin Pop
  @type new_file_storage_dir: str
2373 10c2650b Iustin Pop
  @param new_file_storage_dir: the name we should rename to
2374 10c2650b Iustin Pop
  @rtype: tuple (success,)
2375 10c2650b Iustin Pop
  @return: tuple of one element, C{success}, denoting
2376 10c2650b Iustin Pop
      whether the operation was successful
2377 778b75bb Manuel Franceschini

2378 778b75bb Manuel Franceschini
  """
2379 778b75bb Manuel Franceschini
  old_file_storage_dir = _TransformFileStorageDir(old_file_storage_dir)
2380 778b75bb Manuel Franceschini
  new_file_storage_dir = _TransformFileStorageDir(new_file_storage_dir)
2381 b2b8bcce Iustin Pop
  if not os.path.exists(new_file_storage_dir):
2382 b2b8bcce Iustin Pop
    if os.path.isdir(old_file_storage_dir):
2383 b2b8bcce Iustin Pop
      try:
2384 b2b8bcce Iustin Pop
        os.rename(old_file_storage_dir, new_file_storage_dir)
2385 b2b8bcce Iustin Pop
      except OSError, err:
2386 b2b8bcce Iustin Pop
        _Fail("Cannot rename '%s' to '%s': %s",
2387 b2b8bcce Iustin Pop
              old_file_storage_dir, new_file_storage_dir, err)
2388 778b75bb Manuel Franceschini
    else:
2389 b2b8bcce Iustin Pop
      _Fail("Specified storage dir '%s' is not a directory",
2390 b2b8bcce Iustin Pop
            old_file_storage_dir)
2391 b2b8bcce Iustin Pop
  else:
2392 b2b8bcce Iustin Pop
    if os.path.exists(old_file_storage_dir):
2393 b2b8bcce Iustin Pop
      _Fail("Cannot rename '%s' to '%s': both locations exist",
2394 b2b8bcce Iustin Pop
            old_file_storage_dir, new_file_storage_dir)
2395 778b75bb Manuel Franceschini
2396 778b75bb Manuel Franceschini
2397 c8457ce7 Iustin Pop
def _EnsureJobQueueFile(file_name):
2398 dc31eae3 Michael Hanselmann
  """Checks whether the given filename is in the queue directory.
2399 ca52cdeb Michael Hanselmann

2400 10c2650b Iustin Pop
  @type file_name: str
2401 10c2650b Iustin Pop
  @param file_name: the file name we should check
2402 c8457ce7 Iustin Pop
  @rtype: None
2403 c8457ce7 Iustin Pop
  @raises RPCFail: if the file is not valid
2404 10c2650b Iustin Pop

2405 ca52cdeb Michael Hanselmann
  """
2406 ca52cdeb Michael Hanselmann
  queue_dir = os.path.normpath(constants.QUEUE_DIR)
2407 dc31eae3 Michael Hanselmann
  result = (os.path.commonprefix([queue_dir, file_name]) == queue_dir)
2408 dc31eae3 Michael Hanselmann
2409 dc31eae3 Michael Hanselmann
  if not result:
2410 c8457ce7 Iustin Pop
    _Fail("Passed job queue file '%s' does not belong to"
2411 c8457ce7 Iustin Pop
          " the queue directory '%s'", file_name, queue_dir)
2412 dc31eae3 Michael Hanselmann
2413 dc31eae3 Michael Hanselmann
2414 dc31eae3 Michael Hanselmann
def JobQueueUpdate(file_name, content):
2415 dc31eae3 Michael Hanselmann
  """Updates a file in the queue directory.
2416 dc31eae3 Michael Hanselmann

2417 10c2650b Iustin Pop
  This is just a wrapper over L{utils.WriteFile}, with proper
2418 10c2650b Iustin Pop
  checking.
2419 10c2650b Iustin Pop

2420 10c2650b Iustin Pop
  @type file_name: str
2421 10c2650b Iustin Pop
  @param file_name: the job file name
2422 10c2650b Iustin Pop
  @type content: str
2423 10c2650b Iustin Pop
  @param content: the new job contents
2424 10c2650b Iustin Pop
  @rtype: boolean
2425 10c2650b Iustin Pop
  @return: the success of the operation
2426 10c2650b Iustin Pop

2427 dc31eae3 Michael Hanselmann
  """
2428 c8457ce7 Iustin Pop
  _EnsureJobQueueFile(file_name)
2429 ca52cdeb Michael Hanselmann
2430 ca52cdeb Michael Hanselmann
  # Write and replace the file atomically
2431 12bce260 Michael Hanselmann
  utils.WriteFile(file_name, data=_Decompress(content))
2432 ca52cdeb Michael Hanselmann
2433 ca52cdeb Michael Hanselmann
2434 af5ebcb1 Michael Hanselmann
def JobQueueRename(old, new):
2435 af5ebcb1 Michael Hanselmann
  """Renames a job queue file.
2436 af5ebcb1 Michael Hanselmann

2437 c41eea6e Iustin Pop
  This is just a wrapper over os.rename with proper checking.
2438 10c2650b Iustin Pop

2439 10c2650b Iustin Pop
  @type old: str
2440 10c2650b Iustin Pop
  @param old: the old (actual) file name
2441 10c2650b Iustin Pop
  @type new: str
2442 10c2650b Iustin Pop
  @param new: the desired file name
2443 c8457ce7 Iustin Pop
  @rtype: tuple
2444 c8457ce7 Iustin Pop
  @return: the success of the operation and payload
2445 10c2650b Iustin Pop

2446 af5ebcb1 Michael Hanselmann
  """
2447 c8457ce7 Iustin Pop
  _EnsureJobQueueFile(old)
2448 c8457ce7 Iustin Pop
  _EnsureJobQueueFile(new)
2449 af5ebcb1 Michael Hanselmann
2450 58b22b6e Michael Hanselmann
  utils.RenameFile(old, new, mkdir=True)
2451 af5ebcb1 Michael Hanselmann
2452 af5ebcb1 Michael Hanselmann
2453 5d672980 Iustin Pop
def JobQueueSetDrainFlag(drain_flag):
2454 5d672980 Iustin Pop
  """Set the drain flag for the queue.
2455 5d672980 Iustin Pop

2456 5d672980 Iustin Pop
  This will set or unset the queue drain flag.
2457 5d672980 Iustin Pop

2458 10c2650b Iustin Pop
  @type drain_flag: boolean
2459 5d672980 Iustin Pop
  @param drain_flag: if True, will set the drain flag, otherwise reset it.
2460 c8457ce7 Iustin Pop
  @rtype: truple
2461 c8457ce7 Iustin Pop
  @return: always True, None
2462 10c2650b Iustin Pop
  @warning: the function always returns True
2463 5d672980 Iustin Pop

2464 5d672980 Iustin Pop
  """
2465 5d672980 Iustin Pop
  if drain_flag:
2466 5d672980 Iustin Pop
    utils.WriteFile(constants.JOB_QUEUE_DRAIN_FILE, data="", close=True)
2467 5d672980 Iustin Pop
  else:
2468 5d672980 Iustin Pop
    utils.RemoveFile(constants.JOB_QUEUE_DRAIN_FILE)
2469 5d672980 Iustin Pop
2470 5d672980 Iustin Pop
2471 821d1bd1 Iustin Pop
def BlockdevClose(instance_name, disks):
2472 d61cbe76 Iustin Pop
  """Closes the given block devices.
2473 d61cbe76 Iustin Pop

2474 10c2650b Iustin Pop
  This means they will be switched to secondary mode (in case of
2475 10c2650b Iustin Pop
  DRBD).
2476 10c2650b Iustin Pop

2477 b2e7666a Iustin Pop
  @param instance_name: if the argument is not empty, the symlinks
2478 b2e7666a Iustin Pop
      of this instance will be removed
2479 10c2650b Iustin Pop
  @type disks: list of L{objects.Disk}
2480 10c2650b Iustin Pop
  @param disks: the list of disks to be closed
2481 10c2650b Iustin Pop
  @rtype: tuple (success, message)
2482 10c2650b Iustin Pop
  @return: a tuple of success and message, where success
2483 10c2650b Iustin Pop
      indicates the succes of the operation, and message
2484 10c2650b Iustin Pop
      which will contain the error details in case we
2485 10c2650b Iustin Pop
      failed
2486 d61cbe76 Iustin Pop

2487 d61cbe76 Iustin Pop
  """
2488 d61cbe76 Iustin Pop
  bdevs = []
2489 d61cbe76 Iustin Pop
  for cf in disks:
2490 d61cbe76 Iustin Pop
    rd = _RecursiveFindBD(cf)
2491 d61cbe76 Iustin Pop
    if rd is None:
2492 2cc6781a Iustin Pop
      _Fail("Can't find device %s", cf)
2493 d61cbe76 Iustin Pop
    bdevs.append(rd)
2494 d61cbe76 Iustin Pop
2495 d61cbe76 Iustin Pop
  msg = []
2496 d61cbe76 Iustin Pop
  for rd in bdevs:
2497 d61cbe76 Iustin Pop
    try:
2498 d61cbe76 Iustin Pop
      rd.Close()
2499 d61cbe76 Iustin Pop
    except errors.BlockDeviceError, err:
2500 d61cbe76 Iustin Pop
      msg.append(str(err))
2501 d61cbe76 Iustin Pop
  if msg:
2502 afdc3985 Iustin Pop
    _Fail("Can't make devices secondary: %s", ",".join(msg))
2503 d61cbe76 Iustin Pop
  else:
2504 b2e7666a Iustin Pop
    if instance_name:
2505 5282084b Iustin Pop
      _RemoveBlockDevLinks(instance_name, disks)
2506 d61cbe76 Iustin Pop
2507 d61cbe76 Iustin Pop
2508 6217e295 Iustin Pop
def ValidateHVParams(hvname, hvparams):
2509 6217e295 Iustin Pop
  """Validates the given hypervisor parameters.
2510 6217e295 Iustin Pop

2511 6217e295 Iustin Pop
  @type hvname: string
2512 6217e295 Iustin Pop
  @param hvname: the hypervisor name
2513 6217e295 Iustin Pop
  @type hvparams: dict
2514 6217e295 Iustin Pop
  @param hvparams: the hypervisor parameters to be validated
2515 c26a6bd2 Iustin Pop
  @rtype: None
2516 6217e295 Iustin Pop

2517 6217e295 Iustin Pop
  """
2518 6217e295 Iustin Pop
  try:
2519 6217e295 Iustin Pop
    hv_type = hypervisor.GetHypervisor(hvname)
2520 6217e295 Iustin Pop
    hv_type.ValidateParameters(hvparams)
2521 6217e295 Iustin Pop
  except errors.HypervisorError, err:
2522 afdc3985 Iustin Pop
    _Fail(str(err), log=False)
2523 6217e295 Iustin Pop
2524 6217e295 Iustin Pop
2525 56aa9fd5 Iustin Pop
def DemoteFromMC():
2526 56aa9fd5 Iustin Pop
  """Demotes the current node from master candidate role.
2527 56aa9fd5 Iustin Pop

2528 56aa9fd5 Iustin Pop
  """
2529 56aa9fd5 Iustin Pop
  # try to ensure we're not the master by mistake
2530 56aa9fd5 Iustin Pop
  master, myself = ssconf.GetMasterAndMyself()
2531 56aa9fd5 Iustin Pop
  if master == myself:
2532 afdc3985 Iustin Pop
    _Fail("ssconf status shows I'm the master node, will not demote")
2533 f154a7a3 Michael Hanselmann
2534 f154a7a3 Michael Hanselmann
  result = utils.RunCmd([constants.DAEMON_UTIL, "check", constants.MASTERD])
2535 f154a7a3 Michael Hanselmann
  if not result.failed:
2536 afdc3985 Iustin Pop
    _Fail("The master daemon is running, will not demote")
2537 f154a7a3 Michael Hanselmann
2538 56aa9fd5 Iustin Pop
  try:
2539 9a5cb537 Iustin Pop
    if os.path.isfile(constants.CLUSTER_CONF_FILE):
2540 9a5cb537 Iustin Pop
      utils.CreateBackup(constants.CLUSTER_CONF_FILE)
2541 56aa9fd5 Iustin Pop
  except EnvironmentError, err:
2542 56aa9fd5 Iustin Pop
    if err.errno != errno.ENOENT:
2543 afdc3985 Iustin Pop
      _Fail("Error while backing up cluster file: %s", err, exc=True)
2544 f154a7a3 Michael Hanselmann
2545 56aa9fd5 Iustin Pop
  utils.RemoveFile(constants.CLUSTER_CONF_FILE)
2546 56aa9fd5 Iustin Pop
2547 56aa9fd5 Iustin Pop
2548 6b93ec9d Iustin Pop
def _FindDisks(nodes_ip, disks):
2549 6b93ec9d Iustin Pop
  """Sets the physical ID on disks and returns the block devices.
2550 6b93ec9d Iustin Pop

2551 6b93ec9d Iustin Pop
  """
2552 6b93ec9d Iustin Pop
  # set the correct physical ID
2553 6b93ec9d Iustin Pop
  my_name = utils.HostInfo().name
2554 6b93ec9d Iustin Pop
  for cf in disks:
2555 6b93ec9d Iustin Pop
    cf.SetPhysicalID(my_name, nodes_ip)
2556 6b93ec9d Iustin Pop
2557 6b93ec9d Iustin Pop
  bdevs = []
2558 6b93ec9d Iustin Pop
2559 6b93ec9d Iustin Pop
  for cf in disks:
2560 6b93ec9d Iustin Pop
    rd = _RecursiveFindBD(cf)
2561 6b93ec9d Iustin Pop
    if rd is None:
2562 5a533f8a Iustin Pop
      _Fail("Can't find device %s", cf)
2563 6b93ec9d Iustin Pop
    bdevs.append(rd)
2564 5a533f8a Iustin Pop
  return bdevs
2565 6b93ec9d Iustin Pop
2566 6b93ec9d Iustin Pop
2567 6b93ec9d Iustin Pop
def DrbdDisconnectNet(nodes_ip, disks):
2568 6b93ec9d Iustin Pop
  """Disconnects the network on a list of drbd devices.
2569 6b93ec9d Iustin Pop

2570 6b93ec9d Iustin Pop
  """
2571 5a533f8a Iustin Pop
  bdevs = _FindDisks(nodes_ip, disks)
2572 6b93ec9d Iustin Pop
2573 6b93ec9d Iustin Pop
  # disconnect disks
2574 6b93ec9d Iustin Pop
  for rd in bdevs:
2575 6b93ec9d Iustin Pop
    try:
2576 6b93ec9d Iustin Pop
      rd.DisconnectNet()
2577 6b93ec9d Iustin Pop
    except errors.BlockDeviceError, err:
2578 2cc6781a Iustin Pop
      _Fail("Can't change network configuration to standalone mode: %s",
2579 2cc6781a Iustin Pop
            err, exc=True)
2580 6b93ec9d Iustin Pop
2581 6b93ec9d Iustin Pop
2582 6b93ec9d Iustin Pop
def DrbdAttachNet(nodes_ip, disks, instance_name, multimaster):
2583 6b93ec9d Iustin Pop
  """Attaches the network on a list of drbd devices.
2584 6b93ec9d Iustin Pop

2585 6b93ec9d Iustin Pop
  """
2586 5a533f8a Iustin Pop
  bdevs = _FindDisks(nodes_ip, disks)
2587 6b93ec9d Iustin Pop
2588 6b93ec9d Iustin Pop
  if multimaster:
2589 53c776b5 Iustin Pop
    for idx, rd in enumerate(bdevs):
2590 6b93ec9d Iustin Pop
      try:
2591 53c776b5 Iustin Pop
        _SymlinkBlockDev(instance_name, rd.dev_path, idx)
2592 6b93ec9d Iustin Pop
      except EnvironmentError, err:
2593 2cc6781a Iustin Pop
        _Fail("Can't create symlink: %s", err)
2594 6b93ec9d Iustin Pop
  # reconnect disks, switch to new master configuration and if
2595 6b93ec9d Iustin Pop
  # needed primary mode
2596 6b93ec9d Iustin Pop
  for rd in bdevs:
2597 6b93ec9d Iustin Pop
    try:
2598 6b93ec9d Iustin Pop
      rd.AttachNet(multimaster)
2599 6b93ec9d Iustin Pop
    except errors.BlockDeviceError, err:
2600 2cc6781a Iustin Pop
      _Fail("Can't change network configuration: %s", err)
2601 3c0cdc83 Michael Hanselmann
2602 6b93ec9d Iustin Pop
  # wait until the disks are connected; we need to retry the re-attach
2603 6b93ec9d Iustin Pop
  # if the device becomes standalone, as this might happen if the one
2604 6b93ec9d Iustin Pop
  # node disconnects and reconnects in a different mode before the
2605 6b93ec9d Iustin Pop
  # other node reconnects; in this case, one or both of the nodes will
2606 6b93ec9d Iustin Pop
  # decide it has wrong configuration and switch to standalone
2607 3c0cdc83 Michael Hanselmann
2608 3c0cdc83 Michael Hanselmann
  def _Attach():
2609 6b93ec9d Iustin Pop
    all_connected = True
2610 3c0cdc83 Michael Hanselmann
2611 6b93ec9d Iustin Pop
    for rd in bdevs:
2612 6b93ec9d Iustin Pop
      stats = rd.GetProcStatus()
2613 3c0cdc83 Michael Hanselmann
2614 3c0cdc83 Michael Hanselmann
      all_connected = (all_connected and
2615 3c0cdc83 Michael Hanselmann
                       (stats.is_connected or stats.is_in_resync))
2616 3c0cdc83 Michael Hanselmann
2617 6b93ec9d Iustin Pop
      if stats.is_standalone:
2618 6b93ec9d Iustin Pop
        # peer had different config info and this node became
2619 6b93ec9d Iustin Pop
        # standalone, even though this should not happen with the
2620 6b93ec9d Iustin Pop
        # new staged way of changing disk configs
2621 6b93ec9d Iustin Pop
        try:
2622 c738375b Iustin Pop
          rd.AttachNet(multimaster)
2623 6b93ec9d Iustin Pop
        except errors.BlockDeviceError, err:
2624 2cc6781a Iustin Pop
          _Fail("Can't change network configuration: %s", err)
2625 3c0cdc83 Michael Hanselmann
2626 3c0cdc83 Michael Hanselmann
    if not all_connected:
2627 3c0cdc83 Michael Hanselmann
      raise utils.RetryAgain()
2628 3c0cdc83 Michael Hanselmann
2629 3c0cdc83 Michael Hanselmann
  try:
2630 3c0cdc83 Michael Hanselmann
    # Start with a delay of 100 miliseconds and go up to 5 seconds
2631 3c0cdc83 Michael Hanselmann
    utils.Retry(_Attach, (0.1, 1.5, 5.0), 2 * 60)
2632 3c0cdc83 Michael Hanselmann
  except utils.RetryTimeout:
2633 afdc3985 Iustin Pop
    _Fail("Timeout in disk reconnecting")
2634 3c0cdc83 Michael Hanselmann
2635 6b93ec9d Iustin Pop
  if multimaster:
2636 6b93ec9d Iustin Pop
    # change to primary mode
2637 6b93ec9d Iustin Pop
    for rd in bdevs:
2638 d3da87b8 Iustin Pop
      try:
2639 d3da87b8 Iustin Pop
        rd.Open()
2640 d3da87b8 Iustin Pop
      except errors.BlockDeviceError, err:
2641 2cc6781a Iustin Pop
        _Fail("Can't change to primary mode: %s", err)
2642 6b93ec9d Iustin Pop
2643 6b93ec9d Iustin Pop
2644 6b93ec9d Iustin Pop
def DrbdWaitSync(nodes_ip, disks):
2645 6b93ec9d Iustin Pop
  """Wait until DRBDs have synchronized.
2646 6b93ec9d Iustin Pop

2647 6b93ec9d Iustin Pop
  """
2648 db8667b7 Iustin Pop
  def _helper(rd):
2649 db8667b7 Iustin Pop
    stats = rd.GetProcStatus()
2650 db8667b7 Iustin Pop
    if not (stats.is_connected or stats.is_in_resync):
2651 db8667b7 Iustin Pop
      raise utils.RetryAgain()
2652 db8667b7 Iustin Pop
    return stats
2653 db8667b7 Iustin Pop
2654 5a533f8a Iustin Pop
  bdevs = _FindDisks(nodes_ip, disks)
2655 6b93ec9d Iustin Pop
2656 6b93ec9d Iustin Pop
  min_resync = 100
2657 6b93ec9d Iustin Pop
  alldone = True
2658 6b93ec9d Iustin Pop
  for rd in bdevs:
2659 db8667b7 Iustin Pop
    try:
2660 db8667b7 Iustin Pop
      # poll each second for 15 seconds
2661 db8667b7 Iustin Pop
      stats = utils.Retry(_helper, 1, 15, args=[rd])
2662 db8667b7 Iustin Pop
    except utils.RetryTimeout:
2663 db8667b7 Iustin Pop
      stats = rd.GetProcStatus()
2664 db8667b7 Iustin Pop
      # last check
2665 db8667b7 Iustin Pop
      if not (stats.is_connected or stats.is_in_resync):
2666 db8667b7 Iustin Pop
        _Fail("DRBD device %s is not in sync: stats=%s", rd, stats)
2667 6b93ec9d Iustin Pop
    alldone = alldone and (not stats.is_in_resync)
2668 6b93ec9d Iustin Pop
    if stats.sync_percent is not None:
2669 6b93ec9d Iustin Pop
      min_resync = min(min_resync, stats.sync_percent)
2670 afdc3985 Iustin Pop
2671 c26a6bd2 Iustin Pop
  return (alldone, min_resync)
2672 6b93ec9d Iustin Pop
2673 6b93ec9d Iustin Pop
2674 f5118ade Iustin Pop
def PowercycleNode(hypervisor_type):
2675 f5118ade Iustin Pop
  """Hard-powercycle the node.
2676 f5118ade Iustin Pop

2677 f5118ade Iustin Pop
  Because we need to return first, and schedule the powercycle in the
2678 f5118ade Iustin Pop
  background, we won't be able to report failures nicely.
2679 f5118ade Iustin Pop

2680 f5118ade Iustin Pop
  """
2681 f5118ade Iustin Pop
  hyper = hypervisor.GetHypervisor(hypervisor_type)
2682 f5118ade Iustin Pop
  try:
2683 f5118ade Iustin Pop
    pid = os.fork()
2684 29921401 Iustin Pop
  except OSError:
2685 f5118ade Iustin Pop
    # if we can't fork, we'll pretend that we're in the child process
2686 f5118ade Iustin Pop
    pid = 0
2687 f5118ade Iustin Pop
  if pid > 0:
2688 c26a6bd2 Iustin Pop
    return "Reboot scheduled in 5 seconds"
2689 1af6ac0f Luca Bigliardi
  # ensure the child is running on ram
2690 1af6ac0f Luca Bigliardi
  try:
2691 1af6ac0f Luca Bigliardi
    utils.Mlockall()
2692 20601361 Luca Bigliardi
  except Exception: # pylint: disable-msg=W0703
2693 1af6ac0f Luca Bigliardi
    pass
2694 f5118ade Iustin Pop
  time.sleep(5)
2695 f5118ade Iustin Pop
  hyper.PowercycleNode()
2696 f5118ade Iustin Pop
2697 f5118ade Iustin Pop
2698 a8083063 Iustin Pop
class HooksRunner(object):
2699 a8083063 Iustin Pop
  """Hook runner.
2700 a8083063 Iustin Pop

2701 10c2650b Iustin Pop
  This class is instantiated on the node side (ganeti-noded) and not
2702 10c2650b Iustin Pop
  on the master side.
2703 a8083063 Iustin Pop

2704 a8083063 Iustin Pop
  """
2705 a8083063 Iustin Pop
  def __init__(self, hooks_base_dir=None):
2706 a8083063 Iustin Pop
    """Constructor for hooks runner.
2707 a8083063 Iustin Pop

2708 10c2650b Iustin Pop
    @type hooks_base_dir: str or None
2709 10c2650b Iustin Pop
    @param hooks_base_dir: if not None, this overrides the
2710 10c2650b Iustin Pop
        L{constants.HOOKS_BASE_DIR} (useful for unittests)
2711 a8083063 Iustin Pop

2712 a8083063 Iustin Pop
    """
2713 a8083063 Iustin Pop
    if hooks_base_dir is None:
2714 a8083063 Iustin Pop
      hooks_base_dir = constants.HOOKS_BASE_DIR
2715 fe267188 Iustin Pop
    # yeah, _BASE_DIR is not valid for attributes, we use it like a
2716 fe267188 Iustin Pop
    # constant
2717 fe267188 Iustin Pop
    self._BASE_DIR = hooks_base_dir # pylint: disable-msg=C0103
2718 a8083063 Iustin Pop
2719 a8083063 Iustin Pop
  def RunHooks(self, hpath, phase, env):
2720 a8083063 Iustin Pop
    """Run the scripts in the hooks directory.
2721 a8083063 Iustin Pop

2722 10c2650b Iustin Pop
    @type hpath: str
2723 10c2650b Iustin Pop
    @param hpath: the path to the hooks directory which
2724 10c2650b Iustin Pop
        holds the scripts
2725 10c2650b Iustin Pop
    @type phase: str
2726 10c2650b Iustin Pop
    @param phase: either L{constants.HOOKS_PHASE_PRE} or
2727 10c2650b Iustin Pop
        L{constants.HOOKS_PHASE_POST}
2728 10c2650b Iustin Pop
    @type env: dict
2729 10c2650b Iustin Pop
    @param env: dictionary with the environment for the hook
2730 10c2650b Iustin Pop
    @rtype: list
2731 10c2650b Iustin Pop
    @return: list of 3-element tuples:
2732 10c2650b Iustin Pop
      - script path
2733 10c2650b Iustin Pop
      - script result, either L{constants.HKR_SUCCESS} or
2734 10c2650b Iustin Pop
        L{constants.HKR_FAIL}
2735 10c2650b Iustin Pop
      - output of the script
2736 10c2650b Iustin Pop

2737 10c2650b Iustin Pop
    @raise errors.ProgrammerError: for invalid input
2738 10c2650b Iustin Pop
        parameters
2739 a8083063 Iustin Pop

2740 a8083063 Iustin Pop
    """
2741 a8083063 Iustin Pop
    if phase == constants.HOOKS_PHASE_PRE:
2742 a8083063 Iustin Pop
      suffix = "pre"
2743 a8083063 Iustin Pop
    elif phase == constants.HOOKS_PHASE_POST:
2744 a8083063 Iustin Pop
      suffix = "post"
2745 a8083063 Iustin Pop
    else:
2746 3fb4f740 Iustin Pop
      _Fail("Unknown hooks phase '%s'", phase)
2747 3fb4f740 Iustin Pop
2748 a8083063 Iustin Pop
2749 a8083063 Iustin Pop
    subdir = "%s-%s.d" % (hpath, suffix)
2750 0411c011 Iustin Pop
    dir_name = utils.PathJoin(self._BASE_DIR, subdir)
2751 6bb65e3a Guido Trotter
2752 6bb65e3a Guido Trotter
    results = []
2753 a9b7e346 Iustin Pop
2754 a9b7e346 Iustin Pop
    if not os.path.isdir(dir_name):
2755 a9b7e346 Iustin Pop
      # for non-existing/non-dirs, we simply exit instead of logging a
2756 a9b7e346 Iustin Pop
      # warning at every operation
2757 a9b7e346 Iustin Pop
      return results
2758 a9b7e346 Iustin Pop
2759 a9b7e346 Iustin Pop
    runparts_results = utils.RunParts(dir_name, env=env, reset_env=True)
2760 a9b7e346 Iustin Pop
2761 6bb65e3a Guido Trotter
    for (relname, relstatus, runresult)  in runparts_results:
2762 6bb65e3a Guido Trotter
      if relstatus == constants.RUNPARTS_SKIP:
2763 a8083063 Iustin Pop
        rrval = constants.HKR_SKIP
2764 a8083063 Iustin Pop
        output = ""
2765 6bb65e3a Guido Trotter
      elif relstatus == constants.RUNPARTS_ERR:
2766 6bb65e3a Guido Trotter
        rrval = constants.HKR_FAIL
2767 6bb65e3a Guido Trotter
        output = "Hook script execution error: %s" % runresult
2768 6bb65e3a Guido Trotter
      elif relstatus == constants.RUNPARTS_RUN:
2769 6bb65e3a Guido Trotter
        if runresult.failed:
2770 a8083063 Iustin Pop
          rrval = constants.HKR_FAIL
2771 a8083063 Iustin Pop
        else:
2772 6bb65e3a Guido Trotter
          rrval = constants.HKR_SUCCESS
2773 6bb65e3a Guido Trotter
        output = utils.SafeEncode(runresult.output.strip())
2774 6bb65e3a Guido Trotter
      results.append(("%s/%s" % (subdir, relname), rrval, output))
2775 6bb65e3a Guido Trotter
2776 6bb65e3a Guido Trotter
    return results
2777 3f78eef2 Iustin Pop
2778 3f78eef2 Iustin Pop
2779 8d528b7c Iustin Pop
class IAllocatorRunner(object):
2780 8d528b7c Iustin Pop
  """IAllocator runner.
2781 8d528b7c Iustin Pop

2782 8d528b7c Iustin Pop
  This class is instantiated on the node side (ganeti-noded) and not on
2783 8d528b7c Iustin Pop
  the master side.
2784 8d528b7c Iustin Pop

2785 8d528b7c Iustin Pop
  """
2786 7e950d31 Iustin Pop
  @staticmethod
2787 7e950d31 Iustin Pop
  def Run(name, idata):
2788 8d528b7c Iustin Pop
    """Run an iallocator script.
2789 8d528b7c Iustin Pop

2790 10c2650b Iustin Pop
    @type name: str
2791 10c2650b Iustin Pop
    @param name: the iallocator script name
2792 10c2650b Iustin Pop
    @type idata: str
2793 10c2650b Iustin Pop
    @param idata: the allocator input data
2794 10c2650b Iustin Pop

2795 10c2650b Iustin Pop
    @rtype: tuple
2796 87f5c298 Iustin Pop
    @return: two element tuple of:
2797 87f5c298 Iustin Pop
       - status
2798 87f5c298 Iustin Pop
       - either error message or stdout of allocator (for success)
2799 8d528b7c Iustin Pop

2800 8d528b7c Iustin Pop
    """
2801 8d528b7c Iustin Pop
    alloc_script = utils.FindFile(name, constants.IALLOCATOR_SEARCH_PATH,
2802 8d528b7c Iustin Pop
                                  os.path.isfile)
2803 8d528b7c Iustin Pop
    if alloc_script is None:
2804 87f5c298 Iustin Pop
      _Fail("iallocator module '%s' not found in the search path", name)
2805 8d528b7c Iustin Pop
2806 8d528b7c Iustin Pop
    fd, fin_name = tempfile.mkstemp(prefix="ganeti-iallocator.")
2807 8d528b7c Iustin Pop
    try:
2808 8d528b7c Iustin Pop
      os.write(fd, idata)
2809 8d528b7c Iustin Pop
      os.close(fd)
2810 8d528b7c Iustin Pop
      result = utils.RunCmd([alloc_script, fin_name])
2811 8d528b7c Iustin Pop
      if result.failed:
2812 87f5c298 Iustin Pop
        _Fail("iallocator module '%s' failed: %s, output '%s'",
2813 87f5c298 Iustin Pop
              name, result.fail_reason, result.output)
2814 8d528b7c Iustin Pop
    finally:
2815 8d528b7c Iustin Pop
      os.unlink(fin_name)
2816 8d528b7c Iustin Pop
2817 c26a6bd2 Iustin Pop
    return result.stdout
2818 8d528b7c Iustin Pop
2819 8d528b7c Iustin Pop
2820 3f78eef2 Iustin Pop
class DevCacheManager(object):
2821 c99a3cc0 Manuel Franceschini
  """Simple class for managing a cache of block device information.
2822 3f78eef2 Iustin Pop

2823 3f78eef2 Iustin Pop
  """
2824 3f78eef2 Iustin Pop
  _DEV_PREFIX = "/dev/"
2825 3f78eef2 Iustin Pop
  _ROOT_DIR = constants.BDEV_CACHE_DIR
2826 3f78eef2 Iustin Pop
2827 3f78eef2 Iustin Pop
  @classmethod
2828 3f78eef2 Iustin Pop
  def _ConvertPath(cls, dev_path):
2829 3f78eef2 Iustin Pop
    """Converts a /dev/name path to the cache file name.
2830 3f78eef2 Iustin Pop

2831 3f78eef2 Iustin Pop
    This replaces slashes with underscores and strips the /dev
2832 10c2650b Iustin Pop
    prefix. It then returns the full path to the cache file.
2833 10c2650b Iustin Pop

2834 10c2650b Iustin Pop
    @type dev_path: str
2835 10c2650b Iustin Pop
    @param dev_path: the C{/dev/} path name
2836 10c2650b Iustin Pop
    @rtype: str
2837 10c2650b Iustin Pop
    @return: the converted path name
2838 3f78eef2 Iustin Pop

2839 3f78eef2 Iustin Pop
    """
2840 3f78eef2 Iustin Pop
    if dev_path.startswith(cls._DEV_PREFIX):
2841 3f78eef2 Iustin Pop
      dev_path = dev_path[len(cls._DEV_PREFIX):]
2842 3f78eef2 Iustin Pop
    dev_path = dev_path.replace("/", "_")
2843 0411c011 Iustin Pop
    fpath = utils.PathJoin(cls._ROOT_DIR, "bdev_%s" % dev_path)
2844 3f78eef2 Iustin Pop
    return fpath
2845 3f78eef2 Iustin Pop
2846 3f78eef2 Iustin Pop
  @classmethod
2847 3f78eef2 Iustin Pop
  def UpdateCache(cls, dev_path, owner, on_primary, iv_name):
2848 3f78eef2 Iustin Pop
    """Updates the cache information for a given device.
2849 3f78eef2 Iustin Pop

2850 10c2650b Iustin Pop
    @type dev_path: str
2851 10c2650b Iustin Pop
    @param dev_path: the pathname of the device
2852 10c2650b Iustin Pop
    @type owner: str
2853 10c2650b Iustin Pop
    @param owner: the owner (instance name) of the device
2854 10c2650b Iustin Pop
    @type on_primary: bool
2855 10c2650b Iustin Pop
    @param on_primary: whether this is the primary
2856 10c2650b Iustin Pop
        node nor not
2857 10c2650b Iustin Pop
    @type iv_name: str
2858 10c2650b Iustin Pop
    @param iv_name: the instance-visible name of the
2859 c41eea6e Iustin Pop
        device, as in objects.Disk.iv_name
2860 10c2650b Iustin Pop

2861 10c2650b Iustin Pop
    @rtype: None
2862 10c2650b Iustin Pop

2863 3f78eef2 Iustin Pop
    """
2864 cf5a8306 Iustin Pop
    if dev_path is None:
2865 18682bca Iustin Pop
      logging.error("DevCacheManager.UpdateCache got a None dev_path")
2866 cf5a8306 Iustin Pop
      return
2867 3f78eef2 Iustin Pop
    fpath = cls._ConvertPath(dev_path)
2868 3f78eef2 Iustin Pop
    if on_primary:
2869 3f78eef2 Iustin Pop
      state = "primary"
2870 3f78eef2 Iustin Pop
    else:
2871 3f78eef2 Iustin Pop
      state = "secondary"
2872 3f78eef2 Iustin Pop
    if iv_name is None:
2873 3f78eef2 Iustin Pop
      iv_name = "not_visible"
2874 3f78eef2 Iustin Pop
    fdata = "%s %s %s\n" % (str(owner), state, iv_name)
2875 3f78eef2 Iustin Pop
    try:
2876 3f78eef2 Iustin Pop
      utils.WriteFile(fpath, data=fdata)
2877 3f78eef2 Iustin Pop
    except EnvironmentError, err:
2878 29921401 Iustin Pop
      logging.exception("Can't update bdev cache for %s: %s", dev_path, err)
2879 3f78eef2 Iustin Pop
2880 3f78eef2 Iustin Pop
  @classmethod
2881 3f78eef2 Iustin Pop
  def RemoveCache(cls, dev_path):
2882 3f78eef2 Iustin Pop
    """Remove data for a dev_path.
2883 3f78eef2 Iustin Pop

2884 10c2650b Iustin Pop
    This is just a wrapper over L{utils.RemoveFile} with a converted
2885 10c2650b Iustin Pop
    path name and logging.
2886 10c2650b Iustin Pop

2887 10c2650b Iustin Pop
    @type dev_path: str
2888 10c2650b Iustin Pop
    @param dev_path: the pathname of the device
2889 10c2650b Iustin Pop

2890 10c2650b Iustin Pop
    @rtype: None
2891 10c2650b Iustin Pop

2892 3f78eef2 Iustin Pop
    """
2893 cf5a8306 Iustin Pop
    if dev_path is None:
2894 18682bca Iustin Pop
      logging.error("DevCacheManager.RemoveCache got a None dev_path")
2895 cf5a8306 Iustin Pop
      return
2896 3f78eef2 Iustin Pop
    fpath = cls._ConvertPath(dev_path)
2897 3f78eef2 Iustin Pop
    try:
2898 3f78eef2 Iustin Pop
      utils.RemoveFile(fpath)
2899 3f78eef2 Iustin Pop
    except EnvironmentError, err:
2900 29921401 Iustin Pop
      logging.exception("Can't update bdev cache for %s: %s", dev_path, err)