Statistics
| Branch: | Tag: | Revision:

root / lib / backend.py @ c4feafe8

History | View | Annotate | Download (86 kB)

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

574 10c2650b Iustin Pop
  @type vg_name: str
575 10c2650b Iustin Pop
  @param vg_name: the volume group whose LVs we should list
576 10c2650b Iustin Pop
  @rtype: dict
577 10c2650b Iustin Pop
  @return:
578 10c2650b Iustin Pop
      dictionary of all partions (key) with value being a tuple of
579 10c2650b Iustin Pop
      their size (in MiB), inactive and online status::
580 10c2650b Iustin Pop

581 10c2650b Iustin Pop
        {'test1': ('20.06', True, True)}
582 10c2650b Iustin Pop

583 10c2650b Iustin Pop
      in case of errors, a string is returned with the error
584 10c2650b Iustin Pop
      details.
585 a8083063 Iustin Pop

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

618 10c2650b Iustin Pop
  @rtype: dict
619 10c2650b Iustin Pop
  @return: dictionary with keys volume name and values the
620 10c2650b Iustin Pop
      size of the volume
621 a8083063 Iustin Pop

622 a8083063 Iustin Pop
  """
623 c26a6bd2 Iustin Pop
  return utils.ListVolumeGroups()
624 a8083063 Iustin Pop
625 a8083063 Iustin Pop
626 dcb93971 Michael Hanselmann
def NodeVolumes():
627 dcb93971 Michael Hanselmann
  """List all volumes on this node.
628 dcb93971 Michael Hanselmann

629 10c2650b Iustin Pop
  @rtype: list
630 10c2650b Iustin Pop
  @return:
631 10c2650b Iustin Pop
    A list of dictionaries, each having four keys:
632 10c2650b Iustin Pop
      - name: the logical volume name,
633 10c2650b Iustin Pop
      - size: the size of the logical volume
634 10c2650b Iustin Pop
      - dev: the physical device on which the LV lives
635 10c2650b Iustin Pop
      - vg: the volume group to which it belongs
636 10c2650b Iustin Pop

637 10c2650b Iustin Pop
    In case of errors, we return an empty list and log the
638 10c2650b Iustin Pop
    error.
639 10c2650b Iustin Pop

640 10c2650b Iustin Pop
    Note that since a logical volume can live on multiple physical
641 10c2650b Iustin Pop
    volumes, the resulting list might include a logical volume
642 10c2650b Iustin Pop
    multiple times.
643 10c2650b Iustin Pop

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

673 b1206984 Iustin Pop
  @rtype: boolean
674 b1206984 Iustin Pop
  @return: C{True} if all of them exist, C{False} otherwise
675 a8083063 Iustin Pop

676 a8083063 Iustin Pop
  """
677 35c0c8da Iustin Pop
  missing = []
678 a8083063 Iustin Pop
  for bridge in bridges_list:
679 a8083063 Iustin Pop
    if not utils.BridgeExists(bridge):
680 35c0c8da Iustin Pop
      missing.append(bridge)
681 a8083063 Iustin Pop
682 35c0c8da Iustin Pop
  if missing:
683 1f864b60 Iustin Pop
    _Fail("Missing bridges %s", utils.CommaJoin(missing))
684 35c0c8da Iustin Pop
685 a8083063 Iustin Pop
686 e69d05fd Iustin Pop
def GetInstanceList(hypervisor_list):
687 2f8598a5 Alexander Schreiber
  """Provides a list of instances.
688 a8083063 Iustin Pop

689 e69d05fd Iustin Pop
  @type hypervisor_list: list
690 e69d05fd Iustin Pop
  @param hypervisor_list: the list of hypervisors to query information
691 e69d05fd Iustin Pop

692 e69d05fd Iustin Pop
  @rtype: list
693 e69d05fd Iustin Pop
  @return: a list of all running instances on the current node
694 10c2650b Iustin Pop
    - instance1.example.com
695 10c2650b Iustin Pop
    - instance2.example.com
696 a8083063 Iustin Pop

697 098c0958 Michael Hanselmann
  """
698 e69d05fd Iustin Pop
  results = []
699 e69d05fd Iustin Pop
  for hname in hypervisor_list:
700 e69d05fd Iustin Pop
    try:
701 e69d05fd Iustin Pop
      names = hypervisor.GetHypervisor(hname).ListInstances()
702 e69d05fd Iustin Pop
      results.extend(names)
703 e69d05fd Iustin Pop
    except errors.HypervisorError, err:
704 aca13712 Iustin Pop
      _Fail("Error enumerating instances (hypervisor %s): %s",
705 aca13712 Iustin Pop
            hname, err, exc=True)
706 a8083063 Iustin Pop
707 e69d05fd Iustin Pop
  return results
708 a8083063 Iustin Pop
709 a8083063 Iustin Pop
710 e69d05fd Iustin Pop
def GetInstanceInfo(instance, hname):
711 5bbd3f7f Michael Hanselmann
  """Gives back the information about an instance as a dictionary.
712 a8083063 Iustin Pop

713 e69d05fd Iustin Pop
  @type instance: string
714 e69d05fd Iustin Pop
  @param instance: the instance name
715 e69d05fd Iustin Pop
  @type hname: string
716 e69d05fd Iustin Pop
  @param hname: the hypervisor type of the instance
717 a8083063 Iustin Pop

718 e69d05fd Iustin Pop
  @rtype: dict
719 e69d05fd Iustin Pop
  @return: dictionary with the following keys:
720 e69d05fd Iustin Pop
      - memory: memory size of instance (int)
721 e69d05fd Iustin Pop
      - state: xen state of instance (string)
722 e69d05fd Iustin Pop
      - time: cpu time of instance (float)
723 a8083063 Iustin Pop

724 098c0958 Michael Hanselmann
  """
725 a8083063 Iustin Pop
  output = {}
726 a8083063 Iustin Pop
727 e69d05fd Iustin Pop
  iinfo = hypervisor.GetHypervisor(hname).GetInstanceInfo(instance)
728 a8083063 Iustin Pop
  if iinfo is not None:
729 a8083063 Iustin Pop
    output['memory'] = iinfo[2]
730 a8083063 Iustin Pop
    output['state'] = iinfo[4]
731 a8083063 Iustin Pop
    output['time'] = iinfo[5]
732 a8083063 Iustin Pop
733 c26a6bd2 Iustin Pop
  return output
734 a8083063 Iustin Pop
735 a8083063 Iustin Pop
736 56e7640c Iustin Pop
def GetInstanceMigratable(instance):
737 56e7640c Iustin Pop
  """Gives whether an instance can be migrated.
738 56e7640c Iustin Pop

739 56e7640c Iustin Pop
  @type instance: L{objects.Instance}
740 56e7640c Iustin Pop
  @param instance: object representing the instance to be checked.
741 56e7640c Iustin Pop

742 56e7640c Iustin Pop
  @rtype: tuple
743 56e7640c Iustin Pop
  @return: tuple of (result, description) where:
744 56e7640c Iustin Pop
      - result: whether the instance can be migrated or not
745 56e7640c Iustin Pop
      - description: a description of the issue, if relevant
746 56e7640c Iustin Pop

747 56e7640c Iustin Pop
  """
748 56e7640c Iustin Pop
  hyper = hypervisor.GetHypervisor(instance.hypervisor)
749 afdc3985 Iustin Pop
  iname = instance.name
750 afdc3985 Iustin Pop
  if iname not in hyper.ListInstances():
751 afdc3985 Iustin Pop
    _Fail("Instance %s is not running", iname)
752 56e7640c Iustin Pop
753 56e7640c Iustin Pop
  for idx in range(len(instance.disks)):
754 afdc3985 Iustin Pop
    link_name = _GetBlockDevSymlinkPath(iname, idx)
755 56e7640c Iustin Pop
    if not os.path.islink(link_name):
756 afdc3985 Iustin Pop
      _Fail("Instance %s was not restarted since ganeti 1.2.5", iname)
757 56e7640c Iustin Pop
758 56e7640c Iustin Pop
759 e69d05fd Iustin Pop
def GetAllInstancesInfo(hypervisor_list):
760 a8083063 Iustin Pop
  """Gather data about all instances.
761 a8083063 Iustin Pop

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

766 e69d05fd Iustin Pop
  @type hypervisor_list: list
767 e69d05fd Iustin Pop
  @param hypervisor_list: list of hypervisors to query for instance data
768 e69d05fd Iustin Pop

769 955db481 Guido Trotter
  @rtype: dict
770 e69d05fd Iustin Pop
  @return: dictionary of instance: data, with data having the following keys:
771 e69d05fd Iustin Pop
      - memory: memory size of instance (int)
772 e69d05fd Iustin Pop
      - state: xen state of instance (string)
773 e69d05fd Iustin Pop
      - time: cpu time of instance (float)
774 10c2650b Iustin Pop
      - vcpus: the number of vcpus
775 a8083063 Iustin Pop

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

805 d15a9ad3 Guido Trotter
  @type instance: L{objects.Instance}
806 d15a9ad3 Guido Trotter
  @param instance: Instance whose OS is to be installed
807 e557bae9 Guido Trotter
  @type reinstall: boolean
808 e557bae9 Guido Trotter
  @param reinstall: whether this is an instance reinstall
809 4a0e011f Iustin Pop
  @type debug: integer
810 4a0e011f Iustin Pop
  @param debug: debug level, passed to the OS scripts
