Statistics
| Branch: | Tag: | Revision:

root / lib / backend.py @ e695efbf

History | View | Annotate | Download (100.1 kB)

1 2f31098c Iustin Pop
#
2 a8083063 Iustin Pop
#
3 a8083063 Iustin Pop
4 a025e535 Vitaly Kuznetsov
# Copyright (C) 2006, 2007, 2008, 2009, 2010 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 f81c4737 Michael Hanselmann
import signal
51 a8083063 Iustin Pop
52 a8083063 Iustin Pop
from ganeti import errors
53 a8083063 Iustin Pop
from ganeti import utils
54 a8083063 Iustin Pop
from ganeti import ssh
55 a8083063 Iustin Pop
from ganeti import hypervisor
56 a8083063 Iustin Pop
from ganeti import constants
57 a8083063 Iustin Pop
from ganeti import bdev
58 a8083063 Iustin Pop
from ganeti import objects
59 880478f8 Iustin Pop
from ganeti import ssconf
60 1651d116 Michael Hanselmann
from ganeti import serializer
61 a744b676 Manuel Franceschini
from ganeti import netutils
62 82b22e19 René Nussbaumer
from ganeti import runtime
63 a8083063 Iustin Pop
64 a8083063 Iustin Pop
65 13998ef2 Michael Hanselmann
_BOOT_ID_PATH = "/proc/sys/kernel/random/boot_id"
66 714ea7ca Iustin Pop
_ALLOWED_CLEAN_DIRS = frozenset([
67 714ea7ca Iustin Pop
  constants.DATA_DIR,
68 714ea7ca Iustin Pop
  constants.JOB_QUEUE_ARCHIVE_DIR,
69 714ea7ca Iustin Pop
  constants.QUEUE_DIR,
70 f942a838 Michael Hanselmann
  constants.CRYPTO_KEYS_DIR,
71 714ea7ca Iustin Pop
  ])
72 f942a838 Michael Hanselmann
_MAX_SSL_CERT_VALIDITY = 7 * 24 * 60 * 60
73 f942a838 Michael Hanselmann
_X509_KEY_FILE = "key"
74 f942a838 Michael Hanselmann
_X509_CERT_FILE = "cert"
75 1651d116 Michael Hanselmann
_IES_STATUS_FILE = "status"
76 1651d116 Michael Hanselmann
_IES_PID_FILE = "pid"
77 1651d116 Michael Hanselmann
_IES_CA_FILE = "ca"
78 13998ef2 Michael Hanselmann
79 13998ef2 Michael Hanselmann
80 2cc6781a Iustin Pop
class RPCFail(Exception):
81 2cc6781a Iustin Pop
  """Class denoting RPC failure.
82 2cc6781a Iustin Pop

83 2cc6781a Iustin Pop
  Its argument is the error message.
84 2cc6781a Iustin Pop

85 2cc6781a Iustin Pop
  """
86 2cc6781a Iustin Pop
87 13998ef2 Michael Hanselmann
88 2cc6781a Iustin Pop
def _Fail(msg, *args, **kwargs):
89 2cc6781a Iustin Pop
  """Log an error and the raise an RPCFail exception.
90 2cc6781a Iustin Pop

91 2cc6781a Iustin Pop
  This exception is then handled specially in the ganeti daemon and
92 2cc6781a Iustin Pop
  turned into a 'failed' return type. As such, this function is a
93 2cc6781a Iustin Pop
  useful shortcut for logging the error and returning it to the master
94 2cc6781a Iustin Pop
  daemon.
95 2cc6781a Iustin Pop

96 2cc6781a Iustin Pop
  @type msg: string
97 2cc6781a Iustin Pop
  @param msg: the text of the exception
98 2cc6781a Iustin Pop
  @raise RPCFail
99 2cc6781a Iustin Pop

100 2cc6781a Iustin Pop
  """
101 2cc6781a Iustin Pop
  if args:
102 2cc6781a Iustin Pop
    msg = msg % args
103 afdc3985 Iustin Pop
  if "log" not in kwargs or kwargs["log"]: # if we should log this error
104 afdc3985 Iustin Pop
    if "exc" in kwargs and kwargs["exc"]:
105 afdc3985 Iustin Pop
      logging.exception(msg)
106 afdc3985 Iustin Pop
    else:
107 afdc3985 Iustin Pop
      logging.error(msg)
108 2cc6781a Iustin Pop
  raise RPCFail(msg)
109 2cc6781a Iustin Pop
110 2cc6781a Iustin Pop
111 c657dcc9 Michael Hanselmann
def _GetConfig():
112 93384844 Iustin Pop
  """Simple wrapper to return a SimpleStore.
113 10c2650b Iustin Pop

114 93384844 Iustin Pop
  @rtype: L{ssconf.SimpleStore}
115 93384844 Iustin Pop
  @return: a SimpleStore instance
116 10c2650b Iustin Pop

117 10c2650b Iustin Pop
  """
118 93384844 Iustin Pop
  return ssconf.SimpleStore()
119 c657dcc9 Michael Hanselmann
120 c657dcc9 Michael Hanselmann
121 62c9ec92 Iustin Pop
def _GetSshRunner(cluster_name):
122 10c2650b Iustin Pop
  """Simple wrapper to return an SshRunner.
123 10c2650b Iustin Pop

124 10c2650b Iustin Pop
  @type cluster_name: str
125 10c2650b Iustin Pop
  @param cluster_name: the cluster name, which is needed
126 10c2650b Iustin Pop
      by the SshRunner constructor
127 10c2650b Iustin Pop
  @rtype: L{ssh.SshRunner}
128 10c2650b Iustin Pop
  @return: an SshRunner instance
129 10c2650b Iustin Pop

130 10c2650b Iustin Pop
  """
131 62c9ec92 Iustin Pop
  return ssh.SshRunner(cluster_name)
132 c92b310a Michael Hanselmann
133 c92b310a Michael Hanselmann
134 12bce260 Michael Hanselmann
def _Decompress(data):
135 12bce260 Michael Hanselmann
  """Unpacks data compressed by the RPC client.
136 12bce260 Michael Hanselmann

137 12bce260 Michael Hanselmann
  @type data: list or tuple
138 12bce260 Michael Hanselmann
  @param data: Data sent by RPC client
139 12bce260 Michael Hanselmann
  @rtype: str
140 12bce260 Michael Hanselmann
  @return: Decompressed data
141 12bce260 Michael Hanselmann

142 12bce260 Michael Hanselmann
  """
143 52e2f66e Michael Hanselmann
  assert isinstance(data, (list, tuple))
144 12bce260 Michael Hanselmann
  assert len(data) == 2
145 12bce260 Michael Hanselmann
  (encoding, content) = data
146 12bce260 Michael Hanselmann
  if encoding == constants.RPC_ENCODING_NONE:
147 12bce260 Michael Hanselmann
    return content
148 12bce260 Michael Hanselmann
  elif encoding == constants.RPC_ENCODING_ZLIB_BASE64:
149 12bce260 Michael Hanselmann
    return zlib.decompress(base64.b64decode(content))
150 12bce260 Michael Hanselmann
  else:
151 12bce260 Michael Hanselmann
    raise AssertionError("Unknown data encoding")
152 12bce260 Michael Hanselmann
153 12bce260 Michael Hanselmann
154 3bc6be5c Iustin Pop
def _CleanDirectory(path, exclude=None):
155 76ab5558 Michael Hanselmann
  """Removes all regular files in a directory.
156 76ab5558 Michael Hanselmann

157 10c2650b Iustin Pop
  @type path: str
158 10c2650b Iustin Pop
  @param path: the directory to clean
159 76ab5558 Michael Hanselmann
  @type exclude: list
160 10c2650b Iustin Pop
  @param exclude: list of files to be excluded, defaults
161 10c2650b Iustin Pop
      to the empty list
162 76ab5558 Michael Hanselmann

163 76ab5558 Michael Hanselmann
  """
164 714ea7ca Iustin Pop
  if path not in _ALLOWED_CLEAN_DIRS:
165 714ea7ca Iustin Pop
    _Fail("Path passed to _CleanDirectory not in allowed clean targets: '%s'",
166 714ea7ca Iustin Pop
          path)
167 714ea7ca Iustin Pop
168 3956cee1 Michael Hanselmann
  if not os.path.isdir(path):
169 3956cee1 Michael Hanselmann
    return
170 3bc6be5c Iustin Pop
  if exclude is None:
171 3bc6be5c Iustin Pop
    exclude = []
172 3bc6be5c Iustin Pop
  else:
173 3bc6be5c Iustin Pop
    # Normalize excluded paths
174 3bc6be5c Iustin Pop
    exclude = [os.path.normpath(i) for i in exclude]
175 76ab5558 Michael Hanselmann
176 3956cee1 Michael Hanselmann
  for rel_name in utils.ListVisibleFiles(path):
177 c4feafe8 Iustin Pop
    full_name = utils.PathJoin(path, rel_name)
178 76ab5558 Michael Hanselmann
    if full_name in exclude:
179 76ab5558 Michael Hanselmann
      continue
180 3956cee1 Michael Hanselmann
    if os.path.isfile(full_name) and not os.path.islink(full_name):
181 3956cee1 Michael Hanselmann
      utils.RemoveFile(full_name)
182 3956cee1 Michael Hanselmann
183 3956cee1 Michael Hanselmann
184 360b0dc2 Iustin Pop
def _BuildUploadFileList():
185 360b0dc2 Iustin Pop
  """Build the list of allowed upload files.
186 360b0dc2 Iustin Pop

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

189 360b0dc2 Iustin Pop
  """
190 b397a7d2 Iustin Pop
  allowed_files = set([
191 b397a7d2 Iustin Pop
    constants.CLUSTER_CONF_FILE,
192 b397a7d2 Iustin Pop
    constants.ETC_HOSTS,
193 b397a7d2 Iustin Pop
    constants.SSH_KNOWN_HOSTS_FILE,
194 b397a7d2 Iustin Pop
    constants.VNC_PASSWORD_FILE,
195 b397a7d2 Iustin Pop
    constants.RAPI_CERT_FILE,
196 b397a7d2 Iustin Pop
    constants.RAPI_USERS_FILE,
197 6b7d5878 Michael Hanselmann
    constants.CONFD_HMAC_KEY,
198 ff89a747 Michael Hanselmann
    constants.CLUSTER_DOMAIN_SECRET_FILE,
199 b397a7d2 Iustin Pop
    ])
200 b397a7d2 Iustin Pop
201 b397a7d2 Iustin Pop
  for hv_name in constants.HYPER_TYPES:
202 e5a45a16 Iustin Pop
    hv_class = hypervisor.GetHypervisorClass(hv_name)
203 b397a7d2 Iustin Pop
    allowed_files.update(hv_class.GetAncillaryFiles())
204 b397a7d2 Iustin Pop
205 b397a7d2 Iustin Pop
  return frozenset(allowed_files)
206 360b0dc2 Iustin Pop
207 360b0dc2 Iustin Pop
208 360b0dc2 Iustin Pop
_ALLOWED_UPLOAD_FILES = _BuildUploadFileList()
209 360b0dc2 Iustin Pop
210 360b0dc2 Iustin Pop
211 1bc59f76 Michael Hanselmann
def JobQueuePurge():
212 10c2650b Iustin Pop
  """Removes job queue files and archived jobs.
213 10c2650b Iustin Pop

214 c8457ce7 Iustin Pop
  @rtype: tuple
215 c8457ce7 Iustin Pop
  @return: True, None
216 24fc781f Michael Hanselmann

217 24fc781f Michael Hanselmann
  """
218 1bc59f76 Michael Hanselmann
  _CleanDirectory(constants.QUEUE_DIR, exclude=[constants.JOB_QUEUE_LOCK_FILE])
219 24fc781f Michael Hanselmann
  _CleanDirectory(constants.JOB_QUEUE_ARCHIVE_DIR)
220 24fc781f Michael Hanselmann
221 24fc781f Michael Hanselmann
222 bd1e4562 Iustin Pop
def GetMasterInfo():
223 bd1e4562 Iustin Pop
  """Returns master information.
224 bd1e4562 Iustin Pop

225 bd1e4562 Iustin Pop
  This is an utility function to compute master information, either
226 bd1e4562 Iustin Pop
  for consumption here or from the node daemon.
227 bd1e4562 Iustin Pop

228 bd1e4562 Iustin Pop
  @rtype: tuple
229 d8e0caa6 Manuel Franceschini
  @return: master_netdev, master_ip, master_name, primary_ip_family
230 2a52a064 Iustin Pop
  @raise RPCFail: in case of errors
231 b1b6ea87 Iustin Pop

232 b1b6ea87 Iustin Pop
  """
233 b1b6ea87 Iustin Pop
  try:
234 c657dcc9 Michael Hanselmann
    cfg = _GetConfig()
235 c657dcc9 Michael Hanselmann
    master_netdev = cfg.GetMasterNetdev()
236 c657dcc9 Michael Hanselmann
    master_ip = cfg.GetMasterIP()
237 c657dcc9 Michael Hanselmann
    master_node = cfg.GetMasterNode()
238 d8e0caa6 Manuel Franceschini
    primary_ip_family = cfg.GetPrimaryIPFamily()
239 b1b6ea87 Iustin Pop
  except errors.ConfigurationError, err:
240 29921401 Iustin Pop
    _Fail("Cluster configuration incomplete: %s", err, exc=True)
241 d8e0caa6 Manuel Franceschini
  return (master_netdev, master_ip, master_node, primary_ip_family)
242 b1b6ea87 Iustin Pop
243 b1b6ea87 Iustin Pop
244 3583908a Guido Trotter
def StartMaster(start_daemons, no_voting):
245 a8083063 Iustin Pop
  """Activate local node as master node.
246 a8083063 Iustin Pop

247 91492e57 Iustin Pop
  The function will either try activate the IP address of the master
248 91492e57 Iustin Pop
  (unless someone else has it) or also start the master daemons, based
249 91492e57 Iustin Pop
  on the start_daemons parameter.
250 10c2650b Iustin Pop

251 10c2650b Iustin Pop
  @type start_daemons: boolean
252 91492e57 Iustin Pop
  @param start_daemons: whether to start the master daemons
253 91492e57 Iustin Pop
      (ganeti-masterd and ganeti-rapi), or (if false) activate the
254 91492e57 Iustin Pop
      master ip
255 3583908a Guido Trotter
  @type no_voting: boolean
256 3583908a Guido Trotter
  @param no_voting: whether to start ganeti-masterd without a node vote
257 3583908a Guido Trotter
      (if start_daemons is True), but still non-interactively
258 10c2650b Iustin Pop
  @rtype: None
259 a8083063 Iustin Pop

260 a8083063 Iustin Pop
  """
261 2a52a064 Iustin Pop
  # GetMasterInfo will raise an exception if not able to return data
262 d8e0caa6 Manuel Franceschini
  master_netdev, master_ip, _, family = GetMasterInfo()
263 a8083063 Iustin Pop
264 396b5733 Iustin Pop
  err_msgs = []
265 91492e57 Iustin Pop
  # either start the master and rapi daemons
266 b1b6ea87 Iustin Pop
  if start_daemons:
267 3583908a Guido Trotter
    if no_voting:
268 f154a7a3 Michael Hanselmann
      masterd_args = "--no-voting --yes-do-it"
269 f154a7a3 Michael Hanselmann
    else:
270 f154a7a3 Michael Hanselmann
      masterd_args = ""
271 f154a7a3 Michael Hanselmann
272 f154a7a3 Michael Hanselmann
    env = {
273 f154a7a3 Michael Hanselmann
      "EXTRA_MASTERD_ARGS": masterd_args,
274 f154a7a3 Michael Hanselmann
      }
275 f154a7a3 Michael Hanselmann
276 f154a7a3 Michael Hanselmann
    result = utils.RunCmd([constants.DAEMON_UTIL, "start-master"], env=env)
277 f154a7a3 Michael Hanselmann
    if result.failed:
278 f154a7a3 Michael Hanselmann
      msg = "Can't start Ganeti master: %s" % result.output
279 f154a7a3 Michael Hanselmann
      logging.error(msg)
280 f154a7a3 Michael Hanselmann
      err_msgs.append(msg)
281 91492e57 Iustin Pop
  # or activate the IP
282 91492e57 Iustin Pop
  else:
283 91492e57 Iustin Pop
    if netutils.TcpPing(master_ip, constants.DEFAULT_NODED_PORT):
284 8b312c1d Manuel Franceschini
      if netutils.IPAddress.Own(master_ip):
285 91492e57 Iustin Pop
        # we already have the ip:
286 91492e57 Iustin Pop
        logging.debug("Master IP already configured, doing nothing")
287 91492e57 Iustin Pop
      else:
288 91492e57 Iustin Pop
        msg = "Someone else has the master ip, not activating"
289 91492e57 Iustin Pop
        logging.error(msg)
290 91492e57 Iustin Pop
        err_msgs.append(msg)
291 91492e57 Iustin Pop
    else:
292 d8e0caa6 Manuel Franceschini
      ipcls = netutils.IP4Address
293 d8e0caa6 Manuel Franceschini
      if family == netutils.IP6Address.family:
294 d8e0caa6 Manuel Franceschini
        ipcls = netutils.IP6Address
295 e7323b5e Manuel Franceschini
296 e7323b5e Manuel Franceschini
      result = utils.RunCmd(["ip", "address", "add",
297 d8e0caa6 Manuel Franceschini
                             "%s/%d" % (master_ip, ipcls.iplen),
298 91492e57 Iustin Pop
                             "dev", master_netdev, "label",
299 91492e57 Iustin Pop
                             "%s:0" % master_netdev])
300 91492e57 Iustin Pop
      if result.failed:
301 91492e57 Iustin Pop
        msg = "Can't activate master IP: %s" % result.output
302 91492e57 Iustin Pop
        logging.error(msg)
303 91492e57 Iustin Pop
        err_msgs.append(msg)
304 91492e57 Iustin Pop
305 e7323b5e Manuel Franceschini
      # we ignore the exit code of the following cmds
306 d8e0caa6 Manuel Franceschini
      if ipcls == netutils.IP4Address:
307 e7323b5e Manuel Franceschini
        utils.RunCmd(["arping", "-q", "-U", "-c 3", "-I", master_netdev, "-s",
308 e7323b5e Manuel Franceschini
                      master_ip, master_ip])
309 d8e0caa6 Manuel Franceschini
      elif ipcls == netutils.IP6Address:
310 2dc1237c Manuel Franceschini
        try:
311 2dc1237c Manuel Franceschini
          utils.RunCmd(["ndisc6", "-q", "-r 3", master_ip, master_netdev])
312 2dc1237c Manuel Franceschini
        except errors.OpExecError:
313 2dc1237c Manuel Franceschini
          # TODO: Better error reporting
314 2dc1237c Manuel Franceschini
          logging.warning("Can't execute ndisc6, please install if missing")
315 b726aff0 Iustin Pop
316 396b5733 Iustin Pop
  if err_msgs:
317 396b5733 Iustin Pop
    _Fail("; ".join(err_msgs))
318 afdc3985 Iustin Pop
319 a8083063 Iustin Pop
320 1c65840b Iustin Pop
def StopMaster(stop_daemons):
321 a8083063 Iustin Pop
  """Deactivate this node as master.
322 a8083063 Iustin Pop

323 1c65840b Iustin Pop
  The function will always try to deactivate the IP address of the
324 10c2650b Iustin Pop
  master. It will also stop the master daemons depending on the
325 10c2650b Iustin Pop
  stop_daemons parameter.
326 10c2650b Iustin Pop

327 10c2650b Iustin Pop
  @type stop_daemons: boolean
328 10c2650b Iustin Pop
  @param stop_daemons: whether to also stop the master daemons
329 10c2650b Iustin Pop
      (ganeti-masterd and ganeti-rapi)
330 10c2650b Iustin Pop
  @rtype: None
331 a8083063 Iustin Pop

332 a8083063 Iustin Pop
  """
333 6c00d19a Iustin Pop
  # TODO: log and report back to the caller the error failures; we
334 6c00d19a Iustin Pop
  # need to decide in which case we fail the RPC for this
335 2a52a064 Iustin Pop
336 2a52a064 Iustin Pop
  # GetMasterInfo will raise an exception if not able to return data
337 d8e0caa6 Manuel Franceschini
  master_netdev, master_ip, _, family = GetMasterInfo()
338 a8083063 Iustin Pop
339 d8e0caa6 Manuel Franceschini
  ipcls = netutils.IP4Address
340 d8e0caa6 Manuel Franceschini
  if family == netutils.IP6Address.family:
341 d8e0caa6 Manuel Franceschini
    ipcls = netutils.IP6Address
342 e7323b5e Manuel Franceschini
343 e7323b5e Manuel Franceschini
  result = utils.RunCmd(["ip", "address", "del",
344 d8e0caa6 Manuel Franceschini
                         "%s/%d" % (master_ip, ipcls.iplen),
345 b1b6ea87 Iustin Pop
                         "dev", master_netdev])
346 a8083063 Iustin Pop
  if result.failed:
347 3b9e6a30 Iustin Pop
    logging.error("Can't remove the master IP, error: %s", result.output)
348 b1b6ea87 Iustin Pop
    # but otherwise ignore the failure
349 b1b6ea87 Iustin Pop
350 b1b6ea87 Iustin Pop
  if stop_daemons:
351 f154a7a3 Michael Hanselmann
    result = utils.RunCmd([constants.DAEMON_UTIL, "stop-master"])
352 f154a7a3 Michael Hanselmann
    if result.failed:
353 f154a7a3 Michael Hanselmann
      logging.error("Could not stop Ganeti master, command %s had exitcode %s"
354 f154a7a3 Michael Hanselmann
                    " and error %s",
355 f154a7a3 Michael Hanselmann
                    result.cmd, result.exit_code, result.output)
356 a8083063 Iustin Pop
357 a8083063 Iustin Pop
358 19ddc57a René Nussbaumer
def EtcHostsModify(mode, host, ip):
359 19ddc57a René Nussbaumer
  """Modify a host entry in /etc/hosts.
360 19ddc57a René Nussbaumer

361 19ddc57a René Nussbaumer
  @param mode: The mode to operate. Either add or remove entry
362 19ddc57a René Nussbaumer
  @param host: The host to operate on
363 19ddc57a René Nussbaumer
  @param ip: The ip associated with the entry
364 19ddc57a René Nussbaumer

365 19ddc57a René Nussbaumer
  """
366 19ddc57a René Nussbaumer
  if mode == constants.ETC_HOSTS_ADD:
367 19ddc57a René Nussbaumer
    if not ip:
368 19ddc57a René Nussbaumer
      RPCFail("Mode 'add' needs 'ip' parameter, but parameter not"
369 19ddc57a René Nussbaumer
              " present")
370 19ddc57a René Nussbaumer
    utils.AddHostToEtcHosts(host, ip)
371 19ddc57a René Nussbaumer
  elif mode == constants.ETC_HOSTS_REMOVE:
372 19ddc57a René Nussbaumer
    if ip:
373 19ddc57a René Nussbaumer
      RPCFail("Mode 'remove' does not allow 'ip' parameter, but"
374 19ddc57a René Nussbaumer
              " parameter is present")
375 19ddc57a René Nussbaumer
    utils.RemoveHostFromEtcHosts(host)
376 19ddc57a René Nussbaumer
  else:
377 19ddc57a René Nussbaumer
    RPCFail("Mode not supported")
378 19ddc57a René Nussbaumer
379 19ddc57a René Nussbaumer
380 b989b9d9 Ken Wehr
def LeaveCluster(modify_ssh_setup):
381 10c2650b Iustin Pop
  """Cleans up and remove the current node.
382 10c2650b Iustin Pop

383 10c2650b Iustin Pop
  This function cleans up and prepares the current node to be removed
384 10c2650b Iustin Pop
  from the cluster.
385 10c2650b Iustin Pop

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

390 b989b9d9 Ken Wehr
  @param modify_ssh_setup: boolean
391 b989b9d9 Ken Wehr

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

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

440 098c0958 Michael Hanselmann
  """
441 a8083063 Iustin Pop
  outputarray = {}
442 673cd9c4 René Nussbaumer
443 cb6a0296 Iustin Pop
  if vgname is not None:
444 cb6a0296 Iustin Pop
    vginfo = bdev.LogicalVolume.GetVGInfo([vgname])
445 cb6a0296 Iustin Pop
    vg_free = vg_size = None
446 cb6a0296 Iustin Pop
    if vginfo:
447 cb6a0296 Iustin Pop
      vg_free = int(round(vginfo[0][0], 0))
448 cb6a0296 Iustin Pop
      vg_size = int(round(vginfo[0][1], 0))
449 cb6a0296 Iustin Pop
    outputarray['vg_size'] = vg_size
450 cb6a0296 Iustin Pop
    outputarray['vg_free'] = vg_free
451 cb6a0296 Iustin Pop
452 cb6a0296 Iustin Pop
  if hypervisor_type is not None:
453 cb6a0296 Iustin Pop
    hyper = hypervisor.GetHypervisor(hypervisor_type)
454 cb6a0296 Iustin Pop
    hyp_info = hyper.GetNodeInfo()
455 cb6a0296 Iustin Pop
    if hyp_info is not None:
456 cb6a0296 Iustin Pop
      outputarray.update(hyp_info)
457 a8083063 Iustin Pop
458 13998ef2 Michael Hanselmann
  outputarray["bootid"] = utils.ReadFile(_BOOT_ID_PATH, size=128).rstrip("\n")
459 3ef10550 Michael Hanselmann
460 c26a6bd2 Iustin Pop
  return outputarray
