Statistics
| Branch: | Tag: | Revision:

root / lib / backend.py @ b44b0141

History | View | Annotate | Download (87.3 kB)

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

24 360b0dc2 Iustin Pop
@var _ALLOWED_UPLOAD_FILES: denotes which files are accepted in
25 360b0dc2 Iustin Pop
     the L{UploadFile} function
26 360b0dc2 Iustin Pop

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

66 2cc6781a Iustin Pop
  Its argument is the error message.
67 2cc6781a Iustin Pop

68 2cc6781a Iustin Pop
  """
69 2cc6781a Iustin Pop
70 13998ef2 Michael Hanselmann
71 2cc6781a Iustin Pop
def _Fail(msg, *args, **kwargs):
72 2cc6781a Iustin Pop
  """Log an error and the raise an RPCFail exception.
73 2cc6781a Iustin Pop

74 2cc6781a Iustin Pop
  This exception is then handled specially in the ganeti daemon and
75 2cc6781a Iustin Pop
  turned into a 'failed' return type. As such, this function is a
76 2cc6781a Iustin Pop
  useful shortcut for logging the error and returning it to the master
77 2cc6781a Iustin Pop
  daemon.
78 2cc6781a Iustin Pop

79 2cc6781a Iustin Pop
  @type msg: string
80 2cc6781a Iustin Pop
  @param msg: the text of the exception
81 2cc6781a Iustin Pop
  @raise RPCFail
82 2cc6781a Iustin Pop

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

97 93384844 Iustin Pop
  @rtype: L{ssconf.SimpleStore}
98 93384844 Iustin Pop
  @return: a SimpleStore instance
99 10c2650b Iustin Pop

100 10c2650b Iustin Pop
  """
101 93384844 Iustin Pop
  return ssconf.SimpleStore()
102 c657dcc9 Michael Hanselmann
103 c657dcc9 Michael Hanselmann
104 62c9ec92 Iustin Pop
def _GetSshRunner(cluster_name):
105 10c2650b Iustin Pop
  """Simple wrapper to return an SshRunner.
106 10c2650b Iustin Pop

107 10c2650b Iustin Pop
  @type cluster_name: str
108 10c2650b Iustin Pop
  @param cluster_name: the cluster name, which is needed
109 10c2650b Iustin Pop
      by the SshRunner constructor
110 10c2650b Iustin Pop
  @rtype: L{ssh.SshRunner}
111 10c2650b Iustin Pop
  @return: an SshRunner instance
112 10c2650b Iustin Pop

113 10c2650b Iustin Pop
  """
114 62c9ec92 Iustin Pop
  return ssh.SshRunner(cluster_name)
115 c92b310a Michael Hanselmann
116 c92b310a Michael Hanselmann
117 12bce260 Michael Hanselmann
def _Decompress(data):
118 12bce260 Michael Hanselmann
  """Unpacks data compressed by the RPC client.
119 12bce260 Michael Hanselmann

120 12bce260 Michael Hanselmann
  @type data: list or tuple
121 12bce260 Michael Hanselmann
  @param data: Data sent by RPC client
122 12bce260 Michael Hanselmann
  @rtype: str
123 12bce260 Michael Hanselmann
  @return: Decompressed data
124 12bce260 Michael Hanselmann

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

140 10c2650b Iustin Pop
  @type path: str
141 10c2650b Iustin Pop
  @param path: the directory to clean
142 76ab5558 Michael Hanselmann
  @type exclude: list
143 10c2650b Iustin Pop
  @param exclude: list of files to be excluded, defaults
144 10c2650b Iustin Pop
      to the empty list
145 76ab5558 Michael Hanselmann

146 76ab5558 Michael Hanselmann
  """
147 3956cee1 Michael Hanselmann
  if not os.path.isdir(path):
148 3956cee1 Michael Hanselmann
    return
149 3bc6be5c Iustin Pop
  if exclude is None:
150 3bc6be5c Iustin Pop
    exclude = []
151 3bc6be5c Iustin Pop
  else:
152 3bc6be5c Iustin Pop
    # Normalize excluded paths
153 3bc6be5c Iustin Pop
    exclude = [os.path.normpath(i) for i in exclude]
154 76ab5558 Michael Hanselmann
155 3956cee1 Michael Hanselmann
  for rel_name in utils.ListVisibleFiles(path):
156 76ab5558 Michael Hanselmann
    full_name = os.path.normpath(os.path.join(path, rel_name))
157 76ab5558 Michael Hanselmann
    if full_name in exclude:
158 76ab5558 Michael Hanselmann
      continue
159 3956cee1 Michael Hanselmann
    if os.path.isfile(full_name) and not os.path.islink(full_name):
160 3956cee1 Michael Hanselmann
      utils.RemoveFile(full_name)
161 3956cee1 Michael Hanselmann
162 3956cee1 Michael Hanselmann
163 360b0dc2 Iustin Pop
def _BuildUploadFileList():
164 360b0dc2 Iustin Pop
  """Build the list of allowed upload files.
165 360b0dc2 Iustin Pop

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

168 360b0dc2 Iustin Pop
  """
169 b397a7d2 Iustin Pop
  allowed_files = set([
170 b397a7d2 Iustin Pop
    constants.CLUSTER_CONF_FILE,
171 b397a7d2 Iustin Pop
    constants.ETC_HOSTS,
172 b397a7d2 Iustin Pop
    constants.SSH_KNOWN_HOSTS_FILE,
173 b397a7d2 Iustin Pop
    constants.VNC_PASSWORD_FILE,
174 b397a7d2 Iustin Pop
    constants.RAPI_CERT_FILE,
175 b397a7d2 Iustin Pop
    constants.RAPI_USERS_FILE,
176 4a34c5cf Guido Trotter
    constants.HMAC_CLUSTER_KEY,
177 b397a7d2 Iustin Pop
    ])
178 b397a7d2 Iustin Pop
179 b397a7d2 Iustin Pop
  for hv_name in constants.HYPER_TYPES:
180 e5a45a16 Iustin Pop
    hv_class = hypervisor.GetHypervisorClass(hv_name)
181 b397a7d2 Iustin Pop
    allowed_files.update(hv_class.GetAncillaryFiles())
182 b397a7d2 Iustin Pop
183 b397a7d2 Iustin Pop
  return frozenset(allowed_files)
184 360b0dc2 Iustin Pop
185 360b0dc2 Iustin Pop
186 360b0dc2 Iustin Pop
_ALLOWED_UPLOAD_FILES = _BuildUploadFileList()
187 360b0dc2 Iustin Pop
188 360b0dc2 Iustin Pop
189 1bc59f76 Michael Hanselmann
def JobQueuePurge():
190 10c2650b Iustin Pop
  """Removes job queue files and archived jobs.
191 10c2650b Iustin Pop

192 c8457ce7 Iustin Pop
  @rtype: tuple
193 c8457ce7 Iustin Pop
  @return: True, None
194 24fc781f Michael Hanselmann

195 24fc781f Michael Hanselmann
  """
196 1bc59f76 Michael Hanselmann
  _CleanDirectory(constants.QUEUE_DIR, exclude=[constants.JOB_QUEUE_LOCK_FILE])
197 24fc781f Michael Hanselmann
  _CleanDirectory(constants.JOB_QUEUE_ARCHIVE_DIR)
198 24fc781f Michael Hanselmann
199 24fc781f Michael Hanselmann
200 bd1e4562 Iustin Pop
def GetMasterInfo():
201 bd1e4562 Iustin Pop
  """Returns master information.
202 bd1e4562 Iustin Pop

203 bd1e4562 Iustin Pop
  This is an utility function to compute master information, either
204 bd1e4562 Iustin Pop
  for consumption here or from the node daemon.
205 bd1e4562 Iustin Pop

206 bd1e4562 Iustin Pop
  @rtype: tuple
207 c26a6bd2 Iustin Pop
  @return: master_netdev, master_ip, master_name
208 2a52a064 Iustin Pop
  @raise RPCFail: in case of errors
209 b1b6ea87 Iustin Pop

210 b1b6ea87 Iustin Pop
  """
211 b1b6ea87 Iustin Pop
  try:
212 c657dcc9 Michael Hanselmann
    cfg = _GetConfig()
213 c657dcc9 Michael Hanselmann
    master_netdev = cfg.GetMasterNetdev()
214 c657dcc9 Michael Hanselmann
    master_ip = cfg.GetMasterIP()
215 c657dcc9 Michael Hanselmann
    master_node = cfg.GetMasterNode()
216 b1b6ea87 Iustin Pop
  except errors.ConfigurationError, err:
217 29921401 Iustin Pop
    _Fail("Cluster configuration incomplete: %s", err, exc=True)
218 bd1e4562 Iustin Pop
  return (master_netdev, master_ip, master_node)
219 b1b6ea87 Iustin Pop
220 b1b6ea87 Iustin Pop
221 3583908a Guido Trotter
def StartMaster(start_daemons, no_voting):
222 a8083063 Iustin Pop
  """Activate local node as master node.
223 a8083063 Iustin Pop

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

228 10c2650b Iustin Pop
  @type start_daemons: boolean
229 c26a6bd2 Iustin Pop
  @param start_daemons: whether to also start the master
230 10c2650b Iustin Pop
      daemons (ganeti-masterd and ganeti-rapi)
231 3583908a Guido Trotter
  @type no_voting: boolean
232 3583908a Guido Trotter
  @param no_voting: whether to start ganeti-masterd without a node vote
233 3583908a Guido Trotter
      (if start_daemons is True), but still non-interactively
234 10c2650b Iustin Pop
  @rtype: None
235 a8083063 Iustin Pop

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

286 1c65840b Iustin Pop
  The function will always try to deactivate the IP address of the
287 10c2650b Iustin Pop
  master. It will also stop the master daemons depending on the
288 10c2650b Iustin Pop
  stop_daemons parameter.
289 10c2650b Iustin Pop

290 10c2650b Iustin Pop
  @type stop_daemons: boolean
291 10c2650b Iustin Pop
  @param stop_daemons: whether to also stop the master daemons
292 10c2650b Iustin Pop
      (ganeti-masterd and ganeti-rapi)
293 10c2650b Iustin Pop
  @rtype: None
294 a8083063 Iustin Pop

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

319 7900ed01 Iustin Pop
  This does the following:
320 7900ed01 Iustin Pop
      - updates the hostkeys of the machine (rsa and dsa)
321 7900ed01 Iustin Pop
      - adds the ssh private key to the user
322 7900ed01 Iustin Pop
      - adds the ssh public key to the users' authorized_keys file
323 a8083063 Iustin Pop

324 10c2650b Iustin Pop
  @type dsa: str
325 10c2650b Iustin Pop
  @param dsa: the DSA private key to write
326 10c2650b Iustin Pop
  @type dsapub: str
327 10c2650b Iustin Pop
  @param dsapub: the DSA public key to write
328 10c2650b Iustin Pop
  @type rsa: str
329 10c2650b Iustin Pop
  @param rsa: the RSA private key to write
330 10c2650b Iustin Pop
  @type rsapub: str
331 10c2650b Iustin Pop
  @param rsapub: the RSA public key to write
332 10c2650b Iustin Pop
  @type sshkey: str
333 10c2650b Iustin Pop
  @param sshkey: the SSH private key to write
334 10c2650b Iustin Pop
  @type sshpub: str
335 10c2650b Iustin Pop
  @param sshpub: the SSH public key to write
336 10c2650b Iustin Pop
  @rtype: boolean
337 10c2650b Iustin Pop
  @return: the success of the operation
338 10c2650b Iustin Pop

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

367 10c2650b Iustin Pop
  This function cleans up and prepares the current node to be removed
368 10c2650b Iustin Pop
  from the cluster.
369 10c2650b Iustin Pop

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

374 b989b9d9 Ken Wehr
  @param modify_ssh_setup: boolean
375 b989b9d9 Ken Wehr

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

410 e69d05fd Iustin Pop
  @type vgname: C{string}
411 e69d05fd Iustin Pop
  @param vgname: the name of the volume group to ask for disk space information
412 e69d05fd Iustin Pop
  @type hypervisor_type: C{str}
413 e69d05fd Iustin Pop
  @param hypervisor_type: the name of the hypervisor to ask for
414 e69d05fd Iustin Pop
      memory information
415 e69d05fd Iustin Pop
  @rtype: C{dict}
416 e69d05fd Iustin Pop
  @return: dictionary with the following keys:
417 e69d05fd Iustin Pop
      - vg_size is the size of the configured volume group in MiB
418 e69d05fd Iustin Pop
      - vg_free is the free size of the volume group in MiB
419 e69d05fd Iustin Pop
      - memory_dom0 is the memory allocated for domain0 in MiB
420 e69d05fd Iustin Pop
      - memory_free is the currently available (free) ram in MiB
421 e69d05fd Iustin Pop
      - memory_total is the total number of ram in MiB
422 a8083063 Iustin Pop

423 098c0958 Michael Hanselmann
  """
424 a8083063 Iustin Pop
  outputarray = {}
425 a8083063 Iustin Pop
  vginfo = _GetVGInfo(vgname)
426 a8083063 Iustin Pop
  outputarray['vg_size'] = vginfo['vg_size']
427 a8083063 Iustin Pop
  outputarray['vg_free'] = vginfo['vg_free']
428 a8083063 Iustin Pop
429 e69d05fd Iustin Pop
  hyper = hypervisor.GetHypervisor(hypervisor_type)
430 a8083063 Iustin Pop
  hyp_info = hyper.GetNodeInfo()
431 a8083063 Iustin Pop
  if hyp_info is not None:
432 a8083063 Iustin Pop
    outputarray.update(hyp_info)
433 a8083063 Iustin Pop
434 13998ef2 Michael Hanselmann
  outputarray["bootid"] = utils.ReadFile(_BOOT_ID_PATH, size=128).rstrip("\n")
435 3ef10550 Michael Hanselmann
436 c26a6bd2 Iustin Pop
  return outputarray
437 a8083063 Iustin Pop
438 a8083063 Iustin Pop
439 62c9ec92 Iustin Pop
def VerifyNode(what, cluster_name):
440 a8083063 Iustin Pop
  """Verify the status of the local node.
441 a8083063 Iustin Pop

442 e69d05fd Iustin Pop
  Based on the input L{what} parameter, various checks are done on the
443 e69d05fd Iustin Pop
  local node.
444 e69d05fd Iustin Pop

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

448 e69d05fd Iustin Pop
  If the I{nodelist} key is present, we check that we have
449 e69d05fd Iustin Pop
  connectivity via ssh with the target nodes (and check the hostname
450 e69d05fd Iustin Pop
  report).
451 a8083063 Iustin Pop

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

456 e69d05fd Iustin Pop
  @type what: C{dict}
457 e69d05fd Iustin Pop
  @param what: a dictionary of things to check:
458 e69d05fd Iustin Pop
      - filelist: list of files for which to compute checksums
459 e69d05fd Iustin Pop
      - nodelist: list of nodes we should check ssh communication with
460 e69d05fd Iustin Pop
      - node-net-test: list of nodes we should check node daemon port
461 e69d05fd Iustin Pop
        connectivity with
462 e69d05fd Iustin Pop
      - hypervisor: list with hypervisors to run the verify for
463 10c2650b Iustin Pop
  @rtype: dict
464 10c2650b Iustin Pop
  @return: a dictionary with the same keys as the input dict, and
465 10c2650b Iustin Pop
      values representing the result of the checks
466 a8083063 Iustin Pop

467 a8083063 Iustin Pop
  """
468 a8083063 Iustin Pop
  result = {}
469 a8083063 Iustin Pop
470 25361b9a Iustin Pop
  if constants.NV_HYPERVISOR in what:
471 25361b9a Iustin Pop
    result[constants.NV_HYPERVISOR] = tmp = {}
472 25361b9a Iustin Pop
    for hv_name in what[constants.NV_HYPERVISOR]:
473 25361b9a Iustin Pop
      tmp[hv_name] = hypervisor.GetHypervisor(hv_name).Verify()
474 25361b9a Iustin Pop
475 25361b9a Iustin Pop
  if constants.NV_FILELIST in what:
476 25361b9a Iustin Pop
    result[constants.NV_FILELIST] = utils.FingerprintFiles(
477 25361b9a Iustin Pop
      what[constants.NV_FILELIST])
478 25361b9a Iustin Pop
479 25361b9a Iustin Pop
  if constants.NV_NODELIST in what:
480 25361b9a Iustin Pop
    result[constants.NV_NODELIST] = tmp = {}
481 25361b9a Iustin Pop
    random.shuffle(what[constants.NV_NODELIST])
482 25361b9a Iustin Pop
    for node in what[constants.NV_NODELIST]:
483 62c9ec92 Iustin Pop
      success, message = _GetSshRunner(cluster_name).VerifyNodeHostname(node)
484 a8083063 Iustin Pop
      if not success:
485 25361b9a Iustin Pop
        tmp[node] = message
486 25361b9a Iustin Pop
487 25361b9a Iustin Pop
  if constants.NV_NODENETTEST in what:
488 25361b9a Iustin Pop
    result[constants.NV_NODENETTEST] = tmp = {}
489 9d4bfc96 Iustin Pop
    my_name = utils.HostInfo().name
490 9d4bfc96 Iustin Pop
    my_pip = my_sip = None
491 25361b9a Iustin Pop
    for name, pip, sip in what[constants.NV_NODENETTEST]:
492 9d4bfc96 Iustin Pop
      if name == my_name:
493 9d4bfc96 Iustin Pop
        my_pip = pip
494 9d4bfc96 Iustin Pop
        my_sip = sip
495 9d4bfc96 Iustin Pop
        break
496 9d4bfc96 Iustin Pop
    if not my_pip:
497 25361b9a Iustin Pop
      tmp[my_name] = ("Can't find my own primary/secondary IP"
498 25361b9a Iustin Pop
                      " in the node list")
499 9d4bfc96 Iustin Pop
    else:
500 cd50653c Guido Trotter
      port = utils.GetDaemonPort(constants.NODED)
501 25361b9a Iustin Pop
      for name, pip, sip in what[constants.NV_NODENETTEST]:
502 9d4bfc96 Iustin Pop
        fail = []
503 9d4bfc96 Iustin Pop
        if not utils.TcpPing(pip, port, source=my_pip):
504 9d4bfc96 Iustin Pop
          fail.append("primary")
505 9d4bfc96 Iustin Pop
        if sip != pip:
506 9d4bfc96 Iustin Pop
          if not utils.TcpPing(sip, port, source=my_sip):
507 9d4bfc96 Iustin Pop
            fail.append("secondary")
508 9d4bfc96 Iustin Pop
        if fail:
509 25361b9a Iustin Pop
          tmp[name] = ("failure using the %s interface(s)" %
510 25361b9a Iustin Pop
                       " and ".join(fail))
511 25361b9a Iustin Pop
512 25361b9a Iustin Pop
  if constants.NV_LVLIST in what:
513 25361b9a Iustin Pop
    result[constants.NV_LVLIST] = GetVolumeList(what[constants.NV_LVLIST])
514 25361b9a Iustin Pop
515 25361b9a Iustin Pop
  if constants.NV_INSTANCELIST in what:
516 25361b9a Iustin Pop
    result[constants.NV_INSTANCELIST] = GetInstanceList(
517 25361b9a Iustin Pop
      what[constants.NV_INSTANCELIST])
518 25361b9a Iustin Pop
519 25361b9a Iustin Pop
  if constants.NV_VGLIST in what:
520 e480923b Iustin Pop
    result[constants.NV_VGLIST] = utils.ListVolumeGroups()
521 25361b9a Iustin Pop
522 d091393e Iustin Pop
  if constants.NV_PVLIST in what:
523 d091393e Iustin Pop
    result[constants.NV_PVLIST] = \
524 d091393e Iustin Pop
      bdev.LogicalVolume.GetPVInfo(what[constants.NV_PVLIST],
525 d091393e Iustin Pop
                                   filter_allocatable=False)
526 d091393e Iustin Pop
527 25361b9a Iustin Pop
  if constants.NV_VERSION in what:
528 e9ce0a64 Iustin Pop
    result[constants.NV_VERSION] = (constants.PROTOCOL_VERSION,
529 e9ce0a64 Iustin Pop
                                    constants.RELEASE_VERSION)
530 25361b9a Iustin Pop
531 25361b9a Iustin Pop
  if constants.NV_HVINFO in what:
532 25361b9a Iustin Pop
    hyper = hypervisor.GetHypervisor(what[constants.NV_HVINFO])
533 25361b9a Iustin Pop
    result[constants.NV_HVINFO] = hyper.GetNodeInfo()
534 9d4bfc96 Iustin Pop
535 6d2e83d5 Iustin Pop
  if constants.NV_DRBDLIST in what:
536 6d2e83d5 Iustin Pop
    try:
537 6d2e83d5 Iustin Pop
      used_minors = bdev.DRBD8.GetUsedDevs().keys()
538 f6eaed12 Iustin Pop
    except errors.BlockDeviceError, err:
539 6d2e83d5 Iustin Pop
      logging.warning("Can't get used minors list", exc_info=True)
540 f6eaed12 Iustin Pop
      used_minors = str(err)
541 6d2e83d5 Iustin Pop
    result[constants.NV_DRBDLIST] = used_minors
542 6d2e83d5 Iustin Pop
543 7c0aa8e9 Iustin Pop
  if constants.NV_NODESETUP in what:
544 7c0aa8e9 Iustin Pop
    result[constants.NV_NODESETUP] = tmpr = []
545 7c0aa8e9 Iustin Pop
    if not os.path.isdir("/sys/block") or not os.path.isdir("/sys/class/net"):
546 7c0aa8e9 Iustin Pop
      tmpr.append("The sysfs filesytem doesn't seem to be mounted"
547 7c0aa8e9 Iustin Pop
                  " under /sys, missing required directories /sys/block"
548 7c0aa8e9 Iustin Pop
                  " and /sys/class/net")
549 7c0aa8e9 Iustin Pop
    if (not os.path.isdir("/proc/sys") or
550 7c0aa8e9 Iustin Pop
        not os.path.isfile("/proc/sysrq-trigger")):
551 7c0aa8e9 Iustin Pop
      tmpr.append("The procfs filesystem doesn't seem to be mounted"
552 7c0aa8e9 Iustin Pop
                  " under /proc, missing required directory /proc/sys and"
553 7c0aa8e9 Iustin Pop
                  " the file /proc/sysrq-trigger")
554 313b2dd4 Michael Hanselmann
555 313b2dd4 Michael Hanselmann
  if constants.NV_TIME in what:
556 313b2dd4 Michael Hanselmann
    result[constants.NV_TIME] = utils.SplitTime(time.time())
557 313b2dd4 Michael Hanselmann
558 c26a6bd2 Iustin Pop
  return result
559 a8083063 Iustin Pop
560 a8083063 Iustin Pop
561 a8083063 Iustin Pop
def GetVolumeList(vg_name):
562 a8083063 Iustin Pop
  """Compute list of logical volumes and their size.
563 a8083063 Iustin Pop

564 10c2650b Iustin Pop
  @type vg_name: str
