Statistics
| Branch: | Tag: | Revision:

root / lib / backend.py @ 553bd93f

History | View | Annotate | Download (85.6 kB)

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

909 9332fd8a Iustin Pop

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

2024 c26a6bd2 Iustin Pop
  @rtype: None
2025 a8083063 Iustin Pop

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

2080 10c2650b Iustin Pop
  @type dest: str
2081 10c2650b Iustin Pop
  @param dest: directory containing the export
2082 a8083063 Iustin Pop

2083 10c2650b Iustin Pop
  @rtype: L{objects.SerializableConfigParser}
2084 10c2650b Iustin Pop
  @return: a serializable config file containing the
2085 10c2650b Iustin Pop
      export info
2086 a8083063 Iustin Pop

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

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

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

2153 10c2650b Iustin Pop
  @rtype: list
2154 10c2650b Iustin Pop
  @return: list of the exports
2155 10c2650b Iustin Pop

2156 a8083063 Iustin Pop
  """
2157 a8083063 Iustin Pop
  if os.path.isdir(constants.EXPORT_DIR):
2158 c26a6bd2 Iustin Pop
    return utils.ListVisibleFiles(constants.EXPORT_DIR)
2159 a8083063 Iustin Pop
  else:
2160 afdc3985 Iustin Pop
    _Fail("No exports directory")
2161 a8083063 Iustin Pop
2162 a8083063 Iustin Pop
2163 a8083063 Iustin Pop
def RemoveExport(export):
2164 a8083063 Iustin Pop
  """Remove an existing export from the node.
2165 a8083063 Iustin Pop

2166 10c2650b Iustin Pop
  @type export: str
2167 10c2650b Iustin Pop
  @param export: the name of the export to remove
2168 c26a6bd2 Iustin Pop
  @rtype: None
2169 a8083063 Iustin Pop

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

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

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

2223 778b75bb Manuel Franceschini
  Checks wheter the given file_storage_dir is within the cluster-wide
2224 778b75bb Manuel Franceschini
  default file_storage_dir stored in SimpleStore. Only paths under that
2225 778b75bb Manuel Franceschini
  directory are allowed.
2226 778b75bb Manuel Franceschini

2227 b1206984 Iustin Pop
  @type file_storage_dir: str
2228 b1206984 Iustin Pop
  @param file_storage_dir: the path to check
2229 d61cbe76 Iustin Pop

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

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

2246 b1206984 Iustin Pop
  @type file_storage_dir: str
2247 b1206984 Iustin Pop
  @param file_storage_dir: directory to create
2248 778b75bb Manuel Franceschini

2249 b1206984 Iustin Pop
  @rtype: tuple
2250 b1206984 Iustin Pop
  @return: tuple with first element a boolean indicating wheter dir
2251 b1206984 Iustin Pop
      creation was successful or not
2252 778b75bb Manuel Franceschini

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

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

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

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

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

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

2325 10c2650b Iustin Pop
  @type file_name: str
2326 10c2650b Iustin Pop
  @param file_name: the file name we should check
2327 c8457ce7 Iustin Pop
  @rtype: None
2328 c8457ce7 Iustin Pop
  @raises RPCFail: if the file is not valid
2329 10c2650b Iustin Pop

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

2342 10c2650b Iustin Pop
  This is just a wrapper over L{utils.WriteFile}, with proper
2343 10c2650b Iustin Pop
  checking.
2344 10c2650b Iustin Pop

2345 10c2650b Iustin Pop
  @type file_name: str
2346 10c2650b Iustin Pop
  @param file_name: the job file name
2347 10c2650b Iustin Pop
  @type content: str
2348 10c2650b Iustin Pop
  @param content: the new job contents
2349 10c2650b Iustin Pop
  @rtype: boolean
2350 10c2650b Iustin Pop
  @return: the success of the operation
2351 10c2650b Iustin Pop

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

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

2364 10c2650b Iustin Pop
  @type old: str
2365 10c2650b Iustin Pop
  @param old: the old (actual) file name
2366 10c2650b Iustin Pop
  @type new: str
2367 10c2650b Iustin Pop
  @param new: the desired file name