811 c26a6bd2 Iustin Pop
  @rtype: None
812 a8083063 Iustin Pop

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

838 b1206984 Iustin Pop
  @type instance: L{objects.Instance}
839 d15a9ad3 Guido Trotter
  @param instance: Instance whose OS is to be installed
840 d15a9ad3 Guido Trotter
  @type old_name: string
841 d15a9ad3 Guido Trotter
  @param old_name: previous instance name
842 4a0e011f Iustin Pop
  @type debug: integer
843 4a0e011f Iustin Pop
  @param debug: debug level, passed to the OS scripts
844 10c2650b Iustin Pop
  @rtype: boolean
845 10c2650b Iustin Pop
  @return: the success of the operation
846 decd5f45 Iustin Pop

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

872 10c2650b Iustin Pop
  @type vg_name: str
873 10c2650b Iustin Pop
  @param vg_name: the volume group which we query
874 10c2650b Iustin Pop
  @rtype: dict
875 10c2650b Iustin Pop
  @return:
876 10c2650b Iustin Pop
    A dictionary with the following keys:
877 10c2650b Iustin Pop
      - C{vg_size} is the total size of the volume group in MiB
878 10c2650b Iustin Pop
      - C{vg_free} is the free size of the volume group in MiB
879 10c2650b Iustin Pop
      - C{pv_count} are the number of physical disks in that VG
880 a8083063 Iustin Pop

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

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

917 9332fd8a Iustin Pop
  This is an auxiliary function run when an instance is start (on the primary
918 9332fd8a Iustin Pop
  node) or when an instance is migrated (on the target node).
919 9332fd8a Iustin Pop

920 9332fd8a Iustin Pop

921 5282084b Iustin Pop
  @param instance_name: the name of the target instance
922 5282084b Iustin Pop
  @param device_path: path of the physical block device, on the node
923 5282084b Iustin Pop
  @param idx: the disk index
924 5282084b Iustin Pop
  @return: absolute path to the disk's symlink
925 9332fd8a Iustin Pop

926 9332fd8a Iustin Pop
  """
927 5282084b Iustin Pop
  link_name = _GetBlockDevSymlinkPath(instance_name, idx)
928 9332fd8a Iustin Pop
  try:
929 9332fd8a Iustin Pop
    os.symlink(device_path, link_name)
930 5282084b Iustin Pop
  except OSError, err:
931 5282084b Iustin Pop
    if err.errno == errno.EEXIST:
932 9332fd8a Iustin Pop
      if (not os.path.islink(link_name) or
933 9332fd8a Iustin Pop
          os.readlink(link_name) != device_path):
934 9332fd8a Iustin Pop
        os.remove(link_name)
935 9332fd8a Iustin Pop
        os.symlink(device_path, link_name)
936 9332fd8a Iustin Pop
    else:
937 9332fd8a Iustin Pop
      raise
938 9332fd8a Iustin Pop
939 9332fd8a Iustin Pop
  return link_name
940 9332fd8a Iustin Pop
941 9332fd8a Iustin Pop
942 5282084b Iustin Pop
def _RemoveBlockDevLinks(instance_name, disks):
943 3c9c571d Iustin Pop
  """Remove the block device symlinks belonging to the given instance.
944 3c9c571d Iustin Pop

945 3c9c571d Iustin Pop
  """
946 29921401 Iustin Pop
  for idx, _ in enumerate(disks):
947 5282084b Iustin Pop
    link_name = _GetBlockDevSymlinkPath(instance_name, idx)
948 5282084b Iustin Pop
    if os.path.islink(link_name):
949 3c9c571d Iustin Pop
      try:
950 03dfa658 Iustin Pop
        os.remove(link_name)
951 03dfa658 Iustin Pop
      except OSError:
952 03dfa658 Iustin Pop
        logging.exception("Can't remove symlink '%s'", link_name)
953 3c9c571d Iustin Pop
954 3c9c571d Iustin Pop
955 9332fd8a Iustin Pop
def _GatherAndLinkBlockDevs(instance):
956 a8083063 Iustin Pop
  """Set up an instance's block device(s).
957 a8083063 Iustin Pop

958 a8083063 Iustin Pop
  This is run on the primary node at instance startup. The block
959 a8083063 Iustin Pop
  devices must be already assembled.
960 a8083063 Iustin Pop

961 10c2650b Iustin Pop
  @type instance: L{objects.Instance}
962 10c2650b Iustin Pop
  @param instance: the instance whose disks we shoul assemble
963 069cfbf1 Iustin Pop
  @rtype: list
964 069cfbf1 Iustin Pop
  @return: list of (disk_object, device_path)
965 10c2650b Iustin Pop

966 a8083063 Iustin Pop
  """
967 a8083063 Iustin Pop
  block_devices = []
968 9332fd8a Iustin Pop
  for idx, disk in enumerate(instance.disks):
969 a8083063 Iustin Pop
    device = _RecursiveFindBD(disk)
970 a8083063 Iustin Pop
    if device is None:
971 a8083063 Iustin Pop
      raise errors.BlockDeviceError("Block device '%s' is not set up." %
972 a8083063 Iustin Pop
                                    str(disk))
973 a8083063 Iustin Pop
    device.Open()
974 9332fd8a Iustin Pop
    try:
975 5282084b Iustin Pop
      link_name = _SymlinkBlockDev(instance.name, device.dev_path, idx)
976 9332fd8a Iustin Pop
    except OSError, e:
977 9332fd8a Iustin Pop
      raise errors.BlockDeviceError("Cannot create block device symlink: %s" %
978 9332fd8a Iustin Pop
                                    e.strerror)
979 9332fd8a Iustin Pop
980 9332fd8a Iustin Pop
    block_devices.append((disk, link_name))
981 9332fd8a Iustin Pop
982 a8083063 Iustin Pop
  return block_devices
983 a8083063 Iustin Pop
984 a8083063 Iustin Pop
985 07813a9e Iustin Pop
def StartInstance(instance):
986 a8083063 Iustin Pop
  """Start an instance.
987 a8083063 Iustin Pop

988 10c2650b Iustin Pop
  @type instance: L{objects.Instance}
989 e69d05fd Iustin Pop
  @param instance: the instance object
990 c26a6bd2 Iustin Pop
  @rtype: None
991 a8083063 Iustin Pop

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

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

1015 10c2650b Iustin Pop
  @type instance: L{objects.Instance}
1016 e69d05fd Iustin Pop
  @param instance: the instance object
1017 6263189c Guido Trotter
  @type timeout: integer
1018 6263189c Guido Trotter
  @param timeout: maximum timeout for soft shutdown
1019 c26a6bd2 Iustin Pop
  @rtype: None
1020 a8083063 Iustin Pop

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

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

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

1119 6906a9d8 Guido Trotter
  @type instance: L{objects.Instance}
1120 6906a9d8 Guido Trotter
  @param instance: the instance definition
1121 6906a9d8 Guido Trotter

1122 6906a9d8 Guido Trotter
  """
1123 cd42d0ad Guido Trotter
  hyper = hypervisor.GetHypervisor(instance.hypervisor)
1124 cd42d0ad Guido Trotter
  try:
1125 cd42d0ad Guido Trotter
    info = hyper.MigrationInfo(instance)
1126 cd42d0ad Guido Trotter
  except errors.HypervisorError, err:
1127 2cc6781a Iustin Pop
    _Fail("Failed to fetch migration information: %s", err, exc=True)
1128 c26a6bd2 Iustin Pop
  return info
1129 6906a9d8 Guido Trotter
1130 6906a9d8 Guido Trotter
1131 6906a9d8 Guido Trotter
def AcceptInstance(instance, info, target):
1132 6906a9d8 Guido Trotter
  """Prepare the node to accept an instance.
1133 6906a9d8 Guido Trotter

1134 6906a9d8 Guido Trotter
  @type instance: L{objects.Instance}
1135 6906a9d8 Guido Trotter
  @param instance: the instance definition
1136 6906a9d8 Guido Trotter
  @type info: string/data (opaque)
1137 6906a9d8 Guido Trotter
  @param info: migration information, from the source node
1138 6906a9d8 Guido Trotter
  @type target: string
1139 6906a9d8 Guido Trotter
  @param target: target host (usually ip), on this node
1140 6906a9d8 Guido Trotter

1141 6906a9d8 Guido Trotter
  """
1142 cd42d0ad Guido Trotter
  hyper = hypervisor.GetHypervisor(instance.hypervisor)
1143 cd42d0ad Guido Trotter
  try:
1144 cd42d0ad Guido Trotter
    hyper.AcceptInstance(instance, info, target)
1145 cd42d0ad Guido Trotter
  except errors.HypervisorError, err:
1146 2cc6781a Iustin Pop
    _Fail("Failed to accept instance: %s", err, exc=True)
1147 6906a9d8 Guido Trotter
1148 6906a9d8 Guido Trotter
1149 6906a9d8 Guido Trotter
def FinalizeMigration(instance, info, success):
1150 6906a9d8 Guido Trotter
  """Finalize any preparation to accept an instance.
1151 6906a9d8 Guido Trotter

1152 6906a9d8 Guido Trotter
  @type instance: L{objects.Instance}
1153 6906a9d8 Guido Trotter
  @param instance: the instance definition
1154 6906a9d8 Guido Trotter
  @type info: string/data (opaque)
1155 6906a9d8 Guido Trotter
  @param info: migration information, from the source node