565 10c2650b Iustin Pop
  @param vg_name: the volume group whose LVs we should list
566 10c2650b Iustin Pop
  @rtype: dict
567 10c2650b Iustin Pop
  @return:
568 10c2650b Iustin Pop
      dictionary of all partions (key) with value being a tuple of
569 10c2650b Iustin Pop
      their size (in MiB), inactive and online status::
570 10c2650b Iustin Pop

571 10c2650b Iustin Pop
        {'test1': ('20.06', True, True)}
572 10c2650b Iustin Pop

573 10c2650b Iustin Pop
      in case of errors, a string is returned with the error
574 10c2650b Iustin Pop
      details.
575 a8083063 Iustin Pop

576 a8083063 Iustin Pop
  """
577 cb2037a2 Iustin Pop
  lvs = {}
578 cb2037a2 Iustin Pop
  sep = '|'
579 cb2037a2 Iustin Pop
  result = utils.RunCmd(["lvs", "--noheadings", "--units=m", "--nosuffix",
580 cb2037a2 Iustin Pop
                         "--separator=%s" % sep,
581 cb2037a2 Iustin Pop
                         "-olv_name,lv_size,lv_attr", vg_name])
582 a8083063 Iustin Pop
  if result.failed:
583 29d376ec Iustin Pop
    _Fail("Failed to list logical volumes, lvs output: %s", result.output)
584 cb2037a2 Iustin Pop
585 df4c2628 Iustin Pop
  valid_line_re = re.compile("^ *([^|]+)\|([0-9.]+)\|([^|]{6})\|?$")
586 cb2037a2 Iustin Pop
  for line in result.stdout.splitlines():
587 df4c2628 Iustin Pop
    line = line.strip()
588 df4c2628 Iustin Pop
    match = valid_line_re.match(line)
589 df4c2628 Iustin Pop
    if not match:
590 18682bca Iustin Pop
      logging.error("Invalid line returned from lvs output: '%s'", line)
591 df4c2628 Iustin Pop
      continue
592 df4c2628 Iustin Pop
    name, size, attr = match.groups()
593 cb2037a2 Iustin Pop
    inactive = attr[4] == '-'
594 cb2037a2 Iustin Pop
    online = attr[5] == 'o'
595 33f2a81a Iustin Pop
    virtual = attr[0] == 'v'
596 33f2a81a Iustin Pop
    if virtual:
597 33f2a81a Iustin Pop
      # we don't want to report such volumes as existing, since they
598 33f2a81a Iustin Pop
      # don't really hold data
599 33f2a81a Iustin Pop
      continue
600 cb2037a2 Iustin Pop
    lvs[name] = (size, inactive, online)
601 cb2037a2 Iustin Pop
602 cb2037a2 Iustin Pop
  return lvs
603 a8083063 Iustin Pop
604 a8083063 Iustin Pop
605 a8083063 Iustin Pop
def ListVolumeGroups():
606 2f8598a5 Alexander Schreiber
  """List the volume groups and their size.
607 a8083063 Iustin Pop

608 10c2650b Iustin Pop
  @rtype: dict
609 10c2650b Iustin Pop
  @return: dictionary with keys volume name and values the
610 10c2650b Iustin Pop
      size of the volume
611 a8083063 Iustin Pop

612 a8083063 Iustin Pop
  """
613 c26a6bd2 Iustin Pop
  return utils.ListVolumeGroups()
614 a8083063 Iustin Pop
615 a8083063 Iustin Pop
616 dcb93971 Michael Hanselmann
def NodeVolumes():
617 dcb93971 Michael Hanselmann
  """List all volumes on this node.
618 dcb93971 Michael Hanselmann

619 10c2650b Iustin Pop
  @rtype: list
620 10c2650b Iustin Pop
  @return:
621 10c2650b Iustin Pop
    A list of dictionaries, each having four keys:
622 10c2650b Iustin Pop
      - name: the logical volume name,
623 10c2650b Iustin Pop
      - size: the size of the logical volume
624 10c2650b Iustin Pop
      - dev: the physical device on which the LV lives
625 10c2650b Iustin Pop
      - vg: the volume group to which it belongs
626 10c2650b Iustin Pop

627 10c2650b Iustin Pop
    In case of errors, we return an empty list and log the
628 10c2650b Iustin Pop
    error.
629 10c2650b Iustin Pop

630 10c2650b Iustin Pop
    Note that since a logical volume can live on multiple physical
631 10c2650b Iustin Pop
    volumes, the resulting list might include a logical volume
632 10c2650b Iustin Pop
    multiple times.
633 10c2650b Iustin Pop

634 dcb93971 Michael Hanselmann
  """
635 dcb93971 Michael Hanselmann
  result = utils.RunCmd(["lvs", "--noheadings", "--units=m", "--nosuffix",
636 dcb93971 Michael Hanselmann
                         "--separator=|",
637 dcb93971 Michael Hanselmann
                         "--options=lv_name,lv_size,devices,vg_name"])
638 dcb93971 Michael Hanselmann
  if result.failed:
639 10bfe6cb Iustin Pop
    _Fail("Failed to list logical volumes, lvs output: %s",
640 10bfe6cb Iustin Pop
          result.output)
641 dcb93971 Michael Hanselmann
642 dcb93971 Michael Hanselmann
  def parse_dev(dev):
643 dcb93971 Michael Hanselmann
    if '(' in dev:
644 dcb93971 Michael Hanselmann
      return dev.split('(')[0]
645 dcb93971 Michael Hanselmann
    else:
646 dcb93971 Michael Hanselmann
      return dev
647 dcb93971 Michael Hanselmann
648 dcb93971 Michael Hanselmann
  def map_line(line):
649 dcb93971 Michael Hanselmann
    return {
650 dcb93971 Michael Hanselmann
      'name': line[0].strip(),
651 dcb93971 Michael Hanselmann
      'size': line[1].strip(),
652 dcb93971 Michael Hanselmann
      'dev': parse_dev(line[2].strip()),
653 dcb93971 Michael Hanselmann
      'vg': line[3].strip(),
654 dcb93971 Michael Hanselmann
    }
655 dcb93971 Michael Hanselmann
656 c26a6bd2 Iustin Pop
  return [map_line(line.split('|')) for line in result.stdout.splitlines()
657 c26a6bd2 Iustin Pop
          if line.count('|') >= 3]
658 dcb93971 Michael Hanselmann
659 dcb93971 Michael Hanselmann
660 a8083063 Iustin Pop
def BridgesExist(bridges_list):
661 2f8598a5 Alexander Schreiber
  """Check if a list of bridges exist on the current node.
662 a8083063 Iustin Pop

663 b1206984 Iustin Pop
  @rtype: boolean
664 b1206984 Iustin Pop
  @return: C{True} if all of them exist, C{False} otherwise
665 a8083063 Iustin Pop

666 a8083063 Iustin Pop
  """
667 35c0c8da Iustin Pop
  missing = []
668 a8083063 Iustin Pop
  for bridge in bridges_list:
669 a8083063 Iustin Pop
    if not utils.BridgeExists(bridge):
670 35c0c8da Iustin Pop
      missing.append(bridge)
671 a8083063 Iustin Pop
672 35c0c8da Iustin Pop
  if missing:
673 1f864b60 Iustin Pop
    _Fail("Missing bridges %s", utils.CommaJoin(missing))
674 35c0c8da Iustin Pop
675 a8083063 Iustin Pop
676 e69d05fd Iustin Pop
def GetInstanceList(hypervisor_list):
677 2f8598a5 Alexander Schreiber
  """Provides a list of instances.
678 a8083063 Iustin Pop

679 e69d05fd Iustin Pop
  @type hypervisor_list: list
680 e69d05fd Iustin Pop
  @param hypervisor_list: the list of hypervisors to query information
681 e69d05fd Iustin Pop

682 e69d05fd Iustin Pop
  @rtype: list
683 e69d05fd Iustin Pop
  @return: a list of all running instances on the current node
684 10c2650b Iustin Pop
    - instance1.example.com
685 10c2650b Iustin Pop
    - instance2.example.com
686 a8083063 Iustin Pop

687 098c0958 Michael Hanselmann
  """
688 e69d05fd Iustin Pop
  results = []
689 e69d05fd Iustin Pop
  for hname in hypervisor_list:
690 e69d05fd Iustin Pop
    try:
691 e69d05fd Iustin Pop
      names = hypervisor.GetHypervisor(hname).ListInstances()
692 e69d05fd Iustin Pop
      results.extend(names)
693 e69d05fd Iustin Pop
    except errors.HypervisorError, err:
694 aca13712 Iustin Pop
      _Fail("Error enumerating instances (hypervisor %s): %s",
695 aca13712 Iustin Pop
            hname, err, exc=True)
696 a8083063 Iustin Pop
697 e69d05fd Iustin Pop
  return results
698 a8083063 Iustin Pop
699 a8083063 Iustin Pop
700 e69d05fd Iustin Pop
def GetInstanceInfo(instance, hname):
701 5bbd3f7f Michael Hanselmann
  """Gives back the information about an instance as a dictionary.
702 a8083063 Iustin Pop

703 e69d05fd Iustin Pop
  @type instance: string
704 e69d05fd Iustin Pop
  @param instance: the instance name
705 e69d05fd Iustin Pop
  @type hname: string
706 e69d05fd Iustin Pop
  @param hname: the hypervisor type of the instance
707 a8083063 Iustin Pop

708 e69d05fd Iustin Pop
  @rtype: dict
709 e69d05fd Iustin Pop
  @return: dictionary with the following keys:
710 e69d05fd Iustin Pop
      - memory: memory size of instance (int)
711 e69d05fd Iustin Pop
      - state: xen state of instance (string)
712 e69d05fd Iustin Pop
      - time: cpu time of instance (float)
713 a8083063 Iustin Pop

714 098c0958 Michael Hanselmann
  """
715 a8083063 Iustin Pop
  output = {}
716 a8083063 Iustin Pop
717 e69d05fd Iustin Pop
  iinfo = hypervisor.GetHypervisor(hname).GetInstanceInfo(instance)
718 a8083063 Iustin Pop
  if iinfo is not None:
719 a8083063 Iustin Pop
    output['memory'] = iinfo[2]
720 a8083063 Iustin Pop
    output['state'] = iinfo[4]
721 a8083063 Iustin Pop
    output['time'] = iinfo[5]
722 a8083063 Iustin Pop
723 c26a6bd2 Iustin Pop
  return output
724 a8083063 Iustin Pop
725 a8083063 Iustin Pop
726 56e7640c Iustin Pop
def GetInstanceMigratable(instance):
727 56e7640c Iustin Pop
  """Gives whether an instance can be migrated.
728 56e7640c Iustin Pop

729 56e7640c Iustin Pop
  @type instance: L{objects.Instance}
730 56e7640c Iustin Pop
  @param instance: object representing the instance to be checked.
731 56e7640c Iustin Pop

732 56e7640c Iustin Pop
  @rtype: tuple
733 56e7640c Iustin Pop
  @return: tuple of (result, description) where:
734 56e7640c Iustin Pop
      - result: whether the instance can be migrated or not
735 56e7640c Iustin Pop
      - description: a description of the issue, if relevant
736 56e7640c Iustin Pop

737 56e7640c Iustin Pop
  """
738 56e7640c Iustin Pop
  hyper = hypervisor.GetHypervisor(instance.hypervisor)
739 afdc3985 Iustin Pop
  iname = instance.name
740 afdc3985 Iustin Pop
  if iname not in hyper.ListInstances():
741 afdc3985 Iustin Pop
    _Fail("Instance %s is not running", iname)
742 56e7640c Iustin Pop
743 56e7640c Iustin Pop
  for idx in range(len(instance.disks)):
744 afdc3985 Iustin Pop
    link_name = _GetBlockDevSymlinkPath(iname, idx)
745 56e7640c Iustin Pop
    if not os.path.islink(link_name):
746 afdc3985 Iustin Pop
      _Fail("Instance %s was not restarted since ganeti 1.2.5", iname)
747 56e7640c Iustin Pop
748 56e7640c Iustin Pop
749 e69d05fd Iustin Pop
def GetAllInstancesInfo(hypervisor_list):
750 a8083063 Iustin Pop
  """Gather data about all instances.
751 a8083063 Iustin Pop

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

756 e69d05fd Iustin Pop
  @type hypervisor_list: list
757 e69d05fd Iustin Pop
  @param hypervisor_list: list of hypervisors to query for instance data
758 e69d05fd Iustin Pop

759 955db481 Guido Trotter
  @rtype: dict
760 e69d05fd Iustin Pop
  @return: dictionary of instance: data, with data having the following keys:
761 e69d05fd Iustin Pop
      - memory: memory size of instance (int)
762 e69d05fd Iustin Pop
      - state: xen state of instance (string)
763 e69d05fd Iustin Pop
      - time: cpu time of instance (float)
764 10c2650b Iustin Pop
      - vcpus: the number of vcpus
765 a8083063 Iustin Pop

766 098c0958 Michael Hanselmann
  """
767 a8083063 Iustin Pop
  output = {}
768 a8083063 Iustin Pop
769 e69d05fd Iustin Pop
  for hname in hypervisor_list:
770 e69d05fd Iustin Pop
    iinfo = hypervisor.GetHypervisor(hname).GetAllInstancesInfo()
771 e69d05fd Iustin Pop
    if iinfo:
772 29921401 Iustin Pop
      for name, _, memory, vcpus, state, times in iinfo:
773 f23b5ae8 Iustin Pop
        value = {
774 e69d05fd Iustin Pop
          'memory': memory,
775 e69d05fd Iustin Pop
          'vcpus': vcpus,
776 e69d05fd Iustin Pop
          'state': state,
777 e69d05fd Iustin Pop
          'time': times,
778 e69d05fd Iustin Pop
          }
779 b33b6f55 Iustin Pop
        if name in output:
780 b33b6f55 Iustin Pop
          # we only check static parameters, like memory and vcpus,
781 b33b6f55 Iustin Pop
          # and not state and time which can change between the
782 b33b6f55 Iustin Pop
          # invocations of the different hypervisors
783 b33b6f55 Iustin Pop
          for key in 'memory', 'vcpus':
784 b33b6f55 Iustin Pop
            if value[key] != output[name][key]:
785 2fa74ef4 Iustin Pop
              _Fail("Instance %s is running twice"
786 2fa74ef4 Iustin Pop
                    " with different parameters", name)
787 f23b5ae8 Iustin Pop
        output[name] = value
788 a8083063 Iustin Pop
789 c26a6bd2 Iustin Pop
  return output
790 a8083063 Iustin Pop
791 a8083063 Iustin Pop
792 4a0e011f Iustin Pop
def InstanceOsAdd(instance, reinstall, debug):
793 2f8598a5 Alexander Schreiber
  """Add an OS to an instance.
794 a8083063 Iustin Pop

795 d15a9ad3 Guido Trotter
  @type instance: L{objects.Instance}
796 d15a9ad3 Guido Trotter
  @param instance: Instance whose OS is to be installed
797 e557bae9 Guido Trotter
  @type reinstall: boolean
798 e557bae9 Guido Trotter
  @param reinstall: whether this is an instance reinstall
799 4a0e011f Iustin Pop
  @type debug: integer
800 4a0e011f Iustin Pop
  @param debug: debug level, passed to the OS scripts
801 c26a6bd2 Iustin Pop
  @rtype: None
802 a8083063 Iustin Pop

803 a8083063 Iustin Pop
  """
804 255dcebd Iustin Pop
  inst_os = OSFromDisk(instance.os)
805 255dcebd Iustin Pop
806 4a0e011f Iustin Pop
  create_env = OSEnvironment(instance, inst_os, debug)
807 e557bae9 Guido Trotter
  if reinstall:
808 e557bae9 Guido Trotter
    create_env['INSTANCE_REINSTALL'] = "1"
809 a8083063 Iustin Pop
810 a8083063 Iustin Pop
  logfile = "%s/add-%s-%s-%d.log" % (constants.LOG_OS_DIR, instance.os,
811 a8083063 Iustin Pop
                                     instance.name, int(time.time()))
812 decd5f45 Iustin Pop
813 d868edb4 Iustin Pop
  result = utils.RunCmd([inst_os.create_script], env=create_env,
814 d868edb4 Iustin Pop
                        cwd=inst_os.path, output=logfile,)
815 decd5f45 Iustin Pop
  if result.failed:
816 18682bca Iustin Pop
    logging.error("os create command '%s' returned error: %s, logfile: %s,"
817 d868edb4 Iustin Pop
                  " output: %s", result.cmd, result.fail_reason, logfile,
818 18682bca Iustin Pop
                  result.output)
819 26f15862 Iustin Pop
    lines = [utils.SafeEncode(val)
820 20e01edd Iustin Pop
             for val in utils.TailFile(logfile, lines=20)]
821 afdc3985 Iustin Pop
    _Fail("OS create script failed (%s), last lines in the"
822 afdc3985 Iustin Pop
          " log file:\n%s", result.fail_reason, "\n".join(lines), log=False)
823 decd5f45 Iustin Pop
824 decd5f45 Iustin Pop
825 4a0e011f Iustin Pop
def RunRenameInstance(instance, old_name, debug):
826 decd5f45 Iustin Pop
  """Run the OS rename script for an instance.
827 decd5f45 Iustin Pop

828 b1206984 Iustin Pop
  @type instance: L{objects.Instance}
829 d15a9ad3 Guido Trotter
  @param instance: Instance whose OS is to be installed
830 d15a9ad3 Guido Trotter
  @type old_name: string
831 d15a9ad3 Guido Trotter
  @param old_name: previous instance name
832 4a0e011f Iustin Pop
  @type debug: integer
833 4a0e011f Iustin Pop
  @param debug: debug level, passed to the OS scripts
834 10c2650b Iustin Pop
  @rtype: boolean
835 10c2650b Iustin Pop
  @return: the success of the operation
836 decd5f45 Iustin Pop

837 decd5f45 Iustin Pop
  """
838 decd5f45 Iustin Pop
  inst_os = OSFromDisk(instance.os)
839 decd5f45 Iustin Pop
840 4a0e011f Iustin Pop
  rename_env = OSEnvironment(instance, inst_os, debug)
841 ff38b6c0 Guido Trotter
  rename_env['OLD_INSTANCE_NAME'] = old_name
842 decd5f45 Iustin Pop
843 decd5f45 Iustin Pop
  logfile = "%s/rename-%s-%s-%s-%d.log" % (constants.LOG_OS_DIR, instance.os,
844 decd5f45 Iustin Pop
                                           old_name,
845 decd5f45 Iustin Pop
                                           instance.name, int(time.time()))
846 a8083063 Iustin Pop
847 d868edb4 Iustin Pop
  result = utils.RunCmd([inst_os.rename_script], env=rename_env,
848 d868edb4 Iustin Pop
                        cwd=inst_os.path, output=logfile)
849 a8083063 Iustin Pop
850 a8083063 Iustin Pop
  if result.failed:
851 18682bca Iustin Pop
    logging.error("os create command '%s' returned error: %s output: %s",
852 d868edb4 Iustin Pop
                  result.cmd, result.fail_reason, result.output)
853 26f15862 Iustin Pop
    lines = [utils.SafeEncode(val)
854 96841384 Iustin Pop
             for val in utils.TailFile(logfile, lines=20)]
855 afdc3985 Iustin Pop
    _Fail("OS rename script failed (%s), last lines in the"
856 afdc3985 Iustin Pop
          " log file:\n%s", result.fail_reason, "\n".join(lines), log=False)
857 a8083063 Iustin Pop
858 a8083063 Iustin Pop
859 a8083063 Iustin Pop
def _GetVGInfo(vg_name):
860 5bbd3f7f Michael Hanselmann
  """Get information about the volume group.
861 a8083063 Iustin Pop

862 10c2650b Iustin Pop
  @type vg_name: str
863 10c2650b Iustin Pop
  @param vg_name: the volume group which we query
864 10c2650b Iustin Pop
  @rtype: dict
865 10c2650b Iustin Pop
  @return:
866 10c2650b Iustin Pop
    A dictionary with the following keys:
867 10c2650b Iustin Pop
      - C{vg_size} is the total size of the volume group in MiB
868 10c2650b Iustin Pop
      - C{vg_free} is the free size of the volume group in MiB
869 10c2650b Iustin Pop
      - C{pv_count} are the number of physical disks in that VG
870 a8083063 Iustin Pop

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

874 a8083063 Iustin Pop
  """
875 f4d377e7 Iustin Pop
  retdic = dict.fromkeys(["vg_size", "vg_free", "pv_count"])
876 f4d377e7 Iustin Pop
877 a8083063 Iustin Pop
  retval = utils.RunCmd(["vgs", "-ovg_size,vg_free,pv_count", "--noheadings",
878 a8083063 Iustin Pop
                         "--nosuffix", "--units=m", "--separator=:", vg_name])
879 a8083063 Iustin Pop
880 a8083063 Iustin Pop
  if retval.failed:
881 18682bca Iustin Pop
    logging.error("volume group %s not present", vg_name)
882 f4d377e7 Iustin Pop
    return retdic
883 d87ae7d2 Iustin Pop
  valarr = retval.stdout.strip().rstrip(':').split(':')
884 f4d377e7 Iustin Pop
  if len(valarr) == 3:
885 f4d377e7 Iustin Pop
    try:
886 f4d377e7 Iustin Pop
      retdic = {
887 f4d377e7 Iustin Pop
        "vg_size": int(round(float(valarr[0]), 0)),
888 f4d377e7 Iustin Pop
        "vg_free": int(round(float(valarr[1]), 0)),
889 f4d377e7 Iustin Pop
        "pv_count": int(valarr[2]),
890 f4d377e7 Iustin Pop
        }
891 f4d377e7 Iustin Pop
    except ValueError, err:
892 29921401 Iustin Pop
      logging.exception("Fail to parse vgs output: %s", err)
893 f4d377e7 Iustin Pop
  else:
894 18682bca Iustin Pop
    logging.error("vgs output has the wrong number of fields (expected"
895 18682bca Iustin Pop
                  " three): %s", str(valarr))
896 a8083063 Iustin Pop
  return retdic
897 a8083063 Iustin Pop
898 a8083063 Iustin Pop
899 5282084b Iustin Pop
def _GetBlockDevSymlinkPath(instance_name, idx):
900 5282084b Iustin Pop
  return os.path.join(constants.DISK_LINKS_DIR,
901 5282084b Iustin Pop
                      "%s:%d" % (instance_name, idx))
902 5282084b Iustin Pop
903 5282084b Iustin Pop
904 5282084b Iustin Pop
def _SymlinkBlockDev(instance_name, device_path, idx):
905 9332fd8a Iustin Pop
  """Set up symlinks to a instance's block device.
906 9332fd8a Iustin Pop

907 9332fd8a Iustin Pop
  This is an auxiliary function run when an instance is start (on the primary
908 9332fd8a Iustin Pop
  node) or when an instance is migrated (on the target node).
909 9332fd8a Iustin Pop

910 9332fd8a Iustin Pop

911 5282084b Iustin Pop
  @param instance_name: the name of the target instance
912 5282084b Iustin Pop
  @param device_path: path of the physical block device, on the node