2368 c8457ce7 Iustin Pop
  @rtype: tuple
2369 c8457ce7 Iustin Pop
  @return: the success of the operation and payload
2370 10c2650b Iustin Pop

2371 af5ebcb1 Michael Hanselmann
  """
2372 c8457ce7 Iustin Pop
  _EnsureJobQueueFile(old)
2373 c8457ce7 Iustin Pop
  _EnsureJobQueueFile(new)
2374 af5ebcb1 Michael Hanselmann
2375 58b22b6e Michael Hanselmann
  utils.RenameFile(old, new, mkdir=True)
2376 af5ebcb1 Michael Hanselmann
2377 af5ebcb1 Michael Hanselmann
2378 5d672980 Iustin Pop
def JobQueueSetDrainFlag(drain_flag):
2379 5d672980 Iustin Pop
  """Set the drain flag for the queue.
2380 5d672980 Iustin Pop

2381 5d672980 Iustin Pop
  This will set or unset the queue drain flag.
2382 5d672980 Iustin Pop

2383 10c2650b Iustin Pop
  @type drain_flag: boolean
2384 5d672980 Iustin Pop
  @param drain_flag: if True, will set the drain flag, otherwise reset it.
2385 c8457ce7 Iustin Pop
  @rtype: truple
2386 c8457ce7 Iustin Pop
  @return: always True, None
2387 10c2650b Iustin Pop
  @warning: the function always returns True
2388 5d672980 Iustin Pop

2389 5d672980 Iustin Pop
  """
2390 5d672980 Iustin Pop
  if drain_flag:
2391 5d672980 Iustin Pop
    utils.WriteFile(constants.JOB_QUEUE_DRAIN_FILE, data="", close=True)
2392 5d672980 Iustin Pop
  else:
2393 5d672980 Iustin Pop
    utils.RemoveFile(constants.JOB_QUEUE_DRAIN_FILE)
2394 5d672980 Iustin Pop
2395 5d672980 Iustin Pop
2396 821d1bd1 Iustin Pop
def BlockdevClose(instance_name, disks):
2397 d61cbe76 Iustin Pop
  """Closes the given block devices.
2398 d61cbe76 Iustin Pop

2399 10c2650b Iustin Pop
  This means they will be switched to secondary mode (in case of
2400 10c2650b Iustin Pop
  DRBD).
2401 10c2650b Iustin Pop

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

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

2436 6217e295 Iustin Pop
  @type hvname: string
2437 6217e295 Iustin Pop
  @param hvname: the hypervisor name
2438 6217e295 Iustin Pop
  @type hvparams: dict
2439 6217e295 Iustin Pop
  @param hvparams: the hypervisor parameters to be validated
2440 c26a6bd2 Iustin Pop
  @rtype: None
2441 6217e295 Iustin Pop

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

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

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

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

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

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

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

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

2621 10c2650b Iustin Pop
  This class is instantiated on the node side (ganeti-noded) and not
2622 10c2650b Iustin Pop
  on the master side.
2623 a8083063 Iustin Pop

2624 a8083063 Iustin Pop
  """
2625 a8083063 Iustin Pop
  def __init__(self, hooks_base_dir=None):
2626 a8083063 Iustin Pop
    """Constructor for hooks runner.
2627 a8083063 Iustin Pop

2628 10c2650b Iustin Pop
    @type hooks_base_dir: str or None
2629 10c2650b Iustin Pop
    @param hooks_base_dir: if not None, this overrides the
2630 10c2650b Iustin Pop
        L{constants.HOOKS_BASE_DIR} (useful for unittests)
2631 a8083063 Iustin Pop

2632 a8083063 Iustin Pop
    """
2633 a8083063 Iustin Pop
    if hooks_base_dir is None:
2634 a8083063 Iustin Pop
      hooks_base_dir = constants.HOOKS_BASE_DIR
2635 fe267188 Iustin Pop
    # yeah, _BASE_DIR is not valid for attributes, we use it like a
2636 fe267188 Iustin Pop
    # constant
2637 fe267188 Iustin Pop
    self._BASE_DIR = hooks_base_dir # pylint: disable-msg=C0103
2638 a8083063 Iustin Pop
2639 a8083063 Iustin Pop
  def RunHooks(self, hpath, phase, env):
2640 a8083063 Iustin Pop
    """Run the scripts in the hooks directory.