1156 6906a9d8 Guido Trotter
  @type success: boolean
1157 6906a9d8 Guido Trotter
  @param success: whether the migration was a success or a failure
1158 6906a9d8 Guido Trotter

1159 6906a9d8 Guido Trotter
  """
1160 cd42d0ad Guido Trotter
  hyper = hypervisor.GetHypervisor(instance.hypervisor)
1161 cd42d0ad Guido Trotter
  try:
1162 cd42d0ad Guido Trotter
    hyper.FinalizeMigration(instance, info, success)
1163 cd42d0ad Guido Trotter
  except errors.HypervisorError, err:
1164 2cc6781a Iustin Pop
    _Fail("Failed to finalize migration: %s", err, exc=True)
1165 6906a9d8 Guido Trotter
1166 6906a9d8 Guido Trotter
1167 2a10865c Iustin Pop
def MigrateInstance(instance, target, live):
1168 2a10865c Iustin Pop
  """Migrates an instance to another node.
1169 2a10865c Iustin Pop

1170 b1206984 Iustin Pop
  @type instance: L{objects.Instance}
1171 9f0e6b37 Iustin Pop
  @param instance: the instance definition
1172 9f0e6b37 Iustin Pop
  @type target: string
1173 9f0e6b37 Iustin Pop
  @param target: the target node name
1174 9f0e6b37 Iustin Pop
  @type live: boolean
1175 9f0e6b37 Iustin Pop
  @param live: whether the migration should be done live or not (the
1176 9f0e6b37 Iustin Pop
      interpretation of this parameter is left to the hypervisor)
1177 9f0e6b37 Iustin Pop
  @rtype: tuple
1178 9f0e6b37 Iustin Pop
  @return: a tuple of (success, msg) where:
1179 9f0e6b37 Iustin Pop
      - succes is a boolean denoting the success/failure of the operation
1180 9f0e6b37 Iustin Pop
      - msg is a string with details in case of failure
1181 9f0e6b37 Iustin Pop

1182 2a10865c Iustin Pop
  """
1183 53c776b5 Iustin Pop
  hyper = hypervisor.GetHypervisor(instance.hypervisor)
1184 2a10865c Iustin Pop
1185 2a10865c Iustin Pop
  try:
1186 58d38b02 Iustin Pop
    hyper.MigrateInstance(instance, target, live)
1187 2a10865c Iustin Pop
  except errors.HypervisorError, err:
1188 2cc6781a Iustin Pop
    _Fail("Failed to migrate instance: %s", err, exc=True)
1189 2a10865c Iustin Pop
1190 2a10865c Iustin Pop
1191 821d1bd1 Iustin Pop
def BlockdevCreate(disk, size, owner, on_primary, info):
1192 a8083063 Iustin Pop
  """Creates a block device for an instance.
1193 a8083063 Iustin Pop

1194 b1206984 Iustin Pop
  @type disk: L{objects.Disk}
1195 b1206984 Iustin Pop
  @param disk: the object describing the disk we should create
1196 b1206984 Iustin Pop
  @type size: int
1197 b1206984 Iustin Pop
  @param size: the size of the physical underlying device, in MiB
1198 b1206984 Iustin Pop
  @type owner: str
1199 b1206984 Iustin Pop
  @param owner: the name of the instance for which disk is created,
1200 b1206984 Iustin Pop
      used for device cache data
1201 b1206984 Iustin Pop
  @type on_primary: boolean
1202 b1206984 Iustin Pop
  @param on_primary:  indicates if it is the primary node or not
1203 b1206984 Iustin Pop
  @type info: string
1204 b1206984 Iustin Pop
  @param info: string that will be sent to the physical device
1205 b1206984 Iustin Pop
      creation, used for example to set (LVM) tags on LVs
1206 b1206984 Iustin Pop

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

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

1258 10c2650b Iustin Pop
  @note: This is intended to be called recursively.
1259 10c2650b Iustin Pop

1260 c41eea6e Iustin Pop
  @type disk: L{objects.Disk}
1261 10c2650b Iustin Pop
  @param disk: the disk object we should remove
1262 10c2650b Iustin Pop
  @rtype: boolean
1263 10c2650b Iustin Pop
  @return: the success of the operation
1264 a8083063 Iustin Pop

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

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

1298 10c2650b Iustin Pop
  @note: this function is called recursively.
1299 a8083063 Iustin Pop

1300 10c2650b Iustin Pop
  @type disk: L{objects.Disk}
1301 10c2650b Iustin Pop
  @param disk: the disk we try to assemble
1302 10c2650b Iustin Pop
  @type owner: str
1303 10c2650b Iustin Pop
  @param owner: the name of the instance which owns the disk
1304 10c2650b Iustin Pop
  @type as_primary: boolean
1305 10c2650b Iustin Pop
  @param as_primary: if we should make the block device
1306 10c2650b Iustin Pop
      read/write
1307 a8083063 Iustin Pop

1308 10c2650b Iustin Pop
  @return: the assembled device or None (in case no device
1309 10c2650b Iustin Pop
      was assembled)
1310 10c2650b Iustin Pop
  @raise errors.BlockDeviceError: in case there is an error
1311 10c2650b Iustin Pop
      during the activation of the children or the device
1312 10c2650b Iustin Pop
      itself
1313 a8083063 Iustin Pop

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

1350 a8083063 Iustin Pop
  This is a wrapper over _RecursiveAssembleBD.
1351 a8083063 Iustin Pop

1352 b1206984 Iustin Pop
  @rtype: str or boolean
1353 b1206984 Iustin Pop
  @return: a C{/dev/...} path for primary nodes, and
1354 b1206984 Iustin Pop
      C{True} for secondary nodes
1355 a8083063 Iustin Pop

1356 a8083063 Iustin Pop
  """
1357 53c14ef1 Iustin Pop
  try:
1358 53c14ef1 Iustin Pop
    result = _RecursiveAssembleBD(disk, owner, as_primary)
1359 53c14ef1 Iustin Pop
    if isinstance(result, bdev.BlockDev):
1360 fe267188 Iustin Pop
      # pylint: disable-msg=E1103
1361 53c14ef1 Iustin Pop
      result = result.dev_path
1362 53c14ef1 Iustin Pop
  except errors.BlockDeviceError, err:
1363 afdc3985 Iustin Pop
    _Fail("Error while assembling disk: %s", err, exc=True)
1364 afdc3985 Iustin Pop
1365 c26a6bd2 Iustin Pop
  return result
1366 a8083063 Iustin Pop
1367 a8083063 Iustin Pop
1368 821d1bd1 Iustin Pop
def BlockdevShutdown(disk):
1369 a8083063 Iustin Pop
  """Shut down a block device.
1370 a8083063 Iustin Pop

1371 5bbd3f7f Michael Hanselmann
  First, if the device is assembled (Attach() is successful), then
1372 c41eea6e Iustin Pop
  the device is shutdown. Then the children of the device are
1373 c41eea6e Iustin Pop
  shutdown.
1374 a8083063 Iustin Pop

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

1379 10c2650b Iustin Pop
  @type disk: L{objects.Disk}
1380 10c2650b Iustin Pop
  @param disk: the description of the disk we should
1381 10c2650b Iustin Pop
      shutdown
1382 c26a6bd2 Iustin Pop
  @rtype: None
1383 10c2650b Iustin Pop

1384 a8083063 Iustin Pop
  """
1385 cacfd1fd Iustin Pop
  msgs = []
1386 a8083063 Iustin Pop
  r_dev = _RecursiveFindBD(disk)
1387 a8083063 Iustin Pop
  if r_dev is not None:
1388 3f78eef2 Iustin Pop
    r_path = r_dev.dev_path
1389 cacfd1fd Iustin Pop
    try:
1390 746f7476 Iustin Pop
      r_dev.Shutdown()
1391 746f7476 Iustin Pop
      DevCacheManager.RemoveCache(r_path)
1392 cacfd1fd Iustin Pop
    except errors.BlockDeviceError, err:
1393 cacfd1fd Iustin Pop
      msgs.append(str(err))
1394 746f7476 Iustin Pop
1395 a8083063 Iustin Pop
  if disk.children:
1396 a8083063 Iustin Pop
    for child in disk.children:
1397 c26a6bd2 Iustin Pop
      try:
1398 c26a6bd2 Iustin Pop
        BlockdevShutdown(child)
1399 c26a6bd2 Iustin Pop
      except RPCFail, err:
1400 c26a6bd2 Iustin Pop
        msgs.append(str(err))
1401 746f7476 Iustin Pop
1402 c26a6bd2 Iustin Pop
  if msgs:
1403 afdc3985 Iustin Pop
    _Fail("; ".join(msgs))
1404 a8083063 Iustin Pop
1405 a8083063 Iustin Pop
1406 821d1bd1 Iustin Pop
def BlockdevAddchildren(parent_cdev, new_cdevs):
1407 153d9724 Iustin Pop
  """Extend a mirrored block device.
1408 a8083063 Iustin Pop

1409 10c2650b Iustin Pop
  @type parent_cdev: L{objects.Disk}
1410 10c2650b Iustin Pop
  @param parent_cdev: the disk to which we should add children
1411 10c2650b Iustin Pop
  @type new_cdevs: list of L{objects.Disk}
1412 10c2650b Iustin Pop
  @param new_cdevs: the list of children which we should add
1413 c26a6bd2 Iustin Pop
  @rtype: None