913 5282084b Iustin Pop
  @param idx: the disk index
914 5282084b Iustin Pop
  @return: absolute path to the disk's symlink
915 9332fd8a Iustin Pop

916 9332fd8a Iustin Pop
  """
917 5282084b Iustin Pop
  link_name = _GetBlockDevSymlinkPath(instance_name, idx)
918 9332fd8a Iustin Pop
  try:
919 9332fd8a Iustin Pop
    os.symlink(device_path, link_name)
920 5282084b Iustin Pop
  except OSError, err:
921 5282084b Iustin Pop
    if err.errno == errno.EEXIST:
922 9332fd8a Iustin Pop
      if (not os.path.islink(link_name) or
923 9332fd8a Iustin Pop
          os.readlink(link_name) != device_path):
924 9332fd8a Iustin Pop
        os.remove(link_name)
925 9332fd8a Iustin Pop
        os.symlink(device_path, link_name)
926 9332fd8a Iustin Pop
    else:
927 9332fd8a Iustin Pop
      raise
928 9332fd8a Iustin Pop
929 9332fd8a Iustin Pop
  return link_name
930 9332fd8a Iustin Pop
931 9332fd8a Iustin Pop
932 5282084b Iustin Pop
def _RemoveBlockDevLinks(instance_name, disks):
933 3c9c571d Iustin Pop
  """Remove the block device symlinks belonging to the given instance.
934 3c9c571d Iustin Pop

935 3c9c571d Iustin Pop
  """
936 29921401 Iustin Pop
  for idx, _ in enumerate(disks):
937 5282084b Iustin Pop
    link_name = _GetBlockDevSymlinkPath(instance_name, idx)
938 5282084b Iustin Pop
    if os.path.islink(link_name):
939 3c9c571d Iustin Pop
      try:
940 03dfa658 Iustin Pop
        os.remove(link_name)
941 03dfa658 Iustin Pop
      except OSError:
942 03dfa658 Iustin Pop
        logging.exception("Can't remove symlink '%s'", link_name)
943 3c9c571d Iustin Pop
944 3c9c571d Iustin Pop
945 9332fd8a Iustin Pop
def _GatherAndLinkBlockDevs(instance):
946 a8083063 Iustin Pop
  """Set up an instance's block device(s).
947 a8083063 Iustin Pop

948 a8083063 Iustin Pop
  This is run on the primary node at instance startup. The block
949 a8083063 Iustin Pop
  devices must be already assembled.
950 a8083063 Iustin Pop

951 10c2650b Iustin Pop
  @type instance: L{objects.Instance}
952 10c2650b Iustin Pop
  @param instance: the instance whose disks we shoul assemble
953 069cfbf1 Iustin Pop
  @rtype: list
954 069cfbf1 Iustin Pop
  @return: list of (disk_object, device_path)
955 10c2650b Iustin Pop

956 a8083063 Iustin Pop
  """
957 a8083063 Iustin Pop
  block_devices = []
958 9332fd8a Iustin Pop
  for idx, disk in enumerate(instance.disks):
959 a8083063 Iustin Pop
    device = _RecursiveFindBD(disk)
960 a8083063 Iustin Pop
    if device is None:
961 a8083063 Iustin Pop
      raise errors.BlockDeviceError("Block device '%s' is not set up." %
962 a8083063 Iustin Pop
                                    str(disk))
963 a8083063 Iustin Pop
    device.Open()
964 9332fd8a Iustin Pop
    try:
965 5282084b Iustin Pop
      link_name = _SymlinkBlockDev(instance.name, device.dev_path, idx)
966 9332fd8a Iustin Pop
    except OSError, e:
967 9332fd8a Iustin Pop
      raise errors.BlockDeviceError("Cannot create block device symlink: %s" %
968 9332fd8a Iustin Pop
                                    e.strerror)
969 9332fd8a Iustin Pop
970 9332fd8a Iustin Pop
    block_devices.append((disk, link_name))
971 9332fd8a Iustin Pop
972 a8083063 Iustin Pop
  return block_devices
973 a8083063 Iustin Pop
974 a8083063 Iustin Pop
975 07813a9e Iustin Pop
def StartInstance(instance):
976 a8083063 Iustin Pop
  """Start an instance.
977 a8083063 Iustin Pop

978 10c2650b Iustin Pop
  @type instance: L{objects.Instance}
979 e69d05fd Iustin Pop
  @param instance: the instance object
980 c26a6bd2 Iustin Pop
  @rtype: None
981 a8083063 Iustin Pop

982 098c0958 Michael Hanselmann
  """
983 e69d05fd Iustin Pop
  running_instances = GetInstanceList([instance.hypervisor])
984 a8083063 Iustin Pop
985 a8083063 Iustin Pop
  if instance.name in running_instances:
986 c26a6bd2 Iustin Pop
    logging.info("Instance %s already running, not starting", instance.name)
987 c26a6bd2 Iustin Pop
    return
988 a8083063 Iustin Pop
989 a8083063 Iustin Pop
  try:
990 ec596c24 Iustin Pop
    block_devices = _GatherAndLinkBlockDevs(instance)
991 ec596c24 Iustin Pop
    hyper = hypervisor.GetHypervisor(instance.hypervisor)
992 07813a9e Iustin Pop
    hyper.StartInstance(instance, block_devices)
993 ec596c24 Iustin Pop
  except errors.BlockDeviceError, err:
994 2cc6781a Iustin Pop
    _Fail("Block device error: %s", err, exc=True)
995 a8083063 Iustin Pop
  except errors.HypervisorError, err:
996 5282084b Iustin Pop
    _RemoveBlockDevLinks(instance.name, instance.disks)
997 2cc6781a Iustin Pop
    _Fail("Hypervisor error: %s", err, exc=True)
998 a8083063 Iustin Pop
999 a8083063 Iustin Pop
1000 6263189c Guido Trotter
def InstanceShutdown(instance, timeout):
1001 a8083063 Iustin Pop
  """Shut an instance down.
1002 a8083063 Iustin Pop

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

1005 10c2650b Iustin Pop
  @type instance: L{objects.Instance}
1006 e69d05fd Iustin Pop
  @param instance: the instance object
1007 6263189c Guido Trotter
  @type timeout: integer
1008 6263189c Guido Trotter
  @param timeout: maximum timeout for soft shutdown
1009 c26a6bd2 Iustin Pop
  @rtype: None
1010 a8083063 Iustin Pop

1011 098c0958 Michael Hanselmann
  """
1012 e69d05fd Iustin Pop
  hv_name = instance.hypervisor
1013 e4e9b806 Guido Trotter
  hyper = hypervisor.GetHypervisor(hv_name)
1014 c26a6bd2 Iustin Pop
  iname = instance.name
1015 a8083063 Iustin Pop
1016 3c0cdc83 Michael Hanselmann
  if instance.name not in hyper.ListInstances():
1017 c26a6bd2 Iustin Pop
    logging.info("Instance %s not running, doing nothing", iname)
1018 c26a6bd2 Iustin Pop
    return
1019 a8083063 Iustin Pop
1020 3c0cdc83 Michael Hanselmann
  class _TryShutdown:
1021 3c0cdc83 Michael Hanselmann
    def __init__(self):
1022 3c0cdc83 Michael Hanselmann
      self.tried_once = False
1023 a8083063 Iustin Pop
1024 3c0cdc83 Michael Hanselmann
    def __call__(self):
1025 3c0cdc83 Michael Hanselmann
      if iname not in hyper.ListInstances():
1026 3c0cdc83 Michael Hanselmann
        return
1027 3c0cdc83 Michael Hanselmann
1028 3c0cdc83 Michael Hanselmann
      try:
1029 3c0cdc83 Michael Hanselmann
        hyper.StopInstance(instance, retry=self.tried_once)
1030 3c0cdc83 Michael Hanselmann
      except errors.HypervisorError, err:
1031 3c0cdc83 Michael Hanselmann
        if iname not in hyper.ListInstances():
1032 3c0cdc83 Michael Hanselmann
          # if the instance is no longer existing, consider this a
1033 3c0cdc83 Michael Hanselmann
          # success and go to cleanup
1034 3c0cdc83 Michael Hanselmann
          return
1035 3c0cdc83 Michael Hanselmann
1036 3c0cdc83 Michael Hanselmann
        _Fail("Failed to stop instance %s: %s", iname, err)
1037 3c0cdc83 Michael Hanselmann
1038 3c0cdc83 Michael Hanselmann
      self.tried_once = True
1039 3c0cdc83 Michael Hanselmann
1040 3c0cdc83 Michael Hanselmann
      raise utils.RetryAgain()
1041 3c0cdc83 Michael Hanselmann
1042 3c0cdc83 Michael Hanselmann
  try:
1043 3c0cdc83 Michael Hanselmann
    utils.Retry(_TryShutdown(), 5, timeout)
1044 3c0cdc83 Michael Hanselmann
  except utils.RetryTimeout:
1045 a8083063 Iustin Pop
    # the shutdown did not succeed
1046 e4e9b806 Guido Trotter
    logging.error("Shutdown of '%s' unsuccessful, forcing", iname)
1047 a8083063 Iustin Pop
1048 a8083063 Iustin Pop
    try:
1049 a8083063 Iustin Pop
      hyper.StopInstance(instance, force=True)
1050 a8083063 Iustin Pop
    except errors.HypervisorError, err:
1051 3c0cdc83 Michael Hanselmann
      if iname in hyper.ListInstances():
1052 3782acd7 Iustin Pop
        # only raise an error if the instance still exists, otherwise
1053 3782acd7 Iustin Pop
        # the error could simply be "instance ... unknown"!
1054 3782acd7 Iustin Pop
        _Fail("Failed to force stop instance %s: %s", iname, err)
1055 a8083063 Iustin Pop
1056 a8083063 Iustin Pop
    time.sleep(1)
1057 3c0cdc83 Michael Hanselmann
1058 3c0cdc83 Michael Hanselmann
    if iname in hyper.ListInstances():
1059 c26a6bd2 Iustin Pop
      _Fail("Could not shutdown instance %s even by destroy", iname)
1060 3c9c571d Iustin Pop
1061 c26a6bd2 Iustin Pop
  _RemoveBlockDevLinks(iname, instance.disks)
1062 a8083063 Iustin Pop
1063 a8083063 Iustin Pop
1064 17c3f802 Guido Trotter
def InstanceReboot(instance, reboot_type, shutdown_timeout):
1065 007a2f3e Alexander Schreiber
  """Reboot an instance.
1066 007a2f3e Alexander Schreiber

1067 10c2650b Iustin Pop
  @type instance: L{objects.Instance}
1068 10c2650b Iustin Pop
  @param instance: the instance object to reboot
1069 10c2650b Iustin Pop
  @type reboot_type: str
1070 10c2650b Iustin Pop
  @param reboot_type: the type of reboot, one the following
1071 10c2650b Iustin Pop
    constants:
1072 10c2650b Iustin Pop
      - L{constants.INSTANCE_REBOOT_SOFT}: only reboot the
1073 10c2650b Iustin Pop
        instance OS, do not recreate the VM
1074 10c2650b Iustin Pop
      - L{constants.INSTANCE_REBOOT_HARD}: tear down and
1075 10c2650b Iustin Pop
        restart the VM (at the hypervisor level)
1076 73e5a4f4 Iustin Pop
      - the other reboot type (L{constants.INSTANCE_REBOOT_FULL}) is
1077 73e5a4f4 Iustin Pop
        not accepted here, since that mode is handled differently, in
1078 73e5a4f4 Iustin Pop
        cmdlib, and translates into full stop and start of the
1079 73e5a4f4 Iustin Pop
        instance (instead of a call_instance_reboot RPC)
1080 23057d29 Michael Hanselmann
  @type shutdown_timeout: integer
1081 23057d29 Michael Hanselmann
  @param shutdown_timeout: maximum timeout for soft shutdown
1082 c26a6bd2 Iustin Pop
  @rtype: None
1083 007a2f3e Alexander Schreiber

1084 007a2f3e Alexander Schreiber
  """
1085 e69d05fd Iustin Pop
  running_instances = GetInstanceList([instance.hypervisor])
1086 007a2f3e Alexander Schreiber
1087 007a2f3e Alexander Schreiber
  if instance.name not in running_instances:
1088 2cc6781a Iustin Pop
    _Fail("Cannot reboot instance %s that is not running", instance.name)
1089 007a2f3e Alexander Schreiber
1090 e69d05fd Iustin Pop
  hyper = hypervisor.GetHypervisor(instance.hypervisor)
1091 007a2f3e Alexander Schreiber
  if reboot_type == constants.INSTANCE_REBOOT_SOFT:
1092 007a2f3e Alexander Schreiber
    try:
1093 007a2f3e Alexander Schreiber
      hyper.RebootInstance(instance)
1094 007a2f3e Alexander Schreiber
    except errors.HypervisorError, err:
1095 2cc6781a Iustin Pop
      _Fail("Failed to soft reboot instance %s: %s", instance.name, err)
1096 007a2f3e Alexander Schreiber
  elif reboot_type == constants.INSTANCE_REBOOT_HARD:
1097 007a2f3e Alexander Schreiber
    try:
1098 17c3f802 Guido Trotter
      InstanceShutdown(instance, shutdown_timeout)
1099 07813a9e Iustin Pop
      return StartInstance(instance)
1100 007a2f3e Alexander Schreiber
    except errors.HypervisorError, err:
1101 2cc6781a Iustin Pop
      _Fail("Failed to hard reboot instance %s: %s", instance.name, err)
1102 007a2f3e Alexander Schreiber
  else:
1103 2cc6781a Iustin Pop
    _Fail("Invalid reboot_type received: %s", reboot_type)
1104 007a2f3e Alexander Schreiber
1105 007a2f3e Alexander Schreiber
1106 6906a9d8 Guido Trotter
def MigrationInfo(instance):
1107 6906a9d8 Guido Trotter
  """Gather information about an instance to be migrated.
1108 6906a9d8 Guido Trotter

1109 6906a9d8 Guido Trotter
  @type instance: L{objects.Instance}
1110 6906a9d8 Guido Trotter
  @param instance: the instance definition
1111 6906a9d8 Guido Trotter

1112 6906a9d8 Guido Trotter
  """
1113 cd42d0ad Guido Trotter
  hyper = hypervisor.GetHypervisor(instance.hypervisor)
1114 cd42d0ad Guido Trotter
  try:
1115 cd42d0ad Guido Trotter
    info = hyper.MigrationInfo(instance)
1116 cd42d0ad Guido Trotter
  except errors.HypervisorError, err:
1117 2cc6781a Iustin Pop
    _Fail("Failed to fetch migration information: %s", err, exc=True)
1118 c26a6bd2 Iustin Pop
  return info
1119 6906a9d8 Guido Trotter
1120 6906a9d8 Guido Trotter
1121 6906a9d8 Guido Trotter
def AcceptInstance(instance, info, target):
1122 6906a9d8 Guido Trotter
  """Prepare the node to accept an instance.
1123 6906a9d8 Guido Trotter

1124 6906a9d8 Guido Trotter
  @type instance: L{objects.Instance}
1125 6906a9d8 Guido Trotter
  @param instance: the instance definition
1126 6906a9d8 Guido Trotter
  @type info: string/data (opaque)
1127 6906a9d8 Guido Trotter
  @param info: migration information, from the source node
1128 6906a9d8 Guido Trotter
  @type target: string
1129 6906a9d8 Guido Trotter
  @param target: target host (usually ip), on this node
1130 6906a9d8 Guido Trotter

1131 6906a9d8 Guido Trotter
  """
1132 cd42d0ad Guido Trotter
  hyper = hypervisor.GetHypervisor(instance.hypervisor)
1133 cd42d0ad Guido Trotter
  try:
1134 cd42d0ad Guido Trotter
    hyper.AcceptInstance(instance, info, target)
1135 cd42d0ad Guido Trotter
  except errors.HypervisorError, err:
1136 2cc6781a Iustin Pop
    _Fail("Failed to accept instance: %s", err, exc=True)
1137 6906a9d8 Guido Trotter
1138 6906a9d8 Guido Trotter
1139 6906a9d8 Guido Trotter
def FinalizeMigration(instance, info, success):
1140 6906a9d8 Guido Trotter
  """Finalize any preparation to accept an instance.
1141 6906a9d8 Guido Trotter

1142 6906a9d8 Guido Trotter
  @type instance: L{objects.Instance}
1143 6906a9d8 Guido Trotter
  @param instance: the instance definition
1144 6906a9d8 Guido Trotter
  @type info: string/data (opaque)
1145 6906a9d8 Guido Trotter
  @param info: migration information, from the source node
1146 6906a9d8 Guido Trotter
  @type success: boolean
1147 6906a9d8 Guido Trotter
  @param success: whether the migration was a success or a failure
1148 6906a9d8 Guido Trotter

1149 6906a9d8 Guido Trotter
  """
1150 cd42d0ad Guido Trotter
  hyper = hypervisor.GetHypervisor(instance.hypervisor)
1151 cd42d0ad Guido Trotter
  try:
1152 cd42d0ad Guido Trotter
    hyper.FinalizeMigration(instance, info, success)
1153 cd42d0ad Guido Trotter
  except errors.HypervisorError, err:
1154 2cc6781a Iustin Pop
    _Fail("Failed to finalize migration: %s", err, exc=True)
1155 6906a9d8 Guido Trotter
1156 6906a9d8 Guido Trotter
1157 2a10865c Iustin Pop
def MigrateInstance(instance, target, live):
1158 2a10865c Iustin Pop
  """Migrates an instance to another node.
1159 2a10865c Iustin Pop

1160 b1206984 Iustin Pop
  @type instance: L{objects.Instance}
1161 9f0e6b37 Iustin Pop
  @param instance: the instance definition
1162 9f0e6b37 Iustin Pop
  @type target: string
1163 9f0e6b37 Iustin Pop
  @param target: the target node name
1164 9f0e6b37 Iustin Pop
  @type live: boolean
1165 9f0e6b37 Iustin Pop
  @param live: whether the migration should be done live or not (the
1166 9f0e6b37 Iustin Pop
      interpretation of this parameter is left to the hypervisor)
1167 9f0e6b37 Iustin Pop
  @rtype: tuple
1168 9f0e6b37 Iustin Pop
  @return: a tuple of (success, msg) where:
1169 9f0e6b37 Iustin Pop
      - succes is a boolean denoting the success/failure of the operation
1170 9f0e6b37 Iustin Pop
      - msg is a string with details in case of failure
1171 9f0e6b37 Iustin Pop

1172 2a10865c Iustin Pop
  """
1173 53c776b5 Iustin Pop
  hyper = hypervisor.GetHypervisor(instance.hypervisor)
1174 2a10865c Iustin Pop
1175 2a10865c Iustin Pop
  try:
1176 58d38b02 Iustin Pop
    hyper.MigrateInstance(instance, target, live)
1177 2a10865c Iustin Pop
  except errors.HypervisorError, err:
1178 2cc6781a Iustin Pop
    _Fail("Failed to migrate instance: %s", err, exc=True)
1179 2a10865c Iustin Pop
1180 2a10865c Iustin Pop
1181 821d1bd1 Iustin Pop
def BlockdevCreate(disk, size, owner, on_primary, info):
1182 a8083063 Iustin Pop
  """Creates a block device for an instance.
1183 a8083063 Iustin Pop

1184 b1206984 Iustin Pop
  @type disk: L{objects.Disk}
1185 b1206984 Iustin Pop
  @param disk: the object describing the disk we should create
1186 b1206984 Iustin Pop
  @type size: int
1187 b1206984 Iustin Pop
  @param size: the size of the physical underlying device, in MiB
1188 b1206984 Iustin Pop
  @type owner: str
1189 b1206984 Iustin Pop
  @param owner: the name of the instance for which disk is created,
1190 b1206984 Iustin Pop
      used for device cache data
1191 b1206984 Iustin Pop
  @type on_primary: boolean
1192 b1206984 Iustin Pop
  @param on_primary:  indicates if it is the primary node or not
1193 b1206984 Iustin Pop
  @type info: string
1194 b1206984 Iustin Pop
  @param info: string that will be sent to the physical device
1195 b1206984 Iustin Pop
      creation, used for example to set (LVM) tags on LVs
1196 b1206984 Iustin Pop

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

1201 a8083063 Iustin Pop
  """
1202 7260cfbe Iustin Pop
  # TODO: remove the obsolete 'size' argument
1203 7260cfbe Iustin Pop
  # pylint: disable-msg=W0613
1204 a8083063 Iustin Pop
  clist = []
1205 a8083063 Iustin Pop
  if disk.children:
1206 a8083063 Iustin Pop
    for child in disk.children:
1207 1063abd1 Iustin Pop
      try:
1208 1063abd1 Iustin Pop
        crdev = _RecursiveAssembleBD(child, owner, on_primary)
1209 1063abd1 Iustin Pop
      except errors.BlockDeviceError, err:
1210 2cc6781a Iustin Pop
        _Fail("Can't assemble device %s: %s", child, err)
1211 a8083063 Iustin Pop
      if on_primary or disk.AssembleOnSecondary():
1212 a8083063 Iustin Pop
        # we need the children open in case the device itself has to
1213 a8083063 Iustin Pop
        # be assembled
1214 1063abd1 Iustin Pop
        try:
1215 fe267188 Iustin Pop
          # pylint: disable-msg=E1103
1216 1063abd1 Iustin Pop
          crdev.Open()
1217 1063abd1 Iustin Pop
        except errors.BlockDeviceError, err:
1218 2cc6781a Iustin Pop
          _Fail("Can't make child '%s' read-write: %s", child, err)
1219 a8083063 Iustin Pop
      clist.append(crdev)
1220 a8083063 Iustin Pop
1221 dab69e97 Iustin Pop
  try:
1222 464f8daf Iustin Pop
    device = bdev.Create(disk.dev_type, disk.physical_id, clist, disk.size)
1223 1063abd1 Iustin Pop
  except errors.BlockDeviceError, err:
1224 2cc6781a Iustin Pop
    _Fail("Can't create block device: %s", err)
1225 6c626518 Iustin Pop
1226 a8083063 Iustin Pop
  if on_primary or disk.AssembleOnSecondary():
1227 1063abd1 Iustin Pop
    try:
1228 1063abd1 Iustin Pop
      device.Assemble()
1229 1063abd1 Iustin Pop
    except errors.BlockDeviceError, err:
1230 2cc6781a Iustin Pop
      _Fail("Can't assemble device after creation, unusual event: %s", err)
1231 e31c43f7 Michael Hanselmann
    device.SetSyncSpeed(constants.SYNC_SPEED)
1232 a8083063 Iustin Pop
    if on_primary or disk.OpenOnSecondary():
1233 1063abd1 Iustin Pop
      try:
1234 1063abd1 Iustin Pop
        device.Open(force=True)
1235 1063abd1 Iustin Pop
      except errors.BlockDeviceError, err:
1236 2cc6781a Iustin Pop
        _Fail("Can't make device r/w after creation, unusual event: %s", err)
1237 3f78eef2 Iustin Pop
    DevCacheManager.UpdateCache(device.dev_path, owner,
1238 3f78eef2 Iustin Pop
                                on_primary, disk.iv_name)
1239 a0c3fea1 Michael Hanselmann
1240 a0c3fea1 Michael Hanselmann
  device.SetInfo(info)
1241 a0c3fea1 Michael Hanselmann
1242 c26a6bd2 Iustin Pop
  return device.unique_id
1243 a8083063 Iustin Pop
1244 a8083063 Iustin Pop
1245 821d1bd1 Iustin Pop
def BlockdevRemove(disk):
1246 a8083063 Iustin Pop
  """Remove a block device.