2641 a8083063 Iustin Pop

2642 10c2650b Iustin Pop
    @type hpath: str
2643 10c2650b Iustin Pop
    @param hpath: the path to the hooks directory which
2644 10c2650b Iustin Pop
        holds the scripts
2645 10c2650b Iustin Pop
    @type phase: str
2646 10c2650b Iustin Pop
    @param phase: either L{constants.HOOKS_PHASE_PRE} or
2647 10c2650b Iustin Pop
        L{constants.HOOKS_PHASE_POST}
2648 10c2650b Iustin Pop
    @type env: dict
2649 10c2650b Iustin Pop
    @param env: dictionary with the environment for the hook
2650 10c2650b Iustin Pop
    @rtype: list
2651 10c2650b Iustin Pop
    @return: list of 3-element tuples:
2652 10c2650b Iustin Pop
      - script path
2653 10c2650b Iustin Pop
      - script result, either L{constants.HKR_SUCCESS} or
2654 10c2650b Iustin Pop
        L{constants.HKR_FAIL}
2655 10c2650b Iustin Pop
      - output of the script
2656 10c2650b Iustin Pop

2657 10c2650b Iustin Pop
    @raise errors.ProgrammerError: for invalid input
2658 10c2650b Iustin Pop
        parameters
2659 a8083063 Iustin Pop

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

2695 8d528b7c Iustin Pop
  This class is instantiated on the node side (ganeti-noded) and not on
2696 8d528b7c Iustin Pop
  the master side.
2697 8d528b7c Iustin Pop

2698 8d528b7c Iustin Pop
  """
2699 7e950d31 Iustin Pop
  @staticmethod
2700 7e950d31 Iustin Pop
  def Run(name, idata):
2701 8d528b7c Iustin Pop
    """Run an iallocator script.
2702 8d528b7c Iustin Pop

2703 10c2650b Iustin Pop
    @type name: str
2704 10c2650b Iustin Pop
    @param name: the iallocator script name
2705 10c2650b Iustin Pop
    @type idata: str
2706 10c2650b Iustin Pop
    @param idata: the allocator input data
2707 10c2650b Iustin Pop

2708 10c2650b Iustin Pop
    @rtype: tuple
2709 87f5c298 Iustin Pop
    @return: two element tuple of:
2710 87f5c298 Iustin Pop
       - status
2711 87f5c298 Iustin Pop
       - either error message or stdout of allocator (for success)
2712 8d528b7c Iustin Pop

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

2736 3f78eef2 Iustin Pop
  """
2737 3f78eef2 Iustin Pop
  _DEV_PREFIX = "/dev/"
2738 3f78eef2 Iustin Pop
  _ROOT_DIR = constants.BDEV_CACHE_DIR
2739 3f78eef2 Iustin Pop
2740 3f78eef2 Iustin Pop
  @classmethod
2741 3f78eef2 Iustin Pop
  def _ConvertPath(cls, dev_path):
2742 3f78eef2 Iustin Pop
    """Converts a /dev/name path to the cache file name.
2743 3f78eef2 Iustin Pop

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

2747 10c2650b Iustin Pop
    @type dev_path: str
2748 10c2650b Iustin Pop
    @param dev_path: the C{/dev/} path name
2749 10c2650b Iustin Pop
    @rtype: str
2750 10c2650b Iustin Pop
    @return: the converted path name
2751 3f78eef2 Iustin Pop

2752 3f78eef2 Iustin Pop
    """
2753 3f78eef2 Iustin Pop
    if dev_path.startswith(cls._DEV_PREFIX):
2754 3f78eef2 Iustin Pop
      dev_path = dev_path[len(cls._DEV_PREFIX):]
2755 3f78eef2 Iustin Pop
    dev_path = dev_path.replace("/", "_")
2756 3f78eef2 Iustin Pop
    fpath = "%s/bdev_%s" % (cls._ROOT_DIR, dev_path)
2757 3f78eef2 Iustin Pop
    return fpath
2758 3f78eef2 Iustin Pop
2759 3f78eef2 Iustin Pop
  @classmethod
2760 3f78eef2 Iustin Pop
  def UpdateCache(cls, dev_path, owner, on_primary, iv_name):
2761 3f78eef2 Iustin Pop
    """Updates the cache information for a given device.