461 a8083063 Iustin Pop
462 a8083063 Iustin Pop
463 62c9ec92 Iustin Pop
def VerifyNode(what, cluster_name):
464 a8083063 Iustin Pop
  """Verify the status of the local node.
465 a8083063 Iustin Pop

466 e69d05fd Iustin Pop
  Based on the input L{what} parameter, various checks are done on the
467 e69d05fd Iustin Pop
  local node.
468 e69d05fd Iustin Pop

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

472 e69d05fd Iustin Pop
  If the I{nodelist} key is present, we check that we have
473 e69d05fd Iustin Pop
  connectivity via ssh with the target nodes (and check the hostname
474 e69d05fd Iustin Pop
  report).
475 a8083063 Iustin Pop

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

480 e69d05fd Iustin Pop
  @type what: C{dict}
481 e69d05fd Iustin Pop
  @param what: a dictionary of things to check:
482 e69d05fd Iustin Pop
      - filelist: list of files for which to compute checksums
483 e69d05fd Iustin Pop
      - nodelist: list of nodes we should check ssh communication with
484 e69d05fd Iustin Pop
      - node-net-test: list of nodes we should check node daemon port
485 e69d05fd Iustin Pop
        connectivity with
486 e69d05fd Iustin Pop
      - hypervisor: list with hypervisors to run the verify for
487 10c2650b Iustin Pop
  @rtype: dict
488 10c2650b Iustin Pop
  @return: a dictionary with the same keys as the input dict, and
489 10c2650b Iustin Pop
      values representing the result of the checks
490 a8083063 Iustin Pop

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

625 10c2650b Iustin Pop
  @type vg_name: str
626 10c2650b Iustin Pop
  @param vg_name: the volume group whose LVs we should list
627 10c2650b Iustin Pop
  @rtype: dict
628 10c2650b Iustin Pop
  @return:
629 10c2650b Iustin Pop
      dictionary of all partions (key) with value being a tuple of
630 10c2650b Iustin Pop
      their size (in MiB), inactive and online status::
631 10c2650b Iustin Pop

632 10c2650b Iustin Pop
        {'test1': ('20.06', True, True)}
633 10c2650b Iustin Pop

634 10c2650b Iustin Pop
      in case of errors, a string is returned with the error
635 10c2650b Iustin Pop
      details.
636 a8083063 Iustin Pop

637 a8083063 Iustin Pop
  """
638 cb2037a2 Iustin Pop
  lvs = {}
639 cb2037a2 Iustin Pop
  sep = '|'
640 cb2037a2 Iustin Pop
  result = utils.RunCmd(["lvs", "--noheadings", "--units=m", "--nosuffix",
641 cb2037a2 Iustin Pop
                         "--separator=%s" % sep,
642 cb2037a2 Iustin Pop
                         "-olv_name,lv_size,lv_attr", vg_name])
643 a8083063 Iustin Pop
  if result.failed:
644 29d376ec Iustin Pop
    _Fail("Failed to list logical volumes, lvs output: %s", result.output)
645 cb2037a2 Iustin Pop
646 df4c2628 Iustin Pop
  valid_line_re = re.compile("^ *([^|]+)\|([0-9.]+)\|([^|]{6})\|?$")
647 cb2037a2 Iustin Pop
  for line in result.stdout.splitlines():
648 df4c2628 Iustin Pop
    line = line.strip()
649 df4c2628 Iustin Pop
    match = valid_line_re.match(line)
650 df4c2628 Iustin Pop
    if not match:
651 18682bca Iustin Pop
      logging.error("Invalid line returned from lvs output: '%s'", line)
652 df4c2628 Iustin Pop
      continue
653 df4c2628 Iustin Pop
    name, size, attr = match.groups()
654 cb2037a2 Iustin Pop
    inactive = attr[4] == '-'
655 cb2037a2 Iustin Pop
    online = attr[5] == 'o'
656 33f2a81a Iustin Pop
    virtual = attr[0] == 'v'
657 33f2a81a Iustin Pop
    if virtual:
658 33f2a81a Iustin Pop
      # we don't want to report such volumes as existing, since they
659 33f2a81a Iustin Pop
      # don't really hold data
660 33f2a81a Iustin Pop
      continue
661 cb2037a2 Iustin Pop
    lvs[name] = (size, inactive, online)
662 cb2037a2 Iustin Pop
663 cb2037a2 Iustin Pop
  return lvs
664 a8083063 Iustin Pop
665 a8083063 Iustin Pop
666 a8083063 Iustin Pop
def ListVolumeGroups():
667 2f8598a5 Alexander Schreiber
  """List the volume groups and their size.
668 a8083063 Iustin Pop

669 10c2650b Iustin Pop
  @rtype: dict
670 10c2650b Iustin Pop
  @return: dictionary with keys volume name and values the
671 10c2650b Iustin Pop
      size of the volume
672 a8083063 Iustin Pop

673 a8083063 Iustin Pop
  """
674 c26a6bd2 Iustin Pop
  return utils.ListVolumeGroups()
675 a8083063 Iustin Pop
676 a8083063 Iustin Pop
677 dcb93971 Michael Hanselmann
def NodeVolumes():
678 dcb93971 Michael Hanselmann
  """List all volumes on this node.
679 dcb93971 Michael Hanselmann

680 10c2650b Iustin Pop
  @rtype: list
681 10c2650b Iustin Pop
  @return:
682 10c2650b Iustin Pop
    A list of dictionaries, each having four keys:
683 10c2650b Iustin Pop
      - name: the logical volume name,
684 10c2650b Iustin Pop
      - size: the size of the logical volume
685 10c2650b Iustin Pop
      - dev: the physical device on which the LV lives
686 10c2650b Iustin Pop
      - vg: the volume group to which it belongs
687 10c2650b Iustin Pop

688 10c2650b Iustin Pop
    In case of errors, we return an empty list and log the
689 10c2650b Iustin Pop
    error.
690 10c2650b Iustin Pop

691 10c2650b Iustin Pop
    Note that since a logical volume can live on multiple physical
692 10c2650b Iustin Pop
    volumes, the resulting list might include a logical volume
693 10c2650b Iustin Pop
    multiple times.
694 10c2650b Iustin Pop

695 dcb93971 Michael Hanselmann
  """
696 dcb93971 Michael Hanselmann
  result = utils.RunCmd(["lvs", "--noheadings", "--units=m", "--nosuffix",
697 dcb93971 Michael Hanselmann
                         "--separator=|",
698 dcb93971 Michael Hanselmann
                         "--options=lv_name,lv_size,devices,vg_name"])
699 dcb93971 Michael Hanselmann
  if result.failed:
700 10bfe6cb Iustin Pop
    _Fail("Failed to list logical volumes, lvs output: %s",
701 10bfe6cb Iustin Pop
          result.output)
702 dcb93971 Michael Hanselmann
703 dcb93971 Michael Hanselmann
  def parse_dev(dev):
704 89e5ab02 Iustin Pop
    return dev.split('(')[0]
705 89e5ab02 Iustin Pop
706 89e5ab02 Iustin Pop
  def handle_dev(dev):
707 89e5ab02 Iustin Pop
    return [parse_dev(x) for x in dev.split(",")]
708 dcb93971 Michael Hanselmann
709 dcb93971 Michael Hanselmann
  def map_line(line):
710 89e5ab02 Iustin Pop
    line = [v.strip() for v in line]
711 89e5ab02 Iustin Pop
    return [{'name': line[0], 'size': line[1],
712 89e5ab02 Iustin Pop
             'dev': dev, 'vg': line[3]} for dev in handle_dev(line[2])]
713 89e5ab02 Iustin Pop
714 89e5ab02 Iustin Pop
  all_devs = []
715 89e5ab02 Iustin Pop
  for line in result.stdout.splitlines():
716 89e5ab02 Iustin Pop
    if line.count('|') >= 3:
717 89e5ab02 Iustin Pop
      all_devs.extend(map_line(line.split('|')))
718 89e5ab02 Iustin Pop
    else:
719 89e5ab02 Iustin Pop
      logging.warning("Strange line in the output from lvs: '%s'", line)
720 89e5ab02 Iustin Pop
  return all_devs
721 dcb93971 Michael Hanselmann
722 dcb93971 Michael Hanselmann
723 a8083063 Iustin Pop
def BridgesExist(bridges_list):
724 2f8598a5 Alexander Schreiber
  """Check if a list of bridges exist on the current node.
725 a8083063 Iustin Pop

726 b1206984 Iustin Pop
  @rtype: boolean
727 b1206984 Iustin Pop
  @return: C{True} if all of them exist, C{False} otherwise
728 a8083063 Iustin Pop

729 a8083063 Iustin Pop
  """
730 35c0c8da Iustin Pop
  missing = []
731 a8083063 Iustin Pop
  for bridge in bridges_list:
732 a8083063 Iustin Pop
    if not utils.BridgeExists(bridge):
733 35c0c8da Iustin Pop
      missing.append(bridge)
734 a8083063 Iustin Pop
735 35c0c8da Iustin Pop
  if missing:
736 1f864b60 Iustin Pop
    _Fail("Missing bridges %s", utils.CommaJoin(missing))
737 35c0c8da Iustin Pop
738 a8083063 Iustin Pop
739 e69d05fd Iustin Pop
def GetInstanceList(hypervisor_list):
740 2f8598a5 Alexander Schreiber
  """Provides a list of instances.
741 a8083063 Iustin Pop

742 e69d05fd Iustin Pop
  @type hypervisor_list: list
743 e69d05fd Iustin Pop
  @param hypervisor_list: the list of hypervisors to query information
744 e69d05fd Iustin Pop

745 e69d05fd Iustin Pop
  @rtype: list
746 e69d05fd Iustin Pop
  @return: a list of all running instances on the current node
747 10c2650b Iustin Pop
    - instance1.example.com
748 10c2650b Iustin Pop
    - instance2.example.com
749 a8083063 Iustin Pop

750 098c0958 Michael Hanselmann
  """
751 e69d05fd Iustin Pop
  results = []
752 e69d05fd Iustin Pop
  for hname in hypervisor_list:
753 e69d05fd Iustin Pop
    try:
754 e69d05fd Iustin Pop
      names = hypervisor.GetHypervisor(hname).ListInstances()
755 e69d05fd Iustin Pop
      results.extend(names)
756 e69d05fd Iustin Pop
    except errors.HypervisorError, err:
757 aca13712 Iustin Pop
      _Fail("Error enumerating instances (hypervisor %s): %s",
758 aca13712 Iustin Pop
            hname, err, exc=True)
759 a8083063 Iustin Pop
760 e69d05fd Iustin Pop
  return results
761 a8083063 Iustin Pop
762 a8083063 Iustin Pop
763 e69d05fd Iustin Pop
def GetInstanceInfo(instance, hname):
764 5bbd3f7f Michael Hanselmann
  """Gives back the information about an instance as a dictionary.
765 a8083063 Iustin Pop

766 e69d05fd Iustin Pop
  @type instance: string
767 e69d05fd Iustin Pop
  @param instance: the instance name
768 e69d05fd Iustin Pop
  @type hname: string
769 e69d05fd Iustin Pop
  @param hname: the hypervisor type of the instance
770 a8083063 Iustin Pop

771 e69d05fd Iustin Pop
  @rtype: dict
772 e69d05fd Iustin Pop
  @return: dictionary with the following keys:
773 e69d05fd Iustin Pop
      - memory: memory size of instance (int)
774 e69d05fd Iustin Pop
      - state: xen state of instance (string)
775 e69d05fd Iustin Pop
      - time: cpu time of instance (float)
776 a8083063 Iustin Pop

777 098c0958 Michael Hanselmann
  """
778 a8083063 Iustin Pop
  output = {}
779 a8083063 Iustin Pop
780 e69d05fd Iustin Pop
  iinfo = hypervisor.GetHypervisor(hname).GetInstanceInfo(instance)
781 a8083063 Iustin Pop
  if iinfo is not None:
782 a8083063 Iustin Pop
    output['memory'] = iinfo[2]
783 a8083063 Iustin Pop
    output['state'] = iinfo[4]
784 a8083063 Iustin Pop
    output['time'] = iinfo[5]
785 a8083063 Iustin Pop
786 c26a6bd2 Iustin Pop
  return output
787 a8083063 Iustin Pop
788 a8083063 Iustin Pop
789 56e7640c Iustin Pop
def GetInstanceMigratable(instance):
790 56e7640c Iustin Pop
  """Gives whether an instance can be migrated.
791 56e7640c Iustin Pop

792 56e7640c Iustin Pop
  @type instance: L{objects.Instance}
793 56e7640c Iustin Pop
  @param instance: object representing the instance to be checked.
794 56e7640c Iustin Pop

795 56e7640c Iustin Pop
  @rtype: tuple
796 56e7640c Iustin Pop
  @return: tuple of (result, description) where:
797 56e7640c Iustin Pop
      - result: whether the instance can be migrated or not
798 56e7640c Iustin Pop
      - description: a description of the issue, if relevant
799 56e7640c Iustin Pop

800 56e7640c Iustin Pop
  """
801 56e7640c Iustin Pop
  hyper = hypervisor.GetHypervisor(instance.hypervisor)
802 afdc3985 Iustin Pop
  iname = instance.name
803 afdc3985 Iustin Pop
  if iname not in hyper.ListInstances():
804 afdc3985 Iustin Pop
    _Fail("Instance %s is not running", iname)
805 56e7640c Iustin Pop
806 56e7640c Iustin Pop
  for idx in range(len(instance.disks)):
807 afdc3985 Iustin Pop
    link_name = _GetBlockDevSymlinkPath(iname, idx)
808 56e7640c Iustin Pop
    if not os.path.islink(link_name):
809 b8ebd37b Iustin Pop
      logging.warning("Instance %s is missing symlink %s for disk %d",
810 b8ebd37b Iustin Pop
                      iname, link_name, idx)
811 56e7640c Iustin Pop
812 56e7640c Iustin Pop
813 e69d05fd Iustin Pop
def GetAllInstancesInfo(hypervisor_list):
814 a8083063 Iustin Pop
  """Gather data about all instances.
815 a8083063 Iustin Pop

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

820 e69d05fd Iustin Pop
  @type hypervisor_list: list
821 e69d05fd Iustin Pop
  @param hypervisor_list: list of hypervisors to query for instance data
822 e69d05fd Iustin Pop

823 955db481 Guido Trotter
  @rtype: dict
824 e69d05fd Iustin Pop
  @return: dictionary of instance: data, with data having the following keys:
825 e69d05fd Iustin Pop
      - memory: memory size of instance (int)
826 e69d05fd Iustin Pop
      - state: xen state of instance (string)
827 e69d05fd Iustin Pop
      - time: cpu time of instance (float)
828 10c2650b Iustin Pop
      - vcpus: the number of vcpus
829 a8083063 Iustin Pop

830 098c0958 Michael Hanselmann
  """
831 a8083063 Iustin Pop
  output = {}
832 a8083063 Iustin Pop
833 e69d05fd Iustin Pop
  for hname in hypervisor_list:
834 e69d05fd Iustin Pop
    iinfo = hypervisor.GetHypervisor(hname).GetAllInstancesInfo()
835 e69d05fd Iustin Pop
    if iinfo:
836 29921401 Iustin Pop
      for name, _, memory, vcpus, state, times in iinfo:
837 f23b5ae8 Iustin Pop
        value = {
838 e69d05fd Iustin Pop
          'memory': memory,
839 e69d05fd Iustin Pop
          'vcpus': vcpus,
840 e69d05fd Iustin Pop
          'state': state,
841 e69d05fd Iustin Pop
          'time': times,
842 e69d05fd Iustin Pop
          }
843 b33b6f55 Iustin Pop
        if name in output:
844 b33b6f55 Iustin Pop
          # we only check static parameters, like memory and vcpus,
845 b33b6f55 Iustin Pop
          # and not state and time which can change between the
846 b33b6f55 Iustin Pop
          # invocations of the different hypervisors
847 b33b6f55 Iustin Pop
          for key in 'memory', 'vcpus':
848 b33b6f55 Iustin Pop
            if value[key] != output[name][key]:
849 2fa74ef4 Iustin Pop
              _Fail("Instance %s is running twice"
850 2fa74ef4 Iustin Pop
                    " with different parameters", name)
851 f23b5ae8 Iustin Pop
        output[name] = value
852 a8083063 Iustin Pop
853 c26a6bd2 Iustin Pop
  return output
854 a8083063 Iustin Pop
855 a8083063 Iustin Pop
856 81a3406c Iustin Pop
def _InstanceLogName(kind, os_name, instance):
857 81a3406c Iustin Pop
  """Compute the OS log filename for a given instance and operation.
858 81a3406c Iustin Pop

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

862 81a3406c Iustin Pop
  @type kind: string
863 81a3406c Iustin Pop
  @param kind: the operation type (e.g. add, import, etc.)
864 81a3406c Iustin Pop
  @type os_name: string
865 81a3406c Iustin Pop
  @param os_name: the os name
866 81a3406c Iustin Pop
  @type instance: string
867 81a3406c Iustin Pop
  @param instance: the name of the instance being imported/added/etc.
868 81a3406c Iustin Pop

869 81a3406c Iustin Pop
  """
870 1651d116 Michael Hanselmann
  # TODO: Use tempfile.mkstemp to create unique filename
871 1d466a4f Michael Hanselmann
  base = ("%s-%s-%s-%s.log" %
872 1d466a4f Michael Hanselmann
          (kind, os_name, instance, utils.TimestampForFilename()))
873 81a3406c Iustin Pop
  return utils.PathJoin(constants.LOG_OS_DIR, base)
874 81a3406c Iustin Pop
875 81a3406c Iustin Pop
876 4a0e011f Iustin Pop
def InstanceOsAdd(instance, reinstall, debug):
877 2f8598a5 Alexander Schreiber
  """Add an OS to an instance.
878 a8083063 Iustin Pop

879 d15a9ad3 Guido Trotter
  @type instance: L{objects.Instance}
880 d15a9ad3 Guido Trotter
  @param instance: Instance whose OS is to be installed
881 e557bae9 Guido Trotter
  @type reinstall: boolean
882 e557bae9 Guido Trotter
  @param reinstall: whether this is an instance reinstall
883 4a0e011f Iustin Pop
  @type debug: integer
884 4a0e011f Iustin Pop
  @param debug: debug level, passed to the OS scripts
885 c26a6bd2 Iustin Pop
  @rtype: None
886 a8083063 Iustin Pop

887 a8083063 Iustin Pop
  """
888 255dcebd Iustin Pop
  inst_os = OSFromDisk(instance.os)
889 255dcebd Iustin Pop
890 4a0e011f Iustin Pop
  create_env = OSEnvironment(instance, inst_os, debug)
891 e557bae9 Guido Trotter
  if reinstall:
892 e557bae9 Guido Trotter
    create_env['INSTANCE_REINSTALL'] = "1"
893 a8083063 Iustin Pop
894 81a3406c Iustin Pop
  logfile = _InstanceLogName("add", instance.os, instance.name)
895 decd5f45 Iustin Pop
896 d868edb4 Iustin Pop
  result = utils.RunCmd([inst_os.create_script], env=create_env,
897 d868edb4 Iustin Pop
                        cwd=inst_os.path, output=logfile,)
898 decd5f45 Iustin Pop
  if result.failed:
899 18682bca Iustin Pop
    logging.error("os create command '%s' returned error: %s, logfile: %s,"
900 d868edb4 Iustin Pop
                  " output: %s", result.cmd, result.fail_reason, logfile,
901 18682bca Iustin Pop
                  result.output)
902 26f15862 Iustin Pop
    lines = [utils.SafeEncode(val)
903 20e01edd Iustin Pop
             for val in utils.TailFile(logfile, lines=20)]
904 afdc3985 Iustin Pop
    _Fail("OS create script failed (%s), last lines in the"
905 afdc3985 Iustin Pop
          " log file:\n%s", result.fail_reason, "\n".join(lines), log=False)
906 decd5f45 Iustin Pop
907 decd5f45 Iustin Pop
908 4a0e011f Iustin Pop
def RunRenameInstance(instance, old_name, debug):
909 decd5f45 Iustin Pop
  """Run the OS rename script for an instance.
910 decd5f45 Iustin Pop

911 b1206984 Iustin Pop
  @type instance: L{objects.Instance}
912 d15a9ad3 Guido Trotter
  @param instance: Instance whose OS is to be installed
913 d15a9ad3 Guido Trotter
  @type old_name: string
914 d15a9ad3 Guido Trotter
  @param old_name: previous instance name
915 4a0e011f Iustin Pop
  @type debug: integer
916 4a0e011f Iustin Pop
  @param debug: debug level, passed to the OS scripts
917 10c2650b Iustin Pop
  @rtype: boolean
918 10c2650b Iustin Pop
  @return: the success of the operation
919 decd5f45 Iustin Pop

920 decd5f45 Iustin Pop
  """
921 decd5f45 Iustin Pop
  inst_os = OSFromDisk(instance.os)
922 decd5f45 Iustin Pop
923 4a0e011f Iustin Pop
  rename_env = OSEnvironment(instance, inst_os, debug)
924 ff38b6c0 Guido Trotter
  rename_env['OLD_INSTANCE_NAME'] = old_name
925 decd5f45 Iustin Pop
926 81a3406c Iustin Pop
  logfile = _InstanceLogName("rename", instance.os,
927 81a3406c Iustin Pop
                             "%s-%s" % (old_name, instance.name))
928 a8083063 Iustin Pop
929 d868edb4 Iustin Pop
  result = utils.RunCmd([inst_os.rename_script], env=rename_env,
930 d868edb4 Iustin Pop
                        cwd=inst_os.path, output=logfile)
931 a8083063 Iustin Pop
932 a8083063 Iustin Pop
  if result.failed:
933 18682bca Iustin Pop
    logging.error("os create command '%s' returned error: %s output: %s",
934 d868edb4 Iustin Pop
                  result.cmd, result.fail_reason, result.output)
935 26f15862 Iustin Pop
    lines = [utils.SafeEncode(val)
936 96841384 Iustin Pop
             for val in utils.TailFile(logfile, lines=20)]
937 afdc3985 Iustin Pop
    _Fail("OS rename script failed (%s), last lines in the"
938 afdc3985 Iustin Pop
          " log file:\n%s", result.fail_reason, "\n".join(lines), log=False)
939 a8083063 Iustin Pop
940 a8083063 Iustin Pop
941 5282084b Iustin Pop
def _GetBlockDevSymlinkPath(instance_name, idx):
942 c4feafe8 Iustin Pop
  return utils.PathJoin(constants.DISK_LINKS_DIR,
943 c4feafe8 Iustin Pop
                        "%s:%d" % (instance_name, idx))
944 5282084b Iustin Pop
945 5282084b Iustin Pop
946 5282084b Iustin Pop
def _SymlinkBlockDev(instance_name, device_path, idx):
947 9332fd8a Iustin Pop
  """Set up symlinks to a instance's block device.
948 9332fd8a Iustin Pop

949 9332fd8a Iustin Pop
  This is an auxiliary function run when an instance is start (on the primary
950 9332fd8a Iustin Pop
  node) or when an instance is migrated (on the target node).
951 9332fd8a Iustin Pop

952 9332fd8a Iustin Pop

953 5282084b Iustin Pop
  @param instance_name: the name of the target instance
954 5282084b Iustin Pop
  @param device_path: path of the physical block device, on the node
955 5282084b Iustin Pop
  @param idx: the disk index
956 5282084b Iustin Pop
  @return: absolute path to the disk's symlink
957 9332fd8a Iustin Pop

958 9332fd8a Iustin Pop
  """
959 5282084b Iustin Pop
  link_name = _GetBlockDevSymlinkPath(instance_name, idx)
960 9332fd8a Iustin Pop
  try:
961 9332fd8a Iustin Pop
    os.symlink(device_path, link_name)
962 5282084b Iustin Pop
  except OSError, err:
963 5282084b Iustin Pop
    if err.errno == errno.EEXIST:
964 9332fd8a Iustin Pop
      if (not os.path.islink(link_name) or
965 9332fd8a Iustin Pop
          os.readlink(link_name) != device_path):
966 9332fd8a Iustin Pop
        os.remove(link_name)
967 9332fd8a Iustin Pop
        os.symlink(device_path, link_name)
968 9332fd8a Iustin Pop
    else:
969 9332fd8a Iustin Pop
      raise
970 9332fd8a Iustin Pop
971 9332fd8a Iustin Pop
  return link_name
972 9332fd8a Iustin Pop
973 9332fd8a Iustin Pop
974 5282084b Iustin Pop
def _RemoveBlockDevLinks(instance_name, disks):
975 3c9c571d Iustin Pop
  """Remove the block device symlinks belonging to the given instance.
976 3c9c571d Iustin Pop

977 3c9c571d Iustin Pop
  """
978 29921401 Iustin Pop
  for idx, _ in enumerate(disks):
979 5282084b Iustin Pop
    link_name = _GetBlockDevSymlinkPath(instance_name, idx)
980 5282084b Iustin Pop
    if os.path.islink(link_name):
981 3c9c571d Iustin Pop
      try:
982 03dfa658 Iustin Pop
        os.remove(link_name)
983 03dfa658 Iustin Pop
      except OSError:
984 03dfa658 Iustin Pop
        logging.exception("Can't remove symlink '%s'", link_name)
985 3c9c571d Iustin Pop
986 3c9c571d Iustin Pop
987 9332fd8a Iustin Pop
def _GatherAndLinkBlockDevs(instance):
988 a8083063 Iustin Pop
  """Set up an instance's block device(s).
989 a8083063 Iustin Pop

990 a8083063 Iustin Pop
  This is run on the primary node at instance startup. The block
991 a8083063 Iustin Pop
  devices must be already assembled.
992 a8083063 Iustin Pop

993 10c2650b Iustin Pop
  @type instance: L{objects.Instance}
994 10c2650b Iustin Pop
  @param instance: the instance whose disks we shoul assemble
995 069cfbf1 Iustin Pop
  @rtype: list
996 069cfbf1 Iustin Pop
  @return: list of (disk_object, device_path)
997 10c2650b Iustin Pop

998 a8083063 Iustin Pop
  """
999 a8083063 Iustin Pop
  block_devices = []
1000 9332fd8a Iustin Pop
  for idx, disk in enumerate(instance.disks):
1001 a8083063 Iustin Pop
    device = _RecursiveFindBD(disk)
1002 a8083063 Iustin Pop
    if device is None:
1003 a8083063 Iustin Pop
      raise errors.BlockDeviceError("Block device '%s' is not set up." %
1004 a8083063 Iustin Pop
                                    str(disk))