1414 10c2650b Iustin Pop

1415 a8083063 Iustin Pop
  """
1416 bca2e7f4 Iustin Pop
  parent_bdev = _RecursiveFindBD(parent_cdev)
1417 153d9724 Iustin Pop
  if parent_bdev is None:
1418 2cc6781a Iustin Pop
    _Fail("Can't find parent device '%s' in add children", parent_cdev)
1419 153d9724 Iustin Pop
  new_bdevs = [_RecursiveFindBD(disk) for disk in new_cdevs]
1420 153d9724 Iustin Pop
  if new_bdevs.count(None) > 0:
1421 2cc6781a Iustin Pop
    _Fail("Can't find new device(s) to add: %s:%s", new_bdevs, new_cdevs)
1422 153d9724 Iustin Pop
  parent_bdev.AddChildren(new_bdevs)
1423 a8083063 Iustin Pop
1424 a8083063 Iustin Pop
1425 821d1bd1 Iustin Pop
def BlockdevRemovechildren(parent_cdev, new_cdevs):
1426 153d9724 Iustin Pop
  """Shrink a mirrored block device.
1427 a8083063 Iustin Pop

1428 10c2650b Iustin Pop
  @type parent_cdev: L{objects.Disk}
1429 10c2650b Iustin Pop
  @param parent_cdev: the disk from which we should remove children
1430 10c2650b Iustin Pop
  @type new_cdevs: list of L{objects.Disk}
1431 10c2650b Iustin Pop
  @param new_cdevs: the list of children which we should remove
1432 c26a6bd2 Iustin Pop
  @rtype: None
1433 10c2650b Iustin Pop

1434 a8083063 Iustin Pop
  """
1435 153d9724 Iustin Pop
  parent_bdev = _RecursiveFindBD(parent_cdev)
1436 153d9724 Iustin Pop
  if parent_bdev is None:
1437 2cc6781a Iustin Pop
    _Fail("Can't find parent device '%s' in remove children", parent_cdev)
1438 e739bd57 Iustin Pop
  devs = []
1439 e739bd57 Iustin Pop
  for disk in new_cdevs:
1440 e739bd57 Iustin Pop
    rpath = disk.StaticDevPath()
1441 e739bd57 Iustin Pop
    if rpath is None:
1442 e739bd57 Iustin Pop
      bd = _RecursiveFindBD(disk)
1443 e739bd57 Iustin Pop
      if bd is None:
1444 2cc6781a Iustin Pop
        _Fail("Can't find device %s while removing children", disk)
1445 e739bd57 Iustin Pop
      else:
1446 e739bd57 Iustin Pop
        devs.append(bd.dev_path)
1447 e739bd57 Iustin Pop
    else:
1448 e739bd57 Iustin Pop
      devs.append(rpath)
1449 e739bd57 Iustin Pop
  parent_bdev.RemoveChildren(devs)
1450 a8083063 Iustin Pop
1451 a8083063 Iustin Pop
1452 821d1bd1 Iustin Pop
def BlockdevGetmirrorstatus(disks):
1453 a8083063 Iustin Pop
  """Get the mirroring status of a list of devices.
1454 a8083063 Iustin Pop

1455 10c2650b Iustin Pop
  @type disks: list of L{objects.Disk}
1456 10c2650b Iustin Pop
  @param disks: the list of disks which we should query
1457 10c2650b Iustin Pop
  @rtype: disk
1458 10c2650b Iustin Pop
  @return:
1459 10c2650b Iustin Pop
      a list of (mirror_done, estimated_time) tuples, which
1460 c41eea6e Iustin Pop
      are the result of L{bdev.BlockDev.CombinedSyncStatus}
1461 10c2650b Iustin Pop
  @raise errors.BlockDeviceError: if any of the disks cannot be
1462 10c2650b Iustin Pop
      found
1463 a8083063 Iustin Pop

1464 a8083063 Iustin Pop
  """
1465 a8083063 Iustin Pop
  stats = []
1466 a8083063 Iustin Pop
  for dsk in disks:
1467 a8083063 Iustin Pop
    rbd = _RecursiveFindBD(dsk)
1468 a8083063 Iustin Pop
    if rbd is None:
1469 3efa9051 Iustin Pop
      _Fail("Can't find device %s", dsk)
1470 96acbc09 Michael Hanselmann
1471 36145b12 Michael Hanselmann
    stats.append(rbd.CombinedSyncStatus())
1472 96acbc09 Michael Hanselmann
1473 c26a6bd2 Iustin Pop
  return stats
1474 a8083063 Iustin Pop
1475 a8083063 Iustin Pop
1476 bca2e7f4 Iustin Pop
def _RecursiveFindBD(disk):
1477 a8083063 Iustin Pop
  """Check if a device is activated.
1478 a8083063 Iustin Pop

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

1481 10c2650b Iustin Pop
  @type disk: L{objects.Disk}
1482 10c2650b Iustin Pop
  @param disk: the disk object we need to find
1483 a8083063 Iustin Pop

1484 10c2650b Iustin Pop
  @return: None if the device can't be found,
1485 10c2650b Iustin Pop
      otherwise the device instance
1486 a8083063 Iustin Pop

1487 a8083063 Iustin Pop
  """
1488 a8083063 Iustin Pop
  children = []
1489 a8083063 Iustin Pop
  if disk.children:
1490 a8083063 Iustin Pop
    for chdisk in disk.children:
1491 a8083063 Iustin Pop
      children.append(_RecursiveFindBD(chdisk))
1492 a8083063 Iustin Pop
1493 464f8daf Iustin Pop
  return bdev.FindDevice(disk.dev_type, disk.physical_id, children, disk.size)
1494 a8083063 Iustin Pop
1495 a8083063 Iustin Pop
1496 821d1bd1 Iustin Pop
def BlockdevFind(disk):
1497 a8083063 Iustin Pop
  """Check if a device is activated.
1498 a8083063 Iustin Pop

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

1501 10c2650b Iustin Pop
  @type disk: L{objects.Disk}
1502 10c2650b Iustin Pop
  @param disk: the disk to find
1503 96acbc09 Michael Hanselmann
  @rtype: None or objects.BlockDevStatus
1504 96acbc09 Michael Hanselmann
  @return: None if the disk cannot be found, otherwise a the current
1505 96acbc09 Michael Hanselmann
           information
1506 a8083063 Iustin Pop

1507 a8083063 Iustin Pop
  """
1508 23829f6f Iustin Pop
  try:
1509 23829f6f Iustin Pop
    rbd = _RecursiveFindBD(disk)
1510 23829f6f Iustin Pop
  except errors.BlockDeviceError, err:
1511 2cc6781a Iustin Pop
    _Fail("Failed to find device: %s", err, exc=True)
1512 96acbc09 Michael Hanselmann
1513 a8083063 Iustin Pop
  if rbd is None:
1514 c26a6bd2 Iustin Pop
    return None
1515 96acbc09 Michael Hanselmann
1516 96acbc09 Michael Hanselmann
  return rbd.GetSyncStatus()
1517 a8083063 Iustin Pop
1518 a8083063 Iustin Pop
1519 968a7623 Iustin Pop
def BlockdevGetsize(disks):
1520 968a7623 Iustin Pop
  """Computes the size of the given disks.
1521 968a7623 Iustin Pop

1522 968a7623 Iustin Pop
  If a disk is not found, returns None instead.
1523 968a7623 Iustin Pop

1524 968a7623 Iustin Pop
  @type disks: list of L{objects.Disk}
1525 968a7623 Iustin Pop
  @param disks: the list of disk to compute the size for
1526 968a7623 Iustin Pop
  @rtype: list
1527 968a7623 Iustin Pop
  @return: list with elements None if the disk cannot be found,
1528 968a7623 Iustin Pop
      otherwise the size
1529 968a7623 Iustin Pop

1530 968a7623 Iustin Pop
  """
1531 968a7623 Iustin Pop
  result = []
1532 968a7623 Iustin Pop
  for cf in disks:
1533 968a7623 Iustin Pop
    try:
1534 968a7623 Iustin Pop
      rbd = _RecursiveFindBD(cf)
1535 1122eb25 Iustin Pop
    except errors.BlockDeviceError:
1536 968a7623 Iustin Pop
      result.append(None)
1537 968a7623 Iustin Pop
      continue
1538 968a7623 Iustin Pop
    if rbd is None:
1539 968a7623 Iustin Pop
      result.append(None)
1540 968a7623 Iustin Pop
    else:
1541 968a7623 Iustin Pop
      result.append(rbd.GetActualSize())
1542 968a7623 Iustin Pop
  return result
1543 968a7623 Iustin Pop
1544 968a7623 Iustin Pop
1545 858f3d18 Iustin Pop
def BlockdevExport(disk, dest_node, dest_path, cluster_name):
1546 858f3d18 Iustin Pop
  """Export a block device to a remote node.
1547 858f3d18 Iustin Pop

1548 858f3d18 Iustin Pop
  @type disk: L{objects.Disk}
1549 858f3d18 Iustin Pop
  @param disk: the description of the disk to export
1550 858f3d18 Iustin Pop
  @type dest_node: str
1551 858f3d18 Iustin Pop
  @param dest_node: the destination node to export to
1552 858f3d18 Iustin Pop
  @type dest_path: str
1553 858f3d18 Iustin Pop
  @param dest_path: the destination path on the target node
1554 858f3d18 Iustin Pop
  @type cluster_name: str