1247 a8083063 Iustin Pop

1248 10c2650b Iustin Pop
  @note: This is intended to be called recursively.
1249 10c2650b Iustin Pop

1250 c41eea6e Iustin Pop
  @type disk: L{objects.Disk}
1251 10c2650b Iustin Pop
  @param disk: the disk object we should remove
1252 10c2650b Iustin Pop
  @rtype: boolean
1253 10c2650b Iustin Pop
  @return: the success of the operation
1254 a8083063 Iustin Pop

1255 a8083063 Iustin Pop
  """
1256 e1bc0878 Iustin Pop
  msgs = []
1257 a8083063 Iustin Pop
  try:
1258 bca2e7f4 Iustin Pop
    rdev = _RecursiveFindBD(disk)
1259 a8083063 Iustin Pop
  except errors.BlockDeviceError, err:
1260 a8083063 Iustin Pop
    # probably can't attach
1261 18682bca Iustin Pop
    logging.info("Can't attach to device %s in remove", disk)
1262 a8083063 Iustin Pop
    rdev = None
1263 a8083063 Iustin Pop
  if rdev is not None:
1264 3f78eef2 Iustin Pop
    r_path = rdev.dev_path
1265 e1bc0878 Iustin Pop
    try:
1266 0c6c04ec Iustin Pop
      rdev.Remove()
1267 e1bc0878 Iustin Pop
    except errors.BlockDeviceError, err:
1268 e1bc0878 Iustin Pop
      msgs.append(str(err))
1269 c26a6bd2 Iustin Pop
    if not msgs:
1270 3f78eef2 Iustin Pop
      DevCacheManager.RemoveCache(r_path)
1271 e1bc0878 Iustin Pop
1272 a8083063 Iustin Pop
  if disk.children:
1273 a8083063 Iustin Pop
    for child in disk.children:
1274 c26a6bd2 Iustin Pop
      try:
1275 c26a6bd2 Iustin Pop
        BlockdevRemove(child)
1276 c26a6bd2 Iustin Pop
      except RPCFail, err:
1277 c26a6bd2 Iustin Pop
        msgs.append(str(err))
1278 e1bc0878 Iustin Pop
1279 c26a6bd2 Iustin Pop
  if msgs:
1280 afdc3985 Iustin Pop
    _Fail("; ".join(msgs))
1281 afdc3985 Iustin Pop
1282 a8083063 Iustin Pop
1283 3f78eef2 Iustin Pop
def _RecursiveAssembleBD(disk, owner, as_primary):
1284 a8083063 Iustin Pop
  """Activate a block device for an instance.
1285 a8083063 Iustin Pop

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

1288 10c2650b Iustin Pop
  @note: this function is called recursively.
1289 a8083063 Iustin Pop

1290 10c2650b Iustin Pop
  @type disk: L{objects.Disk}
1291 10c2650b Iustin Pop
  @param disk: the disk we try to assemble
1292 10c2650b Iustin Pop
  @type owner: str
1293 10c2650b Iustin Pop
  @param owner: the name of the instance which owns the disk
1294 10c2650b Iustin Pop
  @type as_primary: boolean
1295 10c2650b Iustin Pop
  @param as_primary: if we should make the block device
1296 10c2650b Iustin Pop
      read/write
1297 a8083063 Iustin Pop

1298 10c2650b Iustin Pop
  @return: the assembled device or None (in case no device
1299 10c2650b Iustin Pop
      was assembled)
1300 10c2650b Iustin Pop
  @raise errors.BlockDeviceError: in case there is an error
1301 10c2650b Iustin Pop
      during the activation of the children or the device
1302 10c2650b Iustin Pop
      itself
1303 a8083063 Iustin Pop

1304 a8083063 Iustin Pop
  """
1305 a8083063 Iustin Pop
  children = []
1306 a8083063 Iustin Pop
  if disk.children:
1307 fc1dc9d7 Iustin Pop
    mcn = disk.ChildrenNeeded()
1308 fc1dc9d7 Iustin Pop
    if mcn == -1:
1309 fc1dc9d7 Iustin Pop
      mcn = 0 # max number of Nones allowed
1310 fc1dc9d7 Iustin Pop
    else:
1311 fc1dc9d7 Iustin Pop
      mcn = len(disk.children) - mcn # max number of Nones
1312 a8083063 Iustin Pop
    for chld_disk in disk.children:
1313 fc1dc9d7 Iustin Pop
      try:
1314 fc1dc9d7 Iustin Pop
        cdev = _RecursiveAssembleBD(chld_disk, owner, as_primary)
1315 fc1dc9d7 Iustin Pop
      except errors.BlockDeviceError, err:
1316 7803d4d3 Iustin Pop
        if children.count(None) >= mcn:
1317 fc1dc9d7 Iustin Pop
          raise
1318 fc1dc9d7 Iustin Pop
        cdev = None
1319 1063abd1 Iustin Pop
        logging.error("Error in child activation (but continuing): %s",
1320 1063abd1 Iustin Pop
                      str(err))
1321 fc1dc9d7 Iustin Pop
      children.append(cdev)
1322 a8083063 Iustin Pop
1323 a8083063 Iustin Pop
  if as_primary or disk.AssembleOnSecondary():
1324 464f8daf Iustin Pop
    r_dev = bdev.Assemble(disk.dev_type, disk.physical_id, children, disk.size)
1325 e31c43f7 Michael Hanselmann
    r_dev.SetSyncSpeed(constants.SYNC_SPEED)
1326 a8083063 Iustin Pop
    result = r_dev
1327 a8083063 Iustin Pop
    if as_primary or disk.OpenOnSecondary():
1328 a8083063 Iustin Pop
      r_dev.Open()
1329 3f78eef2 Iustin Pop
    DevCacheManager.UpdateCache(r_dev.dev_path, owner,
1330 3f78eef2 Iustin Pop
                                as_primary, disk.iv_name)
1331 3f78eef2 Iustin Pop
1332 a8083063 Iustin Pop
  else:
1333 a8083063 Iustin Pop
    result = True
1334 a8083063 Iustin Pop
  return result
1335 a8083063 Iustin Pop
1336 a8083063 Iustin Pop
1337 821d1bd1 Iustin Pop
def BlockdevAssemble(disk, owner, as_primary):
1338 a8083063 Iustin Pop
  """Activate a block device for an instance.
1339 a8083063 Iustin Pop

1340 a8083063 Iustin Pop
  This is a wrapper over _RecursiveAssembleBD.
1341 a8083063 Iustin Pop

1342 b1206984 Iustin Pop
  @rtype: str or boolean
1343 b1206984 Iustin Pop
  @return: a C{/dev/...} path for primary nodes, and
1344 b1206984 Iustin Pop
      C{True} for secondary nodes
1345 a8083063 Iustin Pop

1346 a8083063 Iustin Pop
  """
1347 53c14ef1 Iustin Pop
  try:
1348 53c14ef1 Iustin Pop
    result = _RecursiveAssembleBD(disk, owner, as_primary)
1349 53c14ef1 Iustin Pop
    if isinstance(result, bdev.BlockDev):
1350 fe267188 Iustin Pop
      # pylint: disable-msg=E1103
1351 53c14ef1 Iustin Pop
      result = result.dev_path
1352 53c14ef1 Iustin Pop
  except errors.BlockDeviceError, err:
1353 afdc3985 Iustin Pop
    _Fail("Error while assembling disk: %s", err, exc=True)
1354 afdc3985 Iustin Pop
1355 c26a6bd2 Iustin Pop
  return result
1356 a8083063 Iustin Pop
1357 a8083063 Iustin Pop
1358 821d1bd1 Iustin Pop
def BlockdevShutdown(disk):
1359 a8083063 Iustin Pop
  """Shut down a block device.
1360 a8083063 Iustin Pop

1361 5bbd3f7f Michael Hanselmann
  First, if the device is assembled (Attach() is successful), then
1362 c41eea6e Iustin Pop
  the device is shutdown. Then the children of the device are
1363 c41eea6e Iustin Pop
  shutdown.
1364 a8083063 Iustin Pop

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

1369 10c2650b Iustin Pop
  @type disk: L{objects.Disk}
1370 10c2650b Iustin Pop
  @param disk: the description of the disk we should
1371 10c2650b Iustin Pop
      shutdown
1372 c26a6bd2 Iustin Pop
  @rtype: None
1373 10c2650b Iustin Pop

1374 a8083063 Iustin Pop
  """
1375 cacfd1fd Iustin Pop
  msgs = []
1376 a8083063 Iustin Pop
  r_dev = _RecursiveFindBD(disk)
1377 a8083063 Iustin Pop
  if r_dev is not None:
1378 3f78eef2 Iustin Pop
    r_path = r_dev.dev_path
1379 cacfd1fd Iustin Pop
    try:
1380 746f7476 Iustin Pop
      r_dev.Shutdown()
1381 746f7476 Iustin Pop
      DevCacheManager.RemoveCache(r_path)
1382 cacfd1fd Iustin Pop
    except errors.BlockDeviceError, err:
1383 cacfd1fd Iustin Pop
      msgs.append(str(err))
1384 746f7476 Iustin Pop
1385 a8083063 Iustin Pop
  if disk.children:
1386 a8083063 Iustin Pop
    for child in disk.children:
1387 c26a6bd2 Iustin Pop
      try:
1388 c26a6bd2 Iustin Pop
        BlockdevShutdown(child)
1389 c26a6bd2 Iustin Pop
      except RPCFail, err:
1390 c26a6bd2 Iustin Pop
        msgs.append(str(err))
1391 746f7476 Iustin Pop
1392 c26a6bd2 Iustin Pop
  if msgs:
1393 afdc3985 Iustin Pop
    _Fail("; ".join(msgs))
1394 a8083063 Iustin Pop
1395 a8083063 Iustin Pop
1396 821d1bd1 Iustin Pop
def BlockdevAddchildren(parent_cdev, new_cdevs):
1397 153d9724 Iustin Pop
  """Extend a mirrored block device.
1398 a8083063 Iustin Pop

1399 10c2650b Iustin Pop
  @type parent_cdev: L{objects.Disk}
1400 10c2650b Iustin Pop
  @param parent_cdev: the disk to which we should add children
1401 10c2650b Iustin Pop
  @type new_cdevs: list of L{objects.Disk}
1402 10c2650b Iustin Pop
  @param new_cdevs: the list of children which we should add
1403 c26a6bd2 Iustin Pop
  @rtype: None
1404 10c2650b Iustin Pop

1405 a8083063 Iustin Pop
  """
1406 bca2e7f4 Iustin Pop
  parent_bdev = _RecursiveFindBD(parent_cdev)
1407 153d9724 Iustin Pop
  if parent_bdev is None:
1408 2cc6781a Iustin Pop
    _Fail("Can't find parent device '%s' in add children", parent_cdev)
1409 153d9724 Iustin Pop
  new_bdevs = [_RecursiveFindBD(disk) for disk in new_cdevs]
1410 153d9724 Iustin Pop
  if new_bdevs.count(None) > 0:
1411 2cc6781a Iustin Pop
    _Fail("Can't find new device(s) to add: %s:%s", new_bdevs, new_cdevs)
1412 153d9724 Iustin Pop
  parent_bdev.AddChildren(new_bdevs)
1413 a8083063 Iustin Pop
1414 a8083063 Iustin Pop
1415 821d1bd1 Iustin Pop
def BlockdevRemovechildren(parent_cdev, new_cdevs):
1416 153d9724 Iustin Pop
  """Shrink a mirrored block device.
1417 a8083063 Iustin Pop

1418 10c2650b Iustin Pop
  @type parent_cdev: L{objects.Disk}
1419 10c2650b Iustin Pop
  @param parent_cdev: the disk from which we should remove children
1420 10c2650b Iustin Pop
  @type new_cdevs: list of L{objects.Disk}
1421 10c2650b Iustin Pop
  @param new_cdevs: the list of children which we should remove
1422 c26a6bd2 Iustin Pop
  @rtype: None
1423 10c2650b Iustin Pop

1424 a8083063 Iustin Pop
  """
1425 153d9724 Iustin Pop
  parent_bdev = _RecursiveFindBD(parent_cdev)
1426 153d9724 Iustin Pop
  if parent_bdev is None:
1427 2cc6781a Iustin Pop
    _Fail("Can't find parent device '%s' in remove children", parent_cdev)
1428 e739bd57 Iustin Pop
  devs = []
1429 e739bd57 Iustin Pop
  for disk in new_cdevs:
1430 e739bd57 Iustin Pop
    rpath = disk.StaticDevPath()
1431 e739bd57 Iustin Pop
    if rpath is None:
1432 e739bd57 Iustin Pop
      bd = _RecursiveFindBD(disk)
1433 e739bd57 Iustin Pop
      if bd is None:
1434 2cc6781a Iustin Pop
        _Fail("Can't find device %s while removing children", disk)
1435 e739bd57 Iustin Pop
      else:
1436 e739bd57 Iustin Pop
        devs.append(bd.dev_path)
1437 e739bd57 Iustin Pop
    else:
1438 e739bd57 Iustin Pop
      devs.append(rpath)
1439 e739bd57 Iustin Pop
  parent_bdev.RemoveChildren(devs)
1440 a8083063 Iustin Pop
1441 a8083063 Iustin Pop
1442 821d1bd1 Iustin Pop
def BlockdevGetmirrorstatus(disks):
1443 a8083063 Iustin Pop
  """Get the mirroring status of a list of devices.
1444 a8083063 Iustin Pop

1445 10c2650b Iustin Pop
  @type disks: list of L{objects.Disk}
1446 10c2650b Iustin Pop
  @param disks: the list of disks which we should query
1447 10c2650b Iustin Pop
  @rtype: disk
1448 10c2650b Iustin Pop
  @return:
1449 10c2650b Iustin Pop
      a list of (mirror_done, estimated_time) tuples, which
1450 c41eea6e Iustin Pop
      are the result of L{bdev.BlockDev.CombinedSyncStatus}
1451 10c2650b Iustin Pop
  @raise errors.BlockDeviceError: if any of the disks cannot be
1452 10c2650b Iustin Pop
      found
1453 a8083063 Iustin Pop

1454 a8083063 Iustin Pop
  """
1455 a8083063 Iustin Pop
  stats = []
1456 a8083063 Iustin Pop
  for dsk in disks:
1457 a8083063 Iustin Pop
    rbd = _RecursiveFindBD(dsk)
1458 a8083063 Iustin Pop
    if rbd is None:
1459 3efa9051 Iustin Pop
      _Fail("Can't find device %s", dsk)
1460 96acbc09 Michael Hanselmann
1461 36145b12 Michael Hanselmann
    stats.append(rbd.CombinedSyncStatus())
1462 96acbc09 Michael Hanselmann
1463 c26a6bd2 Iustin Pop
  return stats
1464 a8083063 Iustin Pop
1465 a8083063 Iustin Pop
1466 bca2e7f4 Iustin Pop
def _RecursiveFindBD(disk):
1467 a8083063 Iustin Pop
  """Check if a device is activated.
1468 a8083063 Iustin Pop

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

1471 10c2650b Iustin Pop
  @type disk: L{objects.Disk}
1472 10c2650b Iustin Pop
  @param disk: the disk object we need to find
1473 a8083063 Iustin Pop

1474 10c2650b Iustin Pop
  @return: None if the device can't be found,
1475 10c2650b Iustin Pop
      otherwise the device instance
1476 a8083063 Iustin Pop

1477 a8083063 Iustin Pop
  """
1478 a8083063 Iustin Pop
  children = []
1479 a8083063 Iustin Pop
  if disk.children:
1480 a8083063 Iustin Pop
    for chdisk in disk.children:
1481 a8083063 Iustin Pop
      children.append(_RecursiveFindBD(chdisk))
1482 a8083063 Iustin Pop
1483 464f8daf Iustin Pop
  return bdev.FindDevice(disk.dev_type, disk.physical_id, children, disk.size)
1484 a8083063 Iustin Pop
1485 a8083063 Iustin Pop
1486 821d1bd1 Iustin Pop
def BlockdevFind(disk):
1487 a8083063 Iustin Pop
  """Check if a device is activated.
1488 a8083063 Iustin Pop

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

1491 10c2650b Iustin Pop
  @type disk: L{objects.Disk}
1492 10c2650b Iustin Pop
  @param disk: the disk to find
1493 96acbc09 Michael Hanselmann
  @rtype: None or objects.BlockDevStatus
1494 96acbc09 Michael Hanselmann
  @return: None if the disk cannot be found, otherwise a the current
1495 96acbc09 Michael Hanselmann
           information
1496 a8083063 Iustin Pop

1497 a8083063 Iustin Pop
  """
1498 23829f6f Iustin Pop
  try:
1499 23829f6f Iustin Pop
    rbd = _RecursiveFindBD(disk)
1500 23829f6f Iustin Pop
  except errors.BlockDeviceError, err:
1501 2cc6781a Iustin Pop
    _Fail("Failed to find device: %s", err, exc=True)
1502 96acbc09 Michael Hanselmann
1503 a8083063 Iustin Pop
  if rbd is None:
1504 c26a6bd2 Iustin Pop
    return None
1505 96acbc09 Michael Hanselmann
1506 96acbc09 Michael Hanselmann
  return rbd.GetSyncStatus()
1507 a8083063 Iustin Pop
1508 a8083063 Iustin Pop
1509 968a7623 Iustin Pop
def BlockdevGetsize(disks):
1510 968a7623 Iustin Pop
  """Computes the size of the given disks.
1511 968a7623 Iustin Pop

1512 968a7623 Iustin Pop
  If a disk is not found, returns None instead.
1513 968a7623 Iustin Pop

1514 968a7623 Iustin Pop
  @type disks: list of L{objects.Disk}
1515 968a7623 Iustin Pop
  @param disks: the list of disk to compute the size for
1516 968a7623 Iustin Pop
  @rtype: list
1517 968a7623 Iustin Pop
  @return: list with elements None if the disk cannot be found,
1518 968a7623 Iustin Pop
      otherwise the size
1519 968a7623 Iustin Pop

1520 968a7623 Iustin Pop
  """
1521 968a7623 Iustin Pop
  result = []
1522 968a7623 Iustin Pop
  for cf in disks:
1523 968a7623 Iustin Pop
    try:
1524 968a7623 Iustin Pop
      rbd = _RecursiveFindBD(cf)
1525 1122eb25 Iustin Pop
    except errors.BlockDeviceError:
1526 968a7623 Iustin Pop
      result.append(None)
1527 968a7623 Iustin Pop
      continue
1528 968a7623 Iustin Pop
    if rbd is None:
1529 968a7623 Iustin Pop
      result.append(None)
1530 968a7623 Iustin Pop
    else:
1531 968a7623 Iustin Pop
      result.append(rbd.GetActualSize())
1532 968a7623 Iustin Pop
  return result
1533 968a7623 Iustin Pop
1534 968a7623 Iustin Pop
1535 858f3d18 Iustin Pop
def BlockdevExport(disk, dest_node, dest_path, cluster_name):
1536 858f3d18 Iustin Pop
  """Export a block device to a remote node.
1537 858f3d18 Iustin Pop

1538 858f3d18 Iustin Pop
  @type disk: L{objects.Disk}
1539 858f3d18 Iustin Pop
  @param disk: the description of the disk to export
1540 858f3d18 Iustin Pop
  @type dest_node: str
1541 858f3d18 Iustin Pop
  @param dest_node: the destination node to export to
1542 858f3d18 Iustin Pop
  @type dest_path: str
1543 858f3d18 Iustin Pop
  @param dest_path: the destination path on the target node
1544 858f3d18 Iustin Pop
  @type cluster_name: str
1545 858f3d18 Iustin Pop
  @param cluster_name: the cluster name, needed for SSH hostalias
1546 858f3d18 Iustin Pop
  @rtype: None
1547 858f3d18 Iustin Pop

1548 858f3d18 Iustin Pop
  """
1549 858f3d18 Iustin Pop
  real_disk = _RecursiveFindBD(disk)
1550 858f3d18 Iustin Pop
  if real_disk is None:
1551 858f3d18 Iustin Pop
    _Fail("Block device '%s' is not set up", disk)
1552 858f3d18 Iustin Pop
1553 858f3d18 Iustin Pop
  real_disk.Open()
1554 858f3d18 Iustin Pop
1555 858f3d18 Iustin Pop
  # the block size on the read dd is 1MiB to match our units
1556 858f3d18 Iustin Pop
  expcmd = utils.BuildShellCmd("set -e; set -o pipefail; "
1557 858f3d18 Iustin Pop
                               "dd if=%s bs=1048576 count=%s",
1558 858f3d18 Iustin Pop
                               real_disk.dev_path, str(disk.size))
1559 858f3d18 Iustin Pop
1560 858f3d18 Iustin Pop
  # we set here a smaller block size as, due to ssh buffering, more
1561 858f3d18 Iustin Pop
  # than 64-128k will mostly ignored; we use nocreat to fail if the
1562 858f3d18 Iustin Pop
  # device is not already there or we pass a wrong path; we use
1563 858f3d18 Iustin Pop
  # notrunc to no attempt truncate on an LV device; we use oflag=dsync
1564 858f3d18 Iustin Pop
  # to not buffer too much memory; this means that at best, we flush
1565 858f3d18 Iustin Pop
  # every 64k, which will not be very fast
1566 858f3d18 Iustin Pop
  destcmd = utils.BuildShellCmd("dd of=%s conv=nocreat,notrunc bs=65536"
1567 858f3d18 Iustin Pop
                                " oflag=dsync", dest_path)
1568 858f3d18 Iustin Pop
1569 858f3d18 Iustin Pop
  remotecmd = _GetSshRunner(cluster_name).BuildCmd(dest_node,
1570 858f3d18 Iustin Pop
                                                   constants.GANETI_RUNAS,
1571 858f3d18 Iustin Pop
                                                   destcmd)
1572 858f3d18 Iustin Pop
1573 858f3d18 Iustin Pop
  # all commands have been checked, so we're safe to combine them
1574 858f3d18 Iustin Pop
  command = '|'.join([expcmd, utils.ShellQuoteArgs(remotecmd)])
1575 858f3d18 Iustin Pop
1576 858f3d18 Iustin Pop
  result = utils.RunCmd(["bash", "-c", command])
1577 858f3d18 Iustin Pop
1578 858f3d18 Iustin Pop
  if result.failed:
1579 858f3d18 Iustin Pop
    _Fail("Disk copy command '%s' returned error: %s"
1580 858f3d18 Iustin Pop
          " output: %s", command, result.fail_reason, result.output)
1581 858f3d18 Iustin Pop
1582 858f3d18 Iustin Pop
1583 a8083063 Iustin Pop
def UploadFile(file_name, data, mode, uid, gid, atime, mtime):
1584 a8083063 Iustin Pop
  """Write a file to the filesystem.