1005 a8083063 Iustin Pop
    device.Open()
1006 9332fd8a Iustin Pop
    try:
1007 5282084b Iustin Pop
      link_name = _SymlinkBlockDev(instance.name, device.dev_path, idx)
1008 9332fd8a Iustin Pop
    except OSError, e:
1009 9332fd8a Iustin Pop
      raise errors.BlockDeviceError("Cannot create block device symlink: %s" %
1010 9332fd8a Iustin Pop
                                    e.strerror)
1011 9332fd8a Iustin Pop
1012 9332fd8a Iustin Pop
    block_devices.append((disk, link_name))
1013 9332fd8a Iustin Pop
1014 a8083063 Iustin Pop
  return block_devices
1015 a8083063 Iustin Pop
1016 a8083063 Iustin Pop
1017 07813a9e Iustin Pop
def StartInstance(instance):
1018 a8083063 Iustin Pop
  """Start an instance.
1019 a8083063 Iustin Pop

1020 10c2650b Iustin Pop
  @type instance: L{objects.Instance}
1021 e69d05fd Iustin Pop
  @param instance: the instance object
1022 c26a6bd2 Iustin Pop
  @rtype: None
1023 a8083063 Iustin Pop

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

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

1047 10c2650b Iustin Pop
  @type instance: L{objects.Instance}
1048 e69d05fd Iustin Pop
  @param instance: the instance object
1049 6263189c Guido Trotter
  @type timeout: integer
1050 6263189c Guido Trotter
  @param timeout: maximum timeout for soft shutdown
1051 c26a6bd2 Iustin Pop
  @rtype: None
1052 a8083063 Iustin Pop

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

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

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

1156 6906a9d8 Guido Trotter
  @type instance: L{objects.Instance}
1157 6906a9d8 Guido Trotter
  @param instance: the instance definition
1158 6906a9d8 Guido Trotter

1159 6906a9d8 Guido Trotter
  """
1160 cd42d0ad Guido Trotter
  hyper = hypervisor.GetHypervisor(instance.hypervisor)
1161 cd42d0ad Guido Trotter
  try:
1162 cd42d0ad Guido Trotter
    info = hyper.MigrationInfo(instance)
1163 cd42d0ad Guido Trotter
  except errors.HypervisorError, err:
1164 2cc6781a Iustin Pop
    _Fail("Failed to fetch migration information: %s", err, exc=True)
1165 c26a6bd2 Iustin Pop
  return info
1166 6906a9d8 Guido Trotter
1167 6906a9d8 Guido Trotter
1168 6906a9d8 Guido Trotter
def AcceptInstance(instance, info, target):
1169 6906a9d8 Guido Trotter
  """Prepare the node to accept an instance.
1170 6906a9d8 Guido Trotter

1171 6906a9d8 Guido Trotter
  @type instance: L{objects.Instance}
1172 6906a9d8 Guido Trotter
  @param instance: the instance definition
1173 6906a9d8 Guido Trotter
  @type info: string/data (opaque)
1174 6906a9d8 Guido Trotter
  @param info: migration information, from the source node
1175 6906a9d8 Guido Trotter
  @type target: string
1176 6906a9d8 Guido Trotter
  @param target: target host (usually ip), on this node
1177 6906a9d8 Guido Trotter

1178 6906a9d8 Guido Trotter
  """
1179 cd42d0ad Guido Trotter
  hyper = hypervisor.GetHypervisor(instance.hypervisor)
1180 cd42d0ad Guido Trotter
  try:
1181 cd42d0ad Guido Trotter
    hyper.AcceptInstance(instance, info, target)
1182 cd42d0ad Guido Trotter
  except errors.HypervisorError, err:
1183 2cc6781a Iustin Pop
    _Fail("Failed to accept instance: %s", err, exc=True)
1184 6906a9d8 Guido Trotter
1185 6906a9d8 Guido Trotter
1186 6906a9d8 Guido Trotter
def FinalizeMigration(instance, info, success):
1187 6906a9d8 Guido Trotter
  """Finalize any preparation to accept an instance.
1188 6906a9d8 Guido Trotter

1189 6906a9d8 Guido Trotter
  @type instance: L{objects.Instance}
1190 6906a9d8 Guido Trotter
  @param instance: the instance definition
1191 6906a9d8 Guido Trotter
  @type info: string/data (opaque)
1192 6906a9d8 Guido Trotter
  @param info: migration information, from the source node
1193 6906a9d8 Guido Trotter
  @type success: boolean
1194 6906a9d8 Guido Trotter
  @param success: whether the migration was a success or a failure
1195 6906a9d8 Guido Trotter

1196 6906a9d8 Guido Trotter
  """
1197 cd42d0ad Guido Trotter
  hyper = hypervisor.GetHypervisor(instance.hypervisor)
1198 cd42d0ad Guido Trotter
  try:
1199 cd42d0ad Guido Trotter
    hyper.FinalizeMigration(instance, info, success)
1200 cd42d0ad Guido Trotter
  except errors.HypervisorError, err:
1201 2cc6781a Iustin Pop
    _Fail("Failed to finalize migration: %s", err, exc=True)
1202 6906a9d8 Guido Trotter
1203 6906a9d8 Guido Trotter
1204 2a10865c Iustin Pop
def MigrateInstance(instance, target, live):
1205 2a10865c Iustin Pop
  """Migrates an instance to another node.
1206 2a10865c Iustin Pop

1207 b1206984 Iustin Pop
  @type instance: L{objects.Instance}
1208 9f0e6b37 Iustin Pop
  @param instance: the instance definition
1209 9f0e6b37 Iustin Pop
  @type target: string
1210 9f0e6b37 Iustin Pop
  @param target: the target node name
1211 9f0e6b37 Iustin Pop
  @type live: boolean
1212 9f0e6b37 Iustin Pop
  @param live: whether the migration should be done live or not (the
1213 9f0e6b37 Iustin Pop
      interpretation of this parameter is left to the hypervisor)
1214 9f0e6b37 Iustin Pop
  @rtype: tuple
1215 9f0e6b37 Iustin Pop
  @return: a tuple of (success, msg) where:
1216 9f0e6b37 Iustin Pop
      - succes is a boolean denoting the success/failure of the operation
1217 9f0e6b37 Iustin Pop
      - msg is a string with details in case of failure
1218 9f0e6b37 Iustin Pop

1219 2a10865c Iustin Pop
  """
1220 53c776b5 Iustin Pop
  hyper = hypervisor.GetHypervisor(instance.hypervisor)
1221 2a10865c Iustin Pop
1222 2a10865c Iustin Pop
  try:
1223 58d38b02 Iustin Pop
    hyper.MigrateInstance(instance, target, live)
1224 2a10865c Iustin Pop
  except errors.HypervisorError, err:
1225 2cc6781a Iustin Pop
    _Fail("Failed to migrate instance: %s", err, exc=True)
1226 2a10865c Iustin Pop
1227 2a10865c Iustin Pop
1228 821d1bd1 Iustin Pop
def BlockdevCreate(disk, size, owner, on_primary, info):
1229 a8083063 Iustin Pop
  """Creates a block device for an instance.
1230 a8083063 Iustin Pop

1231 b1206984 Iustin Pop
  @type disk: L{objects.Disk}
1232 b1206984 Iustin Pop
  @param disk: the object describing the disk we should create
1233 b1206984 Iustin Pop
  @type size: int
1234 b1206984 Iustin Pop
  @param size: the size of the physical underlying device, in MiB
1235 b1206984 Iustin Pop
  @type owner: str
1236 b1206984 Iustin Pop
  @param owner: the name of the instance for which disk is created,
1237 b1206984 Iustin Pop
      used for device cache data
1238 b1206984 Iustin Pop
  @type on_primary: boolean
1239 b1206984 Iustin Pop
  @param on_primary:  indicates if it is the primary node or not
1240 b1206984 Iustin Pop
  @type info: string
1241 b1206984 Iustin Pop
  @param info: string that will be sent to the physical device
1242 b1206984 Iustin Pop
      creation, used for example to set (LVM) tags on LVs
1243 b1206984 Iustin Pop

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

1248 a8083063 Iustin Pop
  """
1249 7260cfbe Iustin Pop
  # TODO: remove the obsolete 'size' argument
1250 7260cfbe Iustin Pop
  # pylint: disable-msg=W0613
1251 a8083063 Iustin Pop
  clist = []
1252 a8083063 Iustin Pop
  if disk.children:
1253 a8083063 Iustin Pop
    for child in disk.children:
1254 1063abd1 Iustin Pop
      try:
1255 1063abd1 Iustin Pop
        crdev = _RecursiveAssembleBD(child, owner, on_primary)
1256 1063abd1 Iustin Pop
      except errors.BlockDeviceError, err:
1257 2cc6781a Iustin Pop
        _Fail("Can't assemble device %s: %s", child, err)
1258 a8083063 Iustin Pop
      if on_primary or disk.AssembleOnSecondary():
1259 a8083063 Iustin Pop
        # we need the children open in case the device itself has to
1260 a8083063 Iustin Pop
        # be assembled
1261 1063abd1 Iustin Pop
        try:
1262 fe267188 Iustin Pop
          # pylint: disable-msg=E1103
1263 1063abd1 Iustin Pop
          crdev.Open()
1264 1063abd1 Iustin Pop
        except errors.BlockDeviceError, err:
1265 2cc6781a Iustin Pop
          _Fail("Can't make child '%s' read-write: %s", child, err)
1266 a8083063 Iustin Pop
      clist.append(crdev)
1267 a8083063 Iustin Pop
1268 dab69e97 Iustin Pop
  try:
1269 464f8daf Iustin Pop
    device = bdev.Create(disk.dev_type, disk.physical_id, clist, disk.size)
1270 1063abd1 Iustin Pop
  except errors.BlockDeviceError, err:
1271 2cc6781a Iustin Pop
    _Fail("Can't create block device: %s", err)
1272 6c626518 Iustin Pop
1273 a8083063 Iustin Pop
  if on_primary or disk.AssembleOnSecondary():
1274 1063abd1 Iustin Pop
    try:
1275 1063abd1 Iustin Pop
      device.Assemble()
1276 1063abd1 Iustin Pop
    except errors.BlockDeviceError, err:
1277 2cc6781a Iustin Pop
      _Fail("Can't assemble device after creation, unusual event: %s", err)
1278 e31c43f7 Michael Hanselmann
    device.SetSyncSpeed(constants.SYNC_SPEED)
1279 a8083063 Iustin Pop
    if on_primary or disk.OpenOnSecondary():
1280 1063abd1 Iustin Pop
      try:
1281 1063abd1 Iustin Pop
        device.Open(force=True)
1282 1063abd1 Iustin Pop
      except errors.BlockDeviceError, err:
1283 2cc6781a Iustin Pop
        _Fail("Can't make device r/w after creation, unusual event: %s", err)
1284 3f78eef2 Iustin Pop
    DevCacheManager.UpdateCache(device.dev_path, owner,
1285 3f78eef2 Iustin Pop
                                on_primary, disk.iv_name)
1286 a0c3fea1 Michael Hanselmann
1287 a0c3fea1 Michael Hanselmann
  device.SetInfo(info)
1288 a0c3fea1 Michael Hanselmann
1289 c26a6bd2 Iustin Pop
  return device.unique_id
1290 a8083063 Iustin Pop
1291 a8083063 Iustin Pop
1292 da63bb4e René Nussbaumer
def _WipeDevice(path, offset, size):
1293 69dd363f René Nussbaumer
  """This function actually wipes the device.
1294 69dd363f René Nussbaumer

1295 69dd363f René Nussbaumer
  @param path: The path to the device to wipe
1296 da63bb4e René Nussbaumer
  @param offset: The offset in MiB in the file
1297 da63bb4e René Nussbaumer
  @param size: The size in MiB to write
1298 69dd363f René Nussbaumer

1299 69dd363f René Nussbaumer
  """
1300 da63bb4e René Nussbaumer
  cmd = [constants.DD_CMD, "if=/dev/zero", "seek=%d" % offset,
1301 da63bb4e René Nussbaumer
         "bs=%d" % constants.WIPE_BLOCK_SIZE, "oflag=direct", "of=%s" % path,
1302 da63bb4e René Nussbaumer
         "count=%d" % size]
1303 da63bb4e René Nussbaumer
  result = utils.RunCmd(cmd)
1304 69dd363f René Nussbaumer
1305 69dd363f René Nussbaumer
  if result.failed:
1306 69dd363f René Nussbaumer
    _Fail("Wipe command '%s' exited with error: %s; output: %s", result.cmd,
1307 69dd363f René Nussbaumer
          result.fail_reason, result.output)
1308 69dd363f René Nussbaumer
1309 69dd363f René Nussbaumer
1310 da63bb4e René Nussbaumer
def BlockdevWipe(disk, offset, size):
1311 69dd363f René Nussbaumer
  """Wipes a block device.
1312 69dd363f René Nussbaumer

1313 69dd363f René Nussbaumer
  @type disk: L{objects.Disk}
1314 69dd363f René Nussbaumer
  @param disk: the disk object we want to wipe
1315 da63bb4e René Nussbaumer
  @type offset: int
1316 da63bb4e René Nussbaumer
  @param offset: The offset in MiB in the file
1317 da63bb4e René Nussbaumer
  @type size: int
1318 da63bb4e René Nussbaumer
  @param size: The size in MiB to write
1319 69dd363f René Nussbaumer

1320 69dd363f René Nussbaumer
  """
1321 69dd363f René Nussbaumer
  try:
1322 69dd363f René Nussbaumer
    rdev = _RecursiveFindBD(disk)
1323 da63bb4e René Nussbaumer
  except errors.BlockDeviceError:
1324 da63bb4e René Nussbaumer
    rdev = None
1325 da63bb4e René Nussbaumer
1326 da63bb4e René Nussbaumer
  if not rdev:
1327 da63bb4e René Nussbaumer
    _Fail("Cannot execute wipe for device %s: device not found", disk.iv_name)
1328 da63bb4e René Nussbaumer
1329 da63bb4e René Nussbaumer
  # Do cross verify some of the parameters
1330 da63bb4e René Nussbaumer
  if offset > rdev.size:
1331 da63bb4e René Nussbaumer
    _Fail("Offset is bigger than device size")
1332 da63bb4e René Nussbaumer
  if (offset + size) > rdev.size:
1333 da63bb4e René Nussbaumer
    _Fail("The provided offset and size to wipe is bigger than device size")
1334 69dd363f René Nussbaumer
1335 da63bb4e René Nussbaumer
  _WipeDevice(rdev.dev_path, offset, size)
1336 69dd363f René Nussbaumer
1337 69dd363f René Nussbaumer
1338 821d1bd1 Iustin Pop
def BlockdevRemove(disk):
1339 a8083063 Iustin Pop
  """Remove a block device.
1340 a8083063 Iustin Pop

1341 10c2650b Iustin Pop
  @note: This is intended to be called recursively.
1342 10c2650b Iustin Pop

1343 c41eea6e Iustin Pop
  @type disk: L{objects.Disk}
1344 10c2650b Iustin Pop
  @param disk: the disk object we should remove
1345 10c2650b Iustin Pop
  @rtype: boolean
1346 10c2650b Iustin Pop
  @return: the success of the operation
1347 a8083063 Iustin Pop

1348 a8083063 Iustin Pop
  """
1349 e1bc0878 Iustin Pop
  msgs = []
1350 a8083063 Iustin Pop
  try:
1351 bca2e7f4 Iustin Pop
    rdev = _RecursiveFindBD(disk)
1352 a8083063 Iustin Pop
  except errors.BlockDeviceError, err:
1353 a8083063 Iustin Pop
    # probably can't attach
1354 18682bca Iustin Pop
    logging.info("Can't attach to device %s in remove", disk)
1355 a8083063 Iustin Pop
    rdev = None
1356 a8083063 Iustin Pop
  if rdev is not None:
1357 3f78eef2 Iustin Pop
    r_path = rdev.dev_path
1358 e1bc0878 Iustin Pop
    try:
1359 0c6c04ec Iustin Pop
      rdev.Remove()
1360 e1bc0878 Iustin Pop
    except errors.BlockDeviceError, err:
1361 e1bc0878 Iustin Pop
      msgs.append(str(err))
1362 c26a6bd2 Iustin Pop
    if not msgs:
1363 3f78eef2 Iustin Pop
      DevCacheManager.RemoveCache(r_path)
1364 e1bc0878 Iustin Pop
1365 a8083063 Iustin Pop
  if disk.children:
1366 a8083063 Iustin Pop
    for child in disk.children:
1367 c26a6bd2 Iustin Pop
      try:
1368 c26a6bd2 Iustin Pop
        BlockdevRemove(child)
1369 c26a6bd2 Iustin Pop
      except RPCFail, err:
1370 c26a6bd2 Iustin Pop
        msgs.append(str(err))
1371 e1bc0878 Iustin Pop
1372 c26a6bd2 Iustin Pop
  if msgs:
1373 afdc3985 Iustin Pop
    _Fail("; ".join(msgs))
1374 afdc3985 Iustin Pop
1375 a8083063 Iustin Pop
1376 3f78eef2 Iustin Pop
def _RecursiveAssembleBD(disk, owner, as_primary):
1377 a8083063 Iustin Pop
  """Activate a block device for an instance.
1378 a8083063 Iustin Pop

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

1381 10c2650b Iustin Pop
  @note: this function is called recursively.
1382 a8083063 Iustin Pop

1383 10c2650b Iustin Pop
  @type disk: L{objects.Disk}
1384 10c2650b Iustin Pop
  @param disk: the disk we try to assemble
1385 10c2650b Iustin Pop
  @type owner: str
1386 10c2650b Iustin Pop
  @param owner: the name of the instance which owns the disk
1387 10c2650b Iustin Pop
  @type as_primary: boolean
1388 10c2650b Iustin Pop
  @param as_primary: if we should make the block device
1389 10c2650b Iustin Pop
      read/write
1390 a8083063 Iustin Pop

1391 10c2650b Iustin Pop
  @return: the assembled device or None (in case no device
1392 10c2650b Iustin Pop
      was assembled)
1393 10c2650b Iustin Pop
  @raise errors.BlockDeviceError: in case there is an error
1394 10c2650b Iustin Pop
      during the activation of the children or the device
1395 10c2650b Iustin Pop
      itself
1396 a8083063 Iustin Pop

1397 a8083063 Iustin Pop
  """
1398 a8083063 Iustin Pop
  children = []
1399 a8083063 Iustin Pop
  if disk.children:
1400 fc1dc9d7 Iustin Pop
    mcn = disk.ChildrenNeeded()
1401 fc1dc9d7 Iustin Pop
    if mcn == -1:
1402 fc1dc9d7 Iustin Pop
      mcn = 0 # max number of Nones allowed
1403 fc1dc9d7 Iustin Pop
    else:
1404 fc1dc9d7 Iustin Pop
      mcn = len(disk.children) - mcn # max number of Nones
1405 a8083063 Iustin Pop
    for chld_disk in disk.children:
1406 fc1dc9d7 Iustin Pop
      try:
1407 fc1dc9d7 Iustin Pop
        cdev = _RecursiveAssembleBD(chld_disk, owner, as_primary)
1408 fc1dc9d7 Iustin Pop
      except errors.BlockDeviceError, err:
1409 7803d4d3 Iustin Pop
        if children.count(None) >= mcn:
1410 fc1dc9d7 Iustin Pop
          raise
1411 fc1dc9d7 Iustin Pop
        cdev = None
1412 1063abd1 Iustin Pop
        logging.error("Error in child activation (but continuing): %s",
1413 1063abd1 Iustin Pop
                      str(err))
1414 fc1dc9d7 Iustin Pop
      children.append(cdev)
1415 a8083063 Iustin Pop
1416 a8083063 Iustin Pop
  if as_primary or disk.AssembleOnSecondary():
1417 464f8daf Iustin Pop
    r_dev = bdev.Assemble(disk.dev_type, disk.physical_id, children, disk.size)
1418 e31c43f7 Michael Hanselmann
    r_dev.SetSyncSpeed(constants.SYNC_SPEED)
1419 a8083063 Iustin Pop
    result = r_dev
1420 a8083063 Iustin Pop
    if as_primary or disk.OpenOnSecondary():
1421 a8083063 Iustin Pop
      r_dev.Open()
1422 3f78eef2 Iustin Pop
    DevCacheManager.UpdateCache(r_dev.dev_path, owner,
1423 3f78eef2 Iustin Pop
                                as_primary, disk.iv_name)
1424 3f78eef2 Iustin Pop
1425 a8083063 Iustin Pop
  else:
1426 a8083063 Iustin Pop
    result = True
1427 a8083063 Iustin Pop
  return result
1428 a8083063 Iustin Pop
1429 a8083063 Iustin Pop
1430 821d1bd1 Iustin Pop
def BlockdevAssemble(disk, owner, as_primary):
1431 a8083063 Iustin Pop
  """Activate a block device for an instance.
1432 a8083063 Iustin Pop

1433 a8083063 Iustin Pop
  This is a wrapper over _RecursiveAssembleBD.
1434 a8083063 Iustin Pop

1435 b1206984 Iustin Pop
  @rtype: str or boolean
1436 b1206984 Iustin Pop
  @return: a C{/dev/...} path for primary nodes, and
1437 b1206984 Iustin Pop
      C{True} for secondary nodes
1438 a8083063 Iustin Pop

1439 a8083063 Iustin Pop
  """
1440 53c14ef1 Iustin Pop
  try:
1441 53c14ef1 Iustin Pop
    result = _RecursiveAssembleBD(disk, owner, as_primary)
1442 53c14ef1 Iustin Pop
    if isinstance(result, bdev.BlockDev):
1443 fe267188 Iustin Pop
      # pylint: disable-msg=E1103
1444 53c14ef1 Iustin Pop
      result = result.dev_path
1445 53c14ef1 Iustin Pop
  except errors.BlockDeviceError, err:
1446 afdc3985 Iustin Pop
    _Fail("Error while assembling disk: %s", err, exc=True)
1447 afdc3985 Iustin Pop
1448 c26a6bd2 Iustin Pop
  return result
1449 a8083063 Iustin Pop
1450 a8083063 Iustin Pop
1451 821d1bd1 Iustin Pop
def BlockdevShutdown(disk):
1452 a8083063 Iustin Pop
  """Shut down a block device.
1453 a8083063 Iustin Pop

1454 5bbd3f7f Michael Hanselmann
  First, if the device is assembled (Attach() is successful), then
1455 c41eea6e Iustin Pop
  the device is shutdown. Then the children of the device are
1456 c41eea6e Iustin Pop
  shutdown.
1457 a8083063 Iustin Pop

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

1462 10c2650b Iustin Pop
  @type disk: L{objects.Disk}
1463 10c2650b Iustin Pop
  @param disk: the description of the disk we should
1464 10c2650b Iustin Pop
      shutdown
1465 c26a6bd2 Iustin Pop
  @rtype: None
1466 10c2650b Iustin Pop

1467 a8083063 Iustin Pop
  """
1468 cacfd1fd Iustin Pop
  msgs = []
1469 a8083063 Iustin Pop
  r_dev = _RecursiveFindBD(disk)
1470 a8083063 Iustin Pop
  if r_dev is not None:
1471 3f78eef2 Iustin Pop
    r_path = r_dev.dev_path
1472 cacfd1fd Iustin Pop
    try:
1473 746f7476 Iustin Pop
      r_dev.Shutdown()
1474 746f7476 Iustin Pop
      DevCacheManager.RemoveCache(r_path)
1475 cacfd1fd Iustin Pop
    except errors.BlockDeviceError, err:
1476 cacfd1fd Iustin Pop
      msgs.append(str(err))
1477 746f7476 Iustin Pop
1478 a8083063 Iustin Pop
  if disk.children:
1479 a8083063 Iustin Pop
    for child in disk.children:
1480 c26a6bd2 Iustin Pop
      try:
1481 c26a6bd2 Iustin Pop
        BlockdevShutdown(child)
1482 c26a6bd2 Iustin Pop
      except RPCFail, err:
1483 c26a6bd2 Iustin Pop
        msgs.append(str(err))
1484 746f7476 Iustin Pop
1485 c26a6bd2 Iustin Pop
  if msgs:
1486 afdc3985 Iustin Pop
    _Fail("; ".join(msgs))
1487 a8083063 Iustin Pop
1488 a8083063 Iustin Pop
1489 821d1bd1 Iustin Pop
def BlockdevAddchildren(parent_cdev, new_cdevs):
1490 153d9724 Iustin Pop
  """Extend a mirrored block device.
1491 a8083063 Iustin Pop

1492 10c2650b Iustin Pop
  @type parent_cdev: L{objects.Disk}
1493 10c2650b Iustin Pop
  @param parent_cdev: the disk to which we should add children
1494 10c2650b Iustin Pop
  @type new_cdevs: list of L{objects.Disk}
1495 10c2650b Iustin Pop
  @param new_cdevs: the list of children which we should add
1496 c26a6bd2 Iustin Pop
  @rtype: None
1497 10c2650b Iustin Pop

1498 a8083063 Iustin Pop
  """
1499 bca2e7f4 Iustin Pop
  parent_bdev = _RecursiveFindBD(parent_cdev)
1500 153d9724 Iustin Pop
  if parent_bdev is None:
1501 2cc6781a Iustin Pop
    _Fail("Can't find parent device '%s' in add children", parent_cdev)
1502 153d9724 Iustin Pop
  new_bdevs = [_RecursiveFindBD(disk) for disk in new_cdevs]
1503 153d9724 Iustin Pop
  if new_bdevs.count(None) > 0:
1504 2cc6781a Iustin Pop
    _Fail("Can't find new device(s) to add: %s:%s", new_bdevs, new_cdevs)
1505 153d9724 Iustin Pop
  parent_bdev.AddChildren(new_bdevs)