1555 858f3d18 Iustin Pop
  @param cluster_name: the cluster name, needed for SSH hostalias
1556 858f3d18 Iustin Pop
  @rtype: None
1557 858f3d18 Iustin Pop

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

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

1599 10c2650b Iustin Pop
  @type file_name: str
1600 10c2650b Iustin Pop
  @param file_name: the target file name
1601 10c2650b Iustin Pop
  @type data: str
1602 10c2650b Iustin Pop
  @param data: the new contents of the file
1603 10c2650b Iustin Pop
  @type mode: int
1604 10c2650b Iustin Pop
  @param mode: the mode to give the file (can be None)
1605 10c2650b Iustin Pop
  @type uid: int
1606 10c2650b Iustin Pop
  @param uid: the owner of the file (can be -1 for default)
1607 10c2650b Iustin Pop
  @type gid: int
1608 10c2650b Iustin Pop
  @param gid: the group of the file (can be -1 for default)
1609 10c2650b Iustin Pop
  @type atime: float
1610 10c2650b Iustin Pop
  @param atime: the atime to set on the file (can be None)
1611 10c2650b Iustin Pop
  @type mtime: float
1612 10c2650b Iustin Pop
  @param mtime: the mtime to set on the file (can be None)
1613 c26a6bd2 Iustin Pop
  @rtype: None
1614 10c2650b Iustin Pop

1615 a8083063 Iustin Pop
  """
1616 a8083063 Iustin Pop
  if not os.path.isabs(file_name):
1617 2cc6781a Iustin Pop
    _Fail("Filename passed to UploadFile is not absolute: '%s'", file_name)
1618 a8083063 Iustin Pop
1619 360b0dc2 Iustin Pop
  if file_name not in _ALLOWED_UPLOAD_FILES:
1620 2cc6781a Iustin Pop
    _Fail("Filename passed to UploadFile not in allowed upload targets: '%s'",
1621 2cc6781a Iustin Pop
          file_name)
1622 a8083063 Iustin Pop
1623 12bce260 Michael Hanselmann
  raw_data = _Decompress(data)
1624 12bce260 Michael Hanselmann
1625 12bce260 Michael Hanselmann
  utils.WriteFile(file_name, data=raw_data, mode=mode, uid=uid, gid=gid,
1626 41a57aab Michael Hanselmann
                  atime=atime, mtime=mtime)
1627 a8083063 Iustin Pop
1628 386b57af Iustin Pop
1629 03d1dba2 Michael Hanselmann
def WriteSsconfFiles(values):
1630 89b14f05 Iustin Pop
  """Update all ssconf files.
1631 89b14f05 Iustin Pop

1632 89b14f05 Iustin Pop
  Wrapper around the SimpleStore.WriteFiles.
1633 89b14f05 Iustin Pop

1634 89b14f05 Iustin Pop
  """
1635 89b14f05 Iustin Pop
  ssconf.SimpleStore().WriteFiles(values)
1636 6ddc95ec Michael Hanselmann
1637 6ddc95ec Michael Hanselmann
1638 a8083063 Iustin Pop
def _ErrnoOrStr(err):
1639 a8083063 Iustin Pop
  """Format an EnvironmentError exception.
1640 a8083063 Iustin Pop

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

1645 10c2650b Iustin Pop
  @type err: L{EnvironmentError}
1646 10c2650b Iustin Pop
  @param err: the exception to format
1647 a8083063 Iustin Pop

1648 a8083063 Iustin Pop
  """
1649 a8083063 Iustin Pop
  if hasattr(err, 'errno'):
1650 a8083063 Iustin Pop
    detail = errno.errorcode[err.errno]
1651 a8083063 Iustin Pop
  else:
1652 a8083063 Iustin Pop
    detail = str(err)
1653 a8083063 Iustin Pop
  return detail
1654 a8083063 Iustin Pop
1655 5d0fe286 Iustin Pop
1656 c19f9810 Iustin Pop
def _OSOndiskAPIVersion(os_dir):
1657 2f8598a5 Alexander Schreiber
  """Compute and return the API version of a given OS.
1658 a8083063 Iustin Pop

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

1662 10c2650b Iustin Pop
  @type os_dir: str
1663 c19f9810 Iustin Pop
  @param os_dir: the directory in which we should look for the OS
1664 8e70b181 Iustin Pop
  @rtype: tuple
1665 8e70b181 Iustin Pop
  @return: tuple (status, data) with status denoting the validity and
1666 8e70b181 Iustin Pop
      data holding either the vaid versions or an error message
1667 a8083063 Iustin Pop

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

1699 10c2650b Iustin Pop
  @type top_dirs: list
1700 10c2650b Iustin Pop
  @param top_dirs: the list of directories in which to
1701 10c2650b Iustin Pop
      search (if not given defaults to
1702 10c2650b Iustin Pop
      L{constants.OS_SEARCH_PATH})
1703 10c2650b Iustin Pop
  @rtype: list of L{objects.OS}
1704 ba00557a Guido Trotter
  @return: a list of tuples (name, path, status, diagnose, variants)
1705 255dcebd Iustin Pop
      for all (potential) OSes under all search paths, where:
1706 255dcebd Iustin Pop
          - name is the (potential) OS name
1707 255dcebd Iustin Pop
          - path is the full path to the OS
1708 255dcebd Iustin Pop
          - status True/False is the validity of the OS
1709 255dcebd Iustin Pop
          - diagnose is the error message for an invalid OS, otherwise empty
1710 ba00557a Guido Trotter
          - variants is a list of supported OS variants, if any
1711 a8083063 Iustin Pop

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

1741 a8083063 Iustin Pop
  This function will return an OS instance if the given name is a
1742 8e70b181 Iustin Pop
  valid OS name.
1743 a8083063 Iustin Pop

1744 8ee4dc80 Guido Trotter
  @type base_dir: string
1745 8ee4dc80 Guido Trotter
  @keyword base_dir: Base directory containing OS installations.
1746 8ee4dc80 Guido Trotter
                     Defaults to a search in all the OS_SEARCH_PATH dirs.
1747 255dcebd Iustin Pop
  @rtype: tuple
1748 255dcebd Iustin Pop
  @return: success and either the OS instance if we find a valid one,
1749 255dcebd Iustin Pop
      or error message
1750 7c3d51d4 Guido Trotter

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

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

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

1824 255dcebd Iustin Pop
  @type base_dir: string
1825 255dcebd Iustin Pop
  @keyword base_dir: Base directory containing OS installations.
1826 255dcebd Iustin Pop
                     Defaults to a search in all the OS_SEARCH_PATH dirs.
1827 255dcebd Iustin Pop
  @rtype: L{objects.OS}
1828 255dcebd Iustin Pop
  @return: the OS instance if we find a valid one
1829 255dcebd Iustin Pop
  @raise RPCFail: if we don't find a valid OS
1830 255dcebd Iustin Pop

1831 255dcebd Iustin Pop
  """
1832 69b99987 Michael Hanselmann
  name_only = name.split("+", 1)[0]
1833 6ee7102a Guido Trotter
  status, payload = _TryOSFromDisk(name_only, base_dir)
1834 255dcebd Iustin Pop
1835 255dcebd Iustin Pop
  if not status:
1836 255dcebd Iustin Pop
    _Fail(payload)
1837 a8083063 Iustin Pop
1838 255dcebd Iustin Pop
  return payload
1839 a8083063 Iustin Pop
1840 a8083063 Iustin Pop
1841 099c52ad Iustin Pop
def OSEnvironment(instance, inst_os, debug=0):
1842 2266edb2 Guido Trotter
  """Calculate the environment for an os script.
1843 2266edb2 Guido Trotter

1844 10c2650b Iustin Pop
  @type instance: L{objects.Instance}
1845 2266edb2 Guido Trotter
  @param instance: target instance for the os script run
1846 099c52ad Iustin Pop
  @type inst_os: L{objects.OS}
1847 099c52ad Iustin Pop
  @param inst_os: operating system for which the environment is being built
1848 2266edb2 Guido Trotter
  @type debug: integer
1849 10c2650b Iustin Pop
  @param debug: debug level (0 or 1, for OS Api 10)
1850 2266edb2 Guido Trotter
  @rtype: dict
1851 2266edb2 Guido Trotter
  @return: dict of environment variables
1852 10c2650b Iustin Pop
  @raise errors.BlockDeviceError: if the block device
1853 10c2650b Iustin Pop
      cannot be found
1854 2266edb2 Guido Trotter

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

1910 594609c0 Iustin Pop
  This function is called recursively, with the childrens being the
1911 10c2650b Iustin Pop
  first ones to resize.
1912 594609c0 Iustin Pop

1913 10c2650b Iustin Pop
  @type disk: L{objects.Disk}
1914 10c2650b Iustin Pop
  @param disk: the disk to be grown
1915 10c2650b Iustin Pop
  @rtype: (status, result)
1916 10c2650b Iustin Pop
  @return: a tuple with the status of the operation
1917 10c2650b Iustin Pop
      (True/False), and the errors message if status
1918 10c2650b Iustin Pop
      is False
1919 594609c0 Iustin Pop