1585 a8083063 Iustin Pop

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

1589 10c2650b Iustin Pop
  @type file_name: str
1590 10c2650b Iustin Pop
  @param file_name: the target file name
1591 10c2650b Iustin Pop
  @type data: str
1592 10c2650b Iustin Pop
  @param data: the new contents of the file
1593 10c2650b Iustin Pop
  @type mode: int
1594 10c2650b Iustin Pop
  @param mode: the mode to give the file (can be None)
1595 10c2650b Iustin Pop
  @type uid: int
1596 10c2650b Iustin Pop
  @param uid: the owner of the file (can be -1 for default)
1597 10c2650b Iustin Pop
  @type gid: int
1598 10c2650b Iustin Pop
  @param gid: the group of the file (can be -1 for default)
1599 10c2650b Iustin Pop
  @type atime: float
1600 10c2650b Iustin Pop
  @param atime: the atime to set on the file (can be None)
1601 10c2650b Iustin Pop
  @type mtime: float
1602 10c2650b Iustin Pop
  @param mtime: the mtime to set on the file (can be None)
1603 c26a6bd2 Iustin Pop
  @rtype: None
1604 10c2650b Iustin Pop

1605 a8083063 Iustin Pop
  """
1606 a8083063 Iustin Pop
  if not os.path.isabs(file_name):
1607 2cc6781a Iustin Pop
    _Fail("Filename passed to UploadFile is not absolute: '%s'", file_name)
1608 a8083063 Iustin Pop
1609 360b0dc2 Iustin Pop
  if file_name not in _ALLOWED_UPLOAD_FILES:
1610 2cc6781a Iustin Pop
    _Fail("Filename passed to UploadFile not in allowed upload targets: '%s'",
1611 2cc6781a Iustin Pop
          file_name)
1612 a8083063 Iustin Pop
1613 12bce260 Michael Hanselmann
  raw_data = _Decompress(data)
1614 12bce260 Michael Hanselmann
1615 12bce260 Michael Hanselmann
  utils.WriteFile(file_name, data=raw_data, mode=mode, uid=uid, gid=gid,
1616 41a57aab Michael Hanselmann
                  atime=atime, mtime=mtime)
1617 a8083063 Iustin Pop
1618 386b57af Iustin Pop
1619 03d1dba2 Michael Hanselmann
def WriteSsconfFiles(values):
1620 89b14f05 Iustin Pop
  """Update all ssconf files.
1621 89b14f05 Iustin Pop

1622 89b14f05 Iustin Pop
  Wrapper around the SimpleStore.WriteFiles.
1623 89b14f05 Iustin Pop

1624 89b14f05 Iustin Pop
  """
1625 89b14f05 Iustin Pop
  ssconf.SimpleStore().WriteFiles(values)
1626 6ddc95ec Michael Hanselmann
1627 6ddc95ec Michael Hanselmann
1628 a8083063 Iustin Pop
def _ErrnoOrStr(err):
1629 a8083063 Iustin Pop
  """Format an EnvironmentError exception.
1630 a8083063 Iustin Pop

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

1635 10c2650b Iustin Pop
  @type err: L{EnvironmentError}
1636 10c2650b Iustin Pop
  @param err: the exception to format
1637 a8083063 Iustin Pop

1638 a8083063 Iustin Pop
  """
1639 a8083063 Iustin Pop
  if hasattr(err, 'errno'):
1640 a8083063 Iustin Pop
    detail = errno.errorcode[err.errno]
1641 a8083063 Iustin Pop
  else:
1642 a8083063 Iustin Pop
    detail = str(err)
1643 a8083063 Iustin Pop
  return detail
1644 a8083063 Iustin Pop
1645 5d0fe286 Iustin Pop
1646 c19f9810 Iustin Pop
def _OSOndiskAPIVersion(os_dir):
1647 2f8598a5 Alexander Schreiber
  """Compute and return the API version of a given OS.
1648 a8083063 Iustin Pop

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

1652 10c2650b Iustin Pop
  @type os_dir: str
1653 c19f9810 Iustin Pop
  @param os_dir: the directory in which we should look for the OS
1654 8e70b181 Iustin Pop
  @rtype: tuple
1655 8e70b181 Iustin Pop
  @return: tuple (status, data) with status denoting the validity and
1656 8e70b181 Iustin Pop
      data holding either the vaid versions or an error message
1657 a8083063 Iustin Pop

1658 a8083063 Iustin Pop
  """
1659 b6b45e0d Guido Trotter
  api_file = os.path.sep.join([os_dir, constants.OS_API_FILE])
1660 a8083063 Iustin Pop
1661 a8083063 Iustin Pop
  try:
1662 a8083063 Iustin Pop
    st = os.stat(api_file)
1663 a8083063 Iustin Pop
  except EnvironmentError, err:
1664 b6b45e0d Guido Trotter
    return False, ("Required file '%s' not found under path %s: %s" %
1665 b6b45e0d Guido Trotter
                   (constants.OS_API_FILE, os_dir, _ErrnoOrStr(err)))
1666 a8083063 Iustin Pop
1667 a8083063 Iustin Pop
  if not stat.S_ISREG(stat.S_IFMT(st.st_mode)):
1668 b6b45e0d Guido Trotter
    return False, ("File '%s' in %s is not a regular file" %
1669 b6b45e0d Guido Trotter
                   (constants.OS_API_FILE, os_dir))
1670 a8083063 Iustin Pop
1671 a8083063 Iustin Pop
  try:
1672 3374afa9 Guido Trotter
    api_versions = utils.ReadFile(api_file).splitlines()
1673 a8083063 Iustin Pop
  except EnvironmentError, err:
1674 255dcebd Iustin Pop
    return False, ("Error while reading the API version file at %s: %s" %
1675 255dcebd Iustin Pop
                   (api_file, _ErrnoOrStr(err)))
1676 a8083063 Iustin Pop
1677 a8083063 Iustin Pop
  try:
1678 63b9b186 Guido Trotter
    api_versions = [int(version.strip()) for version in api_versions]
1679 a8083063 Iustin Pop
  except (TypeError, ValueError), err:
1680 255dcebd Iustin Pop
    return False, ("API version(s) can't be converted to integer: %s" %
1681 255dcebd Iustin Pop
                   str(err))
1682 a8083063 Iustin Pop
1683 255dcebd Iustin Pop
  return True, api_versions
1684 a8083063 Iustin Pop
1685 386b57af Iustin Pop
1686 7c3d51d4 Guido Trotter
def DiagnoseOS(top_dirs=None):
1687 a8083063 Iustin Pop
  """Compute the validity for all OSes.
1688 a8083063 Iustin Pop

1689 10c2650b Iustin Pop
  @type top_dirs: list
1690 10c2650b Iustin Pop
  @param top_dirs: the list of directories in which to
1691 10c2650b Iustin Pop
      search (if not given defaults to
1692 10c2650b Iustin Pop
      L{constants.OS_SEARCH_PATH})
1693 10c2650b Iustin Pop
  @rtype: list of L{objects.OS}
1694 ba00557a Guido Trotter
  @return: a list of tuples (name, path, status, diagnose, variants)
1695 255dcebd Iustin Pop
      for all (potential) OSes under all search paths, where:
1696 255dcebd Iustin Pop
          - name is the (potential) OS name
1697 255dcebd Iustin Pop
          - path is the full path to the OS
1698 255dcebd Iustin Pop
          - status True/False is the validity of the OS
1699 255dcebd Iustin Pop
          - diagnose is the error message for an invalid OS, otherwise empty
1700 ba00557a Guido Trotter
          - variants is a list of supported OS variants, if any
1701 a8083063 Iustin Pop

1702 a8083063 Iustin Pop
  """
1703 7c3d51d4 Guido Trotter
  if top_dirs is None:
1704 7c3d51d4 Guido Trotter
    top_dirs = constants.OS_SEARCH_PATH
1705 a8083063 Iustin Pop
1706 a8083063 Iustin Pop
  result = []
1707 65fe4693 Iustin Pop
  for dir_name in top_dirs:
1708 65fe4693 Iustin Pop
    if os.path.isdir(dir_name):
1709 7c3d51d4 Guido Trotter
      try:
1710 65fe4693 Iustin Pop
        f_names = utils.ListVisibleFiles(dir_name)
1711 7c3d51d4 Guido Trotter
      except EnvironmentError, err:
1712 29921401 Iustin Pop
        logging.exception("Can't list the OS directory %s: %s", dir_name, err)
1713 7c3d51d4 Guido Trotter
        break
1714 7c3d51d4 Guido Trotter
      for name in f_names:
1715 255dcebd Iustin Pop
        os_path = os.path.sep.join([dir_name, name])
1716 255dcebd Iustin Pop
        status, os_inst = _TryOSFromDisk(name, base_dir=dir_name)
1717 255dcebd Iustin Pop
        if status:
1718 255dcebd Iustin Pop
          diagnose = ""
1719 ba00557a Guido Trotter
          variants = os_inst.supported_variants
1720 255dcebd Iustin Pop
        else:
1721 255dcebd Iustin Pop
          diagnose = os_inst
1722 ba00557a Guido Trotter
          variants = []
1723 ba00557a Guido Trotter
        result.append((name, os_path, status, diagnose, variants))
1724 a8083063 Iustin Pop
1725 c26a6bd2 Iustin Pop
  return result
1726 a8083063 Iustin Pop
1727 a8083063 Iustin Pop
1728 255dcebd Iustin Pop
def _TryOSFromDisk(name, base_dir=None):
1729 a8083063 Iustin Pop
  """Create an OS instance from disk.
1730 a8083063 Iustin Pop

1731 a8083063 Iustin Pop
  This function will return an OS instance if the given name is a
1732 8e70b181 Iustin Pop
  valid OS name.
1733 a8083063 Iustin Pop

1734 8ee4dc80 Guido Trotter
  @type base_dir: string
1735 8ee4dc80 Guido Trotter
  @keyword base_dir: Base directory containing OS installations.
1736 8ee4dc80 Guido Trotter
                     Defaults to a search in all the OS_SEARCH_PATH dirs.
1737 255dcebd Iustin Pop
  @rtype: tuple
1738 255dcebd Iustin Pop
  @return: success and either the OS instance if we find a valid one,
1739 255dcebd Iustin Pop
      or error message
1740 7c3d51d4 Guido Trotter

1741 a8083063 Iustin Pop
  """
1742 56bcd3f4 Guido Trotter
  if base_dir is None:
1743 57c177af Iustin Pop
    os_dir = utils.FindFile(name, constants.OS_SEARCH_PATH, os.path.isdir)
1744 c34c0cfd Iustin Pop
  else:
1745 f95c81bf Iustin Pop
    os_dir = utils.FindFile(name, [base_dir], os.path.isdir)
1746 f95c81bf Iustin Pop
1747 f95c81bf Iustin Pop
  if os_dir is None:
1748 5c0433d6 Iustin Pop
    return False, "Directory for OS %s not found in search path" % name
1749 a8083063 Iustin Pop
1750 c19f9810 Iustin Pop
  status, api_versions = _OSOndiskAPIVersion(os_dir)
1751 255dcebd Iustin Pop
  if not status:
1752 255dcebd Iustin Pop
    # push the error up
1753 255dcebd Iustin Pop
    return status, api_versions
1754 a8083063 Iustin Pop
1755 d1a7d66f Guido Trotter
  if not constants.OS_API_VERSIONS.intersection(api_versions):
1756 255dcebd Iustin Pop
    return False, ("API version mismatch for path '%s': found %s, want %s." %
1757 d1a7d66f Guido Trotter
                   (os_dir, api_versions, constants.OS_API_VERSIONS))
1758 a8083063 Iustin Pop
1759 41ba4061 Guido Trotter
  # OS Files dictionary, we will populate it with the absolute path names
1760 41ba4061 Guido Trotter
  os_files = dict.fromkeys(constants.OS_SCRIPTS)
1761 a8083063 Iustin Pop
1762 95075fba Guido Trotter
  if max(api_versions) >= constants.OS_API_V15:
1763 95075fba Guido Trotter
    os_files[constants.OS_VARIANTS_FILE] = ''
1764 95075fba Guido Trotter
1765 ea79fc15 Michael Hanselmann
  for filename in os_files:
1766 ea79fc15 Michael Hanselmann
    os_files[filename] = os.path.sep.join([os_dir, filename])
1767 a8083063 Iustin Pop
1768 a8083063 Iustin Pop
    try:
1769 ea79fc15 Michael Hanselmann
      st = os.stat(os_files[filename])
1770 a8083063 Iustin Pop
    except EnvironmentError, err:
1771 41ba4061 Guido Trotter
      return False, ("File '%s' under path '%s' is missing (%s)" %
1772 ea79fc15 Michael Hanselmann
                     (filename, os_dir, _ErrnoOrStr(err)))
1773 a8083063 Iustin Pop
1774 a8083063 Iustin Pop
    if not stat.S_ISREG(stat.S_IFMT(st.st_mode)):
1775 41ba4061 Guido Trotter
      return False, ("File '%s' under path '%s' is not a regular file" %
1776 ea79fc15 Michael Hanselmann
                     (filename, os_dir))
1777 255dcebd Iustin Pop
1778 ea79fc15 Michael Hanselmann
    if filename in constants.OS_SCRIPTS:
1779 0757c107 Guido Trotter
      if stat.S_IMODE(st.st_mode) & stat.S_IXUSR != stat.S_IXUSR:
1780 0757c107 Guido Trotter
        return False, ("File '%s' under path '%s' is not executable" %
1781 ea79fc15 Michael Hanselmann
                       (filename, os_dir))
1782 0757c107 Guido Trotter
1783 95075fba Guido Trotter
  variants = None
1784 95075fba Guido Trotter
  if constants.OS_VARIANTS_FILE in os_files:
1785 95075fba Guido Trotter
    variants_file = os_files[constants.OS_VARIANTS_FILE]
1786 95075fba Guido Trotter
    try:
1787 95075fba Guido Trotter
      variants = utils.ReadFile(variants_file).splitlines()
1788 95075fba Guido Trotter
    except EnvironmentError, err:
1789 95075fba Guido Trotter
      return False, ("Error while reading the OS variants file at %s: %s" %
1790 95075fba Guido Trotter
                     (variants_file, _ErrnoOrStr(err)))
1791 95075fba Guido Trotter
    if not variants:
1792 95075fba Guido Trotter
      return False, ("No supported os variant found")
1793 0757c107 Guido Trotter
1794 8e70b181 Iustin Pop
  os_obj = objects.OS(name=name, path=os_dir,
1795 41ba4061 Guido Trotter
                      create_script=os_files[constants.OS_SCRIPT_CREATE],
1796 41ba4061 Guido Trotter
                      export_script=os_files[constants.OS_SCRIPT_EXPORT],
1797 41ba4061 Guido Trotter
                      import_script=os_files[constants.OS_SCRIPT_IMPORT],
1798 41ba4061 Guido Trotter
                      rename_script=os_files[constants.OS_SCRIPT_RENAME],
1799 95075fba Guido Trotter
                      supported_variants=variants,
1800 255dcebd Iustin Pop
                      api_versions=api_versions)
1801 255dcebd Iustin Pop
  return True, os_obj
1802 255dcebd Iustin Pop
1803 255dcebd Iustin Pop
1804 255dcebd Iustin Pop
def OSFromDisk(name, base_dir=None):
1805 255dcebd Iustin Pop
  """Create an OS instance from disk.
1806 255dcebd Iustin Pop

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

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

1814 255dcebd Iustin Pop
  @type base_dir: string
1815 255dcebd Iustin Pop
  @keyword base_dir: Base directory containing OS installations.
1816 255dcebd Iustin Pop
                     Defaults to a search in all the OS_SEARCH_PATH dirs.
1817 255dcebd Iustin Pop
  @rtype: L{objects.OS}
1818 255dcebd Iustin Pop
  @return: the OS instance if we find a valid one
1819 255dcebd Iustin Pop
  @raise RPCFail: if we don't find a valid OS
1820 255dcebd Iustin Pop

1821 255dcebd Iustin Pop
  """
1822 69b99987 Michael Hanselmann
  name_only = name.split("+", 1)[0]
1823 6ee7102a Guido Trotter
  status, payload = _TryOSFromDisk(name_only, base_dir)
1824 255dcebd Iustin Pop
1825 255dcebd Iustin Pop
  if not status:
1826 255dcebd Iustin Pop
    _Fail(payload)
1827 a8083063 Iustin Pop
1828 255dcebd Iustin Pop
  return payload
1829 a8083063 Iustin Pop
1830 a8083063 Iustin Pop
1831 099c52ad Iustin Pop
def OSEnvironment(instance, inst_os, debug=0):
1832 2266edb2 Guido Trotter
  """Calculate the environment for an os script.
1833 2266edb2 Guido Trotter

1834 10c2650b Iustin Pop
  @type instance: L{objects.Instance}
1835 2266edb2 Guido Trotter
  @param instance: target instance for the os script run
1836 099c52ad Iustin Pop
  @type inst_os: L{objects.OS}
1837 099c52ad Iustin Pop
  @param inst_os: operating system for which the environment is being built
1838 2266edb2 Guido Trotter
  @type debug: integer
1839 10c2650b Iustin Pop
  @param debug: debug level (0 or 1, for OS Api 10)
1840 2266edb2 Guido Trotter
  @rtype: dict
1841 2266edb2 Guido Trotter
  @return: dict of environment variables
1842 10c2650b Iustin Pop
  @raise errors.BlockDeviceError: if the block device
1843 10c2650b Iustin Pop
      cannot be found
1844 2266edb2 Guido Trotter

1845 2266edb2 Guido Trotter
  """
1846 2266edb2 Guido Trotter
  result = {}
1847 099c52ad Iustin Pop
  api_version = \
1848 099c52ad Iustin Pop
    max(constants.OS_API_VERSIONS.intersection(inst_os.api_versions))
1849 d1a7d66f Guido Trotter
  result['OS_API_VERSION'] = '%d' % api_version
1850 2266edb2 Guido Trotter
  result['INSTANCE_NAME'] = instance.name
1851 15552312 Iustin Pop
  result['INSTANCE_OS'] = instance.os
1852 2266edb2 Guido Trotter
  result['HYPERVISOR'] = instance.hypervisor
1853 2266edb2 Guido Trotter
  result['DISK_COUNT'] = '%d' % len(instance.disks)
1854 2266edb2 Guido Trotter
  result['NIC_COUNT'] = '%d' % len(instance.nics)
1855 2266edb2 Guido Trotter
  result['DEBUG_LEVEL'] = '%d' % debug
1856 f11280b5 Guido Trotter
  if api_version >= constants.OS_API_V15:
1857 f11280b5 Guido Trotter
    try:
1858 f11280b5 Guido Trotter
      variant = instance.os.split('+', 1)[1]
1859 f11280b5 Guido Trotter
    except IndexError:
1860 099c52ad Iustin Pop
      variant = inst_os.supported_variants[0]
1861 f11280b5 Guido Trotter
    result['OS_VARIANT'] = variant
1862 2266edb2 Guido Trotter
  for idx, disk in enumerate(instance.disks):
1863 2266edb2 Guido Trotter
    real_disk = _RecursiveFindBD(disk)
1864 2266edb2 Guido Trotter
    if real_disk is None:
1865 2266edb2 Guido Trotter
      raise errors.BlockDeviceError("Block device '%s' is not set up" %
1866 2266edb2 Guido Trotter
                                    str(disk))
1867 2266edb2 Guido Trotter
    real_disk.Open()
1868 2266edb2 Guido Trotter
    result['DISK_%d_PATH' % idx] = real_disk.dev_path
1869 15552312 Iustin Pop
    result['DISK_%d_ACCESS' % idx] = disk.mode
1870 2266edb2 Guido Trotter
    if constants.HV_DISK_TYPE in instance.hvparams:
1871 2266edb2 Guido Trotter
      result['DISK_%d_FRONTEND_TYPE' % idx] = \
1872 2266edb2 Guido Trotter
        instance.hvparams[constants.HV_DISK_TYPE]
1873 2266edb2 Guido Trotter
    if disk.dev_type in constants.LDS_BLOCK:
1874 2266edb2 Guido Trotter
      result['DISK_%d_BACKEND_TYPE' % idx] = 'block'
1875 2266edb2 Guido Trotter
    elif disk.dev_type == constants.LD_FILE:
1876 2266edb2 Guido Trotter
      result['DISK_%d_BACKEND_TYPE' % idx] = \
1877 2266edb2 Guido Trotter
        'file:%s' % disk.physical_id[0]
1878 2266edb2 Guido Trotter
  for idx, nic in enumerate(instance.nics):
1879 2266edb2 Guido Trotter
    result['NIC_%d_MAC' % idx] = nic.mac
1880 2266edb2 Guido Trotter
    if nic.ip:
1881 2266edb2 Guido Trotter
      result['NIC_%d_IP' % idx] = nic.ip
1882 1ba9227f Guido Trotter
    result['NIC_%d_MODE' % idx] = nic.nicparams[constants.NIC_MODE]
1883 1ba9227f Guido Trotter
    if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
1884 1ba9227f Guido Trotter
      result['NIC_%d_BRIDGE' % idx] = nic.nicparams[constants.NIC_LINK]
1885 1ba9227f Guido Trotter
    if nic.nicparams[constants.NIC_LINK]:
1886 1ba9227f Guido Trotter
      result['NIC_%d_LINK' % idx] = nic.nicparams[constants.NIC_LINK]
1887 2266edb2 Guido Trotter
    if constants.HV_NIC_TYPE in instance.hvparams:
1888 2266edb2 Guido Trotter
      result['NIC_%d_FRONTEND_TYPE' % idx] = \
1889 2266edb2 Guido Trotter
        instance.hvparams[constants.HV_NIC_TYPE]
1890 2266edb2 Guido Trotter
1891 67fc3042 Iustin Pop
  for source, kind in [(instance.beparams, "BE"), (instance.hvparams, "HV")]:
1892 67fc3042 Iustin Pop
    for key, value in source.items():
1893 030b218a Iustin Pop
      result["INSTANCE_%s_%s" % (kind, key)] = str(value)
1894 67fc3042 Iustin Pop
1895 2266edb2 Guido Trotter
  return result
1896 a8083063 Iustin Pop
1897 821d1bd1 Iustin Pop
def BlockdevGrow(disk, amount):
1898 594609c0 Iustin Pop
  """Grow a stack of block devices.
1899 594609c0 Iustin Pop

1900 594609c0 Iustin Pop
  This function is called recursively, with the childrens being the
1901 10c2650b Iustin Pop
  first ones to resize.
1902 594609c0 Iustin Pop

1903 10c2650b Iustin Pop
  @type disk: L{objects.Disk}
1904 10c2650b Iustin Pop
  @param disk: the disk to be grown
1905 10c2650b Iustin Pop
  @rtype: (status, result)