1506 a8083063 Iustin Pop
1507 a8083063 Iustin Pop
1508 821d1bd1 Iustin Pop
def BlockdevRemovechildren(parent_cdev, new_cdevs):
1509 153d9724 Iustin Pop
  """Shrink a mirrored block device.
1510 a8083063 Iustin Pop

1511 10c2650b Iustin Pop
  @type parent_cdev: L{objects.Disk}
1512 10c2650b Iustin Pop
  @param parent_cdev: the disk from which we should remove children
1513 10c2650b Iustin Pop
  @type new_cdevs: list of L{objects.Disk}
1514 10c2650b Iustin Pop
  @param new_cdevs: the list of children which we should remove
1515 c26a6bd2 Iustin Pop
  @rtype: None
1516 10c2650b Iustin Pop

1517 a8083063 Iustin Pop
  """
1518 153d9724 Iustin Pop
  parent_bdev = _RecursiveFindBD(parent_cdev)
1519 153d9724 Iustin Pop
  if parent_bdev is None:
1520 2cc6781a Iustin Pop
    _Fail("Can't find parent device '%s' in remove children", parent_cdev)
1521 e739bd57 Iustin Pop
  devs = []
1522 e739bd57 Iustin Pop
  for disk in new_cdevs:
1523 e739bd57 Iustin Pop
    rpath = disk.StaticDevPath()
1524 e739bd57 Iustin Pop
    if rpath is None:
1525 e739bd57 Iustin Pop
      bd = _RecursiveFindBD(disk)
1526 e739bd57 Iustin Pop
      if bd is None:
1527 2cc6781a Iustin Pop
        _Fail("Can't find device %s while removing children", disk)
1528 e739bd57 Iustin Pop
      else:
1529 e739bd57 Iustin Pop
        devs.append(bd.dev_path)
1530 e739bd57 Iustin Pop
    else:
1531 e51db2a6 Iustin Pop
      if not utils.IsNormAbsPath(rpath):
1532 e51db2a6 Iustin Pop
        _Fail("Strange path returned from StaticDevPath: '%s'", rpath)
1533 e739bd57 Iustin Pop
      devs.append(rpath)
1534 e739bd57 Iustin Pop
  parent_bdev.RemoveChildren(devs)
1535 a8083063 Iustin Pop
1536 a8083063 Iustin Pop
1537 821d1bd1 Iustin Pop
def BlockdevGetmirrorstatus(disks):
1538 a8083063 Iustin Pop
  """Get the mirroring status of a list of devices.
1539 a8083063 Iustin Pop

1540 10c2650b Iustin Pop
  @type disks: list of L{objects.Disk}
1541 10c2650b Iustin Pop
  @param disks: the list of disks which we should query
1542 10c2650b Iustin Pop
  @rtype: disk
1543 c6a9dffa Michael Hanselmann
  @return: List of L{objects.BlockDevStatus}, one for each disk
1544 10c2650b Iustin Pop
  @raise errors.BlockDeviceError: if any of the disks cannot be
1545 10c2650b Iustin Pop
      found
1546 a8083063 Iustin Pop

1547 a8083063 Iustin Pop
  """
1548 a8083063 Iustin Pop
  stats = []
1549 a8083063 Iustin Pop
  for dsk in disks:
1550 a8083063 Iustin Pop
    rbd = _RecursiveFindBD(dsk)
1551 a8083063 Iustin Pop
    if rbd is None:
1552 3efa9051 Iustin Pop
      _Fail("Can't find device %s", dsk)
1553 96acbc09 Michael Hanselmann
1554 36145b12 Michael Hanselmann
    stats.append(rbd.CombinedSyncStatus())
1555 96acbc09 Michael Hanselmann
1556 c26a6bd2 Iustin Pop
  return stats
1557 a8083063 Iustin Pop
1558 a8083063 Iustin Pop
1559 c6a9dffa Michael Hanselmann
def BlockdevGetmirrorstatusMulti(disks):
1560 c6a9dffa Michael Hanselmann
  """Get the mirroring status of a list of devices.
1561 c6a9dffa Michael Hanselmann

1562 c6a9dffa Michael Hanselmann
  @type disks: list of L{objects.Disk}
1563 c6a9dffa Michael Hanselmann
  @param disks: the list of disks which we should query
1564 c6a9dffa Michael Hanselmann
  @rtype: disk
1565 c6a9dffa Michael Hanselmann
  @return: List of tuples, (bool, status), one for each disk; bool denotes
1566 c6a9dffa Michael Hanselmann
    success/failure, status is L{objects.BlockDevStatus} on success, string
1567 c6a9dffa Michael Hanselmann
    otherwise
1568 c6a9dffa Michael Hanselmann

1569 c6a9dffa Michael Hanselmann
  """
1570 c6a9dffa Michael Hanselmann
  result = []
1571 c6a9dffa Michael Hanselmann
  for disk in disks:
1572 c6a9dffa Michael Hanselmann
    try:
1573 c6a9dffa Michael Hanselmann
      rbd = _RecursiveFindBD(disk)
1574 c6a9dffa Michael Hanselmann
      if rbd is None:
1575 c6a9dffa Michael Hanselmann
        result.append((False, "Can't find device %s" % disk))
1576 c6a9dffa Michael Hanselmann
        continue
1577 c6a9dffa Michael Hanselmann
1578 c6a9dffa Michael Hanselmann
      status = rbd.CombinedSyncStatus()
1579 c6a9dffa Michael Hanselmann
    except errors.BlockDeviceError, err:
1580 c6a9dffa Michael Hanselmann
      logging.exception("Error while getting disk status")
1581 c6a9dffa Michael Hanselmann
      result.append((False, str(err)))
1582 c6a9dffa Michael Hanselmann
    else:
1583 c6a9dffa Michael Hanselmann
      result.append((True, status))
1584 c6a9dffa Michael Hanselmann
1585 c6a9dffa Michael Hanselmann
  assert len(disks) == len(result)
1586 c6a9dffa Michael Hanselmann
1587 c6a9dffa Michael Hanselmann
  return result
1588 c6a9dffa Michael Hanselmann
1589 c6a9dffa Michael Hanselmann
1590 bca2e7f4 Iustin Pop
def _RecursiveFindBD(disk):
1591 a8083063 Iustin Pop
  """Check if a device is activated.
1592 a8083063 Iustin Pop

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

1595 10c2650b Iustin Pop
  @type disk: L{objects.Disk}
1596 10c2650b Iustin Pop
  @param disk: the disk object we need to find
1597 a8083063 Iustin Pop

1598 10c2650b Iustin Pop
  @return: None if the device can't be found,
1599 10c2650b Iustin Pop
      otherwise the device instance
1600 a8083063 Iustin Pop

1601 a8083063 Iustin Pop
  """
1602 a8083063 Iustin Pop
  children = []
1603 a8083063 Iustin Pop
  if disk.children:
1604 a8083063 Iustin Pop
    for chdisk in disk.children:
1605 a8083063 Iustin Pop
      children.append(_RecursiveFindBD(chdisk))
1606 a8083063 Iustin Pop
1607 464f8daf Iustin Pop
  return bdev.FindDevice(disk.dev_type, disk.physical_id, children, disk.size)
1608 a8083063 Iustin Pop
1609 a8083063 Iustin Pop
1610 f2e07bb4 Michael Hanselmann
def _OpenRealBD(disk):
1611 f2e07bb4 Michael Hanselmann
  """Opens the underlying block device of a disk.
1612 f2e07bb4 Michael Hanselmann

1613 f2e07bb4 Michael Hanselmann
  @type disk: L{objects.Disk}
1614 f2e07bb4 Michael Hanselmann
  @param disk: the disk object we want to open
1615 f2e07bb4 Michael Hanselmann

1616 f2e07bb4 Michael Hanselmann
  """
1617 f2e07bb4 Michael Hanselmann
  real_disk = _RecursiveFindBD(disk)
1618 f2e07bb4 Michael Hanselmann
  if real_disk is None:
1619 f2e07bb4 Michael Hanselmann
    _Fail("Block device '%s' is not set up", disk)
1620 f2e07bb4 Michael Hanselmann
1621 f2e07bb4 Michael Hanselmann
  real_disk.Open()
1622 f2e07bb4 Michael Hanselmann
1623 f2e07bb4 Michael Hanselmann
  return real_disk
1624 f2e07bb4 Michael Hanselmann
1625 f2e07bb4 Michael Hanselmann
1626 821d1bd1 Iustin Pop
def BlockdevFind(disk):
1627 a8083063 Iustin Pop
  """Check if a device is activated.
1628 a8083063 Iustin Pop

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

1631 10c2650b Iustin Pop
  @type disk: L{objects.Disk}
1632 10c2650b Iustin Pop
  @param disk: the disk to find
1633 96acbc09 Michael Hanselmann
  @rtype: None or objects.BlockDevStatus
1634 96acbc09 Michael Hanselmann
  @return: None if the disk cannot be found, otherwise a the current
1635 96acbc09 Michael Hanselmann
           information
1636 a8083063 Iustin Pop

1637 a8083063 Iustin Pop
  """
1638 23829f6f Iustin Pop
  try:
1639 23829f6f Iustin Pop
    rbd = _RecursiveFindBD(disk)
1640 23829f6f Iustin Pop
  except errors.BlockDeviceError, err:
1641 2cc6781a Iustin Pop
    _Fail("Failed to find device: %s", err, exc=True)
1642 96acbc09 Michael Hanselmann
1643 a8083063 Iustin Pop
  if rbd is None:
1644 c26a6bd2 Iustin Pop
    return None
1645 96acbc09 Michael Hanselmann
1646 96acbc09 Michael Hanselmann
  return rbd.GetSyncStatus()
1647 a8083063 Iustin Pop
1648 a8083063 Iustin Pop
1649 968a7623 Iustin Pop
def BlockdevGetsize(disks):
1650 968a7623 Iustin Pop
  """Computes the size of the given disks.
1651 968a7623 Iustin Pop

1652 968a7623 Iustin Pop
  If a disk is not found, returns None instead.
1653 968a7623 Iustin Pop

1654 968a7623 Iustin Pop
  @type disks: list of L{objects.Disk}
1655 968a7623 Iustin Pop
  @param disks: the list of disk to compute the size for
1656 968a7623 Iustin Pop
  @rtype: list
1657 968a7623 Iustin Pop
  @return: list with elements None if the disk cannot be found,
1658 968a7623 Iustin Pop
      otherwise the size
1659 968a7623 Iustin Pop

1660 968a7623 Iustin Pop
  """
1661 968a7623 Iustin Pop
  result = []
1662 968a7623 Iustin Pop
  for cf in disks:
1663 968a7623 Iustin Pop
    try:
1664 968a7623 Iustin Pop
      rbd = _RecursiveFindBD(cf)
1665 1122eb25 Iustin Pop
    except errors.BlockDeviceError:
1666 968a7623 Iustin Pop
      result.append(None)
1667 968a7623 Iustin Pop
      continue
1668 968a7623 Iustin Pop
    if rbd is None:
1669 968a7623 Iustin Pop
      result.append(None)
1670 968a7623 Iustin Pop
    else:
1671 968a7623 Iustin Pop
      result.append(rbd.GetActualSize())
1672 968a7623 Iustin Pop
  return result
1673 968a7623 Iustin Pop
1674 968a7623 Iustin Pop
1675 858f3d18 Iustin Pop
def BlockdevExport(disk, dest_node, dest_path, cluster_name):
1676 858f3d18 Iustin Pop
  """Export a block device to a remote node.
1677 858f3d18 Iustin Pop

1678 858f3d18 Iustin Pop
  @type disk: L{objects.Disk}
1679 858f3d18 Iustin Pop
  @param disk: the description of the disk to export
1680 858f3d18 Iustin Pop
  @type dest_node: str
1681 858f3d18 Iustin Pop
  @param dest_node: the destination node to export to
1682 858f3d18 Iustin Pop
  @type dest_path: str
1683 858f3d18 Iustin Pop
  @param dest_path: the destination path on the target node
1684 858f3d18 Iustin Pop
  @type cluster_name: str
1685 858f3d18 Iustin Pop
  @param cluster_name: the cluster name, needed for SSH hostalias
1686 858f3d18 Iustin Pop
  @rtype: None
1687 858f3d18 Iustin Pop

1688 858f3d18 Iustin Pop
  """
1689 f2e07bb4 Michael Hanselmann
  real_disk = _OpenRealBD(disk)
1690 858f3d18 Iustin Pop
1691 858f3d18 Iustin Pop
  # the block size on the read dd is 1MiB to match our units
1692 858f3d18 Iustin Pop
  expcmd = utils.BuildShellCmd("set -e; set -o pipefail; "
1693 858f3d18 Iustin Pop
                               "dd if=%s bs=1048576 count=%s",
1694 858f3d18 Iustin Pop
                               real_disk.dev_path, str(disk.size))
1695 858f3d18 Iustin Pop
1696 858f3d18 Iustin Pop
  # we set here a smaller block size as, due to ssh buffering, more
1697 858f3d18 Iustin Pop
  # than 64-128k will mostly ignored; we use nocreat to fail if the
1698 858f3d18 Iustin Pop
  # device is not already there or we pass a wrong path; we use
1699 858f3d18 Iustin Pop
  # notrunc to no attempt truncate on an LV device; we use oflag=dsync
1700 858f3d18 Iustin Pop
  # to not buffer too much memory; this means that at best, we flush
1701 858f3d18 Iustin Pop
  # every 64k, which will not be very fast
1702 858f3d18 Iustin Pop
  destcmd = utils.BuildShellCmd("dd of=%s conv=nocreat,notrunc bs=65536"
1703 858f3d18 Iustin Pop
                                " oflag=dsync", dest_path)
1704 858f3d18 Iustin Pop
1705 858f3d18 Iustin Pop
  remotecmd = _GetSshRunner(cluster_name).BuildCmd(dest_node,
1706 858f3d18 Iustin Pop
                                                   constants.GANETI_RUNAS,
1707 858f3d18 Iustin Pop
                                                   destcmd)
1708 858f3d18 Iustin Pop
1709 858f3d18 Iustin Pop
  # all commands have been checked, so we're safe to combine them
1710 858f3d18 Iustin Pop
  command = '|'.join([expcmd, utils.ShellQuoteArgs(remotecmd)])
1711 858f3d18 Iustin Pop
1712 858f3d18 Iustin Pop
  result = utils.RunCmd(["bash", "-c", command])
1713 858f3d18 Iustin Pop
1714 858f3d18 Iustin Pop
  if result.failed:
1715 858f3d18 Iustin Pop
    _Fail("Disk copy command '%s' returned error: %s"
1716 858f3d18 Iustin Pop
          " output: %s", command, result.fail_reason, result.output)
1717 858f3d18 Iustin Pop
1718 858f3d18 Iustin Pop
1719 a8083063 Iustin Pop
def UploadFile(file_name, data, mode, uid, gid, atime, mtime):
1720 a8083063 Iustin Pop
  """Write a file to the filesystem.
1721 a8083063 Iustin Pop

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

1725 10c2650b Iustin Pop
  @type file_name: str
1726 10c2650b Iustin Pop
  @param file_name: the target file name
1727 10c2650b Iustin Pop
  @type data: str
1728 10c2650b Iustin Pop
  @param data: the new contents of the file
1729 10c2650b Iustin Pop
  @type mode: int
1730 10c2650b Iustin Pop
  @param mode: the mode to give the file (can be None)
1731 10c2650b Iustin Pop
  @type uid: int
1732 10c2650b Iustin Pop
  @param uid: the owner of the file (can be -1 for default)
1733 10c2650b Iustin Pop
  @type gid: int
1734 10c2650b Iustin Pop
  @param gid: the group of the file (can be -1 for default)
1735 10c2650b Iustin Pop
  @type atime: float
1736 10c2650b Iustin Pop
  @param atime: the atime to set on the file (can be None)
1737 10c2650b Iustin Pop
  @type mtime: float
1738 10c2650b Iustin Pop
  @param mtime: the mtime to set on the file (can be None)
1739 c26a6bd2 Iustin Pop
  @rtype: None
1740 10c2650b Iustin Pop

1741 a8083063 Iustin Pop
  """
1742 a8083063 Iustin Pop
  if not os.path.isabs(file_name):
1743 2cc6781a Iustin Pop
    _Fail("Filename passed to UploadFile is not absolute: '%s'", file_name)
1744 a8083063 Iustin Pop
1745 360b0dc2 Iustin Pop
  if file_name not in _ALLOWED_UPLOAD_FILES:
1746 2cc6781a Iustin Pop
    _Fail("Filename passed to UploadFile not in allowed upload targets: '%s'",
1747 2cc6781a Iustin Pop
          file_name)
1748 a8083063 Iustin Pop
1749 12bce260 Michael Hanselmann
  raw_data = _Decompress(data)
1750 12bce260 Michael Hanselmann
1751 8f065ae2 Iustin Pop
  utils.SafeWriteFile(file_name, None,
1752 8f065ae2 Iustin Pop
                      data=raw_data, mode=mode, uid=uid, gid=gid,
1753 8f065ae2 Iustin Pop
                      atime=atime, mtime=mtime)
1754 a8083063 Iustin Pop
1755 386b57af Iustin Pop
1756 03d1dba2 Michael Hanselmann
def WriteSsconfFiles(values):
1757 89b14f05 Iustin Pop
  """Update all ssconf files.
1758 89b14f05 Iustin Pop

1759 89b14f05 Iustin Pop
  Wrapper around the SimpleStore.WriteFiles.
1760 89b14f05 Iustin Pop

1761 89b14f05 Iustin Pop
  """
1762 89b14f05 Iustin Pop
  ssconf.SimpleStore().WriteFiles(values)
1763 6ddc95ec Michael Hanselmann
1764 6ddc95ec Michael Hanselmann
1765 a8083063 Iustin Pop
def _ErrnoOrStr(err):
1766 a8083063 Iustin Pop
  """Format an EnvironmentError exception.
1767 a8083063 Iustin Pop

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

1772 10c2650b Iustin Pop
  @type err: L{EnvironmentError}
1773 10c2650b Iustin Pop
  @param err: the exception to format
1774 a8083063 Iustin Pop

1775 a8083063 Iustin Pop
  """
1776 a8083063 Iustin Pop
  if hasattr(err, 'errno'):
1777 a8083063 Iustin Pop
    detail = errno.errorcode[err.errno]
1778 a8083063 Iustin Pop
  else:
1779 a8083063 Iustin Pop
    detail = str(err)
1780 a8083063 Iustin Pop
  return detail
1781 a8083063 Iustin Pop
1782 5d0fe286 Iustin Pop
1783 c19f9810 Iustin Pop
def _OSOndiskAPIVersion(os_dir):
1784 2f8598a5 Alexander Schreiber
  """Compute and return the API version of a given OS.
1785 a8083063 Iustin Pop

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

1789 10c2650b Iustin Pop
  @type os_dir: str
1790 c19f9810 Iustin Pop
  @param os_dir: the directory in which we should look for the OS
1791 8e70b181 Iustin Pop
  @rtype: tuple
1792 8e70b181 Iustin Pop
  @return: tuple (status, data) with status denoting the validity and
1793 8e70b181 Iustin Pop
      data holding either the vaid versions or an error message
1794 a8083063 Iustin Pop

1795 a8083063 Iustin Pop
  """
1796 e02b9114 Iustin Pop
  api_file = utils.PathJoin(os_dir, constants.OS_API_FILE)
1797 a8083063 Iustin Pop
1798 a8083063 Iustin Pop
  try:
1799 a8083063 Iustin Pop
    st = os.stat(api_file)
1800 a8083063 Iustin Pop
  except EnvironmentError, err:
1801 b6b45e0d Guido Trotter
    return False, ("Required file '%s' not found under path %s: %s" %
1802 b6b45e0d Guido Trotter
                   (constants.OS_API_FILE, os_dir, _ErrnoOrStr(err)))
1803 a8083063 Iustin Pop
1804 a8083063 Iustin Pop
  if not stat.S_ISREG(stat.S_IFMT(st.st_mode)):
1805 b6b45e0d Guido Trotter
    return False, ("File '%s' in %s is not a regular file" %
1806 b6b45e0d Guido Trotter
                   (constants.OS_API_FILE, os_dir))
1807 a8083063 Iustin Pop
1808 a8083063 Iustin Pop
  try:
1809 3374afa9 Guido Trotter
    api_versions = utils.ReadFile(api_file).splitlines()
1810 a8083063 Iustin Pop
  except EnvironmentError, err:
1811 255dcebd Iustin Pop
    return False, ("Error while reading the API version file at %s: %s" %
1812 255dcebd Iustin Pop
                   (api_file, _ErrnoOrStr(err)))
1813 a8083063 Iustin Pop
1814 a8083063 Iustin Pop
  try:
1815 63b9b186 Guido Trotter
    api_versions = [int(version.strip()) for version in api_versions]
1816 a8083063 Iustin Pop
  except (TypeError, ValueError), err:
1817 255dcebd Iustin Pop
    return False, ("API version(s) can't be converted to integer: %s" %
1818 255dcebd Iustin Pop
                   str(err))
1819 a8083063 Iustin Pop
1820 255dcebd Iustin Pop
  return True, api_versions
1821 a8083063 Iustin Pop
1822 386b57af Iustin Pop
1823 7c3d51d4 Guido Trotter
def DiagnoseOS(top_dirs=None):
1824 a8083063 Iustin Pop
  """Compute the validity for all OSes.
1825 a8083063 Iustin Pop

1826 10c2650b Iustin Pop
  @type top_dirs: list
1827 10c2650b Iustin Pop
  @param top_dirs: the list of directories in which to
1828 10c2650b Iustin Pop
      search (if not given defaults to
1829 10c2650b Iustin Pop
      L{constants.OS_SEARCH_PATH})
1830 10c2650b Iustin Pop
  @rtype: list of L{objects.OS}
1831 bad78e66 Iustin Pop
  @return: a list of tuples (name, path, status, diagnose, variants,
1832 bad78e66 Iustin Pop
      parameters, api_version) for all (potential) OSes under all
1833 bad78e66 Iustin Pop
      search paths, where:
1834 255dcebd Iustin Pop
          - name is the (potential) OS name
1835 255dcebd Iustin Pop
          - path is the full path to the OS
1836 255dcebd Iustin Pop
          - status True/False is the validity of the OS
1837 255dcebd Iustin Pop
          - diagnose is the error message for an invalid OS, otherwise empty
1838 ba00557a Guido Trotter
          - variants is a list of supported OS variants, if any
1839 c7d04a6b Iustin Pop
          - parameters is a list of (name, help) parameters, if any
1840 bad78e66 Iustin Pop
          - api_version is a list of support OS API versions
1841 a8083063 Iustin Pop

1842 a8083063 Iustin Pop
  """
1843 7c3d51d4 Guido Trotter
  if top_dirs is None:
1844 7c3d51d4 Guido Trotter
    top_dirs = constants.OS_SEARCH_PATH
1845 a8083063 Iustin Pop
1846 a8083063 Iustin Pop
  result = []
1847 65fe4693 Iustin Pop
  for dir_name in top_dirs:
1848 65fe4693 Iustin Pop
    if os.path.isdir(dir_name):
1849 7c3d51d4 Guido Trotter
      try:
1850 65fe4693 Iustin Pop
        f_names = utils.ListVisibleFiles(dir_name)
1851 7c3d51d4 Guido Trotter
      except EnvironmentError, err:
1852 29921401 Iustin Pop
        logging.exception("Can't list the OS directory %s: %s", dir_name, err)
1853 7c3d51d4 Guido Trotter
        break
1854 7c3d51d4 Guido Trotter
      for name in f_names:
1855 e02b9114 Iustin Pop
        os_path = utils.PathJoin(dir_name, name)
1856 255dcebd Iustin Pop
        status, os_inst = _TryOSFromDisk(name, base_dir=dir_name)
1857 255dcebd Iustin Pop
        if status:
1858 255dcebd Iustin Pop
          diagnose = ""
1859 ba00557a Guido Trotter
          variants = os_inst.supported_variants
1860 c7d04a6b Iustin Pop
          parameters = os_inst.supported_parameters
1861 bad78e66 Iustin Pop
          api_versions = os_inst.api_versions
1862 255dcebd Iustin Pop
        else:
1863 255dcebd Iustin Pop
          diagnose = os_inst
1864 bad78e66 Iustin Pop
          variants = parameters = api_versions = []
1865 bad78e66 Iustin Pop
        result.append((name, os_path, status, diagnose, variants,
1866 bad78e66 Iustin Pop
                       parameters, api_versions))
1867 a8083063 Iustin Pop
1868 c26a6bd2 Iustin Pop
  return result
1869 a8083063 Iustin Pop
1870 a8083063 Iustin Pop
1871 255dcebd Iustin Pop
def _TryOSFromDisk(name, base_dir=None):
1872 a8083063 Iustin Pop
  """Create an OS instance from disk.
1873 a8083063 Iustin Pop

1874 a8083063 Iustin Pop
  This function will return an OS instance if the given name is a
1875 8e70b181 Iustin Pop
  valid OS name.
1876 a8083063 Iustin Pop

1877 8ee4dc80 Guido Trotter
  @type base_dir: string
1878 8ee4dc80 Guido Trotter
  @keyword base_dir: Base directory containing OS installations.
1879 8ee4dc80 Guido Trotter
                     Defaults to a search in all the OS_SEARCH_PATH dirs.
1880 255dcebd Iustin Pop
  @rtype: tuple
1881 255dcebd Iustin Pop
  @return: success and either the OS instance if we find a valid one,
1882 255dcebd Iustin Pop
      or error message
1883 7c3d51d4 Guido Trotter

1884 a8083063 Iustin Pop
  """
1885 56bcd3f4 Guido Trotter
  if base_dir is None:
1886 57c177af Iustin Pop
    os_dir = utils.FindFile(name, constants.OS_SEARCH_PATH, os.path.isdir)
1887 c34c0cfd Iustin Pop
  else:
1888 f95c81bf Iustin Pop
    os_dir = utils.FindFile(name, [base_dir], os.path.isdir)
1889 f95c81bf Iustin Pop
1890 f95c81bf Iustin Pop
  if os_dir is None:
1891 5c0433d6 Iustin Pop
    return False, "Directory for OS %s not found in search path" % name
1892 a8083063 Iustin Pop
1893 c19f9810 Iustin Pop
  status, api_versions = _OSOndiskAPIVersion(os_dir)