1920 594609c0 Iustin Pop
  """
1921 594609c0 Iustin Pop
  r_dev = _RecursiveFindBD(disk)
1922 594609c0 Iustin Pop
  if r_dev is None:
1923 afdc3985 Iustin Pop
    _Fail("Cannot find block device %s", disk)
1924 594609c0 Iustin Pop
1925 594609c0 Iustin Pop
  try:
1926 594609c0 Iustin Pop
    r_dev.Grow(amount)
1927 594609c0 Iustin Pop
  except errors.BlockDeviceError, err:
1928 2cc6781a Iustin Pop
    _Fail("Failed to grow block device: %s", err, exc=True)
1929 594609c0 Iustin Pop
1930 594609c0 Iustin Pop
1931 821d1bd1 Iustin Pop
def BlockdevSnapshot(disk):
1932 a8083063 Iustin Pop
  """Create a snapshot copy of a block device.
1933 a8083063 Iustin Pop

1934 a8083063 Iustin Pop
  This function is called recursively, and the snapshot is actually created
1935 a8083063 Iustin Pop
  just for the leaf lvm backend device.
1936 a8083063 Iustin Pop

1937 e9e9263d Guido Trotter
  @type disk: L{objects.Disk}
1938 e9e9263d Guido Trotter
  @param disk: the disk to be snapshotted
1939 e9e9263d Guido Trotter
  @rtype: string
1940 e9e9263d Guido Trotter
  @return: snapshot disk path
1941 a8083063 Iustin Pop

1942 098c0958 Michael Hanselmann
  """
1943 433c63aa Iustin Pop
  if disk.dev_type == constants.LD_DRBD8:
1944 433c63aa Iustin Pop
    if not disk.children:
1945 433c63aa Iustin Pop
      _Fail("DRBD device '%s' without backing storage cannot be snapshotted",
1946 433c63aa Iustin Pop
            disk.unique_id)
1947 433c63aa Iustin Pop
    return BlockdevSnapshot(disk.children[0])
1948 fe96220b Iustin Pop
  elif disk.dev_type == constants.LD_LV:
1949 a8083063 Iustin Pop
    r_dev = _RecursiveFindBD(disk)
1950 a8083063 Iustin Pop
    if r_dev is not None:
1951 433c63aa Iustin Pop
      # FIXME: choose a saner value for the snapshot size
1952 a8083063 Iustin Pop
      # let's stay on the safe side and ask for the full size, for now
1953 c26a6bd2 Iustin Pop
      return r_dev.Snapshot(disk.size)
1954 a8083063 Iustin Pop
    else:
1955 87812fd3 Iustin Pop
      _Fail("Cannot find block device %s", disk)
1956 a8083063 Iustin Pop
  else:
1957 87812fd3 Iustin Pop
    _Fail("Cannot snapshot non-lvm block device '%s' of type '%s'",
1958 87812fd3 Iustin Pop
          disk.unique_id, disk.dev_type)
1959 a8083063 Iustin Pop
1960 a8083063 Iustin Pop
1961 4a0e011f Iustin Pop
def ExportSnapshot(disk, dest_node, instance, cluster_name, idx, debug):
1962 a8083063 Iustin Pop
  """Export a block device snapshot to a remote node.
1963 a8083063 Iustin Pop

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

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

2028 10c2650b Iustin Pop
  @type instance: L{objects.Instance}
2029 10c2650b Iustin Pop
  @param instance: the instance which we export, used for
2030 10c2650b Iustin Pop
      saving configuration
2031 10c2650b Iustin Pop
  @type snap_disks: list of L{objects.Disk}
2032 10c2650b Iustin Pop
  @param snap_disks: list of snapshot block devices, which
2033 10c2650b Iustin Pop
      will be used to get the actual name of the dump file
2034 a8083063 Iustin Pop

2035 c26a6bd2 Iustin Pop
  @rtype: None
2036 a8083063 Iustin Pop

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

2091 10c2650b Iustin Pop
  @type dest: str
2092 10c2650b Iustin Pop
  @param dest: directory containing the export
2093 a8083063 Iustin Pop

2094 10c2650b Iustin Pop
  @rtype: L{objects.SerializableConfigParser}
2095 10c2650b Iustin Pop
  @return: a serializable config file containing the
2096 10c2650b Iustin Pop
      export info
2097 a8083063 Iustin Pop

2098 a8083063 Iustin Pop
  """
2099 c4feafe8 Iustin Pop
  cff = utils.PathJoin(dest, constants.EXPORT_CONF_FILE)
2100 a8083063 Iustin Pop
2101 a8083063 Iustin Pop
  config = objects.SerializableConfigParser()
2102 a8083063 Iustin Pop
  config.read(cff)
2103 a8083063 Iustin Pop
2104 a8083063 Iustin Pop
  if (not config.has_section(constants.INISECT_EXP) or
2105 a8083063 Iustin Pop
      not config.has_section(constants.INISECT_INS)):
2106 3eccac06 Iustin Pop
    _Fail("Export info file doesn't have the required fields")
2107 a8083063 Iustin Pop
2108 c26a6bd2 Iustin Pop
  return config.Dumps()
2109 a8083063 Iustin Pop
2110 a8083063 Iustin Pop
2111 4a0e011f Iustin Pop
def ImportOSIntoInstance(instance, src_node, src_images, cluster_name, debug):
2112 a8083063 Iustin Pop
  """Import an os image into an instance.
2113 a8083063 Iustin Pop

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

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

2164 10c2650b Iustin Pop
  @rtype: list
2165 10c2650b Iustin Pop
  @return: list of the exports
2166 10c2650b Iustin Pop

2167 a8083063 Iustin Pop
  """
2168 a8083063 Iustin Pop
  if os.path.isdir(constants.EXPORT_DIR):
2169 c26a6bd2 Iustin Pop
    return utils.ListVisibleFiles(constants.EXPORT_DIR)
2170 a8083063 Iustin Pop
  else:
2171 afdc3985 Iustin Pop
    _Fail("No exports directory")
2172 a8083063 Iustin Pop
2173 a8083063 Iustin Pop
2174 a8083063 Iustin Pop
def RemoveExport(export):
2175 a8083063 Iustin Pop
  """Remove an existing export from the node.
2176 a8083063 Iustin Pop

2177 10c2650b Iustin Pop
  @type export: str
2178 10c2650b Iustin Pop
  @param export: the name of the export to remove
2179 c26a6bd2 Iustin Pop
  @rtype: None
2180 a8083063 Iustin Pop

2181 098c0958 Michael Hanselmann
  """
2182 c4feafe8 Iustin Pop
  target = utils.PathJoin(constants.EXPORT_DIR, export)
2183 a8083063 Iustin Pop
2184 35fbcd11 Iustin Pop
  try:
2185 35fbcd11 Iustin Pop
    shutil.rmtree(target)
2186 35fbcd11 Iustin Pop
  except EnvironmentError, err:
2187 35fbcd11 Iustin Pop
    _Fail("Error while removing the export: %s", err, exc=True)
2188 a8083063 Iustin Pop
2189 a8083063 Iustin Pop
2190 821d1bd1 Iustin Pop
def BlockdevRename(devlist):
2191 f3e513ad Iustin Pop
  """Rename a list of block devices.
2192 f3e513ad Iustin Pop

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

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

2234 778b75bb Manuel Franceschini
  Checks wheter the given file_storage_dir is within the cluster-wide
2235 778b75bb Manuel Franceschini
  default file_storage_dir stored in SimpleStore. Only paths under that
2236 778b75bb Manuel Franceschini
  directory are allowed.
2237 778b75bb Manuel Franceschini

2238 b1206984 Iustin Pop
  @type file_storage_dir: str
2239 b1206984 Iustin Pop
  @param file_storage_dir: the path to check
2240 d61cbe76 Iustin Pop

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

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

2257 b1206984 Iustin Pop
  @type file_storage_dir: str
2258 b1206984 Iustin Pop
  @param file_storage_dir: directory to create
2259 778b75bb Manuel Franceschini

2260 b1206984 Iustin Pop
  @rtype: tuple
2261 b1206984 Iustin Pop
  @return: tuple with first element a boolean indicating wheter dir
2262 b1206984 Iustin Pop
      creation was successful or not
2263 778b75bb Manuel Franceschini

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

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

2283 10c2650b Iustin Pop
  @type file_storage_dir: str
2284 10c2650b Iustin Pop
  @param file_storage_dir: the directory we should cleanup
2285 10c2650b Iustin Pop
  @rtype: tuple (success,)
2286 10c2650b Iustin Pop
  @return: tuple of one element, C{success}, denoting
2287 5bbd3f7f Michael Hanselmann
      whether the operation was successful
2288 778b75bb Manuel Franceschini

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

2306 10c2650b Iustin Pop
  @type old_file_storage_dir: str
2307 10c2650b Iustin Pop
  @param old_file_storage_dir: the current path
2308 10c2650b Iustin Pop
  @type new_file_storage_dir: str
2309 10c2650b Iustin Pop
  @param new_file_storage_dir: the name we should rename to
2310 10c2650b Iustin Pop
  @rtype: tuple (success,)
2311 10c2650b Iustin Pop
  @return: tuple of one element, C{success}, denoting
2312 10c2650b Iustin Pop
      whether the operation was successful
2313 778b75bb Manuel Franceschini

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

2336 10c2650b Iustin Pop
  @type file_name: str
2337 10c2650b Iustin Pop
  @param file_name: the file name we should check
2338 c8457ce7 Iustin Pop
  @rtype: None
2339 c8457ce7 Iustin Pop
  @raises RPCFail: if the file is not valid
2340 10c2650b Iustin Pop

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