2762 3f78eef2 Iustin Pop

2763 10c2650b Iustin Pop
    @type dev_path: str
2764 10c2650b Iustin Pop
    @param dev_path: the pathname of the device
2765 10c2650b Iustin Pop
    @type owner: str
2766 10c2650b Iustin Pop
    @param owner: the owner (instance name) of the device
2767 10c2650b Iustin Pop
    @type on_primary: bool
2768 10c2650b Iustin Pop
    @param on_primary: whether this is the primary
2769 10c2650b Iustin Pop
        node nor not
2770 10c2650b Iustin Pop
    @type iv_name: str
2771 10c2650b Iustin Pop
    @param iv_name: the instance-visible name of the
2772 c41eea6e Iustin Pop
        device, as in objects.Disk.iv_name
2773 10c2650b Iustin Pop

2774 10c2650b Iustin Pop
    @rtype: None
2775 10c2650b Iustin Pop

2776 3f78eef2 Iustin Pop
    """
2777 cf5a8306 Iustin Pop
    if dev_path is None:
2778 18682bca Iustin Pop
      logging.error("DevCacheManager.UpdateCache got a None dev_path")
2779 cf5a8306 Iustin Pop
      return
2780 3f78eef2 Iustin Pop
    fpath = cls._ConvertPath(dev_path)
2781 3f78eef2 Iustin Pop
    if on_primary:
2782 3f78eef2 Iustin Pop
      state = "primary"
2783 3f78eef2 Iustin Pop
    else:
2784 3f78eef2 Iustin Pop
      state = "secondary"
2785 3f78eef2 Iustin Pop
    if iv_name is None:
2786 3f78eef2 Iustin Pop
      iv_name = "not_visible"
2787 3f78eef2 Iustin Pop
    fdata = "%s %s %s\n" % (str(owner), state, iv_name)
2788 3f78eef2 Iustin Pop
    try:
2789 3f78eef2 Iustin Pop
      utils.WriteFile(fpath, data=fdata)
2790 3f78eef2 Iustin Pop
    except EnvironmentError, err:
2791 29921401 Iustin Pop
      logging.exception("Can't update bdev cache for %s: %s", dev_path, err)
2792 3f78eef2 Iustin Pop
2793 3f78eef2 Iustin Pop
  @classmethod
2794 3f78eef2 Iustin Pop
  def RemoveCache(cls, dev_path):
2795 3f78eef2 Iustin Pop
    """Remove data for a dev_path.
2796 3f78eef2 Iustin Pop

2797 10c2650b Iustin Pop
    This is just a wrapper over L{utils.RemoveFile} with a converted
2798 10c2650b Iustin Pop
    path name and logging.
2799 10c2650b Iustin Pop

2800 10c2650b Iustin Pop
    @type dev_path: str
2801 10c2650b Iustin Pop
    @param dev_path: the pathname of the device
2802 10c2650b Iustin Pop

2803 10c2650b Iustin Pop
    @rtype: None
2804 10c2650b Iustin Pop

2805 3f78eef2 Iustin Pop
    """
2806 cf5a8306 Iustin Pop
    if dev_path is None:
2807 18682bca Iustin Pop
      logging.error("DevCacheManager.RemoveCache got a None dev_path")
2808 cf5a8306 Iustin Pop
      return
2809 3f78eef2 Iustin Pop
    fpath = cls._ConvertPath(dev_path)
2810 3f78eef2 Iustin Pop
    try:
2811 3f78eef2 Iustin Pop
      utils.RemoveFile(fpath)
2812 3f78eef2 Iustin Pop
    except EnvironmentError, err:
2813 29921401 Iustin Pop
      logging.exception("Can't update bdev cache for %s: %s", dev_path, err)