1894 255dcebd Iustin Pop
  if not status:
1895 255dcebd Iustin Pop
    # push the error up
1896 255dcebd Iustin Pop
    return status, api_versions
1897 a8083063 Iustin Pop
1898 d1a7d66f Guido Trotter
  if not constants.OS_API_VERSIONS.intersection(api_versions):
1899 255dcebd Iustin Pop
    return False, ("API version mismatch for path '%s': found %s, want %s." %
1900 d1a7d66f Guido Trotter
                   (os_dir, api_versions, constants.OS_API_VERSIONS))
1901 a8083063 Iustin Pop
1902 41ba4061 Guido Trotter
  # OS Files dictionary, we will populate it with the absolute path names
1903 41ba4061 Guido Trotter
  os_files = dict.fromkeys(constants.OS_SCRIPTS)
1904 a8083063 Iustin Pop
1905 95075fba Guido Trotter
  if max(api_versions) >= constants.OS_API_V15:
1906 95075fba Guido Trotter
    os_files[constants.OS_VARIANTS_FILE] = ''
1907 95075fba Guido Trotter
1908 c7d04a6b Iustin Pop
  if max(api_versions) >= constants.OS_API_V20:
1909 c7d04a6b Iustin Pop
    os_files[constants.OS_PARAMETERS_FILE] = ''
1910 c7d04a6b Iustin Pop
  else:
1911 c7d04a6b Iustin Pop
    del os_files[constants.OS_SCRIPT_VERIFY]
1912 c7d04a6b Iustin Pop
1913 ea79fc15 Michael Hanselmann
  for filename in os_files:
1914 e02b9114 Iustin Pop
    os_files[filename] = utils.PathJoin(os_dir, filename)
1915 a8083063 Iustin Pop
1916 a8083063 Iustin Pop
    try:
1917 ea79fc15 Michael Hanselmann
      st = os.stat(os_files[filename])
1918 a8083063 Iustin Pop
    except EnvironmentError, err:
1919 41ba4061 Guido Trotter
      return False, ("File '%s' under path '%s' is missing (%s)" %
1920 ea79fc15 Michael Hanselmann
                     (filename, os_dir, _ErrnoOrStr(err)))
1921 a8083063 Iustin Pop
1922 a8083063 Iustin Pop
    if not stat.S_ISREG(stat.S_IFMT(st.st_mode)):
1923 41ba4061 Guido Trotter
      return False, ("File '%s' under path '%s' is not a regular file" %
1924 ea79fc15 Michael Hanselmann
                     (filename, os_dir))
1925 255dcebd Iustin Pop
1926 ea79fc15 Michael Hanselmann
    if filename in constants.OS_SCRIPTS:
1927 0757c107 Guido Trotter
      if stat.S_IMODE(st.st_mode) & stat.S_IXUSR != stat.S_IXUSR:
1928 0757c107 Guido Trotter
        return False, ("File '%s' under path '%s' is not executable" %
1929 ea79fc15 Michael Hanselmann
                       (filename, os_dir))
1930 0757c107 Guido Trotter
1931 845da3e8 Iustin Pop
  variants = []
1932 95075fba Guido Trotter
  if constants.OS_VARIANTS_FILE in os_files:
1933 95075fba Guido Trotter
    variants_file = os_files[constants.OS_VARIANTS_FILE]
1934 95075fba Guido Trotter
    try:
1935 95075fba Guido Trotter
      variants = utils.ReadFile(variants_file).splitlines()
1936 95075fba Guido Trotter
    except EnvironmentError, err:
1937 95075fba Guido Trotter
      return False, ("Error while reading the OS variants file at %s: %s" %
1938 95075fba Guido Trotter
                     (variants_file, _ErrnoOrStr(err)))
1939 95075fba Guido Trotter
    if not variants:
1940 95075fba Guido Trotter
      return False, ("No supported os variant found")
1941 0757c107 Guido Trotter
1942 c7d04a6b Iustin Pop
  parameters = []
1943 c7d04a6b Iustin Pop
  if constants.OS_PARAMETERS_FILE in os_files:
1944 c7d04a6b Iustin Pop
    parameters_file = os_files[constants.OS_PARAMETERS_FILE]
1945 c7d04a6b Iustin Pop
    try:
1946 c7d04a6b Iustin Pop
      parameters = utils.ReadFile(parameters_file).splitlines()
1947 c7d04a6b Iustin Pop
    except EnvironmentError, err:
1948 c7d04a6b Iustin Pop
      return False, ("Error while reading the OS parameters file at %s: %s" %
1949 c7d04a6b Iustin Pop
                     (parameters_file, _ErrnoOrStr(err)))
1950 c7d04a6b Iustin Pop
    parameters = [v.split(None, 1) for v in parameters]
1951 c7d04a6b Iustin Pop
1952 8e70b181 Iustin Pop
  os_obj = objects.OS(name=name, path=os_dir,
1953 41ba4061 Guido Trotter
                      create_script=os_files[constants.OS_SCRIPT_CREATE],
1954 41ba4061 Guido Trotter
                      export_script=os_files[constants.OS_SCRIPT_EXPORT],
1955 41ba4061 Guido Trotter
                      import_script=os_files[constants.OS_SCRIPT_IMPORT],
1956 41ba4061 Guido Trotter
                      rename_script=os_files[constants.OS_SCRIPT_RENAME],
1957 40684c3a Iustin Pop
                      verify_script=os_files.get(constants.OS_SCRIPT_VERIFY,
1958 40684c3a Iustin Pop
                                                 None),
1959 95075fba Guido Trotter
                      supported_variants=variants,
1960 c7d04a6b Iustin Pop
                      supported_parameters=parameters,
1961 255dcebd Iustin Pop
                      api_versions=api_versions)
1962 255dcebd Iustin Pop
  return True, os_obj
1963 255dcebd Iustin Pop
1964 255dcebd Iustin Pop
1965 255dcebd Iustin Pop
def OSFromDisk(name, base_dir=None):
1966 255dcebd Iustin Pop
  """Create an OS instance from disk.
1967 255dcebd Iustin Pop

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

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

1975 255dcebd Iustin Pop
  @type base_dir: string
1976 255dcebd Iustin Pop
  @keyword base_dir: Base directory containing OS installations.
1977 255dcebd Iustin Pop
                     Defaults to a search in all the OS_SEARCH_PATH dirs.
1978 255dcebd Iustin Pop
  @rtype: L{objects.OS}
1979 255dcebd Iustin Pop
  @return: the OS instance if we find a valid one
1980 255dcebd Iustin Pop
  @raise RPCFail: if we don't find a valid OS
1981 255dcebd Iustin Pop

1982 255dcebd Iustin Pop
  """
1983 870dc44c Iustin Pop
  name_only = objects.OS.GetName(name)
1984 6ee7102a Guido Trotter
  status, payload = _TryOSFromDisk(name_only, base_dir)
1985 255dcebd Iustin Pop
1986 255dcebd Iustin Pop
  if not status:
1987 255dcebd Iustin Pop
    _Fail(payload)
1988 a8083063 Iustin Pop
1989 255dcebd Iustin Pop
  return payload
1990 a8083063 Iustin Pop
1991 a8083063 Iustin Pop
1992 a025e535 Vitaly Kuznetsov
def OSCoreEnv(os_name, inst_os, os_params, debug=0):
1993 efaa9b06 Iustin Pop
  """Calculate the basic environment for an os script.
1994 2266edb2 Guido Trotter

1995 a025e535 Vitaly Kuznetsov
  @type os_name: str
1996 a025e535 Vitaly Kuznetsov
  @param os_name: full operating system name (including variant)
1997 099c52ad Iustin Pop
  @type inst_os: L{objects.OS}
1998 099c52ad Iustin Pop
  @param inst_os: operating system for which the environment is being built
1999 1bdcbbab Iustin Pop
  @type os_params: dict
2000 1bdcbbab Iustin Pop
  @param os_params: the OS parameters
2001 2266edb2 Guido Trotter
  @type debug: integer
2002 10c2650b Iustin Pop
  @param debug: debug level (0 or 1, for OS Api 10)
2003 2266edb2 Guido Trotter
  @rtype: dict
2004 2266edb2 Guido Trotter
  @return: dict of environment variables
2005 10c2650b Iustin Pop
  @raise errors.BlockDeviceError: if the block device
2006 10c2650b Iustin Pop
      cannot be found
2007 2266edb2 Guido Trotter

2008 2266edb2 Guido Trotter
  """
2009 2266edb2 Guido Trotter
  result = {}
2010 099c52ad Iustin Pop
  api_version = \
2011 099c52ad Iustin Pop
    max(constants.OS_API_VERSIONS.intersection(inst_os.api_versions))
2012 d1a7d66f Guido Trotter
  result['OS_API_VERSION'] = '%d' % api_version
2013 efaa9b06 Iustin Pop
  result['OS_NAME'] = inst_os.name
2014 2266edb2 Guido Trotter
  result['DEBUG_LEVEL'] = '%d' % debug
2015 efaa9b06 Iustin Pop
2016 efaa9b06 Iustin Pop
  # OS variants
2017 f11280b5 Guido Trotter
  if api_version >= constants.OS_API_V15:
2018 870dc44c Iustin Pop
    variant = objects.OS.GetVariant(os_name)
2019 870dc44c Iustin Pop
    if not variant:
2020 099c52ad Iustin Pop
      variant = inst_os.supported_variants[0]
2021 f11280b5 Guido Trotter
    result['OS_VARIANT'] = variant
2022 efaa9b06 Iustin Pop
2023 1bdcbbab Iustin Pop
  # OS params
2024 1bdcbbab Iustin Pop
  for pname, pvalue in os_params.items():
2025 1bdcbbab Iustin Pop
    result['OSP_%s' % pname.upper()] = pvalue
2026 1bdcbbab Iustin Pop
2027 efaa9b06 Iustin Pop
  return result
2028 efaa9b06 Iustin Pop
2029 efaa9b06 Iustin Pop
2030 efaa9b06 Iustin Pop
def OSEnvironment(instance, inst_os, debug=0):
2031 efaa9b06 Iustin Pop
  """Calculate the environment for an os script.
2032 efaa9b06 Iustin Pop

2033 efaa9b06 Iustin Pop
  @type instance: L{objects.Instance}
2034 efaa9b06 Iustin Pop
  @param instance: target instance for the os script run
2035 efaa9b06 Iustin Pop
  @type inst_os: L{objects.OS}
2036 efaa9b06 Iustin Pop
  @param inst_os: operating system for which the environment is being built
2037 efaa9b06 Iustin Pop
  @type debug: integer
2038 efaa9b06 Iustin Pop
  @param debug: debug level (0 or 1, for OS Api 10)
2039 efaa9b06 Iustin Pop
  @rtype: dict
2040 efaa9b06 Iustin Pop
  @return: dict of environment variables
2041 efaa9b06 Iustin Pop
  @raise errors.BlockDeviceError: if the block device
2042 efaa9b06 Iustin Pop
      cannot be found
2043 efaa9b06 Iustin Pop

2044 efaa9b06 Iustin Pop
  """
2045 a025e535 Vitaly Kuznetsov
  result = OSCoreEnv(instance.os, inst_os, instance.osparams, debug=debug)
2046 efaa9b06 Iustin Pop
2047 f2165b8a Iustin Pop
  for attr in ["name", "os", "uuid", "ctime", "mtime"]:
2048 f2165b8a Iustin Pop
    result["INSTANCE_%s" % attr.upper()] = str(getattr(instance, attr))
2049 f2165b8a Iustin Pop
2050 efaa9b06 Iustin Pop
  result['HYPERVISOR'] = instance.hypervisor
2051 efaa9b06 Iustin Pop
  result['DISK_COUNT'] = '%d' % len(instance.disks)
2052 efaa9b06 Iustin Pop
  result['NIC_COUNT'] = '%d' % len(instance.nics)
2053 efaa9b06 Iustin Pop
2054 efaa9b06 Iustin Pop
  # Disks
2055 2266edb2 Guido Trotter
  for idx, disk in enumerate(instance.disks):
2056 f2e07bb4 Michael Hanselmann
    real_disk = _OpenRealBD(disk)
2057 2266edb2 Guido Trotter
    result['DISK_%d_PATH' % idx] = real_disk.dev_path
2058 15552312 Iustin Pop
    result['DISK_%d_ACCESS' % idx] = disk.mode
2059 2266edb2 Guido Trotter
    if constants.HV_DISK_TYPE in instance.hvparams:
2060 2266edb2 Guido Trotter
      result['DISK_%d_FRONTEND_TYPE' % idx] = \
2061 2266edb2 Guido Trotter
        instance.hvparams[constants.HV_DISK_TYPE]
2062 2266edb2 Guido Trotter
    if disk.dev_type in constants.LDS_BLOCK:
2063 2266edb2 Guido Trotter
      result['DISK_%d_BACKEND_TYPE' % idx] = 'block'
2064 2266edb2 Guido Trotter
    elif disk.dev_type == constants.LD_FILE:
2065 2266edb2 Guido Trotter
      result['DISK_%d_BACKEND_TYPE' % idx] = \
2066 2266edb2 Guido Trotter
        'file:%s' % disk.physical_id[0]
2067 efaa9b06 Iustin Pop
2068 efaa9b06 Iustin Pop
  # NICs
2069 2266edb2 Guido Trotter
  for idx, nic in enumerate(instance.nics):
2070 2266edb2 Guido Trotter
    result['NIC_%d_MAC' % idx] = nic.mac
2071 2266edb2 Guido Trotter
    if nic.ip:
2072 2266edb2 Guido Trotter
      result['NIC_%d_IP' % idx] = nic.ip
2073 1ba9227f Guido Trotter
    result['NIC_%d_MODE' % idx] = nic.nicparams[constants.NIC_MODE]
2074 1ba9227f Guido Trotter
    if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
2075 1ba9227f Guido Trotter
      result['NIC_%d_BRIDGE' % idx] = nic.nicparams[constants.NIC_LINK]
2076 1ba9227f Guido Trotter
    if nic.nicparams[constants.NIC_LINK]:
2077 1ba9227f Guido Trotter
      result['NIC_%d_LINK' % idx] = nic.nicparams[constants.NIC_LINK]
2078 2266edb2 Guido Trotter
    if constants.HV_NIC_TYPE in instance.hvparams:
2079 2266edb2 Guido Trotter
      result['NIC_%d_FRONTEND_TYPE' % idx] = \
2080 2266edb2 Guido Trotter
        instance.hvparams[constants.HV_NIC_TYPE]
2081 2266edb2 Guido Trotter
2082 efaa9b06 Iustin Pop
  # HV/BE params
2083 67fc3042 Iustin Pop
  for source, kind in [(instance.beparams, "BE"), (instance.hvparams, "HV")]:
2084 67fc3042 Iustin Pop
    for key, value in source.items():
2085 030b218a Iustin Pop
      result["INSTANCE_%s_%s" % (kind, key)] = str(value)
2086 67fc3042 Iustin Pop
2087 2266edb2 Guido Trotter
  return result
2088 a8083063 Iustin Pop
2089 f2e07bb4 Michael Hanselmann
2090 821d1bd1 Iustin Pop
def BlockdevGrow(disk, amount):
2091 594609c0 Iustin Pop
  """Grow a stack of block devices.
2092 594609c0 Iustin Pop

2093 594609c0 Iustin Pop
  This function is called recursively, with the childrens being the
2094 10c2650b Iustin Pop
  first ones to resize.
2095 594609c0 Iustin Pop

2096 10c2650b Iustin Pop
  @type disk: L{objects.Disk}
2097 10c2650b Iustin Pop
  @param disk: the disk to be grown
2098 10c2650b Iustin Pop
  @rtype: (status, result)
2099 10c2650b Iustin Pop
  @return: a tuple with the status of the operation
2100 10c2650b Iustin Pop
      (True/False), and the errors message if status
2101 10c2650b Iustin Pop
      is False
2102 594609c0 Iustin Pop

2103 594609c0 Iustin Pop
  """
2104 594609c0 Iustin Pop
  r_dev = _RecursiveFindBD(disk)
2105 594609c0 Iustin Pop
  if r_dev is None:
2106 afdc3985 Iustin Pop
    _Fail("Cannot find block device %s", disk)
2107 594609c0 Iustin Pop
2108 594609c0 Iustin Pop
  try:
2109 594609c0 Iustin Pop
    r_dev.Grow(amount)
2110 594609c0 Iustin Pop
  except errors.BlockDeviceError, err:
2111 2cc6781a Iustin Pop
    _Fail("Failed to grow block device: %s", err, exc=True)
2112 594609c0 Iustin Pop
2113 594609c0 Iustin Pop
2114 821d1bd1 Iustin Pop
def BlockdevSnapshot(disk):
2115 a8083063 Iustin Pop
  """Create a snapshot copy of a block device.
2116 a8083063 Iustin Pop

2117 a8083063 Iustin Pop
  This function is called recursively, and the snapshot is actually created
2118 a8083063 Iustin Pop
  just for the leaf lvm backend device.
2119 a8083063 Iustin Pop

2120 e9e9263d Guido Trotter
  @type disk: L{objects.Disk}
2121 e9e9263d Guido Trotter
  @param disk: the disk to be snapshotted
2122 e9e9263d Guido Trotter
  @rtype: string
2123 e9e9263d Guido Trotter
  @return: snapshot disk path
2124 a8083063 Iustin Pop

2125 098c0958 Michael Hanselmann
  """
2126 433c63aa Iustin Pop
  if disk.dev_type == constants.LD_DRBD8:
2127 433c63aa Iustin Pop
    if not disk.children:
2128 433c63aa Iustin Pop
      _Fail("DRBD device '%s' without backing storage cannot be snapshotted",
2129 433c63aa Iustin Pop
            disk.unique_id)
2130 433c63aa Iustin Pop
    return BlockdevSnapshot(disk.children[0])
2131 fe96220b Iustin Pop
  elif disk.dev_type == constants.LD_LV:
2132 a8083063 Iustin Pop
    r_dev = _RecursiveFindBD(disk)
2133 a8083063 Iustin Pop
    if r_dev is not None:
2134 433c63aa Iustin Pop
      # FIXME: choose a saner value for the snapshot size
2135 a8083063 Iustin Pop
      # let's stay on the safe side and ask for the full size, for now
2136 c26a6bd2 Iustin Pop
      return r_dev.Snapshot(disk.size)
2137 a8083063 Iustin Pop
    else:
2138 87812fd3 Iustin Pop
      _Fail("Cannot find block device %s", disk)
2139 a8083063 Iustin Pop
  else:
2140 87812fd3 Iustin Pop
    _Fail("Cannot snapshot non-lvm block device '%s' of type '%s'",
2141 87812fd3 Iustin Pop
          disk.unique_id, disk.dev_type)
2142 a8083063 Iustin Pop
2143 a8083063 Iustin Pop
2144 a8083063 Iustin Pop
def FinalizeExport(instance, snap_disks):
2145 a8083063 Iustin Pop
  """Write out the export configuration information.
2146 a8083063 Iustin Pop

2147 10c2650b Iustin Pop
  @type instance: L{objects.Instance}
2148 10c2650b Iustin Pop
  @param instance: the instance which we export, used for
2149 10c2650b Iustin Pop
      saving configuration
2150 10c2650b Iustin Pop
  @type snap_disks: list of L{objects.Disk}
2151 10c2650b Iustin Pop
  @param snap_disks: list of snapshot block devices, which
2152 10c2650b Iustin Pop
      will be used to get the actual name of the dump file
2153 a8083063 Iustin Pop

2154 c26a6bd2 Iustin Pop
  @rtype: None
2155 a8083063 Iustin Pop

2156 098c0958 Michael Hanselmann
  """
2157 c4feafe8 Iustin Pop
  destdir = utils.PathJoin(constants.EXPORT_DIR, instance.name + ".new")
2158 c4feafe8 Iustin Pop
  finaldestdir = utils.PathJoin(constants.EXPORT_DIR, instance.name)
2159 a8083063 Iustin Pop
2160 a8083063 Iustin Pop
  config = objects.SerializableConfigParser()
2161 a8083063 Iustin Pop
2162 a8083063 Iustin Pop
  config.add_section(constants.INISECT_EXP)
2163 a8083063 Iustin Pop
  config.set(constants.INISECT_EXP, 'version', '0')
2164 a8083063 Iustin Pop
  config.set(constants.INISECT_EXP, 'timestamp', '%d' % int(time.time()))
2165 a8083063 Iustin Pop
  config.set(constants.INISECT_EXP, 'source', instance.primary_node)
2166 a8083063 Iustin Pop
  config.set(constants.INISECT_EXP, 'os', instance.os)
2167 a8083063 Iustin Pop
  config.set(constants.INISECT_EXP, 'compression', 'gzip')
2168 a8083063 Iustin Pop
2169 a8083063 Iustin Pop
  config.add_section(constants.INISECT_INS)
2170 a8083063 Iustin Pop
  config.set(constants.INISECT_INS, 'name', instance.name)
2171 51de46bf Iustin Pop
  config.set(constants.INISECT_INS, 'memory', '%d' %
2172 51de46bf Iustin Pop
             instance.beparams[constants.BE_MEMORY])
2173 51de46bf Iustin Pop
  config.set(constants.INISECT_INS, 'vcpus', '%d' %
2174 51de46bf Iustin Pop
             instance.beparams[constants.BE_VCPUS])
2175 a8083063 Iustin Pop
  config.set(constants.INISECT_INS, 'disk_template', instance.disk_template)
2176 3c8954ad Iustin Pop
  config.set(constants.INISECT_INS, 'hypervisor', instance.hypervisor)
2177 66f93869 Manuel Franceschini
2178 95268cc3 Iustin Pop
  nic_total = 0
2179 a8083063 Iustin Pop
  for nic_count, nic in enumerate(instance.nics):
2180 95268cc3 Iustin Pop
    nic_total += 1
2181 a8083063 Iustin Pop
    config.set(constants.INISECT_INS, 'nic%d_mac' %
2182 a8083063 Iustin Pop
               nic_count, '%s' % nic.mac)
2183 a8083063 Iustin Pop
    config.set(constants.INISECT_INS, 'nic%d_ip' % nic_count, '%s' % nic.ip)
2184 6801eb5c Iustin Pop
    for param in constants.NICS_PARAMETER_TYPES:
2185 6801eb5c Iustin Pop
      config.set(constants.INISECT_INS, 'nic%d_%s' % (nic_count, param),
2186 6801eb5c Iustin Pop
                 '%s' % nic.nicparams.get(param, None))
2187 a8083063 Iustin Pop
  # TODO: redundant: on load can read nics until it doesn't exist
2188 95268cc3 Iustin Pop
  config.set(constants.INISECT_INS, 'nic_count' , '%d' % nic_total)
2189 a8083063 Iustin Pop
2190 726d7d68 Iustin Pop
  disk_total = 0
2191 a8083063 Iustin Pop
  for disk_count, disk in enumerate(snap_disks):
2192 19d7f90a Guido Trotter
    if disk:
2193 726d7d68 Iustin Pop
      disk_total += 1
2194 19d7f90a Guido Trotter
      config.set(constants.INISECT_INS, 'disk%d_ivname' % disk_count,
2195 19d7f90a Guido Trotter
                 ('%s' % disk.iv_name))
2196 19d7f90a Guido Trotter
      config.set(constants.INISECT_INS, 'disk%d_dump' % disk_count,
2197 19d7f90a Guido Trotter
                 ('%s' % disk.physical_id[1]))
2198 19d7f90a Guido Trotter
      config.set(constants.INISECT_INS, 'disk%d_size' % disk_count,
2199 19d7f90a Guido Trotter
                 ('%d' % disk.size))
2200 a8083063 Iustin Pop
2201 726d7d68 Iustin Pop
  config.set(constants.INISECT_INS, 'disk_count' , '%d' % disk_total)
2202 a8083063 Iustin Pop
2203 3c8954ad Iustin Pop
  # New-style hypervisor/backend parameters
2204 3c8954ad Iustin Pop
2205 3c8954ad Iustin Pop
  config.add_section(constants.INISECT_HYP)
2206 3c8954ad Iustin Pop
  for name, value in instance.hvparams.items():
2207 3c8954ad Iustin Pop
    if name not in constants.HVC_GLOBALS:
2208 3c8954ad Iustin Pop
      config.set(constants.INISECT_HYP, name, str(value))
2209 3c8954ad Iustin Pop
2210 3c8954ad Iustin Pop
  config.add_section(constants.INISECT_BEP)
2211 3c8954ad Iustin Pop
  for name, value in instance.beparams.items():
2212 3c8954ad Iustin Pop
    config.set(constants.INISECT_BEP, name, str(value))
2213 3c8954ad Iustin Pop
2214 535b49cb Iustin Pop
  config.add_section(constants.INISECT_OSP)
2215 535b49cb Iustin Pop
  for name, value in instance.osparams.items():
2216 535b49cb Iustin Pop
    config.set(constants.INISECT_OSP, name, str(value))
2217 535b49cb Iustin Pop
2218 c4feafe8 Iustin Pop
  utils.WriteFile(utils.PathJoin(destdir, constants.EXPORT_CONF_FILE),
2219 726d7d68 Iustin Pop
                  data=config.Dumps())
2220 56569f4e Michael Hanselmann
  shutil.rmtree(finaldestdir, ignore_errors=True)
2221 a8083063 Iustin Pop
  shutil.move(destdir, finaldestdir)
2222 a8083063 Iustin Pop
2223 a8083063 Iustin Pop
2224 a8083063 Iustin Pop
def ExportInfo(dest):
2225 a8083063 Iustin Pop
  """Get export configuration information.
2226 a8083063 Iustin Pop

2227 10c2650b Iustin Pop
  @type dest: str
2228 10c2650b Iustin Pop
  @param dest: directory containing the export
2229 a8083063 Iustin Pop

2230 10c2650b Iustin Pop
  @rtype: L{objects.SerializableConfigParser}
2231 10c2650b Iustin Pop
  @return: a serializable config file containing the
2232 10c2650b Iustin Pop
      export info
2233 a8083063 Iustin Pop

2234 a8083063 Iustin Pop
  """