2353 10c2650b Iustin Pop
  This is just a wrapper over L{utils.WriteFile}, with proper
2354 10c2650b Iustin Pop
  checking.
2355 10c2650b Iustin Pop

2356 10c2650b Iustin Pop
  @type file_name: str
2357 10c2650b Iustin Pop
  @param file_name: the job file name
2358 10c2650b Iustin Pop
  @type content: str
2359 10c2650b Iustin Pop
  @param content: the new job contents
2360 10c2650b Iustin Pop
  @rtype: boolean
2361 10c2650b Iustin Pop
  @return: the success of the operation
2362 10c2650b Iustin Pop

2363 dc31eae3 Michael Hanselmann
  """
2364 c8457ce7 Iustin Pop
  _EnsureJobQueueFile(file_name)
2365 ca52cdeb Michael Hanselmann
2366 ca52cdeb Michael Hanselmann
  # Write and replace the file atomically
2367 12bce260 Michael Hanselmann
  utils.WriteFile(file_name, data=_Decompress(content))
2368 ca52cdeb Michael Hanselmann
2369 ca52cdeb Michael Hanselmann
2370 af5ebcb1 Michael Hanselmann
def JobQueueRename(old, new):
2371 af5ebcb1 Michael Hanselmann
  """Renames a job queue file.
2372 af5ebcb1 Michael Hanselmann

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

2375 10c2650b Iustin Pop
  @type old: str
2376 10c2650b Iustin Pop
  @param old: the old (actual) file name
2377 10c2650b Iustin Pop
  @type new: str
2378 10c2650b Iustin Pop
  @param new: the desired file name
2379 c8457ce7 Iustin Pop
  @rtype: tuple
2380 c8457ce7 Iustin Pop
  @return: the success of the operation and payload
2381 10c2650b Iustin Pop

2382 af5ebcb1 Michael Hanselmann
  """
2383 c8457ce7 Iustin Pop
  _EnsureJobQueueFile(old)
2384 c8457ce7 Iustin Pop
  _EnsureJobQueueFile(new)
2385 af5ebcb1 Michael Hanselmann
2386 58b22b6e Michael Hanselmann
  utils.RenameFile(old, new, mkdir=True)
2387 af5ebcb1 Michael Hanselmann
2388 af5ebcb1 Michael Hanselmann
2389 5d672980 Iustin Pop
def JobQueueSetDrainFlag(drain_flag):
2390 5d672980 Iustin Pop
  """Set the drain flag for the queue.
2391 5d672980 Iustin Pop

2392 5d672980 Iustin Pop
  This will set or unset the queue drain flag.
2393 5d672980 Iustin Pop

2394 10c2650b Iustin Pop
  @type drain_flag: boolean
2395 5d672980 Iustin Pop
  @param drain_flag: if True, will set the drain flag, otherwise reset it.
2396 c8457ce7 Iustin Pop
  @rtype: truple
2397 c8457ce7 Iustin Pop
  @return: always True, None
2398 10c2650b Iustin Pop
  @warning: the function always returns True
2399 5d672980 Iustin Pop

2400 5d672980 Iustin Pop
  """
2401 5d672980 Iustin Pop
  if drain_flag:
2402 5d672980 Iustin Pop
    utils.WriteFile(constants.JOB_QUEUE_DRAIN_FILE, data="", close=True)
2403 5d672980 Iustin Pop
  else:
2404 5d672980 Iustin Pop
    utils.RemoveFile(constants.JOB_QUEUE_DRAIN_FILE)
2405 5d672980 Iustin Pop
2406 5d672980 Iustin Pop
2407 821d1bd1 Iustin Pop
def BlockdevClose(instance_name, disks):
2408 d61cbe76 Iustin Pop
  """Closes the given block devices.
2409 d61cbe76 Iustin Pop

2410 10c2650b Iustin Pop
  This means they will be switched to secondary mode (in case of
2411 10c2650b Iustin Pop
  DRBD).
2412 10c2650b Iustin Pop

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

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

2447 6217e295 Iustin Pop
  @type hvname: string
2448 6217e295 Iustin Pop
  @param hvname: the hypervisor name
2449 6217e295 Iustin Pop
  @type hvparams: dict
2450 6217e295 Iustin Pop
  @param hvparams: the hypervisor parameters to be validated
2451 c26a6bd2 Iustin Pop
  @rtype: None
2452 6217e295 Iustin Pop

2453 6217e295 Iustin Pop
  """
2454 6217e295 Iustin Pop
  try:
2455 6217e295 Iustin Pop
    hv_type = hypervisor.GetHypervisor(hvname)
2456 6217e295 Iustin Pop
    hv_type.ValidateParameters(hvparams)
2457 6217e295 Iustin Pop
  except errors.HypervisorError, err:
2458 afdc3985 Iustin Pop
    _Fail(str(err), log=False)
2459 6217e295 Iustin Pop
2460 6217e295 Iustin Pop
2461 56aa9fd5 Iustin Pop
def DemoteFromMC():
2462 56aa9fd5 Iustin Pop
  """Demotes the current node from master candidate role.
2463 56aa9fd5 Iustin Pop

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

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

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

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

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

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

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

2632 10c2650b Iustin Pop
  This class is instantiated on the node side (ganeti-noded) and not
2633 10c2650b Iustin Pop
  on the master side.
2634 a8083063 Iustin Pop

2635 a8083063 Iustin Pop
  """
2636 a8083063 Iustin Pop
  def __init__(self, hooks_base_dir=None):
2637 a8083063 Iustin Pop
    """Constructor for hooks runner.
2638 a8083063 Iustin Pop

2639 10c2650b Iustin Pop
    @type hooks_base_dir: str or None
2640 10c2650b Iustin Pop
    @param hooks_base_dir: if not None, this overrides the
2641 10c2650b Iustin Pop
        L{constants.HOOKS_BASE_DIR} (useful for unittests)
2642 a8083063 Iustin Pop

2643 a8083063 Iustin Pop
    """
2644 a8083063 Iustin Pop
    if hooks_base_dir is None:
2645 a8083063 Iustin Pop
      hooks_base_dir = constants.HOOKS_BASE_DIR
2646 fe267188 Iustin Pop
    # yeah, _BASE_DIR is not valid for attributes, we use it like a
2647 fe267188 Iustin Pop
    # constant
2648 fe267188 Iustin Pop
    self._BASE_DIR = hooks_base_dir # pylint: disable-msg=C0103
2649 a8083063 Iustin Pop
2650 a8083063 Iustin Pop
  def RunHooks(self, hpath, phase, env):
2651 a8083063 Iustin Pop
    """Run the scripts in the hooks directory.
2652 a8083063 Iustin Pop

2653 10c2650b Iustin Pop
    @type hpath: str
2654 10c2650b Iustin Pop
    @param hpath: the path to the hooks directory which
2655 10c2650b Iustin Pop
        holds the scripts
2656 10c2650b Iustin Pop
    @type phase: str
2657 10c2650b Iustin Pop
    @param phase: either L{constants.HOOKS_PHASE_PRE} or
2658 10c2650b Iustin Pop
        L{constants.HOOKS_PHASE_POST}
2659 10c2650b Iustin Pop
    @type env: dict
2660 10c2650b Iustin Pop
    @param env: dictionary with the environment for the hook
2661 10c2650b Iustin Pop
    @rtype: list
2662 10c2650b Iustin Pop
    @return: list of 3-element tuples:
2663 10c2650b Iustin Pop
      - script path
2664 10c2650b Iustin Pop
      - script result, either L{constants.HKR_SUCCESS} or
2665 10c2650b Iustin Pop
        L{constants.HKR_FAIL}
2666 10c2650b Iustin Pop
      - output of the script
2667 10c2650b Iustin Pop

2668 10c2650b Iustin Pop
    @raise errors.ProgrammerError: for invalid input
2669 10c2650b Iustin Pop
        parameters
2670 a8083063 Iustin Pop

2671 a8083063 Iustin Pop
    """
2672 a8083063 Iustin Pop
    if phase == constants.HOOKS_PHASE_PRE:
2673 a8083063 Iustin Pop
      suffix = "pre"
2674 a8083063 Iustin Pop
    elif phase == constants.HOOKS_PHASE_POST:
2675 a8083063 Iustin Pop
      suffix = "post"
2676 a8083063 Iustin Pop
    else:
2677 3fb4f740 Iustin Pop
      _Fail("Unknown hooks phase '%s'", phase)
2678 3fb4f740 Iustin Pop
2679 a8083063 Iustin Pop
2680 a8083063 Iustin Pop
    subdir = "%s-%s.d" % (hpath, suffix)
2681 a8083063 Iustin Pop
    dir_name = "%s/%s" % (self._BASE_DIR, subdir)
2682 6bb65e3a Guido Trotter
    runparts_results = utils.RunParts(dir_name, env=env, reset_env=True)
2683 6bb65e3a Guido Trotter
2684 6bb65e3a Guido Trotter
    results = []
2685 6bb65e3a Guido Trotter
    for (relname, relstatus, runresult)  in runparts_results:
2686 6bb65e3a Guido Trotter
      if relstatus == constants.RUNPARTS_SKIP:
2687 a8083063 Iustin Pop
        rrval = constants.HKR_SKIP
2688 a8083063 Iustin Pop
        output = ""
2689 6bb65e3a Guido Trotter
      elif relstatus == constants.RUNPARTS_ERR:
2690 6bb65e3a Guido Trotter
        rrval = constants.HKR_FAIL
2691 6bb65e3a Guido Trotter
        output = "Hook script execution error: %s" % runresult
2692 6bb65e3a Guido Trotter
      elif relstatus == constants.RUNPARTS_RUN:
2693 6bb65e3a Guido Trotter
        if runresult.failed:
2694 a8083063 Iustin Pop
          rrval = constants.HKR_FAIL
2695 a8083063 Iustin Pop
        else:
2696 6bb65e3a Guido Trotter
          rrval = constants.HKR_SUCCESS
2697 6bb65e3a Guido Trotter
        output = utils.SafeEncode(runresult.output.strip())
2698 6bb65e3a Guido Trotter
      results.append(("%s/%s" % (subdir, relname), rrval, output))
2699 6bb65e3a Guido Trotter
2700 6bb65e3a Guido Trotter
    return results
2701 3f78eef2 Iustin Pop
2702 3f78eef2 Iustin Pop
2703 8d528b7c Iustin Pop
class IAllocatorRunner(object):
2704 8d528b7c Iustin Pop
  """IAllocator runner.