1906 10c2650b Iustin Pop
  @return: a tuple with the status of the operation
1907 10c2650b Iustin Pop
      (True/False), and the errors message if status
1908 10c2650b Iustin Pop
      is False
1909 594609c0 Iustin Pop

1910 594609c0 Iustin Pop
  """
1911 594609c0 Iustin Pop
  r_dev = _RecursiveFindBD(disk)
1912 594609c0 Iustin Pop
  if r_dev is None:
1913 afdc3985 Iustin Pop
    _Fail("Cannot find block device %s", disk)
1914 594609c0 Iustin Pop
1915 594609c0 Iustin Pop
  try:
1916 594609c0 Iustin Pop
    r_dev.Grow(amount)
1917 594609c0 Iustin Pop
  except errors.BlockDeviceError, err:
1918 2cc6781a Iustin Pop
    _Fail("Failed to grow block device: %s", err, exc=True)
1919 594609c0 Iustin Pop
1920 594609c0 Iustin Pop
1921 821d1bd1 Iustin Pop
def BlockdevSnapshot(disk):
1922 a8083063 Iustin Pop
  """Create a snapshot copy of a block device.
1923 a8083063 Iustin Pop

1924 a8083063 Iustin Pop
  This function is called recursively, and the snapshot is actually created
1925 a8083063 Iustin Pop
  just for the leaf lvm backend device.
1926 a8083063 Iustin Pop

1927 e9e9263d Guido Trotter
  @type disk: L{objects.Disk}
1928 e9e9263d Guido Trotter
  @param disk: the disk to be snapshotted
1929 e9e9263d Guido Trotter
  @rtype: string
1930 e9e9263d Guido Trotter
  @return: snapshot disk path
1931 a8083063 Iustin Pop

1932 098c0958 Michael Hanselmann
  """
1933 a8083063 Iustin Pop
  if disk.children:
1934 a8083063 Iustin Pop
    if len(disk.children) == 1:
1935 a8083063 Iustin Pop
      # only one child, let's recurse on it
1936 821d1bd1 Iustin Pop
      return BlockdevSnapshot(disk.children[0])
1937 a8083063 Iustin Pop
    else:
1938 a8083063 Iustin Pop
      # more than one child, choose one that matches
1939 a8083063 Iustin Pop
      for child in disk.children:
1940 a8083063 Iustin Pop
        if child.size == disk.size:
1941 a8083063 Iustin Pop
          # return implies breaking the loop
1942 821d1bd1 Iustin Pop
          return BlockdevSnapshot(child)
1943 fe96220b Iustin Pop
  elif disk.dev_type == constants.LD_LV:
1944 a8083063 Iustin Pop
    r_dev = _RecursiveFindBD(disk)
1945 a8083063 Iustin Pop
    if r_dev is not None:
1946 a8083063 Iustin Pop
      # let's stay on the safe side and ask for the full size, for now
1947 c26a6bd2 Iustin Pop
      return r_dev.Snapshot(disk.size)
1948 a8083063 Iustin Pop
    else:
1949 87812fd3 Iustin Pop
      _Fail("Cannot find block device %s", disk)
1950 a8083063 Iustin Pop
  else:
1951 87812fd3 Iustin Pop
    _Fail("Cannot snapshot non-lvm block device '%s' of type '%s'",
1952 87812fd3 Iustin Pop
          disk.unique_id, disk.dev_type)
1953 a8083063 Iustin Pop
1954 a8083063 Iustin Pop
1955 4a0e011f Iustin Pop
def ExportSnapshot(disk, dest_node, instance, cluster_name, idx, debug):
1956 a8083063 Iustin Pop
  """Export a block device snapshot to a remote node.
1957 a8083063 Iustin Pop

1958 74c47259 Iustin Pop
  @type disk: L{objects.Disk}
1959 74c47259 Iustin Pop
  @param disk: the description of the disk to export
1960 74c47259 Iustin Pop
  @type dest_node: str
1961 74c47259 Iustin Pop
  @param dest_node: the destination node to export to
1962 74c47259 Iustin Pop
  @type instance: L{objects.Instance}
1963 74c47259 Iustin Pop
  @param instance: the instance object to whom the disk belongs
1964 74c47259 Iustin Pop
  @type cluster_name: str
1965 74c47259 Iustin Pop
  @param cluster_name: the cluster name, needed for SSH hostalias
1966 74c47259 Iustin Pop
  @type idx: int
1967 74c47259 Iustin Pop
  @param idx: the index of the disk in the instance's disk list,
1968 74c47259 Iustin Pop
      used to export to the OS scripts environment
1969 4a0e011f Iustin Pop
  @type debug: integer
1970 4a0e011f Iustin Pop
  @param debug: debug level, passed to the OS scripts
1971 c26a6bd2 Iustin Pop
  @rtype: None
1972 a8083063 Iustin Pop

1973 098c0958 Michael Hanselmann
  """
1974 a8083063 Iustin Pop
  inst_os = OSFromDisk(instance.os)
1975 4a0e011f Iustin Pop
  export_env = OSEnvironment(instance, inst_os, debug)
1976 d1a7d66f Guido Trotter
1977 a8083063 Iustin Pop
  export_script = inst_os.export_script
1978 a8083063 Iustin Pop
1979 a8083063 Iustin Pop
  logfile = "%s/exp-%s-%s-%s.log" % (constants.LOG_OS_DIR, inst_os.name,
1980 a8083063 Iustin Pop
                                     instance.name, int(time.time()))
1981 a8083063 Iustin Pop
  if not os.path.exists(constants.LOG_OS_DIR):
1982 a8083063 Iustin Pop
    os.mkdir(constants.LOG_OS_DIR, 0750)
1983 0607699d Guido Trotter
  real_disk = _RecursiveFindBD(disk)
1984 0607699d Guido Trotter
  if real_disk is None:
1985 ba55d062 Iustin Pop
    _Fail("Block device '%s' is not set up", disk)
1986 ba55d062 Iustin Pop
1987 0607699d Guido Trotter
  real_disk.Open()
1988 0607699d Guido Trotter
1989 0607699d Guido Trotter
  export_env['EXPORT_DEVICE'] = real_disk.dev_path
1990 74c47259 Iustin Pop
  export_env['EXPORT_INDEX'] = str(idx)
1991 a8083063 Iustin Pop
1992 a8083063 Iustin Pop
  destdir = os.path.join(constants.EXPORT_DIR, instance.name + ".new")
1993 a8083063 Iustin Pop
  destfile = disk.physical_id[1]
1994 a8083063 Iustin Pop
1995 a8083063 Iustin Pop
  # the target command is built out of three individual commands,
1996 a8083063 Iustin Pop
  # which are joined by pipes; we check each individual command for
1997 a8083063 Iustin Pop
  # valid parameters
1998 a48b08bf Iustin Pop
  expcmd = utils.BuildShellCmd("set -e; set -o pipefail; cd %s; %s 2>%s",
1999 a48b08bf Iustin Pop
                               inst_os.path, export_script, logfile)
2000 a8083063 Iustin Pop
2001 a8083063 Iustin Pop
  comprcmd = "gzip"
2002 a8083063 Iustin Pop
2003 72f0f7fd Iustin Pop
  destcmd = utils.BuildShellCmd("mkdir -p %s && cat > %s/%s",
2004 00003458 Guido Trotter
                                destdir, destdir, destfile)
2005 62c9ec92 Iustin Pop
  remotecmd = _GetSshRunner(cluster_name).BuildCmd(dest_node,
2006 62c9ec92 Iustin Pop
                                                   constants.GANETI_RUNAS,
2007 62c9ec92 Iustin Pop
                                                   destcmd)
2008 a8083063 Iustin Pop
2009 a8083063 Iustin Pop
  # all commands have been checked, so we're safe to combine them
2010 72f0f7fd Iustin Pop
  command = '|'.join([expcmd, comprcmd, utils.ShellQuoteArgs(remotecmd)])
2011 a8083063 Iustin Pop
2012 a48b08bf Iustin Pop
  result = utils.RunCmd(["bash", "-c", command], env=export_env)
2013 a8083063 Iustin Pop
2014 a8083063 Iustin Pop
  if result.failed:
2015 ba55d062 Iustin Pop
    _Fail("OS snapshot export command '%s' returned error: %s"
2016 ba55d062 Iustin Pop
          " output: %s", command, result.fail_reason, result.output)
2017 a8083063 Iustin Pop
2018 a8083063 Iustin Pop
2019 a8083063 Iustin Pop
def FinalizeExport(instance, snap_disks):
2020 a8083063 Iustin Pop
  """Write out the export configuration information.
2021 a8083063 Iustin Pop

2022 10c2650b Iustin Pop
  @type instance: L{objects.Instance}
2023 10c2650b Iustin Pop
  @param instance: the instance which we export, used for
2024 10c2650b Iustin Pop
      saving configuration
2025 10c2650b Iustin Pop
  @type snap_disks: list of L{objects.Disk}
2026 10c2650b Iustin Pop
  @param snap_disks: list of snapshot block devices, which
2027 10c2650b Iustin Pop
      will be used to get the actual name of the dump file
2028 a8083063 Iustin Pop

2029 c26a6bd2 Iustin Pop
  @rtype: None
2030 a8083063 Iustin Pop

2031 098c0958 Michael Hanselmann
  """
2032 a8083063 Iustin Pop
  destdir = os.path.join(constants.EXPORT_DIR, instance.name + ".new")
2033 a8083063 Iustin Pop
  finaldestdir = os.path.join(constants.EXPORT_DIR, instance.name)
2034 a8083063 Iustin Pop
2035 a8083063 Iustin Pop
  config = objects.SerializableConfigParser()
2036 a8083063 Iustin Pop
2037 a8083063 Iustin Pop
  config.add_section(constants.INISECT_EXP)
2038 a8083063 Iustin Pop
  config.set(constants.INISECT_EXP, 'version', '0')
2039 a8083063 Iustin Pop
  config.set(constants.INISECT_EXP, 'timestamp', '%d' % int(time.time()))
2040 a8083063 Iustin Pop
  config.set(constants.INISECT_EXP, 'source', instance.primary_node)
2041 a8083063 Iustin Pop
  config.set(constants.INISECT_EXP, 'os', instance.os)
2042 a8083063 Iustin Pop
  config.set(constants.INISECT_EXP, 'compression', 'gzip')
2043 a8083063 Iustin Pop
2044 a8083063 Iustin Pop
  config.add_section(constants.INISECT_INS)
2045 a8083063 Iustin Pop
  config.set(constants.INISECT_INS, 'name', instance.name)
2046 51de46bf Iustin Pop
  config.set(constants.INISECT_INS, 'memory', '%d' %
2047 51de46bf Iustin Pop
             instance.beparams[constants.BE_MEMORY])
2048 51de46bf Iustin Pop
  config.set(constants.INISECT_INS, 'vcpus', '%d' %
2049 51de46bf Iustin Pop
             instance.beparams[constants.BE_VCPUS])
2050 a8083063 Iustin Pop
  config.set(constants.INISECT_INS, 'disk_template', instance.disk_template)
2051 66f93869 Manuel Franceschini
2052 95268cc3 Iustin Pop
  nic_total = 0
2053 a8083063 Iustin Pop
  for nic_count, nic in enumerate(instance.nics):
2054 95268cc3 Iustin Pop
    nic_total += 1
2055 a8083063 Iustin Pop
    config.set(constants.INISECT_INS, 'nic%d_mac' %
2056 a8083063 Iustin Pop
               nic_count, '%s' % nic.mac)
2057 a8083063 Iustin Pop
    config.set(constants.INISECT_INS, 'nic%d_ip' % nic_count, '%s' % nic.ip)
2058 38206f3c Iustin Pop
    config.set(constants.INISECT_INS, 'nic%d_bridge' % nic_count,
2059 38206f3c Iustin Pop
               '%s' % nic.bridge)
2060 a8083063 Iustin Pop
  # TODO: redundant: on load can read nics until it doesn't exist
2061 95268cc3 Iustin Pop
  config.set(constants.INISECT_INS, 'nic_count' , '%d' % nic_total)
2062 a8083063 Iustin Pop
2063 726d7d68 Iustin Pop
  disk_total = 0
2064 a8083063 Iustin Pop
  for disk_count, disk in enumerate(snap_disks):
2065 19d7f90a Guido Trotter
    if disk:
2066 726d7d68 Iustin Pop
      disk_total += 1
2067 19d7f90a Guido Trotter
      config.set(constants.INISECT_INS, 'disk%d_ivname' % disk_count,
2068 19d7f90a Guido Trotter
                 ('%s' % disk.iv_name))
2069 19d7f90a Guido Trotter
      config.set(constants.INISECT_INS, 'disk%d_dump' % disk_count,
2070 19d7f90a Guido Trotter
                 ('%s' % disk.physical_id[1]))
2071 19d7f90a Guido Trotter
      config.set(constants.INISECT_INS, 'disk%d_size' % disk_count,
2072 19d7f90a Guido Trotter
                 ('%d' % disk.size))
2073 a8083063 Iustin Pop
2074 726d7d68 Iustin Pop
  config.set(constants.INISECT_INS, 'disk_count' , '%d' % disk_total)
2075 a8083063 Iustin Pop
2076 726d7d68 Iustin Pop
  utils.WriteFile(os.path.join(destdir, constants.EXPORT_CONF_FILE),
2077 726d7d68 Iustin Pop
                  data=config.Dumps())
2078 a8083063 Iustin Pop
  shutil.rmtree(finaldestdir, True)
2079 a8083063 Iustin Pop
  shutil.move(destdir, finaldestdir)
2080 a8083063 Iustin Pop
2081 a8083063 Iustin Pop
2082 a8083063 Iustin Pop
def ExportInfo(dest):
2083 a8083063 Iustin Pop
  """Get export configuration information.
2084 a8083063 Iustin Pop

2085 10c2650b Iustin Pop
  @type dest: str
2086 10c2650b Iustin Pop
  @param dest: directory containing the export
2087 a8083063 Iustin Pop

2088 10c2650b Iustin Pop
  @rtype: L{objects.SerializableConfigParser}
2089 10c2650b Iustin Pop
  @return: a serializable config file containing the
2090 10c2650b Iustin Pop
      export info
2091 a8083063 Iustin Pop

2092 a8083063 Iustin Pop
  """
2093 a8083063 Iustin Pop
  cff = os.path.join(dest, constants.EXPORT_CONF_FILE)
2094 a8083063 Iustin Pop
2095 a8083063 Iustin Pop
  config = objects.SerializableConfigParser()
2096 a8083063 Iustin Pop
  config.read(cff)
2097 a8083063 Iustin Pop
2098 a8083063 Iustin Pop
  if (not config.has_section(constants.INISECT_EXP) or
2099 a8083063 Iustin Pop
      not config.has_section(constants.INISECT_INS)):
2100 3eccac06 Iustin Pop
    _Fail("Export info file doesn't have the required fields")
2101 a8083063 Iustin Pop
2102 c26a6bd2 Iustin Pop
  return config.Dumps()
2103 a8083063 Iustin Pop
2104 a8083063 Iustin Pop
2105 4a0e011f Iustin Pop
def ImportOSIntoInstance(instance, src_node, src_images, cluster_name, debug):
2106 a8083063 Iustin Pop
  """Import an os image into an instance.
2107 a8083063 Iustin Pop

2108 b1206984 Iustin Pop
  @type instance: L{objects.Instance}
2109 6c0af70e Guido Trotter
  @param instance: instance to import the disks into
2110 6c0af70e Guido Trotter
  @type src_node: string
2111 6c0af70e Guido Trotter
  @param src_node: source node for the disk images
2112 6c0af70e Guido Trotter
  @type src_images: list of string
2113 6c0af70e Guido Trotter
  @param src_images: absolute paths of the disk images
2114 4a0e011f Iustin Pop
  @type debug: integer
2115 4a0e011f Iustin Pop
  @param debug: debug level, passed to the OS scripts
2116 6c0af70e Guido Trotter
  @rtype: list of boolean
2117 6c0af70e Guido Trotter
  @return: each boolean represent the success of importing the n-th disk
2118 a8083063 Iustin Pop

2119 a8083063 Iustin Pop
  """
2120 a8083063 Iustin Pop
  inst_os = OSFromDisk(instance.os)
2121 4a0e011f Iustin Pop
  import_env = OSEnvironment(instance, inst_os, debug)
2122 a8083063 Iustin Pop
  import_script = inst_os.import_script
2123 a8083063 Iustin Pop
2124 a8083063 Iustin Pop
  logfile = "%s/import-%s-%s-%s.log" % (constants.LOG_OS_DIR, instance.os,
2125 a8083063 Iustin Pop
                                        instance.name, int(time.time()))
2126 a8083063 Iustin Pop
  if not os.path.exists(constants.LOG_OS_DIR):
2127 a8083063 Iustin Pop
    os.mkdir(constants.LOG_OS_DIR, 0750)
2128 a8083063 Iustin Pop
2129 a8083063 Iustin Pop
  comprcmd = "gunzip"
2130 d868edb4 Iustin Pop
  impcmd = utils.BuildShellCmd("(cd %s; %s >%s 2>&1)", inst_os.path,
2131 d868edb4 Iustin Pop
                               import_script, logfile)
2132 a8083063 Iustin Pop
2133 6c0af70e Guido Trotter
  final_result = []
2134 6c0af70e Guido Trotter
  for idx, image in enumerate(src_images):
2135 6c0af70e Guido Trotter
    if image:
2136 6c0af70e Guido Trotter
      destcmd = utils.BuildShellCmd('cat %s', image)
2137 6c0af70e Guido Trotter
      remotecmd = _GetSshRunner(cluster_name).BuildCmd(src_node,
2138 6c0af70e Guido Trotter
                                                       constants.GANETI_RUNAS,
2139 6c0af70e Guido Trotter
                                                       destcmd)
2140 6c0af70e Guido Trotter
      command = '|'.join([utils.ShellQuoteArgs(remotecmd), comprcmd, impcmd])
2141 6c0af70e Guido Trotter
      import_env['IMPORT_DEVICE'] = import_env['DISK_%d_PATH' % idx]
2142 74c47259 Iustin Pop
      import_env['IMPORT_INDEX'] = str(idx)
2143 6c0af70e Guido Trotter
      result = utils.RunCmd(command, env=import_env)
2144 6c0af70e Guido Trotter
      if result.failed:
2145 726d7d68 Iustin Pop
        logging.error("Disk import command '%s' returned error: %s"
2146 726d7d68 Iustin Pop
                      " output: %s", command, result.fail_reason,
2147 726d7d68 Iustin Pop
                      result.output)
2148 944bf548 Iustin Pop
        final_result.append("error importing disk %d: %s, %s" %
2149 944bf548 Iustin Pop
                            (idx, result.fail_reason, result.output[-100]))
2150 a8083063 Iustin Pop
2151 944bf548 Iustin Pop
  if final_result:
2152 afdc3985 Iustin Pop
    _Fail("; ".join(final_result), log=False)
2153 a8083063 Iustin Pop
2154 a8083063 Iustin Pop
2155 a8083063 Iustin Pop
def ListExports():
2156 a8083063 Iustin Pop
  """Return a list of exports currently available on this machine.
2157 098c0958 Michael Hanselmann

2158 10c2650b Iustin Pop
  @rtype: list
2159 10c2650b Iustin Pop
  @return: list of the exports
2160 10c2650b Iustin Pop

2161 a8083063 Iustin Pop
  """
2162 a8083063 Iustin Pop
  if os.path.isdir(constants.EXPORT_DIR):
2163 c26a6bd2 Iustin Pop
    return utils.ListVisibleFiles(constants.EXPORT_DIR)
2164 a8083063 Iustin Pop
  else:
2165 afdc3985 Iustin Pop
    _Fail("No exports directory")
2166 a8083063 Iustin Pop
2167 a8083063 Iustin Pop
2168 a8083063 Iustin Pop
def RemoveExport(export):
2169 a8083063 Iustin Pop
  """Remove an existing export from the node.
2170 a8083063 Iustin Pop

2171 10c2650b Iustin Pop
  @type export: str
2172 10c2650b Iustin Pop
  @param export: the name of the export to remove
2173 c26a6bd2 Iustin Pop
  @rtype: None
2174 a8083063 Iustin Pop

2175 098c0958 Michael Hanselmann
  """
2176 a8083063 Iustin Pop
  target = os.path.join(constants.EXPORT_DIR, export)
2177 a8083063 Iustin Pop
2178 35fbcd11 Iustin Pop
  try:
2179 35fbcd11 Iustin Pop
    shutil.rmtree(target)
2180 35fbcd11 Iustin Pop
  except EnvironmentError, err:
2181 35fbcd11 Iustin Pop
    _Fail("Error while removing the export: %s", err, exc=True)
2182 a8083063 Iustin Pop
2183 a8083063 Iustin Pop
2184 821d1bd1 Iustin Pop
def BlockdevRename(devlist):
2185 f3e513ad Iustin Pop
  """Rename a list of block devices.
2186 f3e513ad Iustin Pop

2187 10c2650b Iustin Pop
  @type devlist: list of tuples
2188 10c2650b Iustin Pop
  @param devlist: list of tuples of the form  (disk,
2189 10c2650b Iustin Pop
      new_logical_id, new_physical_id); disk is an
2190 10c2650b Iustin Pop
      L{objects.Disk} object describing the current disk,
2191 10c2650b Iustin Pop
      and new logical_id/physical_id is the name we
2192 10c2650b Iustin Pop
      rename it to
2193 10c2650b Iustin Pop
  @rtype: boolean
2194 10c2650b Iustin Pop
  @return: True if all renames succeeded, False otherwise
2195 f3e513ad Iustin Pop

2196 f3e513ad Iustin Pop
  """
2197 6b5e3f70 Iustin Pop
  msgs = []
2198 f3e513ad Iustin Pop
  result = True
2199 f3e513ad Iustin Pop
  for disk, unique_id in devlist:
2200 f3e513ad Iustin Pop
    dev = _RecursiveFindBD(disk)
2201 f3e513ad Iustin Pop
    if dev is None:
2202 6b5e3f70 Iustin Pop
      msgs.append("Can't find device %s in rename" % str(disk))
2203 f3e513ad Iustin Pop
      result = False
2204 f3e513ad Iustin Pop
      continue
2205 f3e513ad Iustin Pop
    try:
2206 3f78eef2 Iustin Pop
      old_rpath = dev.dev_path
2207 f3e513ad Iustin Pop
      dev.Rename(unique_id)
2208 3f78eef2 Iustin Pop
      new_rpath = dev.dev_path
2209 3f78eef2 Iustin Pop
      if old_rpath != new_rpath:
2210 3f78eef2 Iustin Pop
        DevCacheManager.RemoveCache(old_rpath)
2211 3f78eef2 Iustin Pop
        # FIXME: we should add the new cache information here, like:
2212 3f78eef2 Iustin Pop
        # DevCacheManager.UpdateCache(new_rpath, owner, ...)
2213 3f78eef2 Iustin Pop
        # but we don't have the owner here - maybe parse from existing
2214 3f78eef2 Iustin Pop
        # cache? for now, we only lose lvm data when we rename, which
2215 3f78eef2 Iustin Pop
        # is less critical than DRBD or MD
2216 f3e513ad Iustin Pop
    except errors.BlockDeviceError, err:
2217 6b5e3f70 Iustin Pop
      msgs.append("Can't rename device '%s' to '%s': %s" %
2218 6b5e3f70 Iustin Pop
                  (dev, unique_id, err))
2219 18682bca Iustin Pop
      logging.exception("Can't rename device '%s' to '%s'", dev, unique_id)
2220 f3e513ad Iustin Pop
      result = False
2221 afdc3985 Iustin Pop
  if not result:
2222 afdc3985 Iustin Pop
    _Fail("; ".join(msgs))
2223 f3e513ad Iustin Pop
2224 f3e513ad Iustin Pop
2225 778b75bb Manuel Franceschini
def _TransformFileStorageDir(file_storage_dir):
2226 778b75bb Manuel Franceschini
  """Checks whether given file_storage_dir is valid.