2235 c4feafe8 Iustin Pop
  cff = utils.PathJoin(dest, constants.EXPORT_CONF_FILE)
2236 a8083063 Iustin Pop
2237 a8083063 Iustin Pop
  config = objects.SerializableConfigParser()
2238 a8083063 Iustin Pop
  config.read(cff)
2239 a8083063 Iustin Pop
2240 a8083063 Iustin Pop
  if (not config.has_section(constants.INISECT_EXP) or
2241 a8083063 Iustin Pop
      not config.has_section(constants.INISECT_INS)):
2242 3eccac06 Iustin Pop
    _Fail("Export info file doesn't have the required fields")
2243 a8083063 Iustin Pop
2244 c26a6bd2 Iustin Pop
  return config.Dumps()
2245 a8083063 Iustin Pop
2246 a8083063 Iustin Pop
2247 a8083063 Iustin Pop
def ListExports():
2248 a8083063 Iustin Pop
  """Return a list of exports currently available on this machine.
2249 098c0958 Michael Hanselmann

2250 10c2650b Iustin Pop
  @rtype: list
2251 10c2650b Iustin Pop
  @return: list of the exports
2252 10c2650b Iustin Pop

2253 a8083063 Iustin Pop
  """
2254 a8083063 Iustin Pop
  if os.path.isdir(constants.EXPORT_DIR):
2255 b5b8309d Guido Trotter
    return sorted(utils.ListVisibleFiles(constants.EXPORT_DIR))
2256 a8083063 Iustin Pop
  else:
2257 afdc3985 Iustin Pop
    _Fail("No exports directory")
2258 a8083063 Iustin Pop
2259 a8083063 Iustin Pop
2260 a8083063 Iustin Pop
def RemoveExport(export):
2261 a8083063 Iustin Pop
  """Remove an existing export from the node.
2262 a8083063 Iustin Pop

2263 10c2650b Iustin Pop
  @type export: str
2264 10c2650b Iustin Pop
  @param export: the name of the export to remove
2265 c26a6bd2 Iustin Pop
  @rtype: None
2266 a8083063 Iustin Pop

2267 098c0958 Michael Hanselmann
  """
2268 c4feafe8 Iustin Pop
  target = utils.PathJoin(constants.EXPORT_DIR, export)
2269 a8083063 Iustin Pop
2270 35fbcd11 Iustin Pop
  try:
2271 35fbcd11 Iustin Pop
    shutil.rmtree(target)
2272 35fbcd11 Iustin Pop
  except EnvironmentError, err:
2273 35fbcd11 Iustin Pop
    _Fail("Error while removing the export: %s", err, exc=True)
2274 a8083063 Iustin Pop
2275 a8083063 Iustin Pop
2276 821d1bd1 Iustin Pop
def BlockdevRename(devlist):
2277 f3e513ad Iustin Pop
  """Rename a list of block devices.
2278 f3e513ad Iustin Pop

2279 10c2650b Iustin Pop
  @type devlist: list of tuples
2280 10c2650b Iustin Pop
  @param devlist: list of tuples of the form  (disk,
2281 10c2650b Iustin Pop
      new_logical_id, new_physical_id); disk is an
2282 10c2650b Iustin Pop
      L{objects.Disk} object describing the current disk,
2283 10c2650b Iustin Pop
      and new logical_id/physical_id is the name we
2284 10c2650b Iustin Pop
      rename it to
2285 10c2650b Iustin Pop
  @rtype: boolean
2286 10c2650b Iustin Pop
  @return: True if all renames succeeded, False otherwise
2287 f3e513ad Iustin Pop

2288 f3e513ad Iustin Pop
  """
2289 6b5e3f70 Iustin Pop
  msgs = []
2290 f3e513ad Iustin Pop
  result = True
2291 f3e513ad Iustin Pop
  for disk, unique_id in devlist:
2292 f3e513ad Iustin Pop
    dev = _RecursiveFindBD(disk)
2293 f3e513ad Iustin Pop
    if dev is None:
2294 6b5e3f70 Iustin Pop
      msgs.append("Can't find device %s in rename" % str(disk))
2295 f3e513ad Iustin Pop
      result = False
2296 f3e513ad Iustin Pop
      continue
2297 f3e513ad Iustin Pop
    try:
2298 3f78eef2 Iustin Pop
      old_rpath = dev.dev_path
2299 f3e513ad Iustin Pop
      dev.Rename(unique_id)
2300 3f78eef2 Iustin Pop
      new_rpath = dev.dev_path
2301 3f78eef2 Iustin Pop
      if old_rpath != new_rpath:
2302 3f78eef2 Iustin Pop
        DevCacheManager.RemoveCache(old_rpath)
2303 3f78eef2 Iustin Pop
        # FIXME: we should add the new cache information here, like:
2304 3f78eef2 Iustin Pop
        # DevCacheManager.UpdateCache(new_rpath, owner, ...)
2305 3f78eef2 Iustin Pop
        # but we don't have the owner here - maybe parse from existing
2306 3f78eef2 Iustin Pop
        # cache? for now, we only lose lvm data when we rename, which
2307 3f78eef2 Iustin Pop
        # is less critical than DRBD or MD
2308 f3e513ad Iustin Pop
    except errors.BlockDeviceError, err:
2309 6b5e3f70 Iustin Pop
      msgs.append("Can't rename device '%s' to '%s': %s" %
2310 6b5e3f70 Iustin Pop
                  (dev, unique_id, err))
2311 18682bca Iustin Pop
      logging.exception("Can't rename device '%s' to '%s'", dev, unique_id)
2312 f3e513ad Iustin Pop
      result = False
2313 afdc3985 Iustin Pop
  if not result:
2314 afdc3985 Iustin Pop
    _Fail("; ".join(msgs))
2315 f3e513ad Iustin Pop
2316 f3e513ad Iustin Pop
2317 778b75bb Manuel Franceschini
def _TransformFileStorageDir(file_storage_dir):
2318 778b75bb Manuel Franceschini
  """Checks whether given file_storage_dir is valid.
2319 778b75bb Manuel Franceschini

2320 778b75bb Manuel Franceschini
  Checks wheter the given file_storage_dir is within the cluster-wide
2321 778b75bb Manuel Franceschini
  default file_storage_dir stored in SimpleStore. Only paths under that
2322 778b75bb Manuel Franceschini
  directory are allowed.
2323 778b75bb Manuel Franceschini

2324 b1206984 Iustin Pop
  @type file_storage_dir: str
2325 b1206984 Iustin Pop
  @param file_storage_dir: the path to check
2326 d61cbe76 Iustin Pop

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

2329 778b75bb Manuel Franceschini
  """
2330 cb7c0198 Iustin Pop
  if not constants.ENABLE_FILE_STORAGE:
2331 cb7c0198 Iustin Pop
    _Fail("File storage disabled at configure time")
2332 c657dcc9 Michael Hanselmann
  cfg = _GetConfig()
2333 778b75bb Manuel Franceschini
  file_storage_dir = os.path.normpath(file_storage_dir)
2334 c657dcc9 Michael Hanselmann
  base_file_storage_dir = cfg.GetFileStorageDir()
2335 56569f4e Michael Hanselmann
  if (os.path.commonprefix([file_storage_dir, base_file_storage_dir]) !=
2336 778b75bb Manuel Franceschini
      base_file_storage_dir):
2337 b2b8bcce Iustin Pop
    _Fail("File storage directory '%s' is not under base file"
2338 b2b8bcce Iustin Pop
          " storage directory '%s'", file_storage_dir, base_file_storage_dir)
2339 778b75bb Manuel Franceschini
  return file_storage_dir
2340 778b75bb Manuel Franceschini
2341 778b75bb Manuel Franceschini
2342 778b75bb Manuel Franceschini
def CreateFileStorageDir(file_storage_dir):
2343 778b75bb Manuel Franceschini
  """Create file storage directory.
2344 778b75bb Manuel Franceschini

2345 b1206984 Iustin Pop
  @type file_storage_dir: str
2346 b1206984 Iustin Pop
  @param file_storage_dir: directory to create
2347 778b75bb Manuel Franceschini

2348 b1206984 Iustin Pop
  @rtype: tuple
2349 b1206984 Iustin Pop
  @return: tuple with first element a boolean indicating wheter dir
2350 b1206984 Iustin Pop
      creation was successful or not
2351 778b75bb Manuel Franceschini

2352 778b75bb Manuel Franceschini
  """
2353 778b75bb Manuel Franceschini
  file_storage_dir = _TransformFileStorageDir(file_storage_dir)
2354 b2b8bcce Iustin Pop
  if os.path.exists(file_storage_dir):
2355 b2b8bcce Iustin Pop
    if not os.path.isdir(file_storage_dir):
2356 b2b8bcce Iustin Pop
      _Fail("Specified storage dir '%s' is not a directory",
2357 b2b8bcce Iustin Pop
            file_storage_dir)
2358 778b75bb Manuel Franceschini
  else:
2359 b2b8bcce Iustin Pop
    try:
2360 b2b8bcce Iustin Pop
      os.makedirs(file_storage_dir, 0750)
2361 b2b8bcce Iustin Pop
    except OSError, err:
2362 b2b8bcce Iustin Pop
      _Fail("Cannot create file storage directory '%s': %s",
2363 b2b8bcce Iustin Pop
            file_storage_dir, err, exc=True)
2364 778b75bb Manuel Franceschini
2365 778b75bb Manuel Franceschini
2366 778b75bb Manuel Franceschini
def RemoveFileStorageDir(file_storage_dir):
2367 778b75bb Manuel Franceschini
  """Remove file storage directory.
2368 778b75bb Manuel Franceschini

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

2371 10c2650b Iustin Pop
  @type file_storage_dir: str
2372 10c2650b Iustin Pop
  @param file_storage_dir: the directory we should cleanup
2373 10c2650b Iustin Pop
  @rtype: tuple (success,)
2374 10c2650b Iustin Pop
  @return: tuple of one element, C{success}, denoting
2375 5bbd3f7f Michael Hanselmann
      whether the operation was successful
2376 778b75bb Manuel Franceschini

2377 778b75bb Manuel Franceschini
  """
2378 778b75bb Manuel Franceschini
  file_storage_dir = _TransformFileStorageDir(file_storage_dir)
2379 b2b8bcce Iustin Pop
  if os.path.exists(file_storage_dir):
2380 b2b8bcce Iustin Pop
    if not os.path.isdir(file_storage_dir):
2381 b2b8bcce Iustin Pop
      _Fail("Specified Storage directory '%s' is not a directory",
2382 b2b8bcce Iustin Pop
            file_storage_dir)
2383 afdc3985 Iustin Pop
    # deletes dir only if empty, otherwise we want to fail the rpc call
2384 b2b8bcce Iustin Pop
    try:
2385 b2b8bcce Iustin Pop
      os.rmdir(file_storage_dir)
2386 b2b8bcce Iustin Pop
    except OSError, err:
2387 b2b8bcce Iustin Pop
      _Fail("Cannot remove file storage directory '%s': %s",
2388 b2b8bcce Iustin Pop
            file_storage_dir, err)
2389 b2b8bcce Iustin Pop
2390 778b75bb Manuel Franceschini
2391 778b75bb Manuel Franceschini
def RenameFileStorageDir(old_file_storage_dir, new_file_storage_dir):
2392 778b75bb Manuel Franceschini
  """Rename the file storage directory.
2393 778b75bb Manuel Franceschini

2394 10c2650b Iustin Pop
  @type old_file_storage_dir: str
2395 10c2650b Iustin Pop
  @param old_file_storage_dir: the current path
2396 10c2650b Iustin Pop
  @type new_file_storage_dir: str
2397 10c2650b Iustin Pop
  @param new_file_storage_dir: the name we should rename to
2398 10c2650b Iustin Pop
  @rtype: tuple (success,)
2399 10c2650b Iustin Pop
  @return: tuple of one element, C{success}, denoting
2400 10c2650b Iustin Pop
      whether the operation was successful
2401 778b75bb Manuel Franceschini

2402 778b75bb Manuel Franceschini
  """
2403 778b75bb Manuel Franceschini
  old_file_storage_dir = _TransformFileStorageDir(old_file_storage_dir)
2404 778b75bb Manuel Franceschini
  new_file_storage_dir = _TransformFileStorageDir(new_file_storage_dir)
2405 b2b8bcce Iustin Pop
  if not os.path.exists(new_file_storage_dir):
2406 b2b8bcce Iustin Pop
    if os.path.isdir(old_file_storage_dir):
2407 b2b8bcce Iustin Pop
      try:
2408 b2b8bcce Iustin Pop
        os.rename(old_file_storage_dir, new_file_storage_dir)
2409 b2b8bcce Iustin Pop
      except OSError, err:
2410 b2b8bcce Iustin Pop
        _Fail("Cannot rename '%s' to '%s': %s",
2411 b2b8bcce Iustin Pop
              old_file_storage_dir, new_file_storage_dir, err)
2412 778b75bb Manuel Franceschini
    else:
2413 b2b8bcce Iustin Pop
      _Fail("Specified storage dir '%s' is not a directory",
2414 b2b8bcce Iustin Pop
            old_file_storage_dir)
2415 b2b8bcce Iustin Pop
  else:
2416 b2b8bcce Iustin Pop
    if os.path.exists(old_file_storage_dir):
2417 b2b8bcce Iustin Pop
      _Fail("Cannot rename '%s' to '%s': both locations exist",
2418 b2b8bcce Iustin Pop
            old_file_storage_dir, new_file_storage_dir)
2419 778b75bb Manuel Franceschini
2420 778b75bb Manuel Franceschini
2421 c8457ce7 Iustin Pop
def _EnsureJobQueueFile(file_name):
2422 dc31eae3 Michael Hanselmann
  """Checks whether the given filename is in the queue directory.
2423 ca52cdeb Michael Hanselmann

2424 10c2650b Iustin Pop
  @type file_name: str
2425 10c2650b Iustin Pop
  @param file_name: the file name we should check
2426 c8457ce7 Iustin Pop
  @rtype: None
2427 c8457ce7 Iustin Pop
  @raises RPCFail: if the file is not valid
2428 10c2650b Iustin Pop

2429 ca52cdeb Michael Hanselmann
  """
2430 ca52cdeb Michael Hanselmann
  queue_dir = os.path.normpath(constants.QUEUE_DIR)
2431 dc31eae3 Michael Hanselmann
  result = (os.path.commonprefix([queue_dir, file_name]) == queue_dir)
2432 dc31eae3 Michael Hanselmann
2433 dc31eae3 Michael Hanselmann
  if not result:
2434 c8457ce7 Iustin Pop
    _Fail("Passed job queue file '%s' does not belong to"
2435 c8457ce7 Iustin Pop
          " the queue directory '%s'", file_name, queue_dir)
2436 dc31eae3 Michael Hanselmann
2437 dc31eae3 Michael Hanselmann
2438 dc31eae3 Michael Hanselmann
def JobQueueUpdate(file_name, content):
2439 dc31eae3 Michael Hanselmann
  """Updates a file in the queue directory.
2440 dc31eae3 Michael Hanselmann

2441 10c2650b Iustin Pop
  This is just a wrapper over L{utils.WriteFile}, with proper
2442 10c2650b Iustin Pop
  checking.
2443 10c2650b Iustin Pop

2444 10c2650b Iustin Pop
  @type file_name: str
2445 10c2650b Iustin Pop
  @param file_name: the job file name
2446 10c2650b Iustin Pop
  @type content: str
2447 10c2650b Iustin Pop
  @param content: the new job contents
2448 10c2650b Iustin Pop
  @rtype: boolean
2449 10c2650b Iustin Pop
  @return: the success of the operation
2450 10c2650b Iustin Pop

2451 dc31eae3 Michael Hanselmann
  """
2452 c8457ce7 Iustin Pop
  _EnsureJobQueueFile(file_name)
2453 82b22e19 René Nussbaumer
  getents = runtime.GetEnts()
2454 ca52cdeb Michael Hanselmann
2455 ca52cdeb Michael Hanselmann
  # Write and replace the file atomically
2456 82b22e19 René Nussbaumer
  utils.WriteFile(file_name, data=_Decompress(content), uid=getents.masterd_uid,
2457 82b22e19 René Nussbaumer
                  gid=getents.masterd_gid)
2458 ca52cdeb Michael Hanselmann
2459 ca52cdeb Michael Hanselmann
2460 af5ebcb1 Michael Hanselmann
def JobQueueRename(old, new):
2461 af5ebcb1 Michael Hanselmann
  """Renames a job queue file.
2462 af5ebcb1 Michael Hanselmann

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

2465 10c2650b Iustin Pop
  @type old: str
2466 10c2650b Iustin Pop
  @param old: the old (actual) file name
2467 10c2650b Iustin Pop
  @type new: str
2468 10c2650b Iustin Pop
  @param new: the desired file name
2469 c8457ce7 Iustin Pop
  @rtype: tuple
2470 c8457ce7 Iustin Pop
  @return: the success of the operation and payload
2471 10c2650b Iustin Pop

2472 af5ebcb1 Michael Hanselmann
  """
2473 c8457ce7 Iustin Pop
  _EnsureJobQueueFile(old)
2474 c8457ce7 Iustin Pop
  _EnsureJobQueueFile(new)
2475 af5ebcb1 Michael Hanselmann
2476 58b22b6e Michael Hanselmann
  utils.RenameFile(old, new, mkdir=True)
2477 af5ebcb1 Michael Hanselmann
2478 af5ebcb1 Michael Hanselmann
2479 821d1bd1 Iustin Pop
def BlockdevClose(instance_name, disks):
2480 d61cbe76 Iustin Pop
  """Closes the given block devices.
2481 d61cbe76 Iustin Pop

2482 10c2650b Iustin Pop
  This means they will be switched to secondary mode (in case of
2483 10c2650b Iustin Pop
  DRBD).
2484 10c2650b Iustin Pop

2485 b2e7666a Iustin Pop
  @param instance_name: if the argument is not empty, the symlinks
2486 b2e7666a Iustin Pop
      of this instance will be removed
2487 10c2650b Iustin Pop
  @type disks: list of L{objects.Disk}
2488 10c2650b Iustin Pop
  @param disks: the list of disks to be closed
2489 10c2650b Iustin Pop
  @rtype: tuple (success, message)
2490 10c2650b Iustin Pop
  @return: a tuple of success and message, where success
2491 10c2650b Iustin Pop
      indicates the succes of the operation, and message
2492 10c2650b Iustin Pop
      which will contain the error details in case we
2493 10c2650b Iustin Pop
      failed
2494 d61cbe76 Iustin Pop

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

2519 6217e295 Iustin Pop
  @type hvname: string
2520 6217e295 Iustin Pop
  @param hvname: the hypervisor name
2521 6217e295 Iustin Pop
  @type hvparams: dict
2522 6217e295 Iustin Pop
  @param hvparams: the hypervisor parameters to be validated
2523 c26a6bd2 Iustin Pop
  @rtype: None
2524 6217e295 Iustin Pop

2525 6217e295 Iustin Pop
  """
2526 6217e295 Iustin Pop
  try:
2527 6217e295 Iustin Pop
    hv_type = hypervisor.GetHypervisor(hvname)
2528 6217e295 Iustin Pop
    hv_type.ValidateParameters(hvparams)
2529 6217e295 Iustin Pop
  except errors.HypervisorError, err:
2530 afdc3985 Iustin Pop
    _Fail(str(err), log=False)
2531 6217e295 Iustin Pop
2532 6217e295 Iustin Pop
2533 acd9ff9e Iustin Pop
def _CheckOSPList(os_obj, parameters):
2534 acd9ff9e Iustin Pop
  """Check whether a list of parameters is supported by the OS.
2535 acd9ff9e Iustin Pop

2536 acd9ff9e Iustin Pop
  @type os_obj: L{objects.OS}
2537 acd9ff9e Iustin Pop
  @param os_obj: OS object to check
2538 acd9ff9e Iustin Pop
  @type parameters: list
2539 acd9ff9e Iustin Pop
  @param parameters: the list of parameters to check
2540 acd9ff9e Iustin Pop

2541 acd9ff9e Iustin Pop
  """
2542 acd9ff9e Iustin Pop
  supported = [v[0] for v in os_obj.supported_parameters]
2543 acd9ff9e Iustin Pop
  delta = frozenset(parameters).difference(supported)
2544 acd9ff9e Iustin Pop
  if delta:
2545 acd9ff9e Iustin Pop
    _Fail("The following parameters are not supported"
2546 acd9ff9e Iustin Pop
          " by the OS %s: %s" % (os_obj.name, utils.CommaJoin(delta)))
2547 acd9ff9e Iustin Pop
2548 acd9ff9e Iustin Pop
2549 acd9ff9e Iustin Pop
def ValidateOS(required, osname, checks, osparams):
2550 acd9ff9e Iustin Pop
  """Validate the given OS' parameters.
2551 acd9ff9e Iustin Pop

2552 acd9ff9e Iustin Pop
  @type required: boolean
2553 acd9ff9e Iustin Pop
  @param required: whether absence of the OS should translate into
2554 acd9ff9e Iustin Pop
      failure or not
2555 acd9ff9e Iustin Pop
  @type osname: string
2556 acd9ff9e Iustin Pop
  @param osname: the OS to be validated
2557 acd9ff9e Iustin Pop
  @type checks: list
2558 acd9ff9e Iustin Pop
  @param checks: list of the checks to run (currently only 'parameters')
2559 acd9ff9e Iustin Pop
  @type osparams: dict
2560 acd9ff9e Iustin Pop
  @param osparams: dictionary with OS parameters
2561 acd9ff9e Iustin Pop
  @rtype: boolean
2562 acd9ff9e Iustin Pop
  @return: True if the validation passed, or False if the OS was not
2563 acd9ff9e Iustin Pop
      found and L{required} was false
2564 acd9ff9e Iustin Pop

2565 acd9ff9e Iustin Pop
  """
2566 acd9ff9e Iustin Pop
  if not constants.OS_VALIDATE_CALLS.issuperset(checks):
2567 acd9ff9e Iustin Pop
    _Fail("Unknown checks required for OS %s: %s", osname,
2568 acd9ff9e Iustin Pop
          set(checks).difference(constants.OS_VALIDATE_CALLS))
2569 acd9ff9e Iustin Pop
2570 870dc44c Iustin Pop
  name_only = objects.OS.GetName(osname)
2571 acd9ff9e Iustin Pop
  status, tbv = _TryOSFromDisk(name_only, None)
2572 acd9ff9e Iustin Pop
2573 acd9ff9e Iustin Pop
  if not status:
2574 acd9ff9e Iustin Pop
    if required:
2575 acd9ff9e Iustin Pop
      _Fail(tbv)
2576 acd9ff9e Iustin Pop
    else:
2577 acd9ff9e Iustin Pop
      return False
2578 acd9ff9e Iustin Pop
2579 72db3fd7 Iustin Pop
  if max(tbv.api_versions) < constants.OS_API_V20:
2580 72db3fd7 Iustin Pop
    return True
2581 72db3fd7 Iustin Pop
2582 acd9ff9e Iustin Pop
  if constants.OS_VALIDATE_PARAMETERS in checks:
2583 acd9ff9e Iustin Pop
    _CheckOSPList(tbv, osparams.keys())
2584 acd9ff9e Iustin Pop
2585 a025e535 Vitaly Kuznetsov
  validate_env = OSCoreEnv(osname, tbv, osparams)
2586 acd9ff9e Iustin Pop
  result = utils.RunCmd([tbv.verify_script] + checks, env=validate_env,
2587 acd9ff9e Iustin Pop
                        cwd=tbv.path)
2588 acd9ff9e Iustin Pop
  if result.failed:
2589 acd9ff9e Iustin Pop
    logging.error("os validate command '%s' returned error: %s output: %s",
2590 acd9ff9e Iustin Pop
                  result.cmd, result.fail_reason, result.output)
2591 acd9ff9e Iustin Pop
    _Fail("OS validation script failed (%s), output: %s",
2592 acd9ff9e Iustin Pop
          result.fail_reason, result.output, log=False)
2593 acd9ff9e Iustin Pop
2594 acd9ff9e Iustin Pop
  return True
2595 acd9ff9e Iustin Pop
2596 acd9ff9e Iustin Pop
2597 56aa9fd5 Iustin Pop
def DemoteFromMC():
2598 56aa9fd5 Iustin Pop
  """Demotes the current node from master candidate role.
2599 56aa9fd5 Iustin Pop

2600 56aa9fd5 Iustin Pop
  """
2601 56aa9fd5 Iustin Pop
  # try to ensure we're not the master by mistake
2602 56aa9fd5 Iustin Pop
  master, myself = ssconf.GetMasterAndMyself()
2603 56aa9fd5 Iustin Pop
  if master == myself:
2604 afdc3985 Iustin Pop
    _Fail("ssconf status shows I'm the master node, will not demote")
2605 f154a7a3 Michael Hanselmann
2606 f154a7a3 Michael Hanselmann
  result = utils.RunCmd([constants.DAEMON_UTIL, "check", constants.MASTERD])
2607 f154a7a3 Michael Hanselmann
  if not result.failed:
2608 afdc3985 Iustin Pop
    _Fail("The master daemon is running, will not demote")
2609 f154a7a3 Michael Hanselmann
2610 56aa9fd5 Iustin Pop
  try:
2611 9a5cb537 Iustin Pop
    if os.path.isfile(constants.CLUSTER_CONF_FILE):
2612 9a5cb537 Iustin Pop
      utils.CreateBackup(constants.CLUSTER_CONF_FILE)
2613 56aa9fd5 Iustin Pop
  except EnvironmentError, err:
2614 56aa9fd5 Iustin Pop
    if err.errno != errno.ENOENT:
2615 afdc3985 Iustin Pop
      _Fail("Error while backing up cluster file: %s", err, exc=True)
2616 f154a7a3 Michael Hanselmann
2617 56aa9fd5 Iustin Pop
  utils.RemoveFile(constants.CLUSTER_CONF_FILE)