2705 8d528b7c Iustin Pop

2706 8d528b7c Iustin Pop
  This class is instantiated on the node side (ganeti-noded) and not on
2707 8d528b7c Iustin Pop
  the master side.
2708 8d528b7c Iustin Pop

2709 8d528b7c Iustin Pop
  """
2710 7e950d31 Iustin Pop
  @staticmethod
2711 7e950d31 Iustin Pop
  def Run(name, idata):
2712 8d528b7c Iustin Pop
    """Run an iallocator script.
2713 8d528b7c Iustin Pop

2714 10c2650b Iustin Pop
    @type name: str
2715 10c2650b Iustin Pop
    @param name: the iallocator script name
2716 10c2650b Iustin Pop
    @type idata: str
2717 10c2650b Iustin Pop
    @param idata: the allocator input data
2718 10c2650b Iustin Pop

2719 10c2650b Iustin Pop
    @rtype: tuple
2720 87f5c298 Iustin Pop
    @return: two element tuple of:
2721 87f5c298 Iustin Pop
       - status
2722 87f5c298 Iustin Pop
       - either error message or stdout of allocator (for success)
2723 8d528b7c Iustin Pop

2724 8d528b7c Iustin Pop
    """
2725 8d528b7c Iustin Pop
    alloc_script = utils.FindFile(name, constants.IALLOCATOR_SEARCH_PATH,
2726 8d528b7c Iustin Pop
                                  os.path.isfile)
2727 8d528b7c Iustin Pop
    if alloc_script is None:
2728 87f5c298 Iustin Pop
      _Fail("iallocator module '%s' not found in the search path", name)
2729 8d528b7c Iustin Pop
2730 8d528b7c Iustin Pop
    fd, fin_name = tempfile.mkstemp(prefix="ganeti-iallocator.")
2731 8d528b7c Iustin Pop
    try:
2732 8d528b7c Iustin Pop
      os.write(fd, idata)
2733 8d528b7c Iustin Pop
      os.close(fd)
2734 8d528b7c Iustin Pop
      result = utils.RunCmd([alloc_script, fin_name])
2735 8d528b7c Iustin Pop
      if result.failed:
2736 87f5c298 Iustin Pop
        _Fail("iallocator module '%s' failed: %s, output '%s'",
2737 87f5c298 Iustin Pop
              name, result.fail_reason, result.output)
2738 8d528b7c Iustin Pop
    finally:
2739 8d528b7c Iustin Pop
      os.unlink(fin_name)
2740 8d528b7c Iustin Pop
2741 c26a6bd2 Iustin Pop
    return result.stdout
2742 8d528b7c Iustin Pop
2743 8d528b7c Iustin Pop
2744 3f78eef2 Iustin Pop
class DevCacheManager(object):
2745 c99a3cc0 Manuel Franceschini
  """Simple class for managing a cache of block device information.
2746 3f78eef2 Iustin Pop

2747 3f78eef2 Iustin Pop
  """
2748 3f78eef2 Iustin Pop
  _DEV_PREFIX = "/dev/"
2749 3f78eef2 Iustin Pop
  _ROOT_DIR = constants.BDEV_CACHE_DIR
2750 3f78eef2 Iustin Pop
2751 3f78eef2 Iustin Pop
  @classmethod
2752 3f78eef2 Iustin Pop
  def _ConvertPath(cls, dev_path):
2753 3f78eef2 Iustin Pop
    """Converts a /dev/name path to the cache file name.
2754 3f78eef2 Iustin Pop

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

2758 10c2650b Iustin Pop
    @type dev_path: str
2759 10c2650b Iustin Pop
    @param dev_path: the C{/dev/} path name
2760 10c2650b Iustin Pop
    @rtype: str
2761 10c2650b Iustin Pop
    @return: the converted path name
2762 3f78eef2 Iustin Pop

2763 3f78eef2 Iustin Pop
    """
2764 3f78eef2 Iustin Pop
    if dev_path.startswith(cls._DEV_PREFIX):
2765 3f78eef2 Iustin Pop
      dev_path = dev_path[len(cls._DEV_PREFIX):]
2766 3f78eef2 Iustin Pop
    dev_path = dev_path.replace("/", "_")
2767 3f78eef2 Iustin Pop
    fpath = "%s/bdev_%s" % (cls._ROOT_DIR, dev_path)
2768 3f78eef2 Iustin Pop
    return fpath
2769 3f78eef2 Iustin Pop
2770 3f78eef2 Iustin Pop
  @classmethod
2771 3f78eef2 Iustin Pop
  def UpdateCache(cls, dev_path, owner, on_primary, iv_name):
2772 3f78eef2 Iustin Pop
    """Updates the cache information for a given device.
2773 3f78eef2 Iustin Pop

2774 10c2650b Iustin Pop
    @type dev_path: str
2775 10c2650b Iustin Pop
    @param dev_path: the pathname of the device
2776 10c2650b Iustin Pop
    @type owner: str
2777 10c2650b Iustin Pop
    @param owner: the owner (instance name) of the device
2778 10c2650b Iustin Pop
    @type on_primary: bool
2779 10c2650b Iustin Pop
    @param on_primary: whether this is the primary
2780 10c2650b Iustin Pop
        node nor not
2781 10c2650b Iustin Pop
    @type iv_name: str
2782 10c2650b Iustin Pop
    @param iv_name: the instance-visible name of the
2783 c41eea6e Iustin Pop
        device, as in objects.Disk.iv_name
2784 10c2650b Iustin Pop

2785 10c2650b Iustin Pop
    @rtype: None
2786 10c2650b Iustin Pop

2787 3f78eef2 Iustin Pop
    """
2788 cf5a8306 Iustin Pop
    if dev_path is None:
2789 18682bca Iustin Pop
      logging.error("DevCacheManager.UpdateCache got a None dev_path")
2790 cf5a8306 Iustin Pop
      return
2791 3f78eef2 Iustin Pop
    fpath = cls._ConvertPath(dev_path)
2792 3f78eef2 Iustin Pop
    if on_primary:
2793 3f78eef2 Iustin Pop
      state = "primary"
2794 3f78eef2 Iustin Pop
    else:
2795 3f78eef2 Iustin Pop
      state = "secondary"
2796 3f78eef2 Iustin Pop
    if iv_name is None:
2797 3f78eef2 Iustin Pop
      iv_name = "not_visible"
2798 3f78eef2 Iustin Pop
    fdata = "%s %s %s\n" % (str(owner), state, iv_name)
2799 3f78eef2 Iustin Pop
    try:
2800 3f78eef2 Iustin Pop
      utils.WriteFile(fpath, data=fdata)
2801 3f78eef2 Iustin Pop
    except EnvironmentError, err:
2802 29921401 Iustin Pop
      logging.exception("Can't update bdev cache for %s: %s", dev_path, err)
2803 3f78eef2 Iustin Pop
2804 3f78eef2 Iustin Pop
  @classmethod
2805 3f78eef2 Iustin Pop
  def RemoveCache(cls, dev_path):
2806 3f78eef2 Iustin Pop
    """Remove data for a dev_path.
2807 3f78eef2 Iustin Pop

2808 10c2650b Iustin Pop
    This is just a wrapper over L{utils.RemoveFile} with a converted
2809 10c2650b Iustin Pop
    path name and logging.
2810 10c2650b Iustin Pop

2811 10c2650b Iustin Pop
    @type dev_path: str
2812 10c2650b Iustin Pop
    @param dev_path: the pathname of the device
2813 10c2650b Iustin Pop

2814 10c2650b Iustin Pop
    @rtype: None
2815 10c2650b Iustin Pop

2816 3f78eef2 Iustin Pop
    """
2817 cf5a8306 Iustin Pop
    if dev_path is None:
2818 18682bca Iustin Pop
      logging.error("DevCacheManager.RemoveCache got a None dev_path")
2819 cf5a8306 Iustin Pop
      return
2820 3f78eef2 Iustin Pop
    fpath = cls._ConvertPath(dev_path)
2821 3f78eef2 Iustin Pop
    try:
2822 3f78eef2 Iustin Pop
      utils.RemoveFile(fpath)
2823 3f78eef2 Iustin Pop
    except EnvironmentError, err:
2824 29921401 Iustin Pop
      logging.exception("Can't update bdev cache for %s: %s", dev_path, err)