2227 778b75bb Manuel Franceschini

2228 778b75bb Manuel Franceschini
  Checks wheter the given file_storage_dir is within the cluster-wide
2229 778b75bb Manuel Franceschini
  default file_storage_dir stored in SimpleStore. Only paths under that
2230 778b75bb Manuel Franceschini
  directory are allowed.
2231 778b75bb Manuel Franceschini

2232 b1206984 Iustin Pop
  @type file_storage_dir: str
2233 b1206984 Iustin Pop
  @param file_storage_dir: the path to check
2234 d61cbe76 Iustin Pop

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

2237 778b75bb Manuel Franceschini
  """
2238 c657dcc9 Michael Hanselmann
  cfg = _GetConfig()
2239 778b75bb Manuel Franceschini
  file_storage_dir = os.path.normpath(file_storage_dir)
2240 c657dcc9 Michael Hanselmann
  base_file_storage_dir = cfg.GetFileStorageDir()
2241 778b75bb Manuel Franceschini
  if (not os.path.commonprefix([file_storage_dir, base_file_storage_dir]) ==
2242 778b75bb Manuel Franceschini
      base_file_storage_dir):
2243 b2b8bcce Iustin Pop
    _Fail("File storage directory '%s' is not under base file"
2244 b2b8bcce Iustin Pop
          " storage directory '%s'", file_storage_dir, base_file_storage_dir)
2245 778b75bb Manuel Franceschini
  return file_storage_dir
2246 778b75bb Manuel Franceschini
2247 778b75bb Manuel Franceschini
2248 778b75bb Manuel Franceschini
def CreateFileStorageDir(file_storage_dir):
2249 778b75bb Manuel Franceschini
  """Create file storage directory.
2250 778b75bb Manuel Franceschini

2251 b1206984 Iustin Pop
  @type file_storage_dir: str
2252 b1206984 Iustin Pop
  @param file_storage_dir: directory to create
2253 778b75bb Manuel Franceschini

2254 b1206984 Iustin Pop
  @rtype: tuple
2255 b1206984 Iustin Pop
  @return: tuple with first element a boolean indicating wheter dir
2256 b1206984 Iustin Pop
      creation was successful or not
2257 778b75bb Manuel Franceschini

2258 778b75bb Manuel Franceschini
  """
2259 778b75bb Manuel Franceschini
  file_storage_dir = _TransformFileStorageDir(file_storage_dir)
2260 b2b8bcce Iustin Pop
  if os.path.exists(file_storage_dir):
2261 b2b8bcce Iustin Pop
    if not os.path.isdir(file_storage_dir):
2262 b2b8bcce Iustin Pop
      _Fail("Specified storage dir '%s' is not a directory",
2263 b2b8bcce Iustin Pop
            file_storage_dir)
2264 778b75bb Manuel Franceschini
  else:
2265 b2b8bcce Iustin Pop
    try:
2266 b2b8bcce Iustin Pop
      os.makedirs(file_storage_dir, 0750)
2267 b2b8bcce Iustin Pop
    except OSError, err:
2268 b2b8bcce Iustin Pop
      _Fail("Cannot create file storage directory '%s': %s",
2269 b2b8bcce Iustin Pop
            file_storage_dir, err, exc=True)
2270 778b75bb Manuel Franceschini
2271 778b75bb Manuel Franceschini
2272 778b75bb Manuel Franceschini
def RemoveFileStorageDir(file_storage_dir):
2273 778b75bb Manuel Franceschini
  """Remove file storage directory.
2274 778b75bb Manuel Franceschini

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

2277 10c2650b Iustin Pop
  @type file_storage_dir: str
2278 10c2650b Iustin Pop
  @param file_storage_dir: the directory we should cleanup
2279 10c2650b Iustin Pop
  @rtype: tuple (success,)
2280 10c2650b Iustin Pop
  @return: tuple of one element, C{success}, denoting
2281 5bbd3f7f Michael Hanselmann
      whether the operation was successful
2282 778b75bb Manuel Franceschini

2283 778b75bb Manuel Franceschini
  """
2284 778b75bb Manuel Franceschini
  file_storage_dir = _TransformFileStorageDir(file_storage_dir)
2285 b2b8bcce Iustin Pop
  if os.path.exists(file_storage_dir):
2286 b2b8bcce Iustin Pop
    if not os.path.isdir(file_storage_dir):
2287 b2b8bcce Iustin Pop
      _Fail("Specified Storage directory '%s' is not a directory",
2288 b2b8bcce Iustin Pop
            file_storage_dir)
2289 afdc3985 Iustin Pop
    # deletes dir only if empty, otherwise we want to fail the rpc call
2290 b2b8bcce Iustin Pop
    try:
2291 b2b8bcce Iustin Pop
      os.rmdir(file_storage_dir)
2292 b2b8bcce Iustin Pop
    except OSError, err:
2293 b2b8bcce Iustin Pop
      _Fail("Cannot remove file storage directory '%s': %s",
2294 b2b8bcce Iustin Pop
            file_storage_dir, err)
2295 b2b8bcce Iustin Pop
2296 778b75bb Manuel Franceschini
2297 778b75bb Manuel Franceschini
def RenameFileStorageDir(old_file_storage_dir, new_file_storage_dir):
2298 778b75bb Manuel Franceschini
  """Rename the file storage directory.
2299 778b75bb Manuel Franceschini

2300 10c2650b Iustin Pop
  @type old_file_storage_dir: str
2301 10c2650b Iustin Pop
  @param old_file_storage_dir: the current path
2302 10c2650b Iustin Pop
  @type new_file_storage_dir: str
2303 10c2650b Iustin Pop
  @param new_file_storage_dir: the name we should rename to
2304 10c2650b Iustin Pop
  @rtype: tuple (success,)
2305 10c2650b Iustin Pop
  @return: tuple of one element, C{success}, denoting
2306 10c2650b Iustin Pop
      whether the operation was successful
2307 778b75bb Manuel Franceschini

2308 778b75bb Manuel Franceschini
  """
2309 778b75bb Manuel Franceschini
  old_file_storage_dir = _TransformFileStorageDir(old_file_storage_dir)
2310 778b75bb Manuel Franceschini
  new_file_storage_dir = _TransformFileStorageDir(new_file_storage_dir)
2311 b2b8bcce Iustin Pop
  if not os.path.exists(new_file_storage_dir):
2312 b2b8bcce Iustin Pop
    if os.path.isdir(old_file_storage_dir):
2313 b2b8bcce Iustin Pop
      try:
2314 b2b8bcce Iustin Pop
        os.rename(old_file_storage_dir, new_file_storage_dir)
2315 b2b8bcce Iustin Pop
      except OSError, err:
2316 b2b8bcce Iustin Pop
        _Fail("Cannot rename '%s' to '%s': %s",
2317 b2b8bcce Iustin Pop
              old_file_storage_dir, new_file_storage_dir, err)
2318 778b75bb Manuel Franceschini
    else:
2319 b2b8bcce Iustin Pop
      _Fail("Specified storage dir '%s' is not a directory",
2320 b2b8bcce Iustin Pop
            old_file_storage_dir)
2321 b2b8bcce Iustin Pop
  else:
2322 b2b8bcce Iustin Pop
    if os.path.exists(old_file_storage_dir):
2323 b2b8bcce Iustin Pop
      _Fail("Cannot rename '%s' to '%s': both locations exist",
2324 b2b8bcce Iustin Pop
            old_file_storage_dir, new_file_storage_dir)
2325 778b75bb Manuel Franceschini
2326 778b75bb Manuel Franceschini
2327 c8457ce7 Iustin Pop
def _EnsureJobQueueFile(file_name):
2328 dc31eae3 Michael Hanselmann
  """Checks whether the given filename is in the queue directory.
2329 ca52cdeb Michael Hanselmann

2330 10c2650b Iustin Pop
  @type file_name: str
2331 10c2650b Iustin Pop
  @param file_name: the file name we should check
2332 c8457ce7 Iustin Pop
  @rtype: None
2333 c8457ce7 Iustin Pop
  @raises RPCFail: if the file is not valid
2334 10c2650b Iustin Pop

2335 ca52cdeb Michael Hanselmann
  """
2336 ca52cdeb Michael Hanselmann
  queue_dir = os.path.normpath(constants.QUEUE_DIR)
2337 dc31eae3 Michael Hanselmann
  result = (os.path.commonprefix([queue_dir, file_name]) == queue_dir)
2338 dc31eae3 Michael Hanselmann
2339 dc31eae3 Michael Hanselmann
  if not result:
2340 c8457ce7 Iustin Pop
    _Fail("Passed job queue file '%s' does not belong to"
2341 c8457ce7 Iustin Pop
          " the queue directory '%s'", file_name, queue_dir)
2342 dc31eae3 Michael Hanselmann
2343 dc31eae3 Michael Hanselmann
2344 dc31eae3 Michael Hanselmann
def JobQueueUpdate(file_name, content):
2345 dc31eae3 Michael Hanselmann
  """Updates a file in the queue directory.
2346 dc31eae3 Michael Hanselmann

2347 10c2650b Iustin Pop
  This is just a wrapper over L{utils.WriteFile}, with proper
2348 10c2650b Iustin Pop
  checking.
2349 10c2650b Iustin Pop

2350 10c2650b Iustin Pop
  @type file_name: str
2351 10c2650b Iustin Pop
  @param file_name: the job file name
2352 10c2650b Iustin Pop
  @type content: str
2353 10c2650b Iustin Pop
  @param content: the new job contents
2354 10c2650b Iustin Pop
  @rtype: boolean
2355 10c2650b Iustin Pop
  @return: the success of the operation
2356 10c2650b Iustin Pop

2357 dc31eae3 Michael Hanselmann
  """
2358 c8457ce7 Iustin Pop
  _EnsureJobQueueFile(file_name)
2359 ca52cdeb Michael Hanselmann
2360 ca52cdeb Michael Hanselmann
  # Write and replace the file atomically
2361 12bce260 Michael Hanselmann
  utils.WriteFile(file_name, data=_Decompress(content))
2362 ca52cdeb Michael Hanselmann
2363 ca52cdeb Michael Hanselmann
2364 af5ebcb1 Michael Hanselmann
def JobQueueRename(old, new):
2365 af5ebcb1 Michael Hanselmann
  """Renames a job queue file.
2366 af5ebcb1 Michael Hanselmann

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

2369 10c2650b Iustin Pop
  @type old: str
2370 10c2650b Iustin Pop
  @param old: the old (actual) file name
2371 10c2650b Iustin Pop
  @type new: str
2372 10c2650b Iustin Pop
  @param new: the desired file name
2373 c8457ce7 Iustin Pop
  @rtype: tuple
2374 c8457ce7 Iustin Pop
  @return: the success of the operation and payload
2375 10c2650b Iustin Pop

2376 af5ebcb1 Michael Hanselmann
  """
2377 c8457ce7 Iustin Pop
  _EnsureJobQueueFile(old)
2378 c8457ce7 Iustin Pop
  _EnsureJobQueueFile(new)
2379 af5ebcb1 Michael Hanselmann
2380 58b22b6e Michael Hanselmann
  utils.RenameFile(old, new, mkdir=True)
2381 af5ebcb1 Michael Hanselmann
2382 af5ebcb1 Michael Hanselmann
2383 5d672980 Iustin Pop
def JobQueueSetDrainFlag(drain_flag):
2384 5d672980 Iustin Pop
  """Set the drain flag for the queue.
2385 5d672980 Iustin Pop

2386 5d672980 Iustin Pop
  This will set or unset the queue drain flag.
2387 5d672980 Iustin Pop

2388 10c2650b Iustin Pop
  @type drain_flag: boolean
2389 5d672980 Iustin Pop
  @param drain_flag: if True, will set the drain flag, otherwise reset it.
2390 c8457ce7 Iustin Pop
  @rtype: truple
2391 c8457ce7 Iustin Pop
  @return: always True, None
2392 10c2650b Iustin Pop
  @warning: the function always returns True
2393 5d672980 Iustin Pop

2394 5d672980 Iustin Pop
  """
2395 5d672980 Iustin Pop
  if drain_flag:
2396 5d672980 Iustin Pop
    utils.WriteFile(constants.JOB_QUEUE_DRAIN_FILE, data="", close=True)
2397 5d672980 Iustin Pop
  else:
2398 5d672980 Iustin Pop
    utils.RemoveFile(constants.JOB_QUEUE_DRAIN_FILE)
2399 5d672980 Iustin Pop
2400 5d672980 Iustin Pop
2401 821d1bd1 Iustin Pop
def BlockdevClose(instance_name, disks):
2402 d61cbe76 Iustin Pop
  """Closes the given block devices.
2403 d61cbe76 Iustin Pop

2404 10c2650b Iustin Pop
  This means they will be switched to secondary mode (in case of
2405 10c2650b Iustin Pop
  DRBD).
2406 10c2650b Iustin Pop

2407 b2e7666a Iustin Pop
  @param instance_name: if the argument is not empty, the symlinks
2408 b2e7666a Iustin Pop
      of this instance will be removed
2409 10c2650b Iustin Pop
  @type disks: list of L{objects.Disk}
2410 10c2650b Iustin Pop
  @param disks: the list of disks to be closed
2411 10c2650b Iustin Pop
  @rtype: tuple (success, message)
2412 10c2650b Iustin Pop
  @return: a tuple of success and message, where success
2413 10c2650b Iustin Pop
      indicates the succes of the operation, and message
2414 10c2650b Iustin Pop
      which will contain the error details in case we
2415 10c2650b Iustin Pop
      failed
2416 d61cbe76 Iustin Pop

2417 d61cbe76 Iustin Pop
  """
2418 d61cbe76 Iustin Pop
  bdevs = []
2419 d61cbe76 Iustin Pop
  for cf in disks:
2420 d61cbe76 Iustin Pop
    rd = _RecursiveFindBD(cf)
2421 d61cbe76 Iustin Pop
    if rd is None:
2422 2cc6781a Iustin Pop
      _Fail("Can't find device %s", cf)
2423 d61cbe76 Iustin Pop
    bdevs.append(rd)
2424 d61cbe76 Iustin Pop
2425 d61cbe76 Iustin Pop
  msg = []
2426 d61cbe76 Iustin Pop
  for rd in bdevs:
2427 d61cbe76 Iustin Pop
    try:
2428 d61cbe76 Iustin Pop
      rd.Close()
2429 d61cbe76 Iustin Pop
    except errors.BlockDeviceError, err:
2430 d61cbe76 Iustin Pop
      msg.append(str(err))
2431 d61cbe76 Iustin Pop
  if msg:
2432 afdc3985 Iustin Pop
    _Fail("Can't make devices secondary: %s", ",".join(msg))
2433 d61cbe76 Iustin Pop
  else:
2434 b2e7666a Iustin Pop
    if instance_name:
2435 5282084b Iustin Pop
      _RemoveBlockDevLinks(instance_name, disks)
2436 d61cbe76 Iustin Pop
2437 d61cbe76 Iustin Pop
2438 6217e295 Iustin Pop
def ValidateHVParams(hvname, hvparams):
2439 6217e295 Iustin Pop
  """Validates the given hypervisor parameters.
2440 6217e295 Iustin Pop

2441 6217e295 Iustin Pop
  @type hvname: string
2442 6217e295 Iustin Pop
  @param hvname: the hypervisor name
2443 6217e295 Iustin Pop
  @type hvparams: dict
2444 6217e295 Iustin Pop
  @param hvparams: the hypervisor parameters to be validated
2445 c26a6bd2 Iustin Pop
  @rtype: None
2446 6217e295 Iustin Pop

2447 6217e295 Iustin Pop
  """
2448 6217e295 Iustin Pop
  try:
2449 6217e295 Iustin Pop
    hv_type = hypervisor.GetHypervisor(hvname)
2450 6217e295 Iustin Pop
    hv_type.ValidateParameters(hvparams)
2451 6217e295 Iustin Pop
  except errors.HypervisorError, err:
2452 afdc3985 Iustin Pop
    _Fail(str(err), log=False)
2453 6217e295 Iustin Pop
2454 6217e295 Iustin Pop
2455 56aa9fd5 Iustin Pop
def DemoteFromMC():
2456 56aa9fd5 Iustin Pop
  """Demotes the current node from master candidate role.
2457 56aa9fd5 Iustin Pop

2458 56aa9fd5 Iustin Pop
  """
2459 56aa9fd5 Iustin Pop
  # try to ensure we're not the master by mistake
2460 56aa9fd5 Iustin Pop
  master, myself = ssconf.GetMasterAndMyself()
2461 56aa9fd5 Iustin Pop
  if master == myself:
2462 afdc3985 Iustin Pop
    _Fail("ssconf status shows I'm the master node, will not demote")
2463 f154a7a3 Michael Hanselmann
2464 f154a7a3 Michael Hanselmann
  result = utils.RunCmd([constants.DAEMON_UTIL, "check", constants.MASTERD])
2465 f154a7a3 Michael Hanselmann
  if not result.failed:
2466 afdc3985 Iustin Pop
    _Fail("The master daemon is running, will not demote")
2467 f154a7a3 Michael Hanselmann
2468 56aa9fd5 Iustin Pop
  try:
2469 9a5cb537 Iustin Pop
    if os.path.isfile(constants.CLUSTER_CONF_FILE):
2470 9a5cb537 Iustin Pop
      utils.CreateBackup(constants.CLUSTER_CONF_FILE)
2471 56aa9fd5 Iustin Pop
  except EnvironmentError, err:
2472 56aa9fd5 Iustin Pop
    if err.errno != errno.ENOENT:
2473 afdc3985 Iustin Pop
      _Fail("Error while backing up cluster file: %s", err, exc=True)
2474 f154a7a3 Michael Hanselmann
2475 56aa9fd5 Iustin Pop
  utils.RemoveFile(constants.CLUSTER_CONF_FILE)
2476 56aa9fd5 Iustin Pop
2477 56aa9fd5 Iustin Pop
2478 6b93ec9d Iustin Pop
def _FindDisks(nodes_ip, disks):
2479 6b93ec9d Iustin Pop
  """Sets the physical ID on disks and returns the block devices.
2480 6b93ec9d Iustin Pop

2481 6b93ec9d Iustin Pop
  """
2482 6b93ec9d Iustin Pop
  # set the correct physical ID
2483 6b93ec9d Iustin Pop
  my_name = utils.HostInfo().name
2484 6b93ec9d Iustin Pop
  for cf in disks:
2485 6b93ec9d Iustin Pop
    cf.SetPhysicalID(my_name, nodes_ip)
2486 6b93ec9d Iustin Pop
2487 6b93ec9d Iustin Pop
  bdevs = []
2488 6b93ec9d Iustin Pop
2489 6b93ec9d Iustin Pop
  for cf in disks:
2490 6b93ec9d Iustin Pop
    rd = _RecursiveFindBD(cf)
2491 6b93ec9d Iustin Pop
    if rd is None:
2492 5a533f8a Iustin Pop
      _Fail("Can't find device %s", cf)
2493 6b93ec9d Iustin Pop
    bdevs.append(rd)
2494 5a533f8a Iustin Pop
  return bdevs
2495 6b93ec9d Iustin Pop
2496 6b93ec9d Iustin Pop
2497 6b93ec9d Iustin Pop
def DrbdDisconnectNet(nodes_ip, disks):
2498 6b93ec9d Iustin Pop
  """Disconnects the network on a list of drbd devices.
2499 6b93ec9d Iustin Pop

2500 6b93ec9d Iustin Pop
  """
2501 5a533f8a Iustin Pop
  bdevs = _FindDisks(nodes_ip, disks)
2502 6b93ec9d Iustin Pop
2503 6b93ec9d Iustin Pop
  # disconnect disks
2504 6b93ec9d Iustin Pop
  for rd in bdevs:
2505 6b93ec9d Iustin Pop
    try:
2506 6b93ec9d Iustin Pop
      rd.DisconnectNet()
2507 6b93ec9d Iustin Pop
    except errors.BlockDeviceError, err:
2508 2cc6781a Iustin Pop
      _Fail("Can't change network configuration to standalone mode: %s",
2509 2cc6781a Iustin Pop
            err, exc=True)
2510 6b93ec9d Iustin Pop
2511 6b93ec9d Iustin Pop
2512 6b93ec9d Iustin Pop
def DrbdAttachNet(nodes_ip, disks, instance_name, multimaster):
2513 6b93ec9d Iustin Pop
  """Attaches the network on a list of drbd devices.
2514 6b93ec9d Iustin Pop