2618 56aa9fd5 Iustin Pop
2619 56aa9fd5 Iustin Pop
2620 f942a838 Michael Hanselmann
def _GetX509Filenames(cryptodir, name):
2621 f942a838 Michael Hanselmann
  """Returns the full paths for the private key and certificate.
2622 f942a838 Michael Hanselmann

2623 f942a838 Michael Hanselmann
  """
2624 f942a838 Michael Hanselmann
  return (utils.PathJoin(cryptodir, name),
2625 f942a838 Michael Hanselmann
          utils.PathJoin(cryptodir, name, _X509_KEY_FILE),
2626 f942a838 Michael Hanselmann
          utils.PathJoin(cryptodir, name, _X509_CERT_FILE))
2627 f942a838 Michael Hanselmann
2628 f942a838 Michael Hanselmann
2629 f942a838 Michael Hanselmann
def CreateX509Certificate(validity, cryptodir=constants.CRYPTO_KEYS_DIR):
2630 f942a838 Michael Hanselmann
  """Creates a new X509 certificate for SSL/TLS.
2631 f942a838 Michael Hanselmann

2632 f942a838 Michael Hanselmann
  @type validity: int
2633 f942a838 Michael Hanselmann
  @param validity: Validity in seconds
2634 f942a838 Michael Hanselmann
  @rtype: tuple; (string, string)
2635 f942a838 Michael Hanselmann
  @return: Certificate name and public part
2636 f942a838 Michael Hanselmann

2637 f942a838 Michael Hanselmann
  """
2638 f942a838 Michael Hanselmann
  (key_pem, cert_pem) = \
2639 b705c7a6 Manuel Franceschini
    utils.GenerateSelfSignedX509Cert(netutils.Hostname.GetSysName(),
2640 f942a838 Michael Hanselmann
                                     min(validity, _MAX_SSL_CERT_VALIDITY))
2641 f942a838 Michael Hanselmann
2642 f942a838 Michael Hanselmann
  cert_dir = tempfile.mkdtemp(dir=cryptodir,
2643 f942a838 Michael Hanselmann
                              prefix="x509-%s-" % utils.TimestampForFilename())
2644 f942a838 Michael Hanselmann
  try:
2645 f942a838 Michael Hanselmann
    name = os.path.basename(cert_dir)
2646 f942a838 Michael Hanselmann
    assert len(name) > 5
2647 f942a838 Michael Hanselmann
2648 f942a838 Michael Hanselmann
    (_, key_file, cert_file) = _GetX509Filenames(cryptodir, name)
2649 f942a838 Michael Hanselmann
2650 f942a838 Michael Hanselmann
    utils.WriteFile(key_file, mode=0400, data=key_pem)
2651 f942a838 Michael Hanselmann
    utils.WriteFile(cert_file, mode=0400, data=cert_pem)
2652 f942a838 Michael Hanselmann
2653 f942a838 Michael Hanselmann
    # Never return private key as it shouldn't leave the node
2654 f942a838 Michael Hanselmann
    return (name, cert_pem)
2655 f942a838 Michael Hanselmann
  except Exception:
2656 f942a838 Michael Hanselmann
    shutil.rmtree(cert_dir, ignore_errors=True)
2657 f942a838 Michael Hanselmann
    raise
2658 f942a838 Michael Hanselmann
2659 f942a838 Michael Hanselmann
2660 f942a838 Michael Hanselmann
def RemoveX509Certificate(name, cryptodir=constants.CRYPTO_KEYS_DIR):
2661 f942a838 Michael Hanselmann
  """Removes a X509 certificate.
2662 f942a838 Michael Hanselmann

2663 f942a838 Michael Hanselmann
  @type name: string
2664 f942a838 Michael Hanselmann
  @param name: Certificate name
2665 f942a838 Michael Hanselmann

2666 f942a838 Michael Hanselmann
  """
2667 f942a838 Michael Hanselmann
  (cert_dir, key_file, cert_file) = _GetX509Filenames(cryptodir, name)
2668 f942a838 Michael Hanselmann
2669 f942a838 Michael Hanselmann
  utils.RemoveFile(key_file)
2670 f942a838 Michael Hanselmann
  utils.RemoveFile(cert_file)
2671 f942a838 Michael Hanselmann
2672 f942a838 Michael Hanselmann
  try:
2673 f942a838 Michael Hanselmann
    os.rmdir(cert_dir)
2674 f942a838 Michael Hanselmann
  except EnvironmentError, err:
2675 f942a838 Michael Hanselmann
    _Fail("Cannot remove certificate directory '%s': %s",
2676 f942a838 Michael Hanselmann
          cert_dir, err)
2677 f942a838 Michael Hanselmann
2678 f942a838 Michael Hanselmann
2679 1651d116 Michael Hanselmann
def _GetImportExportIoCommand(instance, mode, ieio, ieargs):
2680 1651d116 Michael Hanselmann
  """Returns the command for the requested input/output.
2681 1651d116 Michael Hanselmann

2682 1651d116 Michael Hanselmann
  @type instance: L{objects.Instance}
2683 1651d116 Michael Hanselmann
  @param instance: The instance object
2684 1651d116 Michael Hanselmann
  @param mode: Import/export mode
2685 1651d116 Michael Hanselmann
  @param ieio: Input/output type
2686 1651d116 Michael Hanselmann
  @param ieargs: Input/output arguments
2687 1651d116 Michael Hanselmann

2688 1651d116 Michael Hanselmann
  """
2689 1651d116 Michael Hanselmann
  assert mode in (constants.IEM_IMPORT, constants.IEM_EXPORT)
2690 1651d116 Michael Hanselmann
2691 1651d116 Michael Hanselmann
  env = None
2692 1651d116 Michael Hanselmann
  prefix = None
2693 1651d116 Michael Hanselmann
  suffix = None
2694 2ad5550d Michael Hanselmann
  exp_size = None
2695 1651d116 Michael Hanselmann
2696 1651d116 Michael Hanselmann
  if ieio == constants.IEIO_FILE:
2697 1651d116 Michael Hanselmann
    (filename, ) = ieargs
2698 1651d116 Michael Hanselmann
2699 1651d116 Michael Hanselmann
    if not utils.IsNormAbsPath(filename):
2700 1651d116 Michael Hanselmann
      _Fail("Path '%s' is not normalized or absolute", filename)
2701 1651d116 Michael Hanselmann
2702 1651d116 Michael Hanselmann
    directory = os.path.normpath(os.path.dirname(filename))
2703 1651d116 Michael Hanselmann
2704 1651d116 Michael Hanselmann
    if (os.path.commonprefix([constants.EXPORT_DIR, directory]) !=
2705 1651d116 Michael Hanselmann
        constants.EXPORT_DIR):
2706 1651d116 Michael Hanselmann
      _Fail("File '%s' is not under exports directory '%s'",
2707 1651d116 Michael Hanselmann
            filename, constants.EXPORT_DIR)
2708 1651d116 Michael Hanselmann
2709 1651d116 Michael Hanselmann
    # Create directory
2710 1651d116 Michael Hanselmann
    utils.Makedirs(directory, mode=0750)
2711 1651d116 Michael Hanselmann
2712 1651d116 Michael Hanselmann
    quoted_filename = utils.ShellQuote(filename)
2713 1651d116 Michael Hanselmann
2714 1651d116 Michael Hanselmann
    if mode == constants.IEM_IMPORT:
2715 1651d116 Michael Hanselmann
      suffix = "> %s" % quoted_filename
2716 1651d116 Michael Hanselmann
    elif mode == constants.IEM_EXPORT:
2717 1651d116 Michael Hanselmann
      suffix = "< %s" % quoted_filename
2718 1651d116 Michael Hanselmann
2719 2ad5550d Michael Hanselmann
      # Retrieve file size
2720 2ad5550d Michael Hanselmann
      try:
2721 2ad5550d Michael Hanselmann
        st = os.stat(filename)
2722 2ad5550d Michael Hanselmann
      except EnvironmentError, err:
2723 2ad5550d Michael Hanselmann
        logging.error("Can't stat(2) %s: %s", filename, err)
2724 2ad5550d Michael Hanselmann
      else:
2725 2ad5550d Michael Hanselmann
        exp_size = utils.BytesToMebibyte(st.st_size)
2726 2ad5550d Michael Hanselmann
2727 1651d116 Michael Hanselmann
  elif ieio == constants.IEIO_RAW_DISK:
2728 1651d116 Michael Hanselmann
    (disk, ) = ieargs
2729 1651d116 Michael Hanselmann
2730 1651d116 Michael Hanselmann
    real_disk = _OpenRealBD(disk)
2731 1651d116 Michael Hanselmann
2732 1651d116 Michael Hanselmann
    if mode == constants.IEM_IMPORT:
2733 1651d116 Michael Hanselmann
      # we set here a smaller block size as, due to transport buffering, more
2734 1651d116 Michael Hanselmann
      # than 64-128k will mostly ignored; we use nocreat to fail if the device
2735 1651d116 Michael Hanselmann
      # is not already there or we pass a wrong path; we use notrunc to no
2736 1651d116 Michael Hanselmann
      # attempt truncate on an LV device; we use oflag=dsync to not buffer too
2737 1651d116 Michael Hanselmann
      # much memory; this means that at best, we flush every 64k, which will
2738 1651d116 Michael Hanselmann
      # not be very fast
2739 1651d116 Michael Hanselmann
      suffix = utils.BuildShellCmd(("| dd of=%s conv=nocreat,notrunc"
2740 1651d116 Michael Hanselmann
                                    " bs=%s oflag=dsync"),
2741 1651d116 Michael Hanselmann
                                    real_disk.dev_path,
2742 1651d116 Michael Hanselmann
                                    str(64 * 1024))
2743 1651d116 Michael Hanselmann
2744 1651d116 Michael Hanselmann
    elif mode == constants.IEM_EXPORT:
2745 1651d116 Michael Hanselmann
      # the block size on the read dd is 1MiB to match our units
2746 1651d116 Michael Hanselmann
      prefix = utils.BuildShellCmd("dd if=%s bs=%s count=%s |",
2747 1651d116 Michael Hanselmann
                                   real_disk.dev_path,
2748 1651d116 Michael Hanselmann
                                   str(1024 * 1024), # 1 MB
2749 1651d116 Michael Hanselmann
                                   str(disk.size))
2750 2ad5550d Michael Hanselmann
      exp_size = disk.size
2751 1651d116 Michael Hanselmann
2752 1651d116 Michael Hanselmann
  elif ieio == constants.IEIO_SCRIPT:
2753 1651d116 Michael Hanselmann
    (disk, disk_index, ) = ieargs
2754 1651d116 Michael Hanselmann
2755 1651d116 Michael Hanselmann
    assert isinstance(disk_index, (int, long))
2756 1651d116 Michael Hanselmann
2757 1651d116 Michael Hanselmann
    real_disk = _OpenRealBD(disk)
2758 1651d116 Michael Hanselmann
2759 1651d116 Michael Hanselmann
    inst_os = OSFromDisk(instance.os)
2760 1651d116 Michael Hanselmann
    env = OSEnvironment(instance, inst_os)
2761 1651d116 Michael Hanselmann
2762 1651d116 Michael Hanselmann
    if mode == constants.IEM_IMPORT:
2763 1651d116 Michael Hanselmann
      env["IMPORT_DEVICE"] = env["DISK_%d_PATH" % disk_index]
2764 1651d116 Michael Hanselmann
      env["IMPORT_INDEX"] = str(disk_index)
2765 1651d116 Michael Hanselmann
      script = inst_os.import_script
2766 1651d116 Michael Hanselmann
2767 1651d116 Michael Hanselmann
    elif mode == constants.IEM_EXPORT:
2768 1651d116 Michael Hanselmann
      env["EXPORT_DEVICE"] = real_disk.dev_path
2769 1651d116 Michael Hanselmann
      env["EXPORT_INDEX"] = str(disk_index)
2770 1651d116 Michael Hanselmann
      script = inst_os.export_script
2771 1651d116 Michael Hanselmann
2772 1651d116 Michael Hanselmann
    # TODO: Pass special environment only to script
2773 1651d116 Michael Hanselmann
    script_cmd = utils.BuildShellCmd("( cd %s && %s; )", inst_os.path, script)
2774 1651d116 Michael Hanselmann
2775 1651d116 Michael Hanselmann
    if mode == constants.IEM_IMPORT:
2776 1651d116 Michael Hanselmann
      suffix = "| %s" % script_cmd
2777 1651d116 Michael Hanselmann
2778 1651d116 Michael Hanselmann
    elif mode == constants.IEM_EXPORT:
2779 1651d116 Michael Hanselmann
      prefix = "%s |" % script_cmd
2780 1651d116 Michael Hanselmann
2781 2ad5550d Michael Hanselmann
    # Let script predict size
2782 2ad5550d Michael Hanselmann
    exp_size = constants.IE_CUSTOM_SIZE
2783 2ad5550d Michael Hanselmann
2784 1651d116 Michael Hanselmann
  else:
2785 1651d116 Michael Hanselmann
    _Fail("Invalid %s I/O mode %r", mode, ieio)
2786 1651d116 Michael Hanselmann
2787 2ad5550d Michael Hanselmann
  return (env, prefix, suffix, exp_size)
2788 1651d116 Michael Hanselmann
2789 1651d116 Michael Hanselmann
2790 1651d116 Michael Hanselmann
def _CreateImportExportStatusDir(prefix):
2791 1651d116 Michael Hanselmann
  """Creates status directory for import/export.
2792 1651d116 Michael Hanselmann

2793 1651d116 Michael Hanselmann
  """
2794 1651d116 Michael Hanselmann
  return tempfile.mkdtemp(dir=constants.IMPORT_EXPORT_DIR,
2795 1651d116 Michael Hanselmann
                          prefix=("%s-%s-" %
2796 1651d116 Michael Hanselmann
                                  (prefix, utils.TimestampForFilename())))
2797 1651d116 Michael Hanselmann
2798 1651d116 Michael Hanselmann
2799 eb630f50 Michael Hanselmann
def StartImportExportDaemon(mode, opts, host, port, instance, ieio, ieioargs):
2800 1651d116 Michael Hanselmann
  """Starts an import or export daemon.
2801 1651d116 Michael Hanselmann

2802 1651d116 Michael Hanselmann
  @param mode: Import/output mode
2803 eb630f50 Michael Hanselmann
  @type opts: L{objects.ImportExportOptions}
2804 eb630f50 Michael Hanselmann
  @param opts: Daemon options
2805 1651d116 Michael Hanselmann
  @type host: string
2806 1651d116 Michael Hanselmann
  @param host: Remote host for export (None for import)
2807 1651d116 Michael Hanselmann
  @type port: int
2808 1651d116 Michael Hanselmann
  @param port: Remote port for export (None for import)
2809 1651d116 Michael Hanselmann
  @type instance: L{objects.Instance}
2810 1651d116 Michael Hanselmann
  @param instance: Instance object
2811 1651d116 Michael Hanselmann
  @param ieio: Input/output type
2812 1651d116 Michael Hanselmann
  @param ieioargs: Input/output arguments
2813 1651d116 Michael Hanselmann

2814 1651d116 Michael Hanselmann
  """
2815 1651d116 Michael Hanselmann
  if mode == constants.IEM_IMPORT:
2816 1651d116 Michael Hanselmann
    prefix = "import"
2817 1651d116 Michael Hanselmann
2818 1651d116 Michael Hanselmann
    if not (host is None and port is None):
2819 1651d116 Michael Hanselmann
      _Fail("Can not specify host or port on import")
2820 1651d116 Michael Hanselmann
2821 1651d116 Michael Hanselmann
  elif mode == constants.IEM_EXPORT:
2822 1651d116 Michael Hanselmann
    prefix = "export"
2823 1651d116 Michael Hanselmann
2824 1651d116 Michael Hanselmann
    if host is None or port is None:
2825 1651d116 Michael Hanselmann
      _Fail("Host and port must be specified for an export")
2826 1651d116 Michael Hanselmann
2827 1651d116 Michael Hanselmann
  else:
2828 1651d116 Michael Hanselmann
    _Fail("Invalid mode %r", mode)
2829 1651d116 Michael Hanselmann
2830 eb630f50 Michael Hanselmann
  if (opts.key_name is None) ^ (opts.ca_pem is None):
2831 1651d116 Michael Hanselmann
    _Fail("Cluster certificate can only be used for both key and CA")
2832 1651d116 Michael Hanselmann
2833 2ad5550d Michael Hanselmann
  (cmd_env, cmd_prefix, cmd_suffix, exp_size) = \
2834 1651d116 Michael Hanselmann
    _GetImportExportIoCommand(instance, mode, ieio, ieioargs)
2835 1651d116 Michael Hanselmann
2836 eb630f50 Michael Hanselmann
  if opts.key_name is None:
2837 1651d116 Michael Hanselmann
    # Use server.pem
2838 1651d116 Michael Hanselmann
    key_path = constants.NODED_CERT_FILE
2839 1651d116 Michael Hanselmann
    cert_path = constants.NODED_CERT_FILE
2840 eb630f50 Michael Hanselmann
    assert opts.ca_pem is None
2841 1651d116 Michael Hanselmann
  else:
2842 1651d116 Michael Hanselmann
    (_, key_path, cert_path) = _GetX509Filenames(constants.CRYPTO_KEYS_DIR,
2843 eb630f50 Michael Hanselmann
                                                 opts.key_name)
2844 eb630f50 Michael Hanselmann
    assert opts.ca_pem is not None
2845 1651d116 Michael Hanselmann
2846 63bcea2a Michael Hanselmann
  for i in [key_path, cert_path]:
2847 dcaabc4f Michael Hanselmann
    if not os.path.exists(i):
2848 63bcea2a Michael Hanselmann
      _Fail("File '%s' does not exist" % i)
2849 63bcea2a Michael Hanselmann
2850 1651d116 Michael Hanselmann
  status_dir = _CreateImportExportStatusDir(prefix)
2851 1651d116 Michael Hanselmann
  try:
2852 1651d116 Michael Hanselmann
    status_file = utils.PathJoin(status_dir, _IES_STATUS_FILE)
2853 1651d116 Michael Hanselmann
    pid_file = utils.PathJoin(status_dir, _IES_PID_FILE)
2854 63bcea2a Michael Hanselmann
    ca_file = utils.PathJoin(status_dir, _IES_CA_FILE)
2855 1651d116 Michael Hanselmann
2856 eb630f50 Michael Hanselmann
    if opts.ca_pem is None:
2857 1651d116 Michael Hanselmann
      # Use server.pem
2858 63bcea2a Michael Hanselmann
      ca = utils.ReadFile(constants.NODED_CERT_FILE)
2859 eb630f50 Michael Hanselmann
    else:
2860 eb630f50 Michael Hanselmann
      ca = opts.ca_pem
2861 63bcea2a Michael Hanselmann
2862 eb630f50 Michael Hanselmann
    # Write CA file
2863 63bcea2a Michael Hanselmann
    utils.WriteFile(ca_file, data=ca, mode=0400)
2864 1651d116 Michael Hanselmann
2865 1651d116 Michael Hanselmann
    cmd = [
2866 1651d116 Michael Hanselmann
      constants.IMPORT_EXPORT_DAEMON,
2867 1651d116 Michael Hanselmann
      status_file, mode,
2868 1651d116 Michael Hanselmann
      "--key=%s" % key_path,
2869 1651d116 Michael Hanselmann
      "--cert=%s" % cert_path,
2870 63bcea2a Michael Hanselmann
      "--ca=%s" % ca_file,
2871 1651d116 Michael Hanselmann
      ]
2872 1651d116 Michael Hanselmann
2873 1651d116 Michael Hanselmann
    if host:
2874 1651d116 Michael Hanselmann
      cmd.append("--host=%s" % host)
2875 1651d116 Michael Hanselmann
2876 1651d116 Michael Hanselmann
    if port:
2877 1651d116 Michael Hanselmann
      cmd.append("--port=%s" % port)
2878 1651d116 Michael Hanselmann
2879 a5310c2a Michael Hanselmann
    if opts.compress:
2880 a5310c2a Michael Hanselmann
      cmd.append("--compress=%s" % opts.compress)
2881 a5310c2a Michael Hanselmann
2882 af1d39b1 Michael Hanselmann
    if opts.magic:
2883 af1d39b1 Michael Hanselmann
      cmd.append("--magic=%s" % opts.magic)
2884 af1d39b1 Michael Hanselmann
2885 2ad5550d Michael Hanselmann
    if exp_size is not None:
2886 2ad5550d Michael Hanselmann
      cmd.append("--expected-size=%s" % exp_size)
2887 2ad5550d Michael Hanselmann
2888 1651d116 Michael Hanselmann
    if cmd_prefix:
2889 1651d116 Michael Hanselmann
      cmd.append("--cmd-prefix=%s" % cmd_prefix)
2890 1651d116 Michael Hanselmann
2891 1651d116 Michael Hanselmann
    if cmd_suffix:
2892 1651d116 Michael Hanselmann
      cmd.append("--cmd-suffix=%s" % cmd_suffix)
2893 1651d116 Michael Hanselmann
2894 1651d116 Michael Hanselmann
    logfile = _InstanceLogName(prefix, instance.os, instance.name)
2895 1651d116 Michael Hanselmann
2896 1651d116 Michael Hanselmann
    # TODO: Once _InstanceLogName uses tempfile.mkstemp, StartDaemon has
2897 1651d116 Michael Hanselmann
    # support for receiving a file descriptor for output
2898 1651d116 Michael Hanselmann
    utils.StartDaemon(cmd, env=cmd_env, pidfile=pid_file,
2899 1651d116 Michael Hanselmann
                      output=logfile)
2900 1651d116 Michael Hanselmann
2901 1651d116 Michael Hanselmann
    # The import/export name is simply the status directory name
2902 1651d116 Michael Hanselmann
    return os.path.basename(status_dir)
2903 1651d116 Michael Hanselmann
2904 1651d116 Michael Hanselmann
  except Exception:
2905 1651d116 Michael Hanselmann
    shutil.rmtree(status_dir, ignore_errors=True)
2906 1651d116 Michael Hanselmann
    raise
2907 1651d116 Michael Hanselmann
2908 1651d116 Michael Hanselmann
2909 1651d116 Michael Hanselmann
def GetImportExportStatus(names):
2910 1651d116 Michael Hanselmann
  """Returns import/export daemon status.
2911 1651d116 Michael Hanselmann

2912 1651d116 Michael Hanselmann
  @type names: sequence
2913 1651d116 Michael Hanselmann
  @param names: List of names
2914 1651d116 Michael Hanselmann
  @rtype: List of dicts
2915 1651d116 Michael Hanselmann
  @return: Returns a list of the state of each named import/export or None if a
2916 1651d116 Michael Hanselmann
           status couldn't be read
2917 1651d116 Michael Hanselmann

2918 1651d116 Michael Hanselmann
  """
2919 1651d116 Michael Hanselmann
  result = []
2920 1651d116 Michael Hanselmann
2921 1651d116 Michael Hanselmann
  for name in names:
2922 1651d116 Michael Hanselmann
    status_file = utils.PathJoin(constants.IMPORT_EXPORT_DIR, name,
2923 1651d116 Michael Hanselmann
                                 _IES_STATUS_FILE)
2924 1651d116 Michael Hanselmann
2925 1651d116 Michael Hanselmann
    try:
2926 1651d116 Michael Hanselmann
      data = utils.ReadFile(status_file)
2927 1651d116 Michael Hanselmann
    except EnvironmentError, err:
2928 1651d116 Michael Hanselmann
      if err.errno != errno.ENOENT:
2929 1651d116 Michael Hanselmann
        raise
2930 1651d116 Michael Hanselmann
      data = None
2931 1651d116 Michael Hanselmann
2932 1651d116 Michael Hanselmann
    if not data:
2933 1651d116 Michael Hanselmann
      result.append(None)
2934 1651d116 Michael Hanselmann
      continue
2935 1651d116 Michael Hanselmann
2936 1651d116 Michael Hanselmann
    result.append(serializer.LoadJson(data))
2937 1651d116 Michael Hanselmann
2938 1651d116 Michael Hanselmann
  return result
2939 1651d116 Michael Hanselmann
2940 1651d116 Michael Hanselmann
2941 f81c4737 Michael Hanselmann
def AbortImportExport(name):
2942 f81c4737 Michael Hanselmann
  """Sends SIGTERM to a running import/export daemon.
2943 f81c4737 Michael Hanselmann

2944 f81c4737 Michael Hanselmann
  """
2945 f81c4737 Michael Hanselmann
  logging.info("Abort import/export %s", name)
2946 f81c4737 Michael Hanselmann
2947 f81c4737 Michael Hanselmann
  status_dir = utils.PathJoin(constants.IMPORT_EXPORT_DIR, name)
2948 f81c4737 Michael Hanselmann
  pid = utils.ReadLockedPidFile(utils.PathJoin(status_dir, _IES_PID_FILE))
2949 f81c4737 Michael Hanselmann
2950 f81c4737 Michael Hanselmann
  if pid:
2951 f81c4737 Michael Hanselmann
    logging.info("Import/export %s is running with PID %s, sending SIGTERM",
2952 f81c4737 Michael Hanselmann
                 name, pid)
2953 560cbec1 Michael Hanselmann
    utils.IgnoreProcessNotFound(os.kill, pid, signal.SIGTERM)
2954 f81c4737 Michael Hanselmann
2955 f81c4737 Michael Hanselmann
2956 1651d116 Michael Hanselmann
def CleanupImportExport(name):
2957 1651d116 Michael Hanselmann
  """Cleanup after an import or export.
2958 1651d116 Michael Hanselmann

2959 1651d116 Michael Hanselmann
  If the import/export daemon is still running it's killed. Afterwards the
2960 1651d116 Michael Hanselmann
  whole status directory is removed.
2961 1651d116 Michael Hanselmann

2962 1651d116 Michael Hanselmann
  """
2963 1651d116 Michael Hanselmann
  logging.info("Finalizing import/export %s", name)
2964 1651d116 Michael Hanselmann
2965 1651d116 Michael Hanselmann
  status_dir = utils.PathJoin(constants.IMPORT_EXPORT_DIR, name)
2966 1651d116 Michael Hanselmann
2967 debed9ae Michael Hanselmann
  pid = utils.ReadLockedPidFile(utils.PathJoin(status_dir, _IES_PID_FILE))