2515 6b93ec9d Iustin Pop
  """
2516 5a533f8a Iustin Pop
  bdevs = _FindDisks(nodes_ip, disks)
2517 6b93ec9d Iustin Pop
2518 6b93ec9d Iustin Pop
  if multimaster:
2519 53c776b5 Iustin Pop
    for idx, rd in enumerate(bdevs):
2520 6b93ec9d Iustin Pop
      try:
2521 53c776b5 Iustin Pop
        _SymlinkBlockDev(instance_name, rd.dev_path, idx)
2522 6b93ec9d Iustin Pop
      except EnvironmentError, err:
2523 2cc6781a Iustin Pop
        _Fail("Can't create symlink: %s", err)
2524 6b93ec9d Iustin Pop
  # reconnect disks, switch to new master configuration and if
2525 6b93ec9d Iustin Pop
  # needed primary mode
2526 6b93ec9d Iustin Pop
  for rd in bdevs:
2527 6b93ec9d Iustin Pop
    try:
2528 6b93ec9d Iustin Pop
      rd.AttachNet(multimaster)
2529 6b93ec9d Iustin Pop
    except errors.BlockDeviceError, err:
2530 2cc6781a Iustin Pop
      _Fail("Can't change network configuration: %s", err)
2531 3c0cdc83 Michael Hanselmann
2532 6b93ec9d Iustin Pop
  # wait until the disks are connected; we need to retry the re-attach
2533 6b93ec9d Iustin Pop
  # if the device becomes standalone, as this might happen if the one
2534 6b93ec9d Iustin Pop
  # node disconnects and reconnects in a different mode before the
2535 6b93ec9d Iustin Pop
  # other node reconnects; in this case, one or both of the nodes will
2536 6b93ec9d Iustin Pop
  # decide it has wrong configuration and switch to standalone
2537 3c0cdc83 Michael Hanselmann
2538 3c0cdc83 Michael Hanselmann
  def _Attach():
2539 6b93ec9d Iustin Pop
    all_connected = True
2540 3c0cdc83 Michael Hanselmann
2541 6b93ec9d Iustin Pop
    for rd in bdevs:
2542 6b93ec9d Iustin Pop
      stats = rd.GetProcStatus()
2543 3c0cdc83 Michael Hanselmann
2544 3c0cdc83 Michael Hanselmann
      all_connected = (all_connected and
2545 3c0cdc83 Michael Hanselmann
                       (stats.is_connected or stats.is_in_resync))
2546 3c0cdc83 Michael Hanselmann
2547 6b93ec9d Iustin Pop
      if stats.is_standalone:
2548 6b93ec9d Iustin Pop
        # peer had different config info and this node became
2549 6b93ec9d Iustin Pop
        # standalone, even though this should not happen with the
2550 6b93ec9d Iustin Pop
        # new staged way of changing disk configs
2551 6b93ec9d Iustin Pop
        try:
2552 c738375b Iustin Pop
          rd.AttachNet(multimaster)
2553 6b93ec9d Iustin Pop
        except errors.BlockDeviceError, err:
2554 2cc6781a Iustin Pop
          _Fail("Can't change network configuration: %s", err)
2555 3c0cdc83 Michael Hanselmann
2556 3c0cdc83 Michael Hanselmann
    if not all_connected:
2557 3c0cdc83 Michael Hanselmann
      raise utils.RetryAgain()
2558 3c0cdc83 Michael Hanselmann
2559 3c0cdc83 Michael Hanselmann
  try:
2560 3c0cdc83 Michael Hanselmann
    # Start with a delay of 100 miliseconds and go up to 5 seconds
2561 3c0cdc83 Michael Hanselmann
    utils.Retry(_Attach, (0.1, 1.5, 5.0), 2 * 60)
2562 3c0cdc83 Michael Hanselmann
  except utils.RetryTimeout:
2563 afdc3985 Iustin Pop
    _Fail("Timeout in disk reconnecting")
2564 3c0cdc83 Michael Hanselmann
2565 6b93ec9d Iustin Pop
  if multimaster:
2566 6b93ec9d Iustin Pop
    # change to primary mode
2567 6b93ec9d Iustin Pop
    for rd in bdevs:
2568 d3da87b8 Iustin Pop
      try:
2569 d3da87b8 Iustin Pop
        rd.Open()
2570 d3da87b8 Iustin Pop
      except errors.BlockDeviceError, err:
2571 2cc6781a Iustin Pop
        _Fail("Can't change to primary mode: %s", err)
2572 6b93ec9d Iustin Pop
2573 6b93ec9d Iustin Pop
2574 6b93ec9d Iustin Pop
def DrbdWaitSync(nodes_ip, disks):
2575 6b93ec9d Iustin Pop
  """Wait until DRBDs have synchronized.
2576 6b93ec9d Iustin Pop

2577 6b93ec9d Iustin Pop
  """
2578 db8667b7 Iustin Pop
  def _helper(rd):
2579 db8667b7 Iustin Pop
    stats = rd.GetProcStatus()
2580 db8667b7 Iustin Pop
    if not (stats.is_connected or stats.is_in_resync):
2581 db8667b7 Iustin Pop
      raise utils.RetryAgain()
2582 db8667b7 Iustin Pop
    return stats
2583 db8667b7 Iustin Pop
2584 5a533f8a Iustin Pop
  bdevs = _FindDisks(nodes_ip, disks)
2585 6b93ec9d Iustin Pop
2586 6b93ec9d Iustin Pop
  min_resync = 100
2587 6b93ec9d Iustin Pop
  alldone = True
2588 6b93ec9d Iustin Pop
  for rd in bdevs:
2589 db8667b7 Iustin Pop
    try:
2590 db8667b7 Iustin Pop
      # poll each second for 15 seconds
2591 db8667b7 Iustin Pop
      stats = utils.Retry(_helper, 1, 15, args=[rd])
2592 db8667b7 Iustin Pop
    except utils.RetryTimeout:
2593 db8667b7 Iustin Pop
      stats = rd.GetProcStatus()
2594 db8667b7 Iustin Pop
      # last check
2595 db8667b7 Iustin Pop
      if not (stats.is_connected or stats.is_in_resync):
2596 db8667b7 Iustin Pop
        _Fail("DRBD device %s is not in sync: stats=%s", rd, stats)
2597 6b93ec9d Iustin Pop
    alldone = alldone and (not stats.is_in_resync)
2598 6b93ec9d Iustin Pop
    if stats.sync_percent is not None:
2599 6b93ec9d Iustin Pop
      min_resync = min(min_resync, stats.sync_percent)
2600 afdc3985 Iustin Pop
2601 c26a6bd2 Iustin Pop
  return (alldone, min_resync)
2602 6b93ec9d Iustin Pop
2603 6b93ec9d Iustin Pop
2604 f5118ade Iustin Pop
def PowercycleNode(hypervisor_type):
2605 f5118ade Iustin Pop
  """Hard-powercycle the node.
2606 f5118ade Iustin Pop

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

2610 f5118ade Iustin Pop
  """
2611 f5118ade Iustin Pop
  hyper = hypervisor.GetHypervisor(hypervisor_type)
2612 f5118ade Iustin Pop
  try:
2613 f5118ade Iustin Pop
    pid = os.fork()
2614 29921401 Iustin Pop
  except OSError:
2615 f5118ade Iustin Pop
    # if we can't fork, we'll pretend that we're in the child process
2616 f5118ade Iustin Pop
    pid = 0
2617 f5118ade Iustin Pop
  if pid > 0:
2618 c26a6bd2 Iustin Pop
    return "Reboot scheduled in 5 seconds"
2619 f5118ade Iustin Pop
  time.sleep(5)
2620 f5118ade Iustin Pop
  hyper.PowercycleNode()
2621 f5118ade Iustin Pop
2622 f5118ade Iustin Pop
2623 a8083063 Iustin Pop
class HooksRunner(object):
2624 a8083063 Iustin Pop
  """Hook runner.
2625 a8083063 Iustin Pop

2626 10c2650b Iustin Pop
  This class is instantiated on the node side (ganeti-noded) and not
2627 10c2650b Iustin Pop
  on the master side.
2628 a8083063 Iustin Pop

2629 a8083063 Iustin Pop
  """
2630 a8083063 Iustin Pop
  def __init__(self, hooks_base_dir=None):
2631 a8083063 Iustin Pop
    """Constructor for hooks runner.
2632 a8083063 Iustin Pop

2633 10c2650b Iustin Pop
    @type hooks_base_dir: str or None
2634 10c2650b Iustin Pop
    @param hooks_base_dir: if not None, this overrides the
2635 10c2650b Iustin Pop
        L{constants.HOOKS_BASE_DIR} (useful for unittests)
2636 a8083063 Iustin Pop

2637 a8083063 Iustin Pop
    """
2638 a8083063 Iustin Pop
    if hooks_base_dir is None:
2639 a8083063 Iustin Pop
      hooks_base_dir = constants.HOOKS_BASE_DIR
2640 fe267188 Iustin Pop
    # yeah, _BASE_DIR is not valid for attributes, we use it like a
2641 fe267188 Iustin Pop
    # constant
2642 fe267188 Iustin Pop
    self._BASE_DIR = hooks_base_dir # pylint: disable-msg=C0103
2643 a8083063 Iustin Pop
2644 a8083063 Iustin Pop
  @staticmethod
2645 a8083063 Iustin Pop
  def ExecHook(script, env):
2646 a8083063 Iustin Pop
    """Exec one hook script.
2647 a8083063 Iustin Pop

2648 10c2650b Iustin Pop
    @type script: str
2649 10c2650b Iustin Pop
    @param script: the full path to the script
2650 10c2650b Iustin Pop
    @type env: dict
2651 10c2650b Iustin Pop
    @param env: the environment with which to exec the script
2652 10c2650b Iustin Pop
    @rtype: tuple (success, message)
2653 10c2650b Iustin Pop
    @return: a tuple of success and message, where success
2654 10c2650b Iustin Pop
        indicates the succes of the operation, and message
2655 10c2650b Iustin Pop
        which will contain the error details in case we
2656 10c2650b Iustin Pop
        failed
2657 a8083063 Iustin Pop

2658 a8083063 Iustin Pop
    """
2659 a8083063 Iustin Pop
    # exec the process using subprocess and log the output
2660 a8083063 Iustin Pop
    fdstdin = None
2661 a8083063 Iustin Pop
    try:
2662 a8083063 Iustin Pop
      fdstdin = open("/dev/null", "r")
2663 a8083063 Iustin Pop
      child = subprocess.Popen([script], stdin=fdstdin, stdout=subprocess.PIPE,
2664 a8083063 Iustin Pop
                               stderr=subprocess.STDOUT, close_fds=True,
2665 147af04d Iustin Pop
                               shell=False, cwd="/", env=env)
2666 a8083063 Iustin Pop
      output = ""
2667 a8083063 Iustin Pop
      try:
2668 a8083063 Iustin Pop
        output = child.stdout.read(4096)
2669 a8083063 Iustin Pop
        child.stdout.close()
2670 a8083063 Iustin Pop
      except EnvironmentError, err:
2671 a8083063 Iustin Pop
        output += "Hook script error: %s" % str(err)
2672 a8083063 Iustin Pop
2673 a8083063 Iustin Pop
      while True:
2674 a8083063 Iustin Pop
        try:
2675 a8083063 Iustin Pop
          result = child.wait()
2676 a8083063 Iustin Pop
          break
2677 a8083063 Iustin Pop
        except EnvironmentError, err:
2678 a8083063 Iustin Pop
          if err.errno == errno.EINTR:
2679 a8083063 Iustin Pop
            continue
2680 a8083063 Iustin Pop
          raise
2681 a8083063 Iustin Pop
    finally:
2682 a8083063 Iustin Pop
      # try not to leak fds
2683 a8083063 Iustin Pop
      for fd in (fdstdin, ):
2684 a8083063 Iustin Pop
        if fd is not None:
2685 a8083063 Iustin Pop
          try:
2686 a8083063 Iustin Pop
            fd.close()
2687 a8083063 Iustin Pop
          except EnvironmentError, err:
2688 a8083063 Iustin Pop
            # just log the error
2689 18682bca Iustin Pop
            #logging.exception("Error while closing fd %s", fd)
2690 a8083063 Iustin Pop
            pass
2691 a8083063 Iustin Pop
2692 26f15862 Iustin Pop
    return result == 0, utils.SafeEncode(output.strip())
2693 a8083063 Iustin Pop
2694 a8083063 Iustin Pop
  def RunHooks(self, hpath, phase, env):
2695 a8083063 Iustin Pop
    """Run the scripts in the hooks directory.
2696 a8083063 Iustin Pop

2697 10c2650b Iustin Pop
    @type hpath: str
2698 10c2650b Iustin Pop
    @param hpath: the path to the hooks directory which
2699 10c2650b Iustin Pop
        holds the scripts
2700 10c2650b Iustin Pop
    @type phase: str
2701 10c2650b Iustin Pop
    @param phase: either L{constants.HOOKS_PHASE_PRE} or
2702 10c2650b Iustin Pop
        L{constants.HOOKS_PHASE_POST}
2703 10c2650b Iustin Pop
    @type env: dict
2704 10c2650b Iustin Pop
    @param env: dictionary with the environment for the hook
2705 10c2650b Iustin Pop
    @rtype: list
2706 10c2650b Iustin Pop
    @return: list of 3-element tuples:
2707 10c2650b Iustin Pop
      - script path
2708 10c2650b Iustin Pop
      - script result, either L{constants.HKR_SUCCESS} or
2709 10c2650b Iustin Pop
        L{constants.HKR_FAIL}
2710 10c2650b Iustin Pop
      - output of the script
2711 10c2650b Iustin Pop

2712 10c2650b Iustin Pop
    @raise errors.ProgrammerError: for invalid input
2713 10c2650b Iustin Pop
        parameters
2714 a8083063 Iustin Pop

2715 a8083063 Iustin Pop
    """
2716 a8083063 Iustin Pop
    if phase == constants.HOOKS_PHASE_PRE:
2717 a8083063 Iustin Pop
      suffix = "pre"
2718 a8083063 Iustin Pop
    elif phase == constants.HOOKS_PHASE_POST:
2719 a8083063 Iustin Pop
      suffix = "post"
2720 a8083063 Iustin Pop
    else:
2721 3fb4f740 Iustin Pop
      _Fail("Unknown hooks phase '%s'", phase)
2722 3fb4f740 Iustin Pop
2723 a8083063 Iustin Pop
    rr = []
2724 a8083063 Iustin Pop
2725 a8083063 Iustin Pop
    subdir = "%s-%s.d" % (hpath, suffix)
2726 a8083063 Iustin Pop
    dir_name = "%s/%s" % (self._BASE_DIR, subdir)
2727 a8083063 Iustin Pop
    try:
2728 eedbda4b Michael Hanselmann
      dir_contents = utils.ListVisibleFiles(dir_name)
2729 29921401 Iustin Pop
    except OSError:
2730 10c2650b Iustin Pop
      # FIXME: must log output in case of failures
2731 c26a6bd2 Iustin Pop
      return rr
2732 a8083063 Iustin Pop
2733 a8083063 Iustin Pop
    # we use the standard python sort order,
2734 a8083063 Iustin Pop
    # so 00name is the recommended naming scheme
2735 a8083063 Iustin Pop
    dir_contents.sort()
2736 a8083063 Iustin Pop
    for relname in dir_contents:
2737 a8083063 Iustin Pop
      fname = os.path.join(dir_name, relname)
2738 a8083063 Iustin Pop
      if not (os.path.isfile(fname) and os.access(fname, os.X_OK) and
2739 4fe80ef2 Iustin Pop
              constants.EXT_PLUGIN_MASK.match(relname) is not None):
2740 a8083063 Iustin Pop
        rrval = constants.HKR_SKIP
2741 a8083063 Iustin Pop
        output = ""
2742 a8083063 Iustin Pop
      else:
2743 a8083063 Iustin Pop
        result, output = self.ExecHook(fname, env)
2744 a8083063 Iustin Pop
        if not result:
2745 a8083063 Iustin Pop
          rrval = constants.HKR_FAIL
2746 a8083063 Iustin Pop
        else:
2747 a8083063 Iustin Pop
          rrval = constants.HKR_SUCCESS
2748 a8083063 Iustin Pop
      rr.append(("%s/%s" % (subdir, relname), rrval, output))
2749 a8083063 Iustin Pop
2750 c26a6bd2 Iustin Pop
    return rr
2751 3f78eef2 Iustin Pop
2752 3f78eef2 Iustin Pop
2753 8d528b7c Iustin Pop
class IAllocatorRunner(object):
2754 8d528b7c Iustin Pop
  """IAllocator runner.
2755 8d528b7c Iustin Pop

2756 8d528b7c Iustin Pop
  This class is instantiated on the node side (ganeti-noded) and not on
2757 8d528b7c Iustin Pop
  the master side.
2758 8d528b7c Iustin Pop

2759 8d528b7c Iustin Pop
  """
2760 7e950d31 Iustin Pop
  @staticmethod
2761 7e950d31 Iustin Pop
  def Run(name, idata):
2762 8d528b7c Iustin Pop
    """Run an iallocator script.
2763 8d528b7c Iustin Pop

2764 10c2650b Iustin Pop
    @type name: str
2765 10c2650b Iustin Pop
    @param name: the iallocator script name
2766 10c2650b Iustin Pop
    @type idata: str
2767 10c2650b Iustin Pop
    @param idata: the allocator input data
2768 10c2650b Iustin Pop

2769 10c2650b Iustin Pop
    @rtype: tuple
2770 87f5c298 Iustin Pop
    @return: two element tuple of:
2771 87f5c298 Iustin Pop
       - status
2772 87f5c298 Iustin Pop
       - either error message or stdout of allocator (for success)
2773 8d528b7c Iustin Pop

2774 8d528b7c Iustin Pop
    """
2775 8d528b7c Iustin Pop
    alloc_script = utils.FindFile(name, constants.IALLOCATOR_SEARCH_PATH,
2776 8d528b7c Iustin Pop
                                  os.path.isfile)
2777 8d528b7c Iustin Pop
    if alloc_script is None:
2778 87f5c298 Iustin Pop
      _Fail("iallocator module '%s' not found in the search path", name)
2779 8d528b7c Iustin Pop
2780 8d528b7c Iustin Pop
    fd, fin_name = tempfile.mkstemp(prefix="ganeti-iallocator.")
2781 8d528b7c Iustin Pop
    try:
2782 8d528b7c Iustin Pop
      os.write(fd, idata)
2783 8d528b7c Iustin Pop
      os.close(fd)
2784 8d528b7c Iustin Pop
      result = utils.RunCmd([alloc_script, fin_name])
2785 8d528b7c Iustin Pop
      if result.failed:
2786 87f5c298 Iustin Pop
        _Fail("iallocator module '%s' failed: %s, output '%s'",
2787 87f5c298 Iustin Pop
              name, result.fail_reason, result.output)
2788 8d528b7c Iustin Pop
    finally:
2789 8d528b7c Iustin Pop
      os.unlink(fin_name)
2790 8d528b7c Iustin Pop
2791 c26a6bd2 Iustin Pop
    return result.stdout
2792 8d528b7c Iustin Pop
2793 8d528b7c Iustin Pop
2794 3f78eef2 Iustin Pop
class DevCacheManager(object):
2795 c99a3cc0 Manuel Franceschini
  """Simple class for managing a cache of block device information.
2796 3f78eef2 Iustin Pop

2797 3f78eef2 Iustin Pop
  """
2798 3f78eef2 Iustin Pop
  _DEV_PREFIX = "/dev/"
2799 3f78eef2 Iustin Pop
  _ROOT_DIR = constants.BDEV_CACHE_DIR
2800 3f78eef2 Iustin Pop
2801 3f78eef2 Iustin Pop
  @classmethod
2802 3f78eef2 Iustin Pop
  def _ConvertPath(cls, dev_path):
2803 3f78eef2 Iustin Pop
    """Converts a /dev/name path to the cache file name.
2804 3f78eef2 Iustin Pop

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

2808 10c2650b Iustin Pop
    @type dev_path: str
2809 10c2650b Iustin Pop
    @param dev_path: the C{/dev/} path name
2810 10c2650b Iustin Pop
    @rtype: str
2811 10c2650b Iustin Pop
    @return: the converted path name
2812 3f78eef2 Iustin Pop

2813 3f78eef2 Iustin Pop
    """
2814 3f78eef2 Iustin Pop
    if dev_path.startswith(cls._DEV_PREFIX):
2815 3f78eef2 Iustin Pop
      dev_path = dev_path[len(cls._DEV_PREFIX):]
2816 3f78eef2 Iustin Pop
    dev_path = dev_path.replace("/", "_")
2817 3f78eef2 Iustin Pop
    fpath = "%s/bdev_%s" % (cls._ROOT_DIR, dev_path)
2818 3f78eef2 Iustin Pop
    return fpath
2819 3f78eef2 Iustin Pop
2820 3f78eef2 Iustin Pop
  @classmethod
2821 3f78eef2 Iustin Pop
  def UpdateCache(cls, dev_path, owner, on_primary, iv_name):
2822 3f78eef2 Iustin Pop
    """Updates the cache information for a given device.
2823 3f78eef2 Iustin Pop

2824 10c2650b Iustin Pop
    @type dev_path: str
2825 10c2650b Iustin Pop
    @param dev_path: the pathname of the device
2826 10c2650b Iustin Pop
    @type owner: str
2827 10c2650b Iustin Pop
    @param owner: the owner (instance name) of the device
2828 10c2650b Iustin Pop
    @type on_primary: bool
2829 10c2650b Iustin Pop
    @param on_primary: whether this is the primary
2830 10c2650b Iustin Pop
        node nor not
2831 10c2650b Iustin Pop
    @type iv_name: str
2832 10c2650b Iustin Pop
    @param iv_name: the instance-visible name of the
2833 c41eea6e Iustin Pop
        device, as in objects.Disk.iv_name
2834 10c2650b Iustin Pop

2835 10c2650b Iustin Pop
    @rtype: None
2836 10c2650b Iustin Pop

2837 3f78eef2 Iustin Pop
    """
2838 cf5a8306 Iustin Pop
    if dev_path is None:
2839 18682bca Iustin Pop
      logging.error("DevCacheManager.UpdateCache got a None dev_path")
2840 cf5a8306 Iustin Pop
      return
2841 3f78eef2 Iustin Pop
    fpath = cls._ConvertPath(dev_path)
2842 3f78eef2 Iustin Pop
    if on_primary:
2843 3f78eef2 Iustin Pop
      state = "primary"
2844 3f78eef2 Iustin Pop
    else:
2845 3f78eef2 Iustin Pop
      state = "secondary"
2846 3f78eef2 Iustin Pop
    if iv_name is None:
2847 3f78eef2 Iustin Pop
      iv_name = "not_visible"
2848 3f78eef2 Iustin Pop
    fdata = "%s %s %s\n" % (str(owner), state, iv_name)
2849 3f78eef2 Iustin Pop
    try:
2850 3f78eef2 Iustin Pop
      utils.WriteFile(fpath, data=fdata)
2851 3f78eef2 Iustin Pop
    except EnvironmentError, err:
2852 29921401 Iustin Pop
      logging.exception("Can't update bdev cache for %s: %s", dev_path, err)
2853 3f78eef2 Iustin Pop
2854 3f78eef2 Iustin Pop
  @classmethod
2855 3f78eef2 Iustin Pop
  def RemoveCache(cls, dev_path):
2856 3f78eef2 Iustin Pop
    """Remove data for a dev_path.
2857 3f78eef2 Iustin Pop

2858 10c2650b Iustin Pop
    This is just a wrapper over L{utils.RemoveFile} with a converted
2859 10c2650b Iustin Pop
    path name and logging.
2860 10c2650b Iustin Pop

2861 10c2650b Iustin Pop
    @type dev_path: str
2862 10c2650b Iustin Pop
    @param dev_path: the pathname of the device
2863 10c2650b Iustin Pop

2864 10c2650b Iustin Pop
    @rtype: None
2865 10c2650b Iustin Pop

2866 3f78eef2 Iustin Pop
    """
2867 cf5a8306 Iustin Pop
    if dev_path is None:
2868 18682bca Iustin Pop
      logging.error("DevCacheManager.RemoveCache got a None dev_path")
2869 cf5a8306 Iustin Pop
      return
2870 3f78eef2 Iustin Pop
    fpath = cls._ConvertPath(dev_path)
2871 3f78eef2 Iustin Pop
    try:
2872 3f78eef2 Iustin Pop
      utils.RemoveFile(fpath)
2873 3f78eef2 Iustin Pop
    except EnvironmentError, err:
2874 29921401 Iustin Pop
      logging.exception("Can't update bdev cache for %s: %s", dev_path, err)