2968 1651d116 Michael Hanselmann
2969 1651d116 Michael Hanselmann
  if pid:
2970 1651d116 Michael Hanselmann
    logging.info("Import/export %s is still running with PID %s",
2971 1651d116 Michael Hanselmann
                 name, pid)
2972 1651d116 Michael Hanselmann
    utils.KillProcess(pid, waitpid=False)
2973 1651d116 Michael Hanselmann
2974 1651d116 Michael Hanselmann
  shutil.rmtree(status_dir, ignore_errors=True)
2975 1651d116 Michael Hanselmann
2976 1651d116 Michael Hanselmann
2977 6b93ec9d Iustin Pop
def _FindDisks(nodes_ip, disks):
2978 6b93ec9d Iustin Pop
  """Sets the physical ID on disks and returns the block devices.
2979 6b93ec9d Iustin Pop

2980 6b93ec9d Iustin Pop
  """
2981 6b93ec9d Iustin Pop
  # set the correct physical ID
2982 b705c7a6 Manuel Franceschini
  my_name = netutils.Hostname.GetSysName()
2983 6b93ec9d Iustin Pop
  for cf in disks:
2984 6b93ec9d Iustin Pop
    cf.SetPhysicalID(my_name, nodes_ip)
2985 6b93ec9d Iustin Pop
2986 6b93ec9d Iustin Pop
  bdevs = []
2987 6b93ec9d Iustin Pop
2988 6b93ec9d Iustin Pop
  for cf in disks:
2989 6b93ec9d Iustin Pop
    rd = _RecursiveFindBD(cf)
2990 6b93ec9d Iustin Pop
    if rd is None:
2991 5a533f8a Iustin Pop
      _Fail("Can't find device %s", cf)
2992 6b93ec9d Iustin Pop
    bdevs.append(rd)
2993 5a533f8a Iustin Pop
  return bdevs
2994 6b93ec9d Iustin Pop
2995 6b93ec9d Iustin Pop
2996 6b93ec9d Iustin Pop
def DrbdDisconnectNet(nodes_ip, disks):
2997 6b93ec9d Iustin Pop
  """Disconnects the network on a list of drbd devices.
2998 6b93ec9d Iustin Pop

2999 6b93ec9d Iustin Pop
  """
3000 5a533f8a Iustin Pop
  bdevs = _FindDisks(nodes_ip, disks)
3001 6b93ec9d Iustin Pop
3002 6b93ec9d Iustin Pop
  # disconnect disks
3003 6b93ec9d Iustin Pop
  for rd in bdevs:
3004 6b93ec9d Iustin Pop
    try:
3005 6b93ec9d Iustin Pop
      rd.DisconnectNet()
3006 6b93ec9d Iustin Pop
    except errors.BlockDeviceError, err:
3007 2cc6781a Iustin Pop
      _Fail("Can't change network configuration to standalone mode: %s",
3008 2cc6781a Iustin Pop
            err, exc=True)
3009 6b93ec9d Iustin Pop
3010 6b93ec9d Iustin Pop
3011 6b93ec9d Iustin Pop
def DrbdAttachNet(nodes_ip, disks, instance_name, multimaster):
3012 6b93ec9d Iustin Pop
  """Attaches the network on a list of drbd devices.
3013 6b93ec9d Iustin Pop

3014 6b93ec9d Iustin Pop
  """
3015 5a533f8a Iustin Pop
  bdevs = _FindDisks(nodes_ip, disks)
3016 6b93ec9d Iustin Pop
3017 6b93ec9d Iustin Pop
  if multimaster:
3018 53c776b5 Iustin Pop
    for idx, rd in enumerate(bdevs):
3019 6b93ec9d Iustin Pop
      try:
3020 53c776b5 Iustin Pop
        _SymlinkBlockDev(instance_name, rd.dev_path, idx)
3021 6b93ec9d Iustin Pop
      except EnvironmentError, err:
3022 2cc6781a Iustin Pop
        _Fail("Can't create symlink: %s", err)
3023 6b93ec9d Iustin Pop
  # reconnect disks, switch to new master configuration and if
3024 6b93ec9d Iustin Pop
  # needed primary mode
3025 6b93ec9d Iustin Pop
  for rd in bdevs:
3026 6b93ec9d Iustin Pop
    try:
3027 6b93ec9d Iustin Pop
      rd.AttachNet(multimaster)
3028 6b93ec9d Iustin Pop
    except errors.BlockDeviceError, err:
3029 2cc6781a Iustin Pop
      _Fail("Can't change network configuration: %s", err)
3030 3c0cdc83 Michael Hanselmann
3031 6b93ec9d Iustin Pop
  # wait until the disks are connected; we need to retry the re-attach
3032 6b93ec9d Iustin Pop
  # if the device becomes standalone, as this might happen if the one
3033 6b93ec9d Iustin Pop
  # node disconnects and reconnects in a different mode before the
3034 6b93ec9d Iustin Pop
  # other node reconnects; in this case, one or both of the nodes will
3035 6b93ec9d Iustin Pop
  # decide it has wrong configuration and switch to standalone
3036 3c0cdc83 Michael Hanselmann
3037 3c0cdc83 Michael Hanselmann
  def _Attach():
3038 6b93ec9d Iustin Pop
    all_connected = True
3039 3c0cdc83 Michael Hanselmann
3040 6b93ec9d Iustin Pop
    for rd in bdevs:
3041 6b93ec9d Iustin Pop
      stats = rd.GetProcStatus()
3042 3c0cdc83 Michael Hanselmann
3043 3c0cdc83 Michael Hanselmann
      all_connected = (all_connected and
3044 3c0cdc83 Michael Hanselmann
                       (stats.is_connected or stats.is_in_resync))
3045 3c0cdc83 Michael Hanselmann
3046 6b93ec9d Iustin Pop
      if stats.is_standalone:
3047 6b93ec9d Iustin Pop
        # peer had different config info and this node became
3048 6b93ec9d Iustin Pop
        # standalone, even though this should not happen with the
3049 6b93ec9d Iustin Pop
        # new staged way of changing disk configs
3050 6b93ec9d Iustin Pop
        try:
3051 c738375b Iustin Pop
          rd.AttachNet(multimaster)
3052 6b93ec9d Iustin Pop
        except errors.BlockDeviceError, err:
3053 2cc6781a Iustin Pop
          _Fail("Can't change network configuration: %s", err)
3054 3c0cdc83 Michael Hanselmann
3055 3c0cdc83 Michael Hanselmann
    if not all_connected:
3056 3c0cdc83 Michael Hanselmann
      raise utils.RetryAgain()
3057 3c0cdc83 Michael Hanselmann
3058 3c0cdc83 Michael Hanselmann
  try:
3059 3c0cdc83 Michael Hanselmann
    # Start with a delay of 100 miliseconds and go up to 5 seconds
3060 3c0cdc83 Michael Hanselmann
    utils.Retry(_Attach, (0.1, 1.5, 5.0), 2 * 60)
3061 3c0cdc83 Michael Hanselmann
  except utils.RetryTimeout:
3062 afdc3985 Iustin Pop
    _Fail("Timeout in disk reconnecting")
3063 3c0cdc83 Michael Hanselmann
3064 6b93ec9d Iustin Pop
  if multimaster:
3065 6b93ec9d Iustin Pop
    # change to primary mode
3066 6b93ec9d Iustin Pop
    for rd in bdevs:
3067 d3da87b8 Iustin Pop
      try:
3068 d3da87b8 Iustin Pop
        rd.Open()
3069 d3da87b8 Iustin Pop
      except errors.BlockDeviceError, err:
3070 2cc6781a Iustin Pop
        _Fail("Can't change to primary mode: %s", err)
3071 6b93ec9d Iustin Pop
3072 6b93ec9d Iustin Pop
3073 6b93ec9d Iustin Pop
def DrbdWaitSync(nodes_ip, disks):
3074 6b93ec9d Iustin Pop
  """Wait until DRBDs have synchronized.
3075 6b93ec9d Iustin Pop

3076 6b93ec9d Iustin Pop
  """
3077 db8667b7 Iustin Pop
  def _helper(rd):
3078 db8667b7 Iustin Pop
    stats = rd.GetProcStatus()
3079 db8667b7 Iustin Pop
    if not (stats.is_connected or stats.is_in_resync):
3080 db8667b7 Iustin Pop
      raise utils.RetryAgain()
3081 db8667b7 Iustin Pop
    return stats
3082 db8667b7 Iustin Pop
3083 5a533f8a Iustin Pop
  bdevs = _FindDisks(nodes_ip, disks)
3084 6b93ec9d Iustin Pop
3085 6b93ec9d Iustin Pop
  min_resync = 100
3086 6b93ec9d Iustin Pop
  alldone = True
3087 6b93ec9d Iustin Pop
  for rd in bdevs:
3088 db8667b7 Iustin Pop
    try:
3089 db8667b7 Iustin Pop
      # poll each second for 15 seconds
3090 db8667b7 Iustin Pop
      stats = utils.Retry(_helper, 1, 15, args=[rd])
3091 db8667b7 Iustin Pop
    except utils.RetryTimeout:
3092 db8667b7 Iustin Pop
      stats = rd.GetProcStatus()
3093 db8667b7 Iustin Pop
      # last check
3094 db8667b7 Iustin Pop
      if not (stats.is_connected or stats.is_in_resync):
3095 db8667b7 Iustin Pop
        _Fail("DRBD device %s is not in sync: stats=%s", rd, stats)
3096 6b93ec9d Iustin Pop
    alldone = alldone and (not stats.is_in_resync)
3097 6b93ec9d Iustin Pop
    if stats.sync_percent is not None:
3098 6b93ec9d Iustin Pop
      min_resync = min(min_resync, stats.sync_percent)
3099 afdc3985 Iustin Pop
3100 c26a6bd2 Iustin Pop
  return (alldone, min_resync)
3101 6b93ec9d Iustin Pop
3102 6b93ec9d Iustin Pop
3103 c46b9782 Luca Bigliardi
def GetDrbdUsermodeHelper():
3104 c46b9782 Luca Bigliardi
  """Returns DRBD usermode helper currently configured.
3105 c46b9782 Luca Bigliardi

3106 c46b9782 Luca Bigliardi
  """
3107 c46b9782 Luca Bigliardi
  try:
3108 c46b9782 Luca Bigliardi
    return bdev.BaseDRBD.GetUsermodeHelper()
3109 c46b9782 Luca Bigliardi
  except errors.BlockDeviceError, err:
3110 c46b9782 Luca Bigliardi
    _Fail(str(err))
3111 c46b9782 Luca Bigliardi
3112 c46b9782 Luca Bigliardi
3113 f5118ade Iustin Pop
def PowercycleNode(hypervisor_type):
3114 f5118ade Iustin Pop
  """Hard-powercycle the node.
3115 f5118ade Iustin Pop

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

3119 f5118ade Iustin Pop
  """
3120 f5118ade Iustin Pop
  hyper = hypervisor.GetHypervisor(hypervisor_type)
3121 f5118ade Iustin Pop
  try:
3122 f5118ade Iustin Pop
    pid = os.fork()
3123 29921401 Iustin Pop
  except OSError:
3124 f5118ade Iustin Pop
    # if we can't fork, we'll pretend that we're in the child process
3125 f5118ade Iustin Pop
    pid = 0
3126 f5118ade Iustin Pop
  if pid > 0:
3127 c26a6bd2 Iustin Pop
    return "Reboot scheduled in 5 seconds"
3128 1af6ac0f Luca Bigliardi
  # ensure the child is running on ram
3129 1af6ac0f Luca Bigliardi
  try:
3130 1af6ac0f Luca Bigliardi
    utils.Mlockall()
3131 20601361 Luca Bigliardi
  except Exception: # pylint: disable-msg=W0703
3132 1af6ac0f Luca Bigliardi
    pass
3133 f5118ade Iustin Pop
  time.sleep(5)
3134 f5118ade Iustin Pop
  hyper.PowercycleNode()
3135 f5118ade Iustin Pop
3136 f5118ade Iustin Pop
3137 a8083063 Iustin Pop
class HooksRunner(object):
3138 a8083063 Iustin Pop
  """Hook runner.
3139 a8083063 Iustin Pop

3140 10c2650b Iustin Pop
  This class is instantiated on the node side (ganeti-noded) and not
3141 10c2650b Iustin Pop
  on the master side.
3142 a8083063 Iustin Pop

3143 a8083063 Iustin Pop
  """
3144 a8083063 Iustin Pop
  def __init__(self, hooks_base_dir=None):
3145 a8083063 Iustin Pop
    """Constructor for hooks runner.
3146 a8083063 Iustin Pop

3147 10c2650b Iustin Pop
    @type hooks_base_dir: str or None
3148 10c2650b Iustin Pop
    @param hooks_base_dir: if not None, this overrides the
3149 10c2650b Iustin Pop
        L{constants.HOOKS_BASE_DIR} (useful for unittests)
3150 a8083063 Iustin Pop

3151 a8083063 Iustin Pop
    """
3152 a8083063 Iustin Pop
    if hooks_base_dir is None:
3153 a8083063 Iustin Pop
      hooks_base_dir = constants.HOOKS_BASE_DIR
3154 fe267188 Iustin Pop
    # yeah, _BASE_DIR is not valid for attributes, we use it like a
3155 fe267188 Iustin Pop
    # constant
3156 fe267188 Iustin Pop
    self._BASE_DIR = hooks_base_dir # pylint: disable-msg=C0103
3157 a8083063 Iustin Pop
3158 a8083063 Iustin Pop
  def RunHooks(self, hpath, phase, env):
3159 a8083063 Iustin Pop
    """Run the scripts in the hooks directory.
3160 a8083063 Iustin Pop

3161 10c2650b Iustin Pop
    @type hpath: str
3162 10c2650b Iustin Pop
    @param hpath: the path to the hooks directory which
3163 10c2650b Iustin Pop
        holds the scripts
3164 10c2650b Iustin Pop
    @type phase: str
3165 10c2650b Iustin Pop
    @param phase: either L{constants.HOOKS_PHASE_PRE} or
3166 10c2650b Iustin Pop
        L{constants.HOOKS_PHASE_POST}
3167 10c2650b Iustin Pop
    @type env: dict
3168 10c2650b Iustin Pop
    @param env: dictionary with the environment for the hook
3169 10c2650b Iustin Pop
    @rtype: list
3170 10c2650b Iustin Pop
    @return: list of 3-element tuples:
3171 10c2650b Iustin Pop
      - script path
3172 10c2650b Iustin Pop
      - script result, either L{constants.HKR_SUCCESS} or
3173 10c2650b Iustin Pop
        L{constants.HKR_FAIL}
3174 10c2650b Iustin Pop
      - output of the script
3175 10c2650b Iustin Pop

3176 10c2650b Iustin Pop
    @raise errors.ProgrammerError: for invalid input
3177 10c2650b Iustin Pop
        parameters
3178 a8083063 Iustin Pop

3179 a8083063 Iustin Pop
    """
3180 a8083063 Iustin Pop
    if phase == constants.HOOKS_PHASE_PRE:
3181 a8083063 Iustin Pop
      suffix = "pre"
3182 a8083063 Iustin Pop
    elif phase == constants.HOOKS_PHASE_POST:
3183 a8083063 Iustin Pop
      suffix = "post"
3184 a8083063 Iustin Pop
    else:
3185 3fb4f740 Iustin Pop
      _Fail("Unknown hooks phase '%s'", phase)
3186 3fb4f740 Iustin Pop
3187 a8083063 Iustin Pop
3188 a8083063 Iustin Pop
    subdir = "%s-%s.d" % (hpath, suffix)
3189 0411c011 Iustin Pop
    dir_name = utils.PathJoin(self._BASE_DIR, subdir)
3190 6bb65e3a Guido Trotter
3191 6bb65e3a Guido Trotter
    results = []
3192 a9b7e346 Iustin Pop
3193 a9b7e346 Iustin Pop
    if not os.path.isdir(dir_name):
3194 a9b7e346 Iustin Pop
      # for non-existing/non-dirs, we simply exit instead of logging a
3195 a9b7e346 Iustin Pop
      # warning at every operation
3196 a9b7e346 Iustin Pop
      return results
3197 a9b7e346 Iustin Pop
3198 a9b7e346 Iustin Pop
    runparts_results = utils.RunParts(dir_name, env=env, reset_env=True)
3199 a9b7e346 Iustin Pop
3200 6bb65e3a Guido Trotter
    for (relname, relstatus, runresult)  in runparts_results:
3201 6bb65e3a Guido Trotter
      if relstatus == constants.RUNPARTS_SKIP:
3202 a8083063 Iustin Pop
        rrval = constants.HKR_SKIP
3203 a8083063 Iustin Pop
        output = ""
3204 6bb65e3a Guido Trotter
      elif relstatus == constants.RUNPARTS_ERR:
3205 6bb65e3a Guido Trotter
        rrval = constants.HKR_FAIL
3206 6bb65e3a Guido Trotter
        output = "Hook script execution error: %s" % runresult
3207 6bb65e3a Guido Trotter
      elif relstatus == constants.RUNPARTS_RUN:
3208 6bb65e3a Guido Trotter
        if runresult.failed:
3209 a8083063 Iustin Pop
          rrval = constants.HKR_FAIL
3210 a8083063 Iustin Pop
        else:
3211 6bb65e3a Guido Trotter
          rrval = constants.HKR_SUCCESS
3212 6bb65e3a Guido Trotter
        output = utils.SafeEncode(runresult.output.strip())
3213 6bb65e3a Guido Trotter
      results.append(("%s/%s" % (subdir, relname), rrval, output))
3214 6bb65e3a Guido Trotter
3215 6bb65e3a Guido Trotter
    return results
3216 3f78eef2 Iustin Pop
3217 3f78eef2 Iustin Pop
3218 8d528b7c Iustin Pop
class IAllocatorRunner(object):
3219 8d528b7c Iustin Pop
  """IAllocator runner.
3220 8d528b7c Iustin Pop

3221 8d528b7c Iustin Pop
  This class is instantiated on the node side (ganeti-noded) and not on
3222 8d528b7c Iustin Pop
  the master side.
3223 8d528b7c Iustin Pop

3224 8d528b7c Iustin Pop
  """
3225 7e950d31 Iustin Pop
  @staticmethod
3226 7e950d31 Iustin Pop
  def Run(name, idata):
3227 8d528b7c Iustin Pop
    """Run an iallocator script.
3228 8d528b7c Iustin Pop

3229 10c2650b Iustin Pop
    @type name: str
3230 10c2650b Iustin Pop
    @param name: the iallocator script name
3231 10c2650b Iustin Pop
    @type idata: str
3232 10c2650b Iustin Pop
    @param idata: the allocator input data
3233 10c2650b Iustin Pop

3234 10c2650b Iustin Pop
    @rtype: tuple
3235 87f5c298 Iustin Pop
    @return: two element tuple of:
3236 87f5c298 Iustin Pop
       - status
3237 87f5c298 Iustin Pop
       - either error message or stdout of allocator (for success)
3238 8d528b7c Iustin Pop

3239 8d528b7c Iustin Pop
    """
3240 8d528b7c Iustin Pop
    alloc_script = utils.FindFile(name, constants.IALLOCATOR_SEARCH_PATH,
3241 8d528b7c Iustin Pop
                                  os.path.isfile)
3242 8d528b7c Iustin Pop
    if alloc_script is None:
3243 87f5c298 Iustin Pop
      _Fail("iallocator module '%s' not found in the search path", name)
3244 8d528b7c Iustin Pop
3245 8d528b7c Iustin Pop
    fd, fin_name = tempfile.mkstemp(prefix="ganeti-iallocator.")
3246 8d528b7c Iustin Pop
    try:
3247 8d528b7c Iustin Pop
      os.write(fd, idata)
3248 8d528b7c Iustin Pop
      os.close(fd)
3249 8d528b7c Iustin Pop
      result = utils.RunCmd([alloc_script, fin_name])
3250 8d528b7c Iustin Pop
      if result.failed:
3251 87f5c298 Iustin Pop
        _Fail("iallocator module '%s' failed: %s, output '%s'",
3252 87f5c298 Iustin Pop
              name, result.fail_reason, result.output)
3253 8d528b7c Iustin Pop
    finally:
3254 8d528b7c Iustin Pop
      os.unlink(fin_name)
3255 8d528b7c Iustin Pop
3256 c26a6bd2 Iustin Pop
    return result.stdout
3257 8d528b7c Iustin Pop
3258 8d528b7c Iustin Pop
3259 3f78eef2 Iustin Pop
class DevCacheManager(object):
3260 c99a3cc0 Manuel Franceschini
  """Simple class for managing a cache of block device information.
3261 3f78eef2 Iustin Pop

3262 3f78eef2 Iustin Pop
  """
3263 3f78eef2 Iustin Pop
  _DEV_PREFIX = "/dev/"
3264 3f78eef2 Iustin Pop
  _ROOT_DIR = constants.BDEV_CACHE_DIR
3265 3f78eef2 Iustin Pop
3266 3f78eef2 Iustin Pop
  @classmethod
3267 3f78eef2 Iustin Pop
  def _ConvertPath(cls, dev_path):
3268 3f78eef2 Iustin Pop
    """Converts a /dev/name path to the cache file name.
3269 3f78eef2 Iustin Pop

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

3273 10c2650b Iustin Pop
    @type dev_path: str
3274 10c2650b Iustin Pop
    @param dev_path: the C{/dev/} path name
3275 10c2650b Iustin Pop
    @rtype: str
3276 10c2650b Iustin Pop
    @return: the converted path name
3277 3f78eef2 Iustin Pop

3278 3f78eef2 Iustin Pop
    """
3279 3f78eef2 Iustin Pop
    if dev_path.startswith(cls._DEV_PREFIX):
3280 3f78eef2 Iustin Pop
      dev_path = dev_path[len(cls._DEV_PREFIX):]
3281 3f78eef2 Iustin Pop
    dev_path = dev_path.replace("/", "_")
3282 0411c011 Iustin Pop
    fpath = utils.PathJoin(cls._ROOT_DIR, "bdev_%s" % dev_path)
3283 3f78eef2 Iustin Pop
    return fpath
3284 3f78eef2 Iustin Pop
3285 3f78eef2 Iustin Pop
  @classmethod
3286 3f78eef2 Iustin Pop
  def UpdateCache(cls, dev_path, owner, on_primary, iv_name):
3287 3f78eef2 Iustin Pop
    """Updates the cache information for a given device.
3288 3f78eef2 Iustin Pop

3289 10c2650b Iustin Pop
    @type dev_path: str
3290 10c2650b Iustin Pop
    @param dev_path: the pathname of the device
3291 10c2650b Iustin Pop
    @type owner: str
3292 10c2650b Iustin Pop
    @param owner: the owner (instance name) of the device
3293 10c2650b Iustin Pop
    @type on_primary: bool
3294 10c2650b Iustin Pop
    @param on_primary: whether this is the primary
3295 10c2650b Iustin Pop
        node nor not
3296 10c2650b Iustin Pop
    @type iv_name: str
3297 10c2650b Iustin Pop
    @param iv_name: the instance-visible name of the
3298 c41eea6e Iustin Pop
        device, as in objects.Disk.iv_name
3299 10c2650b Iustin Pop

3300 10c2650b Iustin Pop
    @rtype: None
3301 10c2650b Iustin Pop

3302 3f78eef2 Iustin Pop
    """
3303 cf5a8306 Iustin Pop
    if dev_path is None:
3304 18682bca Iustin Pop
      logging.error("DevCacheManager.UpdateCache got a None dev_path")
3305 cf5a8306 Iustin Pop
      return
3306 3f78eef2 Iustin Pop
    fpath = cls._ConvertPath(dev_path)
3307 3f78eef2 Iustin Pop
    if on_primary:
3308 3f78eef2 Iustin Pop
      state = "primary"
3309 3f78eef2 Iustin Pop
    else:
3310 3f78eef2 Iustin Pop
      state = "secondary"
3311 3f78eef2 Iustin Pop
    if iv_name is None:
3312 3f78eef2 Iustin Pop
      iv_name = "not_visible"
3313 3f78eef2 Iustin Pop
    fdata = "%s %s %s\n" % (str(owner), state, iv_name)
3314 3f78eef2 Iustin Pop
    try:
3315 3f78eef2 Iustin Pop
      utils.WriteFile(fpath, data=fdata)
3316 3f78eef2 Iustin Pop
    except EnvironmentError, err:
3317 29921401 Iustin Pop
      logging.exception("Can't update bdev cache for %s: %s", dev_path, err)
3318 3f78eef2 Iustin Pop
3319 3f78eef2 Iustin Pop
  @classmethod
3320 3f78eef2 Iustin Pop
  def RemoveCache(cls, dev_path):
3321 3f78eef2 Iustin Pop
    """Remove data for a dev_path.
3322 3f78eef2 Iustin Pop

3323 10c2650b Iustin Pop
    This is just a wrapper over L{utils.RemoveFile} with a converted
3324 10c2650b Iustin Pop
    path name and logging.
3325 10c2650b Iustin Pop

3326 10c2650b Iustin Pop
    @type dev_path: str
3327 10c2650b Iustin Pop
    @param dev_path: the pathname of the device
3328 10c2650b Iustin Pop

3329 10c2650b Iustin Pop
    @rtype: None
3330 10c2650b Iustin Pop

3331 3f78eef2 Iustin Pop
    """
3332 cf5a8306 Iustin Pop
    if dev_path is None:
3333 18682bca Iustin Pop
      logging.error("DevCacheManager.RemoveCache got a None dev_path")
3334 cf5a8306 Iustin Pop
      return
3335 3f78eef2 Iustin Pop
    fpath = cls._ConvertPath(dev_path)
3336 3f78eef2 Iustin Pop
    try:
3337 3f78eef2 Iustin Pop
      utils.RemoveFile(fpath)
3338 3f78eef2 Iustin Pop
    except EnvironmentError, err:
3339 29921401 Iustin Pop
      logging.exception("Can't update bdev cache for %s: %s", dev_path, err)