Statistics
| Branch: | Tag: | Revision:

root / lib / utils.py @ 2557ff82

History | View | Annotate | Download (33.8 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 a8083063 Iustin Pop
"""Ganeti small utilities
23 899d2a81 Michael Hanselmann

24 a8083063 Iustin Pop
"""
25 a8083063 Iustin Pop
26 a8083063 Iustin Pop
27 a8083063 Iustin Pop
import sys
28 a8083063 Iustin Pop
import os
29 a8083063 Iustin Pop
import sha
30 a8083063 Iustin Pop
import time
31 113b55aa Iustin Pop
import subprocess
32 a8083063 Iustin Pop
import re
33 a8083063 Iustin Pop
import socket
34 a8083063 Iustin Pop
import tempfile
35 a8083063 Iustin Pop
import shutil
36 4ca1b175 Alexander Schreiber
import errno
37 2f8b60b3 Iustin Pop
import pwd
38 78feb6fb Guido Trotter
import itertools
39 9c233417 Iustin Pop
import select
40 9c233417 Iustin Pop
import fcntl
41 8f765069 Iustin Pop
import resource
42 bb698c1f Iustin Pop
import logging
43 de499029 Michael Hanselmann
import signal
44 9c233417 Iustin Pop
45 9c233417 Iustin Pop
from cStringIO import StringIO
46 a8083063 Iustin Pop
47 a8083063 Iustin Pop
from ganeti import errors
48 3aecd2c7 Iustin Pop
from ganeti import constants
49 a8083063 Iustin Pop
50 16abfbc2 Alexander Schreiber
51 a8083063 Iustin Pop
_locksheld = []
52 a8083063 Iustin Pop
_re_shell_unquoted = re.compile('^[-.,=:/_+@A-Za-z0-9]+$')
53 a8083063 Iustin Pop
54 f362096f Iustin Pop
debug = False
55 b74159ee Iustin Pop
no_fork = False
56 f362096f Iustin Pop
57 7c0d6283 Michael Hanselmann
58 a8083063 Iustin Pop
class RunResult(object):
59 a8083063 Iustin Pop
  """Simple class for holding the result of running external programs.
60 a8083063 Iustin Pop

61 a8083063 Iustin Pop
  Instance variables:
62 a8083063 Iustin Pop
    exit_code: the exit code of the program, or None (if the program
63 a8083063 Iustin Pop
               didn't exit())
64 a8083063 Iustin Pop
    signal: numeric signal that caused the program to finish, or None
65 a8083063 Iustin Pop
            (if the program wasn't terminated by a signal)
66 a8083063 Iustin Pop
    stdout: the standard output of the program
67 a8083063 Iustin Pop
    stderr: the standard error of the program
68 a8083063 Iustin Pop
    failed: a Boolean value which is True in case the program was
69 a8083063 Iustin Pop
            terminated by a signal or exited with a non-zero exit code
70 a8083063 Iustin Pop
    fail_reason: a string detailing the termination reason
71 a8083063 Iustin Pop

72 a8083063 Iustin Pop
  """
73 a8083063 Iustin Pop
  __slots__ = ["exit_code", "signal", "stdout", "stderr",
74 a8083063 Iustin Pop
               "failed", "fail_reason", "cmd"]
75 a8083063 Iustin Pop
76 a8083063 Iustin Pop
77 38206f3c Iustin Pop
  def __init__(self, exit_code, signal_, stdout, stderr, cmd):
78 a8083063 Iustin Pop
    self.cmd = cmd
79 a8083063 Iustin Pop
    self.exit_code = exit_code
80 38206f3c Iustin Pop
    self.signal = signal_
81 a8083063 Iustin Pop
    self.stdout = stdout
82 a8083063 Iustin Pop
    self.stderr = stderr
83 38206f3c Iustin Pop
    self.failed = (signal_ is not None or exit_code != 0)
84 a8083063 Iustin Pop
85 a8083063 Iustin Pop
    if self.signal is not None:
86 a8083063 Iustin Pop
      self.fail_reason = "terminated by signal %s" % self.signal
87 a8083063 Iustin Pop
    elif self.exit_code is not None:
88 a8083063 Iustin Pop
      self.fail_reason = "exited with exit code %s" % self.exit_code
89 a8083063 Iustin Pop
    else:
90 a8083063 Iustin Pop
      self.fail_reason = "unable to determine termination reason"
91 a8083063 Iustin Pop
92 bb698c1f Iustin Pop
    if self.failed:
93 bb698c1f Iustin Pop
      logging.debug("Command '%s' failed (%s); output: %s",
94 bb698c1f Iustin Pop
                    self.cmd, self.fail_reason, self.output)
95 f362096f Iustin Pop
96 a8083063 Iustin Pop
  def _GetOutput(self):
97 a8083063 Iustin Pop
    """Returns the combined stdout and stderr for easier usage.
98 a8083063 Iustin Pop

99 a8083063 Iustin Pop
    """
100 a8083063 Iustin Pop
    return self.stdout + self.stderr
101 a8083063 Iustin Pop
102 a8083063 Iustin Pop
  output = property(_GetOutput, None, None, "Return full output")
103 a8083063 Iustin Pop
104 a8083063 Iustin Pop
105 2557ff82 Guido Trotter
def RunCmd(cmd, env=None):
106 a8083063 Iustin Pop
  """Execute a (shell) command.
107 a8083063 Iustin Pop

108 a8083063 Iustin Pop
  The command should not read from its standard input, as it will be
109 a8083063 Iustin Pop
  closed.
110 a8083063 Iustin Pop

111 e326d4e5 Guido Trotter
  @param cmd: Command to run
112 e326d4e5 Guido Trotter
  @type  cmd: string or list
113 2557ff82 Guido Trotter
  @param env: Additional environment
114 2557ff82 Guido Trotter
  @type env: dict
115 e326d4e5 Guido Trotter
  @return: `RunResult` instance
116 e326d4e5 Guido Trotter
  @rtype: RunResult
117 a8083063 Iustin Pop

118 a8083063 Iustin Pop
  """
119 b74159ee Iustin Pop
  if no_fork:
120 b74159ee Iustin Pop
    raise errors.ProgrammerError("utils.RunCmd() called with fork() disabled")
121 b74159ee Iustin Pop
122 a8083063 Iustin Pop
  if isinstance(cmd, list):
123 a8083063 Iustin Pop
    cmd = [str(val) for val in cmd]
124 113b55aa Iustin Pop
    strcmd = " ".join(cmd)
125 113b55aa Iustin Pop
    shell = False
126 113b55aa Iustin Pop
  else:
127 113b55aa Iustin Pop
    strcmd = cmd
128 113b55aa Iustin Pop
    shell = True
129 bb698c1f Iustin Pop
  logging.debug("RunCmd '%s'", strcmd)
130 2557ff82 Guido Trotter
131 2557ff82 Guido Trotter
  cmd_env = os.environ.copy()
132 2557ff82 Guido Trotter
  cmd_env["LC_ALL"] = "C"
133 2557ff82 Guido Trotter
  if env is not None:
134 2557ff82 Guido Trotter
    cmd_env.update(env)
135 2557ff82 Guido Trotter
136 9c233417 Iustin Pop
  poller = select.poll()
137 113b55aa Iustin Pop
  child = subprocess.Popen(cmd, shell=shell,
138 113b55aa Iustin Pop
                           stderr=subprocess.PIPE,
139 113b55aa Iustin Pop
                           stdout=subprocess.PIPE,
140 113b55aa Iustin Pop
                           stdin=subprocess.PIPE,
141 2557ff82 Guido Trotter
                           close_fds=True, env=cmd_env)
142 113b55aa Iustin Pop
143 113b55aa Iustin Pop
  child.stdin.close()
144 9c233417 Iustin Pop
  poller.register(child.stdout, select.POLLIN)
145 9c233417 Iustin Pop
  poller.register(child.stderr, select.POLLIN)
146 9c233417 Iustin Pop
  out = StringIO()
147 9c233417 Iustin Pop
  err = StringIO()
148 9c233417 Iustin Pop
  fdmap = {
149 9c233417 Iustin Pop
    child.stdout.fileno(): (out, child.stdout),
150 9c233417 Iustin Pop
    child.stderr.fileno(): (err, child.stderr),
151 9c233417 Iustin Pop
    }
152 9c233417 Iustin Pop
  for fd in fdmap:
153 9c233417 Iustin Pop
    status = fcntl.fcntl(fd, fcntl.F_GETFL)
154 9c233417 Iustin Pop
    fcntl.fcntl(fd, fcntl.F_SETFL, status | os.O_NONBLOCK)
155 9c233417 Iustin Pop
156 9c233417 Iustin Pop
  while fdmap:
157 9c233417 Iustin Pop
    for fd, event in poller.poll():
158 9c233417 Iustin Pop
      if event & select.POLLIN or event & select.POLLPRI:
159 9c233417 Iustin Pop
        data = fdmap[fd][1].read()
160 9c233417 Iustin Pop
        # no data from read signifies EOF (the same as POLLHUP)
161 9c233417 Iustin Pop
        if not data:
162 9c233417 Iustin Pop
          poller.unregister(fd)
163 9c233417 Iustin Pop
          del fdmap[fd]
164 9c233417 Iustin Pop
          continue
165 9c233417 Iustin Pop
        fdmap[fd][0].write(data)
166 9c233417 Iustin Pop
      if (event & select.POLLNVAL or event & select.POLLHUP or
167 9c233417 Iustin Pop
          event & select.POLLERR):
168 9c233417 Iustin Pop
        poller.unregister(fd)
169 9c233417 Iustin Pop
        del fdmap[fd]
170 9c233417 Iustin Pop
171 9c233417 Iustin Pop
  out = out.getvalue()
172 9c233417 Iustin Pop
  err = err.getvalue()
173 a8083063 Iustin Pop
174 a8083063 Iustin Pop
  status = child.wait()
175 113b55aa Iustin Pop
  if status >= 0:
176 113b55aa Iustin Pop
    exitcode = status
177 38206f3c Iustin Pop
    signal_ = None
178 a8083063 Iustin Pop
  else:
179 a8083063 Iustin Pop
    exitcode = None
180 38206f3c Iustin Pop
    signal_ = -status
181 a8083063 Iustin Pop
182 38206f3c Iustin Pop
  return RunResult(exitcode, signal_, out, err, strcmd)
183 a8083063 Iustin Pop
184 a8083063 Iustin Pop
185 a8083063 Iustin Pop
def RemoveFile(filename):
186 a8083063 Iustin Pop
  """Remove a file ignoring some errors.
187 a8083063 Iustin Pop

188 a8083063 Iustin Pop
  Remove a file, ignoring non-existing ones or directories. Other
189 a8083063 Iustin Pop
  errors are passed.
190 a8083063 Iustin Pop

191 a8083063 Iustin Pop
  """
192 a8083063 Iustin Pop
  try:
193 a8083063 Iustin Pop
    os.unlink(filename)
194 a8083063 Iustin Pop
  except OSError, err:
195 4ca1b175 Alexander Schreiber
    if err.errno not in (errno.ENOENT, errno.EISDIR):
196 a8083063 Iustin Pop
      raise
197 a8083063 Iustin Pop
198 a8083063 Iustin Pop
199 a8083063 Iustin Pop
def _FingerprintFile(filename):
200 a8083063 Iustin Pop
  """Compute the fingerprint of a file.
201 a8083063 Iustin Pop

202 a8083063 Iustin Pop
  If the file does not exist, a None will be returned
203 a8083063 Iustin Pop
  instead.
204 a8083063 Iustin Pop

205 a8083063 Iustin Pop
  Args:
206 a8083063 Iustin Pop
    filename - Filename (str)
207 a8083063 Iustin Pop

208 a8083063 Iustin Pop
  """
209 a8083063 Iustin Pop
  if not (os.path.exists(filename) and os.path.isfile(filename)):
210 a8083063 Iustin Pop
    return None
211 a8083063 Iustin Pop
212 a8083063 Iustin Pop
  f = open(filename)
213 a8083063 Iustin Pop
214 a8083063 Iustin Pop
  fp = sha.sha()
215 a8083063 Iustin Pop
  while True:
216 a8083063 Iustin Pop
    data = f.read(4096)
217 a8083063 Iustin Pop
    if not data:
218 a8083063 Iustin Pop
      break
219 a8083063 Iustin Pop
220 a8083063 Iustin Pop
    fp.update(data)
221 a8083063 Iustin Pop
222 a8083063 Iustin Pop
  return fp.hexdigest()
223 a8083063 Iustin Pop
224 a8083063 Iustin Pop
225 a8083063 Iustin Pop
def FingerprintFiles(files):
226 a8083063 Iustin Pop
  """Compute fingerprints for a list of files.
227 a8083063 Iustin Pop

228 a8083063 Iustin Pop
  Args:
229 a8083063 Iustin Pop
    files - array of filenames.  ( [str, ...] )
230 a8083063 Iustin Pop

231 a8083063 Iustin Pop
  Return value:
232 a8083063 Iustin Pop
    dictionary of filename: fingerprint for the files that exist
233 a8083063 Iustin Pop

234 a8083063 Iustin Pop
  """
235 a8083063 Iustin Pop
  ret = {}
236 a8083063 Iustin Pop
237 a8083063 Iustin Pop
  for filename in files:
238 a8083063 Iustin Pop
    cksum = _FingerprintFile(filename)
239 a8083063 Iustin Pop
    if cksum:
240 a8083063 Iustin Pop
      ret[filename] = cksum
241 a8083063 Iustin Pop
242 a8083063 Iustin Pop
  return ret
243 a8083063 Iustin Pop
244 a8083063 Iustin Pop
245 a8083063 Iustin Pop
def CheckDict(target, template, logname=None):
246 a8083063 Iustin Pop
  """Ensure a dictionary has a required set of keys.
247 a8083063 Iustin Pop

248 a8083063 Iustin Pop
  For the given dictionaries `target` and `template`, ensure target
249 a8083063 Iustin Pop
  has all the keys from template. Missing keys are added with values
250 a8083063 Iustin Pop
  from template.
251 a8083063 Iustin Pop

252 a8083063 Iustin Pop
  Args:
253 a8083063 Iustin Pop
    target   - the dictionary to check
254 a8083063 Iustin Pop
    template - template dictionary
255 a8083063 Iustin Pop
    logname  - a caller-chosen string to identify the debug log
256 a8083063 Iustin Pop
               entry; if None, no logging will be done
257 a8083063 Iustin Pop

258 a8083063 Iustin Pop
  Returns value:
259 a8083063 Iustin Pop
    None
260 a8083063 Iustin Pop

261 a8083063 Iustin Pop
  """
262 a8083063 Iustin Pop
  missing = []
263 a8083063 Iustin Pop
  for k in template:
264 a8083063 Iustin Pop
    if k not in target:
265 a8083063 Iustin Pop
      missing.append(k)
266 a8083063 Iustin Pop
      target[k] = template[k]
267 a8083063 Iustin Pop
268 a8083063 Iustin Pop
  if missing and logname:
269 bb698c1f Iustin Pop
    logging.warning('%s missing keys %s', logname, ', '.join(missing))
270 a8083063 Iustin Pop
271 a8083063 Iustin Pop
272 a8083063 Iustin Pop
def IsProcessAlive(pid):
273 a8083063 Iustin Pop
  """Check if a given pid exists on the system.
274 a8083063 Iustin Pop

275 a8083063 Iustin Pop
  Returns: true or false, depending on if the pid exists or not
276 a8083063 Iustin Pop

277 d9f311d7 Iustin Pop
  Remarks: zombie processes treated as not alive, and giving a pid <=
278 d9f311d7 Iustin Pop
  0 makes the function to return False.
279 a8083063 Iustin Pop

280 a8083063 Iustin Pop
  """
281 d9f311d7 Iustin Pop
  if pid <= 0:
282 d9f311d7 Iustin Pop
    return False
283 d9f311d7 Iustin Pop
284 a8083063 Iustin Pop
  try:
285 a8083063 Iustin Pop
    f = open("/proc/%d/status" % pid)
286 a8083063 Iustin Pop
  except IOError, err:
287 4ca1b175 Alexander Schreiber
    if err.errno in (errno.ENOENT, errno.ENOTDIR):
288 a8083063 Iustin Pop
      return False
289 a8083063 Iustin Pop
290 a8083063 Iustin Pop
  alive = True
291 a8083063 Iustin Pop
  try:
292 a8083063 Iustin Pop
    data = f.readlines()
293 a8083063 Iustin Pop
    if len(data) > 1:
294 a8083063 Iustin Pop
      state = data[1].split()
295 a8083063 Iustin Pop
      if len(state) > 1 and state[1] == "Z":
296 a8083063 Iustin Pop
        alive = False
297 a8083063 Iustin Pop
  finally:
298 a8083063 Iustin Pop
    f.close()
299 a8083063 Iustin Pop
300 a8083063 Iustin Pop
  return alive
301 a8083063 Iustin Pop
302 a8083063 Iustin Pop
303 d9f311d7 Iustin Pop
def ReadPidFile(pidfile):
304 d9f311d7 Iustin Pop
  """Read the pid from a file.
305 fee80e90 Guido Trotter

306 d9f311d7 Iustin Pop
  @param pidfile: Path to a file containing the pid to be checked
307 d9f311d7 Iustin Pop
  @type  pidfile: string (filename)
308 d9f311d7 Iustin Pop
  @return: The process id, if the file exista and contains a valid PID,
309 d9f311d7 Iustin Pop
           otherwise 0
310 d9f311d7 Iustin Pop
  @rtype: int
311 fee80e90 Guido Trotter

312 fee80e90 Guido Trotter
  """
313 fee80e90 Guido Trotter
  try:
314 fee80e90 Guido Trotter
    pf = open(pidfile, 'r')
315 d9f311d7 Iustin Pop
  except EnvironmentError, err:
316 d9f311d7 Iustin Pop
    if err.errno != errno.ENOENT:
317 d9f311d7 Iustin Pop
      logging.exception("Can't read pid file?!")
318 d9f311d7 Iustin Pop
    return 0
319 fee80e90 Guido Trotter
320 fee80e90 Guido Trotter
  try:
321 fee80e90 Guido Trotter
    pid = int(pf.read())
322 d9f311d7 Iustin Pop
  except ValueError, err:
323 8161a646 Iustin Pop
    logging.info("Can't parse pid file contents", exc_info=True)
324 d9f311d7 Iustin Pop
    return 0
325 fee80e90 Guido Trotter
326 d9f311d7 Iustin Pop
  return pid
327 fee80e90 Guido Trotter
328 fee80e90 Guido Trotter
329 a8083063 Iustin Pop
def MatchNameComponent(key, name_list):
330 a8083063 Iustin Pop
  """Try to match a name against a list.
331 a8083063 Iustin Pop

332 a8083063 Iustin Pop
  This function will try to match a name like test1 against a list
333 a8083063 Iustin Pop
  like ['test1.example.com', 'test2.example.com', ...]. Against this
334 a8083063 Iustin Pop
  list, 'test1' as well as 'test1.example' will match, but not
335 a8083063 Iustin Pop
  'test1.ex'. A multiple match will be considered as no match at all
336 a8083063 Iustin Pop
  (e.g. 'test1' against ['test1.example.com', 'test1.example.org']).
337 a8083063 Iustin Pop

338 a8083063 Iustin Pop
  Args:
339 a8083063 Iustin Pop
    key: the name to be searched
340 a8083063 Iustin Pop
    name_list: the list of strings against which to search the key
341 a8083063 Iustin Pop

342 a8083063 Iustin Pop
  Returns:
343 a8083063 Iustin Pop
    None if there is no match *or* if there are multiple matches
344 a8083063 Iustin Pop
    otherwise the element from the list which matches
345 a8083063 Iustin Pop

346 a8083063 Iustin Pop
  """
347 a8083063 Iustin Pop
  mo = re.compile("^%s(\..*)?$" % re.escape(key))
348 a8083063 Iustin Pop
  names_filtered = [name for name in name_list if mo.match(name) is not None]
349 a8083063 Iustin Pop
  if len(names_filtered) != 1:
350 a8083063 Iustin Pop
    return None
351 a8083063 Iustin Pop
  return names_filtered[0]
352 a8083063 Iustin Pop
353 a8083063 Iustin Pop
354 bcf043c9 Iustin Pop
class HostInfo:
355 89e1fc26 Iustin Pop
  """Class implementing resolver and hostname functionality
356 bcf043c9 Iustin Pop

357 bcf043c9 Iustin Pop
  """
358 89e1fc26 Iustin Pop
  def __init__(self, name=None):
359 bcf043c9 Iustin Pop
    """Initialize the host name object.
360 bcf043c9 Iustin Pop

361 89e1fc26 Iustin Pop
    If the name argument is not passed, it will use this system's
362 89e1fc26 Iustin Pop
    name.
363 bcf043c9 Iustin Pop

364 bcf043c9 Iustin Pop
    """
365 89e1fc26 Iustin Pop
    if name is None:
366 89e1fc26 Iustin Pop
      name = self.SysName()
367 89e1fc26 Iustin Pop
368 89e1fc26 Iustin Pop
    self.query = name
369 89e1fc26 Iustin Pop
    self.name, self.aliases, self.ipaddrs = self.LookupHostname(name)
370 bcf043c9 Iustin Pop
    self.ip = self.ipaddrs[0]
371 bcf043c9 Iustin Pop
372 c8a0948f Michael Hanselmann
  def ShortName(self):
373 c8a0948f Michael Hanselmann
    """Returns the hostname without domain.
374 c8a0948f Michael Hanselmann

375 c8a0948f Michael Hanselmann
    """
376 c8a0948f Michael Hanselmann
    return self.name.split('.')[0]
377 c8a0948f Michael Hanselmann
378 89e1fc26 Iustin Pop
  @staticmethod
379 89e1fc26 Iustin Pop
  def SysName():
380 89e1fc26 Iustin Pop
    """Return the current system's name.
381 bcf043c9 Iustin Pop

382 89e1fc26 Iustin Pop
    This is simply a wrapper over socket.gethostname()
383 a8083063 Iustin Pop

384 89e1fc26 Iustin Pop
    """
385 89e1fc26 Iustin Pop
    return socket.gethostname()
386 a8083063 Iustin Pop
387 89e1fc26 Iustin Pop
  @staticmethod
388 89e1fc26 Iustin Pop
  def LookupHostname(hostname):
389 89e1fc26 Iustin Pop
    """Look up hostname
390 a8083063 Iustin Pop

391 89e1fc26 Iustin Pop
    Args:
392 89e1fc26 Iustin Pop
      hostname: hostname to look up
393 89e1fc26 Iustin Pop

394 89e1fc26 Iustin Pop
    Returns:
395 89e1fc26 Iustin Pop
      a tuple (name, aliases, ipaddrs) as returned by socket.gethostbyname_ex
396 89e1fc26 Iustin Pop
      in case of errors in resolving, we raise a ResolverError
397 89e1fc26 Iustin Pop

398 89e1fc26 Iustin Pop
    """
399 89e1fc26 Iustin Pop
    try:
400 89e1fc26 Iustin Pop
      result = socket.gethostbyname_ex(hostname)
401 89e1fc26 Iustin Pop
    except socket.gaierror, err:
402 89e1fc26 Iustin Pop
      # hostname not found in DNS
403 89e1fc26 Iustin Pop
      raise errors.ResolverError(hostname, err.args[0], err.args[1])
404 a8083063 Iustin Pop
405 89e1fc26 Iustin Pop
    return result
406 a8083063 Iustin Pop
407 a8083063 Iustin Pop
408 a8083063 Iustin Pop
def ListVolumeGroups():
409 a8083063 Iustin Pop
  """List volume groups and their size
410 a8083063 Iustin Pop

411 a8083063 Iustin Pop
  Returns:
412 a8083063 Iustin Pop
     Dictionary with keys volume name and values the size of the volume
413 a8083063 Iustin Pop

414 a8083063 Iustin Pop
  """
415 a8083063 Iustin Pop
  command = "vgs --noheadings --units m --nosuffix -o name,size"
416 a8083063 Iustin Pop
  result = RunCmd(command)
417 a8083063 Iustin Pop
  retval = {}
418 a8083063 Iustin Pop
  if result.failed:
419 a8083063 Iustin Pop
    return retval
420 a8083063 Iustin Pop
421 a8083063 Iustin Pop
  for line in result.stdout.splitlines():
422 a8083063 Iustin Pop
    try:
423 a8083063 Iustin Pop
      name, size = line.split()
424 a8083063 Iustin Pop
      size = int(float(size))
425 a8083063 Iustin Pop
    except (IndexError, ValueError), err:
426 bb698c1f Iustin Pop
      logging.error("Invalid output from vgs (%s): %s", err, line)
427 a8083063 Iustin Pop
      continue
428 a8083063 Iustin Pop
429 a8083063 Iustin Pop
    retval[name] = size
430 a8083063 Iustin Pop
431 a8083063 Iustin Pop
  return retval
432 a8083063 Iustin Pop
433 a8083063 Iustin Pop
434 a8083063 Iustin Pop
def BridgeExists(bridge):
435 a8083063 Iustin Pop
  """Check whether the given bridge exists in the system
436 a8083063 Iustin Pop

437 a8083063 Iustin Pop
  Returns:
438 a8083063 Iustin Pop
     True if it does, false otherwise.
439 a8083063 Iustin Pop

440 a8083063 Iustin Pop
  """
441 a8083063 Iustin Pop
  return os.path.isdir("/sys/class/net/%s/bridge" % bridge)
442 a8083063 Iustin Pop
443 a8083063 Iustin Pop
444 a8083063 Iustin Pop
def NiceSort(name_list):
445 a8083063 Iustin Pop
  """Sort a list of strings based on digit and non-digit groupings.
446 a8083063 Iustin Pop

447 a8083063 Iustin Pop
  Given a list of names ['a1', 'a10', 'a11', 'a2'] this function will
448 a8083063 Iustin Pop
  sort the list in the logical order ['a1', 'a2', 'a10', 'a11'].
449 a8083063 Iustin Pop

450 a8083063 Iustin Pop
  The sort algorithm breaks each name in groups of either only-digits
451 a8083063 Iustin Pop
  or no-digits. Only the first eight such groups are considered, and
452 a8083063 Iustin Pop
  after that we just use what's left of the string.
453 a8083063 Iustin Pop

454 a8083063 Iustin Pop
  Return value
455 a8083063 Iustin Pop
    - a copy of the list sorted according to our algorithm
456 a8083063 Iustin Pop

457 a8083063 Iustin Pop
  """
458 a8083063 Iustin Pop
  _SORTER_BASE = "(\D+|\d+)"
459 a8083063 Iustin Pop
  _SORTER_FULL = "^%s%s?%s?%s?%s?%s?%s?%s?.*$" % (_SORTER_BASE, _SORTER_BASE,
460 a8083063 Iustin Pop
                                                  _SORTER_BASE, _SORTER_BASE,
461 a8083063 Iustin Pop
                                                  _SORTER_BASE, _SORTER_BASE,
462 a8083063 Iustin Pop
                                                  _SORTER_BASE, _SORTER_BASE)
463 a8083063 Iustin Pop
  _SORTER_RE = re.compile(_SORTER_FULL)
464 a8083063 Iustin Pop
  _SORTER_NODIGIT = re.compile("^\D*$")
465 a8083063 Iustin Pop
  def _TryInt(val):
466 a8083063 Iustin Pop
    """Attempts to convert a variable to integer."""
467 a8083063 Iustin Pop
    if val is None or _SORTER_NODIGIT.match(val):
468 a8083063 Iustin Pop
      return val
469 a8083063 Iustin Pop
    rval = int(val)
470 a8083063 Iustin Pop
    return rval
471 a8083063 Iustin Pop
472 a8083063 Iustin Pop
  to_sort = [([_TryInt(grp) for grp in _SORTER_RE.match(name).groups()], name)
473 a8083063 Iustin Pop
             for name in name_list]
474 a8083063 Iustin Pop
  to_sort.sort()
475 a8083063 Iustin Pop
  return [tup[1] for tup in to_sort]
476 a8083063 Iustin Pop
477 a8083063 Iustin Pop
478 a8083063 Iustin Pop
def TryConvert(fn, val):
479 a8083063 Iustin Pop
  """Try to convert a value ignoring errors.
480 a8083063 Iustin Pop

481 a8083063 Iustin Pop
  This function tries to apply function `fn` to `val`. If no
482 a8083063 Iustin Pop
  ValueError or TypeError exceptions are raised, it will return the
483 a8083063 Iustin Pop
  result, else it will return the original value. Any other exceptions
484 a8083063 Iustin Pop
  are propagated to the caller.
485 a8083063 Iustin Pop

486 a8083063 Iustin Pop
  """
487 a8083063 Iustin Pop
  try:
488 a8083063 Iustin Pop
    nv = fn(val)
489 a8083063 Iustin Pop
  except (ValueError, TypeError), err:
490 a8083063 Iustin Pop
    nv = val
491 a8083063 Iustin Pop
  return nv
492 a8083063 Iustin Pop
493 a8083063 Iustin Pop
494 a8083063 Iustin Pop
def IsValidIP(ip):
495 a8083063 Iustin Pop
  """Verifies the syntax of an IP address.
496 a8083063 Iustin Pop

497 a8083063 Iustin Pop
  This function checks if the ip address passes is valid or not based
498 a8083063 Iustin Pop
  on syntax (not ip range, class calculations or anything).
499 a8083063 Iustin Pop

500 a8083063 Iustin Pop
  """
501 a8083063 Iustin Pop
  unit = "(0|[1-9]\d{0,2})"
502 a8083063 Iustin Pop
  return re.match("^%s\.%s\.%s\.%s$" % (unit, unit, unit, unit), ip)
503 a8083063 Iustin Pop
504 a8083063 Iustin Pop
505 a8083063 Iustin Pop
def IsValidShellParam(word):
506 a8083063 Iustin Pop
  """Verifies is the given word is safe from the shell's p.o.v.
507 a8083063 Iustin Pop

508 a8083063 Iustin Pop
  This means that we can pass this to a command via the shell and be
509 a8083063 Iustin Pop
  sure that it doesn't alter the command line and is passed as such to
510 a8083063 Iustin Pop
  the actual command.
511 a8083063 Iustin Pop

512 a8083063 Iustin Pop
  Note that we are overly restrictive here, in order to be on the safe
513 a8083063 Iustin Pop
  side.
514 a8083063 Iustin Pop

515 a8083063 Iustin Pop
  """
516 a8083063 Iustin Pop
  return bool(re.match("^[-a-zA-Z0-9._+/:%@]+$", word))
517 a8083063 Iustin Pop
518 a8083063 Iustin Pop
519 a8083063 Iustin Pop
def BuildShellCmd(template, *args):
520 a8083063 Iustin Pop
  """Build a safe shell command line from the given arguments.
521 a8083063 Iustin Pop

522 a8083063 Iustin Pop
  This function will check all arguments in the args list so that they
523 a8083063 Iustin Pop
  are valid shell parameters (i.e. they don't contain shell
524 a8083063 Iustin Pop
  metacharaters). If everything is ok, it will return the result of
525 a8083063 Iustin Pop
  template % args.
526 a8083063 Iustin Pop

527 a8083063 Iustin Pop
  """
528 a8083063 Iustin Pop
  for word in args:
529 a8083063 Iustin Pop
    if not IsValidShellParam(word):
530 3ecf6786 Iustin Pop
      raise errors.ProgrammerError("Shell argument '%s' contains"
531 3ecf6786 Iustin Pop
                                   " invalid characters" % word)
532 a8083063 Iustin Pop
  return template % args
533 a8083063 Iustin Pop
534 a8083063 Iustin Pop
535 a8083063 Iustin Pop
def FormatUnit(value):
536 a8083063 Iustin Pop
  """Formats an incoming number of MiB with the appropriate unit.
537 a8083063 Iustin Pop

538 a8083063 Iustin Pop
  Value needs to be passed as a numeric type. Return value is always a string.
539 a8083063 Iustin Pop

540 a8083063 Iustin Pop
  """
541 a8083063 Iustin Pop
  if value < 1024:
542 a8083063 Iustin Pop
    return "%dM" % round(value, 0)
543 a8083063 Iustin Pop
544 a8083063 Iustin Pop
  elif value < (1024 * 1024):
545 a8083063 Iustin Pop
    return "%0.1fG" % round(float(value) / 1024, 1)
546 a8083063 Iustin Pop
547 a8083063 Iustin Pop
  else:
548 a8083063 Iustin Pop
    return "%0.1fT" % round(float(value) / 1024 / 1024, 1)
549 a8083063 Iustin Pop
550 a8083063 Iustin Pop
551 a8083063 Iustin Pop
def ParseUnit(input_string):
552 a8083063 Iustin Pop
  """Tries to extract number and scale from the given string.
553 a8083063 Iustin Pop

554 a8083063 Iustin Pop
  Input must be in the format NUMBER+ [DOT NUMBER+] SPACE* [UNIT]. If no unit
555 a8083063 Iustin Pop
  is specified, it defaults to MiB. Return value is always an int in MiB.
556 a8083063 Iustin Pop

557 a8083063 Iustin Pop
  """
558 a8083063 Iustin Pop
  m = re.match('^([.\d]+)\s*([a-zA-Z]+)?$', input_string)
559 a8083063 Iustin Pop
  if not m:
560 3ecf6786 Iustin Pop
    raise errors.UnitParseError("Invalid format")
561 a8083063 Iustin Pop
562 a8083063 Iustin Pop
  value = float(m.groups()[0])
563 a8083063 Iustin Pop
564 a8083063 Iustin Pop
  unit = m.groups()[1]
565 a8083063 Iustin Pop
  if unit:
566 a8083063 Iustin Pop
    lcunit = unit.lower()
567 a8083063 Iustin Pop
  else:
568 a8083063 Iustin Pop
    lcunit = 'm'
569 a8083063 Iustin Pop
570 a8083063 Iustin Pop
  if lcunit in ('m', 'mb', 'mib'):
571 a8083063 Iustin Pop
    # Value already in MiB
572 a8083063 Iustin Pop
    pass
573 a8083063 Iustin Pop
574 a8083063 Iustin Pop
  elif lcunit in ('g', 'gb', 'gib'):
575 a8083063 Iustin Pop
    value *= 1024
576 a8083063 Iustin Pop
577 a8083063 Iustin Pop
  elif lcunit in ('t', 'tb', 'tib'):
578 a8083063 Iustin Pop
    value *= 1024 * 1024
579 a8083063 Iustin Pop
580 a8083063 Iustin Pop
  else:
581 3ecf6786 Iustin Pop
    raise errors.UnitParseError("Unknown unit: %s" % unit)
582 a8083063 Iustin Pop
583 a8083063 Iustin Pop
  # Make sure we round up
584 a8083063 Iustin Pop
  if int(value) < value:
585 a8083063 Iustin Pop
    value += 1
586 a8083063 Iustin Pop
587 a8083063 Iustin Pop
  # Round up to the next multiple of 4
588 a8083063 Iustin Pop
  value = int(value)
589 a8083063 Iustin Pop
  if value % 4:
590 a8083063 Iustin Pop
    value += 4 - value % 4
591 a8083063 Iustin Pop
592 a8083063 Iustin Pop
  return value
593 a8083063 Iustin Pop
594 a8083063 Iustin Pop
595 a8083063 Iustin Pop
def AddAuthorizedKey(file_name, key):
596 a8083063 Iustin Pop
  """Adds an SSH public key to an authorized_keys file.
597 a8083063 Iustin Pop

598 a8083063 Iustin Pop
  Args:
599 a8083063 Iustin Pop
    file_name: Path to authorized_keys file
600 a8083063 Iustin Pop
    key: String containing key
601 a8083063 Iustin Pop
  """
602 a8083063 Iustin Pop
  key_fields = key.split()
603 a8083063 Iustin Pop
604 a8083063 Iustin Pop
  f = open(file_name, 'a+')
605 a8083063 Iustin Pop
  try:
606 a8083063 Iustin Pop
    nl = True
607 a8083063 Iustin Pop
    for line in f:
608 a8083063 Iustin Pop
      # Ignore whitespace changes
609 a8083063 Iustin Pop
      if line.split() == key_fields:
610 a8083063 Iustin Pop
        break
611 a8083063 Iustin Pop
      nl = line.endswith('\n')
612 a8083063 Iustin Pop
    else:
613 a8083063 Iustin Pop
      if not nl:
614 a8083063 Iustin Pop
        f.write("\n")
615 a8083063 Iustin Pop
      f.write(key.rstrip('\r\n'))
616 a8083063 Iustin Pop
      f.write("\n")
617 a8083063 Iustin Pop
      f.flush()
618 a8083063 Iustin Pop
  finally:
619 a8083063 Iustin Pop
    f.close()
620 a8083063 Iustin Pop
621 a8083063 Iustin Pop
622 a8083063 Iustin Pop
def RemoveAuthorizedKey(file_name, key):
623 a8083063 Iustin Pop
  """Removes an SSH public key from an authorized_keys file.
624 a8083063 Iustin Pop

625 a8083063 Iustin Pop
  Args:
626 a8083063 Iustin Pop
    file_name: Path to authorized_keys file
627 a8083063 Iustin Pop
    key: String containing key
628 a8083063 Iustin Pop
  """
629 a8083063 Iustin Pop
  key_fields = key.split()
630 a8083063 Iustin Pop
631 a8083063 Iustin Pop
  fd, tmpname = tempfile.mkstemp(dir=os.path.dirname(file_name))
632 a8083063 Iustin Pop
  try:
633 59f82e3f Michael Hanselmann
    out = os.fdopen(fd, 'w')
634 a8083063 Iustin Pop
    try:
635 59f82e3f Michael Hanselmann
      f = open(file_name, 'r')
636 59f82e3f Michael Hanselmann
      try:
637 59f82e3f Michael Hanselmann
        for line in f:
638 59f82e3f Michael Hanselmann
          # Ignore whitespace changes while comparing lines
639 59f82e3f Michael Hanselmann
          if line.split() != key_fields:
640 59f82e3f Michael Hanselmann
            out.write(line)
641 899d2a81 Michael Hanselmann
642 899d2a81 Michael Hanselmann
        out.flush()
643 899d2a81 Michael Hanselmann
        os.rename(tmpname, file_name)
644 899d2a81 Michael Hanselmann
      finally:
645 899d2a81 Michael Hanselmann
        f.close()
646 899d2a81 Michael Hanselmann
    finally:
647 899d2a81 Michael Hanselmann
      out.close()
648 899d2a81 Michael Hanselmann
  except:
649 899d2a81 Michael Hanselmann
    RemoveFile(tmpname)
650 899d2a81 Michael Hanselmann
    raise
651 899d2a81 Michael Hanselmann
652 899d2a81 Michael Hanselmann
653 9440aeab Michael Hanselmann
def SetEtcHostsEntry(file_name, ip, hostname, aliases):
654 9440aeab Michael Hanselmann
  """Sets the name of an IP address and hostname in /etc/hosts.
655 899d2a81 Michael Hanselmann

656 899d2a81 Michael Hanselmann
  """
657 7fbb1f65 Michael Hanselmann
  # Ensure aliases are unique
658 7fbb1f65 Michael Hanselmann
  aliases = UniqueSequence([hostname] + aliases)[1:]
659 7fbb1f65 Michael Hanselmann
660 9440aeab Michael Hanselmann
  fd, tmpname = tempfile.mkstemp(dir=os.path.dirname(file_name))
661 899d2a81 Michael Hanselmann
  try:
662 9440aeab Michael Hanselmann
    out = os.fdopen(fd, 'w')
663 9440aeab Michael Hanselmann
    try:
664 9440aeab Michael Hanselmann
      f = open(file_name, 'r')
665 9440aeab Michael Hanselmann
      try:
666 9440aeab Michael Hanselmann
        written = False
667 9440aeab Michael Hanselmann
        for line in f:
668 9440aeab Michael Hanselmann
          fields = line.split()
669 7e3dbb94 Iustin Pop
          if fields and not fields[0].startswith('#') and ip == fields[0]:
670 9440aeab Michael Hanselmann
            continue
671 9440aeab Michael Hanselmann
          out.write(line)
672 9440aeab Michael Hanselmann
673 7e3dbb94 Iustin Pop
        out.write("%s\t%s" % (ip, hostname))
674 9440aeab Michael Hanselmann
        if aliases:
675 9440aeab Michael Hanselmann
          out.write(" %s" % ' '.join(aliases))
676 9440aeab Michael Hanselmann
        out.write('\n')
677 9440aeab Michael Hanselmann
678 9440aeab Michael Hanselmann
        out.flush()
679 2e3e75b7 Michael Hanselmann
        os.fsync(out)
680 9440aeab Michael Hanselmann
        os.rename(tmpname, file_name)
681 9440aeab Michael Hanselmann
      finally:
682 9440aeab Michael Hanselmann
        f.close()
683 9440aeab Michael Hanselmann
    finally:
684 9440aeab Michael Hanselmann
      out.close()
685 9440aeab Michael Hanselmann
  except:
686 9440aeab Michael Hanselmann
    RemoveFile(tmpname)
687 9440aeab Michael Hanselmann
    raise
688 899d2a81 Michael Hanselmann
689 899d2a81 Michael Hanselmann
690 d9c02ca6 Michael Hanselmann
def AddHostToEtcHosts(hostname):
691 d9c02ca6 Michael Hanselmann
  """Wrapper around SetEtcHostsEntry.
692 d9c02ca6 Michael Hanselmann

693 d9c02ca6 Michael Hanselmann
  """
694 d9c02ca6 Michael Hanselmann
  hi = HostInfo(name=hostname)
695 d9c02ca6 Michael Hanselmann
  SetEtcHostsEntry(constants.ETC_HOSTS, hi.ip, hi.name, [hi.ShortName()])
696 d9c02ca6 Michael Hanselmann
697 d9c02ca6 Michael Hanselmann
698 899d2a81 Michael Hanselmann
def RemoveEtcHostsEntry(file_name, hostname):
699 3e1cdf9f Michael Hanselmann
  """Removes a hostname from /etc/hosts.
700 899d2a81 Michael Hanselmann

701 9440aeab Michael Hanselmann
  IP addresses without names are removed from the file.
702 899d2a81 Michael Hanselmann
  """
703 899d2a81 Michael Hanselmann
  fd, tmpname = tempfile.mkstemp(dir=os.path.dirname(file_name))
704 899d2a81 Michael Hanselmann
  try:
705 899d2a81 Michael Hanselmann
    out = os.fdopen(fd, 'w')
706 899d2a81 Michael Hanselmann
    try:
707 899d2a81 Michael Hanselmann
      f = open(file_name, 'r')
708 899d2a81 Michael Hanselmann
      try:
709 899d2a81 Michael Hanselmann
        for line in f:
710 899d2a81 Michael Hanselmann
          fields = line.split()
711 899d2a81 Michael Hanselmann
          if len(fields) > 1 and not fields[0].startswith('#'):
712 899d2a81 Michael Hanselmann
            names = fields[1:]
713 899d2a81 Michael Hanselmann
            if hostname in names:
714 899d2a81 Michael Hanselmann
              while hostname in names:
715 899d2a81 Michael Hanselmann
                names.remove(hostname)
716 899d2a81 Michael Hanselmann
              if names:
717 9440aeab Michael Hanselmann
                out.write("%s %s\n" % (fields[0], ' '.join(names)))
718 899d2a81 Michael Hanselmann
              continue
719 899d2a81 Michael Hanselmann
720 899d2a81 Michael Hanselmann
          out.write(line)
721 59f82e3f Michael Hanselmann
722 59f82e3f Michael Hanselmann
        out.flush()
723 2e3e75b7 Michael Hanselmann
        os.fsync(out)
724 59f82e3f Michael Hanselmann
        os.rename(tmpname, file_name)
725 59f82e3f Michael Hanselmann
      finally:
726 59f82e3f Michael Hanselmann
        f.close()
727 a8083063 Iustin Pop
    finally:
728 59f82e3f Michael Hanselmann
      out.close()
729 59f82e3f Michael Hanselmann
  except:
730 59f82e3f Michael Hanselmann
    RemoveFile(tmpname)
731 59f82e3f Michael Hanselmann
    raise
732 a8083063 Iustin Pop
733 a8083063 Iustin Pop
734 d9c02ca6 Michael Hanselmann
def RemoveHostFromEtcHosts(hostname):
735 d9c02ca6 Michael Hanselmann
  """Wrapper around RemoveEtcHostsEntry.
736 d9c02ca6 Michael Hanselmann

737 d9c02ca6 Michael Hanselmann
  """
738 d9c02ca6 Michael Hanselmann
  hi = HostInfo(name=hostname)
739 d9c02ca6 Michael Hanselmann
  RemoveEtcHostsEntry(constants.ETC_HOSTS, hi.name)
740 d9c02ca6 Michael Hanselmann
  RemoveEtcHostsEntry(constants.ETC_HOSTS, hi.ShortName())
741 d9c02ca6 Michael Hanselmann
742 d9c02ca6 Michael Hanselmann
743 a8083063 Iustin Pop
def CreateBackup(file_name):
744 a8083063 Iustin Pop
  """Creates a backup of a file.
745 a8083063 Iustin Pop

746 a8083063 Iustin Pop
  Returns: the path to the newly created backup file.
747 a8083063 Iustin Pop

748 a8083063 Iustin Pop
  """
749 a8083063 Iustin Pop
  if not os.path.isfile(file_name):
750 3ecf6786 Iustin Pop
    raise errors.ProgrammerError("Can't make a backup of a non-file '%s'" %
751 3ecf6786 Iustin Pop
                                file_name)
752 a8083063 Iustin Pop
753 081b1e69 Michael Hanselmann
  prefix = '%s.backup-%d.' % (os.path.basename(file_name), int(time.time()))
754 65fe4693 Iustin Pop
  dir_name = os.path.dirname(file_name)
755 081b1e69 Michael Hanselmann
756 081b1e69 Michael Hanselmann
  fsrc = open(file_name, 'rb')
757 081b1e69 Michael Hanselmann
  try:
758 65fe4693 Iustin Pop
    (fd, backup_name) = tempfile.mkstemp(prefix=prefix, dir=dir_name)
759 081b1e69 Michael Hanselmann
    fdst = os.fdopen(fd, 'wb')
760 081b1e69 Michael Hanselmann
    try:
761 081b1e69 Michael Hanselmann
      shutil.copyfileobj(fsrc, fdst)
762 081b1e69 Michael Hanselmann
    finally:
763 081b1e69 Michael Hanselmann
      fdst.close()
764 081b1e69 Michael Hanselmann
  finally:
765 081b1e69 Michael Hanselmann
    fsrc.close()
766 081b1e69 Michael Hanselmann
767 a8083063 Iustin Pop
  return backup_name
768 a8083063 Iustin Pop
769 a8083063 Iustin Pop
770 a8083063 Iustin Pop
def ShellQuote(value):
771 a8083063 Iustin Pop
  """Quotes shell argument according to POSIX.
772 3ecf6786 Iustin Pop

773 a8083063 Iustin Pop
  """
774 a8083063 Iustin Pop
  if _re_shell_unquoted.match(value):
775 a8083063 Iustin Pop
    return value
776 a8083063 Iustin Pop
  else:
777 a8083063 Iustin Pop
    return "'%s'" % value.replace("'", "'\\''")
778 a8083063 Iustin Pop
779 a8083063 Iustin Pop
780 a8083063 Iustin Pop
def ShellQuoteArgs(args):
781 a8083063 Iustin Pop
  """Quotes all given shell arguments and concatenates using spaces.
782 a8083063 Iustin Pop

783 a8083063 Iustin Pop
  """
784 a8083063 Iustin Pop
  return ' '.join([ShellQuote(i) for i in args])
785 88d14415 Michael Hanselmann
786 88d14415 Michael Hanselmann
787 b15d625f Iustin Pop
def TcpPing(target, port, timeout=10, live_port_needed=False, source=None):
788 2c30e9d7 Alexander Schreiber
  """Simple ping implementation using TCP connect(2).
789 2c30e9d7 Alexander Schreiber

790 b15d625f Iustin Pop
  Try to do a TCP connect(2) from an optional source IP to the
791 b15d625f Iustin Pop
  specified target IP and the specified target port. If the optional
792 b15d625f Iustin Pop
  parameter live_port_needed is set to true, requires the remote end
793 b15d625f Iustin Pop
  to accept the connection. The timeout is specified in seconds and
794 b15d625f Iustin Pop
  defaults to 10 seconds. If the source optional argument is not
795 b15d625f Iustin Pop
  passed, the source address selection is left to the kernel,
796 b15d625f Iustin Pop
  otherwise we try to connect using the passed address (failures to
797 b15d625f Iustin Pop
  bind other than EADDRNOTAVAIL will be ignored).
798 2c30e9d7 Alexander Schreiber

799 2c30e9d7 Alexander Schreiber
  """
800 2c30e9d7 Alexander Schreiber
  sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
801 2c30e9d7 Alexander Schreiber
802 2c30e9d7 Alexander Schreiber
  sucess = False
803 2c30e9d7 Alexander Schreiber
804 b15d625f Iustin Pop
  if source is not None:
805 b15d625f Iustin Pop
    try:
806 b15d625f Iustin Pop
      sock.bind((source, 0))
807 b15d625f Iustin Pop
    except socket.error, (errcode, errstring):
808 b15d625f Iustin Pop
      if errcode == errno.EADDRNOTAVAIL:
809 b15d625f Iustin Pop
        success = False
810 2c30e9d7 Alexander Schreiber
811 2c30e9d7 Alexander Schreiber
  sock.settimeout(timeout)
812 2c30e9d7 Alexander Schreiber
813 2c30e9d7 Alexander Schreiber
  try:
814 2c30e9d7 Alexander Schreiber
    sock.connect((target, port))
815 2c30e9d7 Alexander Schreiber
    sock.close()
816 2c30e9d7 Alexander Schreiber
    success = True
817 2c30e9d7 Alexander Schreiber
  except socket.timeout:
818 2c30e9d7 Alexander Schreiber
    success = False
819 2c30e9d7 Alexander Schreiber
  except socket.error, (errcode, errstring):
820 4ca1b175 Alexander Schreiber
    success = (not live_port_needed) and (errcode == errno.ECONNREFUSED)
821 2c30e9d7 Alexander Schreiber
822 2c30e9d7 Alexander Schreiber
  return success
823 eedbda4b Michael Hanselmann
824 eedbda4b Michael Hanselmann
825 eedbda4b Michael Hanselmann
def ListVisibleFiles(path):
826 eedbda4b Michael Hanselmann
  """Returns a list of all visible files in a directory.
827 eedbda4b Michael Hanselmann

828 eedbda4b Michael Hanselmann
  """
829 f3299a07 Michael Hanselmann
  files = [i for i in os.listdir(path) if not i.startswith(".")]
830 f3299a07 Michael Hanselmann
  files.sort()
831 f3299a07 Michael Hanselmann
  return files
832 2f8b60b3 Iustin Pop
833 2f8b60b3 Iustin Pop
834 257f4c0a Iustin Pop
def GetHomeDir(user, default=None):
835 257f4c0a Iustin Pop
  """Try to get the homedir of the given user.
836 257f4c0a Iustin Pop

837 257f4c0a Iustin Pop
  The user can be passed either as a string (denoting the name) or as
838 257f4c0a Iustin Pop
  an integer (denoting the user id). If the user is not found, the
839 257f4c0a Iustin Pop
  'default' argument is returned, which defaults to None.
840 2f8b60b3 Iustin Pop

841 2f8b60b3 Iustin Pop
  """
842 2f8b60b3 Iustin Pop
  try:
843 257f4c0a Iustin Pop
    if isinstance(user, basestring):
844 257f4c0a Iustin Pop
      result = pwd.getpwnam(user)
845 257f4c0a Iustin Pop
    elif isinstance(user, (int, long)):
846 257f4c0a Iustin Pop
      result = pwd.getpwuid(user)
847 257f4c0a Iustin Pop
    else:
848 257f4c0a Iustin Pop
      raise errors.ProgrammerError("Invalid type passed to GetHomeDir (%s)" %
849 257f4c0a Iustin Pop
                                   type(user))
850 2f8b60b3 Iustin Pop
  except KeyError:
851 2f8b60b3 Iustin Pop
    return default
852 2f8b60b3 Iustin Pop
  return result.pw_dir
853 59072e7e Michael Hanselmann
854 59072e7e Michael Hanselmann
855 24818e8f Michael Hanselmann
def NewUUID():
856 59072e7e Michael Hanselmann
  """Returns a random UUID.
857 59072e7e Michael Hanselmann

858 59072e7e Michael Hanselmann
  """
859 59072e7e Michael Hanselmann
  f = open("/proc/sys/kernel/random/uuid", "r")
860 59072e7e Michael Hanselmann
  try:
861 59072e7e Michael Hanselmann
    return f.read(128).rstrip("\n")
862 59072e7e Michael Hanselmann
  finally:
863 59072e7e Michael Hanselmann
    f.close()
864 087b34fe Iustin Pop
865 087b34fe Iustin Pop
866 087b34fe Iustin Pop
def WriteFile(file_name, fn=None, data=None,
867 087b34fe Iustin Pop
              mode=None, uid=-1, gid=-1,
868 71714516 Michael Hanselmann
              atime=None, mtime=None, close=True,
869 04a8d789 Michael Hanselmann
              dry_run=False, backup=False,
870 71714516 Michael Hanselmann
              prewrite=None, postwrite=None):
871 087b34fe Iustin Pop
  """(Over)write a file atomically.
872 087b34fe Iustin Pop

873 087b34fe Iustin Pop
  The file_name and either fn (a function taking one argument, the
874 087b34fe Iustin Pop
  file descriptor, and which should write the data to it) or data (the
875 087b34fe Iustin Pop
  contents of the file) must be passed. The other arguments are
876 087b34fe Iustin Pop
  optional and allow setting the file mode, owner and group, and the
877 087b34fe Iustin Pop
  mtime/atime of the file.
878 087b34fe Iustin Pop

879 087b34fe Iustin Pop
  If the function doesn't raise an exception, it has succeeded and the
880 087b34fe Iustin Pop
  target file has the new contents. If the file has raised an
881 087b34fe Iustin Pop
  exception, an existing target file should be unmodified and the
882 087b34fe Iustin Pop
  temporary file should be removed.
883 087b34fe Iustin Pop

884 71714516 Michael Hanselmann
  Args:
885 71714516 Michael Hanselmann
    file_name: New filename
886 71714516 Michael Hanselmann
    fn: Content writing function, called with file descriptor as parameter
887 71714516 Michael Hanselmann
    data: Content as string
888 71714516 Michael Hanselmann
    mode: File mode
889 71714516 Michael Hanselmann
    uid: Owner
890 71714516 Michael Hanselmann
    gid: Group
891 71714516 Michael Hanselmann
    atime: Access time
892 71714516 Michael Hanselmann
    mtime: Modification time
893 71714516 Michael Hanselmann
    close: Whether to close file after writing it
894 71714516 Michael Hanselmann
    prewrite: Function object called before writing content
895 71714516 Michael Hanselmann
    postwrite: Function object called after writing content
896 71714516 Michael Hanselmann

897 71714516 Michael Hanselmann
  Returns:
898 71714516 Michael Hanselmann
    None if "close" parameter evaluates to True, otherwise file descriptor.
899 71714516 Michael Hanselmann

900 087b34fe Iustin Pop
  """
901 04a8d789 Michael Hanselmann
  if not os.path.isabs(file_name):
902 087b34fe Iustin Pop
    raise errors.ProgrammerError("Path passed to WriteFile is not"
903 087b34fe Iustin Pop
                                 " absolute: '%s'" % file_name)
904 087b34fe Iustin Pop
905 087b34fe Iustin Pop
  if [fn, data].count(None) != 1:
906 087b34fe Iustin Pop
    raise errors.ProgrammerError("fn or data required")
907 087b34fe Iustin Pop
908 087b34fe Iustin Pop
  if [atime, mtime].count(None) == 1:
909 087b34fe Iustin Pop
    raise errors.ProgrammerError("Both atime and mtime must be either"
910 087b34fe Iustin Pop
                                 " set or None")
911 087b34fe Iustin Pop
912 70f4497c Michael Hanselmann
  if backup and not dry_run and os.path.isfile(file_name):
913 70f4497c Michael Hanselmann
    CreateBackup(file_name)
914 087b34fe Iustin Pop
915 087b34fe Iustin Pop
  dir_name, base_name = os.path.split(file_name)
916 087b34fe Iustin Pop
  fd, new_name = tempfile.mkstemp('.new', base_name, dir_name)
917 087b34fe Iustin Pop
  # here we need to make sure we remove the temp file, if any error
918 087b34fe Iustin Pop
  # leaves it in place
919 087b34fe Iustin Pop
  try:
920 087b34fe Iustin Pop
    if uid != -1 or gid != -1:
921 087b34fe Iustin Pop
      os.chown(new_name, uid, gid)
922 087b34fe Iustin Pop
    if mode:
923 087b34fe Iustin Pop
      os.chmod(new_name, mode)
924 71714516 Michael Hanselmann
    if callable(prewrite):
925 71714516 Michael Hanselmann
      prewrite(fd)
926 087b34fe Iustin Pop
    if data is not None:
927 087b34fe Iustin Pop
      os.write(fd, data)
928 087b34fe Iustin Pop
    else:
929 087b34fe Iustin Pop
      fn(fd)
930 71714516 Michael Hanselmann
    if callable(postwrite):
931 71714516 Michael Hanselmann
      postwrite(fd)
932 087b34fe Iustin Pop
    os.fsync(fd)
933 087b34fe Iustin Pop
    if atime is not None and mtime is not None:
934 087b34fe Iustin Pop
      os.utime(new_name, (atime, mtime))
935 70f4497c Michael Hanselmann
    if not dry_run:
936 70f4497c Michael Hanselmann
      os.rename(new_name, file_name)
937 087b34fe Iustin Pop
  finally:
938 71714516 Michael Hanselmann
    if close:
939 71714516 Michael Hanselmann
      os.close(fd)
940 71714516 Michael Hanselmann
      result = None
941 71714516 Michael Hanselmann
    else:
942 71714516 Michael Hanselmann
      result = fd
943 087b34fe Iustin Pop
    RemoveFile(new_name)
944 78feb6fb Guido Trotter
945 71714516 Michael Hanselmann
  return result
946 71714516 Michael Hanselmann
947 78feb6fb Guido Trotter
948 7b4126b7 Iustin Pop
def FirstFree(seq, base=0):
949 7b4126b7 Iustin Pop
  """Returns the first non-existing integer from seq.
950 7b4126b7 Iustin Pop

951 7b4126b7 Iustin Pop
  The seq argument should be a sorted list of positive integers. The
952 7b4126b7 Iustin Pop
  first time the index of an element is smaller than the element
953 7b4126b7 Iustin Pop
  value, the index will be returned.
954 7b4126b7 Iustin Pop

955 7b4126b7 Iustin Pop
  The base argument is used to start at a different offset,
956 7b4126b7 Iustin Pop
  i.e. [3, 4, 6] with offset=3 will return 5.
957 7b4126b7 Iustin Pop

958 7b4126b7 Iustin Pop
  Example: [0, 1, 3] will return 2.
959 7b4126b7 Iustin Pop

960 7b4126b7 Iustin Pop
  """
961 7b4126b7 Iustin Pop
  for idx, elem in enumerate(seq):
962 7b4126b7 Iustin Pop
    assert elem >= base, "Passed element is higher than base offset"
963 7b4126b7 Iustin Pop
    if elem > idx + base:
964 7b4126b7 Iustin Pop
      # idx is not used
965 7b4126b7 Iustin Pop
      return idx + base
966 7b4126b7 Iustin Pop
  return None
967 7b4126b7 Iustin Pop
968 7b4126b7 Iustin Pop
969 78feb6fb Guido Trotter
def all(seq, pred=bool):
970 78feb6fb Guido Trotter
  "Returns True if pred(x) is True for every element in the iterable"
971 78feb6fb Guido Trotter
  for elem in itertools.ifilterfalse(pred, seq):
972 78feb6fb Guido Trotter
    return False
973 78feb6fb Guido Trotter
  return True
974 78feb6fb Guido Trotter
975 78feb6fb Guido Trotter
976 78feb6fb Guido Trotter
def any(seq, pred=bool):
977 78feb6fb Guido Trotter
  "Returns True if pred(x) is True for at least one element in the iterable"
978 78feb6fb Guido Trotter
  for elem in itertools.ifilter(pred, seq):
979 78feb6fb Guido Trotter
    return True
980 78feb6fb Guido Trotter
  return False
981 f7414041 Michael Hanselmann
982 f7414041 Michael Hanselmann
983 f7414041 Michael Hanselmann
def UniqueSequence(seq):
984 f7414041 Michael Hanselmann
  """Returns a list with unique elements.
985 f7414041 Michael Hanselmann

986 f7414041 Michael Hanselmann
  Element order is preserved.
987 f7414041 Michael Hanselmann
  """
988 f7414041 Michael Hanselmann
  seen = set()
989 f7414041 Michael Hanselmann
  return [i for i in seq if i not in seen and not seen.add(i)]
990 1862d460 Alexander Schreiber
991 1862d460 Alexander Schreiber
992 1862d460 Alexander Schreiber
def IsValidMac(mac):
993 1862d460 Alexander Schreiber
  """Predicate to check if a MAC address is valid.
994 1862d460 Alexander Schreiber

995 1862d460 Alexander Schreiber
  Checks wether the supplied MAC address is formally correct, only
996 1862d460 Alexander Schreiber
  accepts colon separated format.
997 1862d460 Alexander Schreiber
  """
998 1862d460 Alexander Schreiber
  mac_check = re.compile("^([0-9a-f]{2}(:|$)){6}$")
999 1862d460 Alexander Schreiber
  return mac_check.match(mac) is not None
1000 06009e27 Iustin Pop
1001 06009e27 Iustin Pop
1002 06009e27 Iustin Pop
def TestDelay(duration):
1003 06009e27 Iustin Pop
  """Sleep for a fixed amount of time.
1004 06009e27 Iustin Pop

1005 06009e27 Iustin Pop
  """
1006 06009e27 Iustin Pop
  if duration < 0:
1007 06009e27 Iustin Pop
    return False
1008 06009e27 Iustin Pop
  time.sleep(duration)
1009 06009e27 Iustin Pop
  return True
1010 8f765069 Iustin Pop
1011 8f765069 Iustin Pop
1012 8ff612c2 Iustin Pop
def Daemonize(logfile, noclose_fds=None):
1013 8f765069 Iustin Pop
  """Daemonize the current process.
1014 8f765069 Iustin Pop

1015 8f765069 Iustin Pop
  This detaches the current process from the controlling terminal and
1016 8f765069 Iustin Pop
  runs it in the background as a daemon.
1017 8f765069 Iustin Pop

1018 8f765069 Iustin Pop
  """
1019 8f765069 Iustin Pop
  UMASK = 077
1020 8f765069 Iustin Pop
  WORKDIR = "/"
1021 8f765069 Iustin Pop
  # Default maximum for the number of available file descriptors.
1022 8f765069 Iustin Pop
  if 'SC_OPEN_MAX' in os.sysconf_names:
1023 8f765069 Iustin Pop
    try:
1024 8f765069 Iustin Pop
      MAXFD = os.sysconf('SC_OPEN_MAX')
1025 8f765069 Iustin Pop
      if MAXFD < 0:
1026 8f765069 Iustin Pop
        MAXFD = 1024
1027 8f765069 Iustin Pop
    except OSError:
1028 8f765069 Iustin Pop
      MAXFD = 1024
1029 8f765069 Iustin Pop
  else:
1030 8f765069 Iustin Pop
    MAXFD = 1024
1031 8f765069 Iustin Pop
1032 8f765069 Iustin Pop
  # this might fail
1033 8f765069 Iustin Pop
  pid = os.fork()
1034 8f765069 Iustin Pop
  if (pid == 0):  # The first child.
1035 8f765069 Iustin Pop
    os.setsid()
1036 8f765069 Iustin Pop
    # this might fail
1037 8f765069 Iustin Pop
    pid = os.fork() # Fork a second child.
1038 8f765069 Iustin Pop
    if (pid == 0):  # The second child.
1039 8f765069 Iustin Pop
      os.chdir(WORKDIR)
1040 8f765069 Iustin Pop
      os.umask(UMASK)
1041 8f765069 Iustin Pop
    else:
1042 8f765069 Iustin Pop
      # exit() or _exit()?  See below.
1043 8f765069 Iustin Pop
      os._exit(0) # Exit parent (the first child) of the second child.
1044 8f765069 Iustin Pop
  else:
1045 8f765069 Iustin Pop
    os._exit(0) # Exit parent of the first child.
1046 8f765069 Iustin Pop
  maxfd = resource.getrlimit(resource.RLIMIT_NOFILE)[1]
1047 8f765069 Iustin Pop
  if (maxfd == resource.RLIM_INFINITY):
1048 8f765069 Iustin Pop
    maxfd = MAXFD
1049 8f765069 Iustin Pop
1050 8f765069 Iustin Pop
  # Iterate through and close all file descriptors.
1051 8f765069 Iustin Pop
  for fd in range(0, maxfd):
1052 8ff612c2 Iustin Pop
    if noclose_fds and fd in noclose_fds:
1053 8ff612c2 Iustin Pop
      continue
1054 8f765069 Iustin Pop
    try:
1055 8f765069 Iustin Pop
      os.close(fd)
1056 8f765069 Iustin Pop
    except OSError: # ERROR, fd wasn't open to begin with (ignored)
1057 8f765069 Iustin Pop
      pass
1058 8f765069 Iustin Pop
  os.open(logfile, os.O_RDWR|os.O_CREAT|os.O_APPEND, 0600)
1059 8f765069 Iustin Pop
  # Duplicate standard input to standard output and standard error.
1060 8f765069 Iustin Pop
  os.dup2(0, 1)     # standard output (1)
1061 8f765069 Iustin Pop
  os.dup2(0, 2)     # standard error (2)
1062 8f765069 Iustin Pop
  return 0
1063 57c177af Iustin Pop
1064 57c177af Iustin Pop
1065 53beffbb Iustin Pop
def DaemonPidFileName(name):
1066 b330ac0b Guido Trotter
  """Compute a ganeti pid file absolute path, given the daemon name.
1067 b330ac0b Guido Trotter

1068 b330ac0b Guido Trotter
  """
1069 b330ac0b Guido Trotter
  return os.path.join(constants.RUN_GANETI_DIR, "%s.pid" % name)
1070 b330ac0b Guido Trotter
1071 b330ac0b Guido Trotter
1072 b330ac0b Guido Trotter
def WritePidFile(name):
1073 b330ac0b Guido Trotter
  """Write the current process pidfile.
1074 b330ac0b Guido Trotter

1075 b330ac0b Guido Trotter
  The file will be written to constants.RUN_GANETI_DIR/name.pid
1076 b330ac0b Guido Trotter

1077 b330ac0b Guido Trotter
  """
1078 b330ac0b Guido Trotter
  pid = os.getpid()
1079 53beffbb Iustin Pop
  pidfilename = DaemonPidFileName(name)
1080 d9f311d7 Iustin Pop
  if IsProcessAlive(ReadPidFile(pidfilename)):
1081 533bb4b1 Michael Hanselmann
    raise errors.GenericError("%s contains a live process" % pidfilename)
1082 b330ac0b Guido Trotter
1083 b330ac0b Guido Trotter
  WriteFile(pidfilename, data="%d\n" % pid)
1084 b330ac0b Guido Trotter
1085 b330ac0b Guido Trotter
1086 b330ac0b Guido Trotter
def RemovePidFile(name):
1087 b330ac0b Guido Trotter
  """Remove the current process pidfile.
1088 b330ac0b Guido Trotter

1089 b330ac0b Guido Trotter
  Any errors are ignored.
1090 b330ac0b Guido Trotter

1091 b330ac0b Guido Trotter
  """
1092 b330ac0b Guido Trotter
  pid = os.getpid()
1093 53beffbb Iustin Pop
  pidfilename = DaemonPidFileName(name)
1094 b330ac0b Guido Trotter
  # TODO: we could check here that the file contains our pid
1095 b330ac0b Guido Trotter
  try:
1096 b330ac0b Guido Trotter
    RemoveFile(pidfilename)
1097 b330ac0b Guido Trotter
  except:
1098 b330ac0b Guido Trotter
    pass
1099 b330ac0b Guido Trotter
1100 b330ac0b Guido Trotter
1101 38206f3c Iustin Pop
def KillProcess(pid, signal_=signal.SIGTERM, timeout=30):
1102 b2a1f511 Iustin Pop
  """Kill a process given by its pid.
1103 b2a1f511 Iustin Pop

1104 b2a1f511 Iustin Pop
  @type pid: int
1105 b2a1f511 Iustin Pop
  @param pid: The PID to terminate.
1106 38206f3c Iustin Pop
  @type signal_: int
1107 38206f3c Iustin Pop
  @param signal_: The signal to send, by default SIGTERM
1108 b2a1f511 Iustin Pop
  @type timeout: int
1109 b2a1f511 Iustin Pop
  @param timeout: The timeout after which, if the process is still alive,
1110 b2a1f511 Iustin Pop
                  a SIGKILL will be sent. If not positive, no such checking
1111 b2a1f511 Iustin Pop
                  will be done
1112 b2a1f511 Iustin Pop

1113 b2a1f511 Iustin Pop
  """
1114 b2a1f511 Iustin Pop
  if pid <= 0:
1115 b2a1f511 Iustin Pop
    # kill with pid=0 == suicide
1116 b2a1f511 Iustin Pop
    raise errors.ProgrammerError("Invalid pid given '%s'" % pid)
1117 b2a1f511 Iustin Pop
1118 b2a1f511 Iustin Pop
  if not IsProcessAlive(pid):
1119 b2a1f511 Iustin Pop
    return
1120 38206f3c Iustin Pop
  os.kill(pid, signal_)
1121 b2a1f511 Iustin Pop
  if timeout <= 0:
1122 b2a1f511 Iustin Pop
    return
1123 b2a1f511 Iustin Pop
  end = time.time() + timeout
1124 b2a1f511 Iustin Pop
  while time.time() < end and IsProcessAlive(pid):
1125 b2a1f511 Iustin Pop
    time.sleep(0.1)
1126 b2a1f511 Iustin Pop
  if IsProcessAlive(pid):
1127 b2a1f511 Iustin Pop
    os.kill(pid, signal.SIGKILL)
1128 b2a1f511 Iustin Pop
1129 b2a1f511 Iustin Pop
1130 57c177af Iustin Pop
def FindFile(name, search_path, test=os.path.exists):
1131 57c177af Iustin Pop
  """Look for a filesystem object in a given path.
1132 57c177af Iustin Pop

1133 57c177af Iustin Pop
  This is an abstract method to search for filesystem object (files,
1134 57c177af Iustin Pop
  dirs) under a given search path.
1135 57c177af Iustin Pop

1136 57c177af Iustin Pop
  Args:
1137 57c177af Iustin Pop
    - name: the name to look for
1138 57c177af Iustin Pop
    - search_path: list of directory names
1139 57c177af Iustin Pop
    - test: the test which the full path must satisfy
1140 57c177af Iustin Pop
      (defaults to os.path.exists)
1141 57c177af Iustin Pop

1142 57c177af Iustin Pop
  Returns:
1143 57c177af Iustin Pop
    - full path to the item if found
1144 57c177af Iustin Pop
    - None otherwise
1145 57c177af Iustin Pop

1146 57c177af Iustin Pop
  """
1147 57c177af Iustin Pop
  for dir_name in search_path:
1148 57c177af Iustin Pop
    item_name = os.path.sep.join([dir_name, name])
1149 57c177af Iustin Pop
    if test(item_name):
1150 57c177af Iustin Pop
      return item_name
1151 57c177af Iustin Pop
  return None
1152 8d1a2a64 Michael Hanselmann
1153 8d1a2a64 Michael Hanselmann
1154 8d1a2a64 Michael Hanselmann
def CheckVolumeGroupSize(vglist, vgname, minsize):
1155 8d1a2a64 Michael Hanselmann
  """Checks if the volume group list is valid.
1156 8d1a2a64 Michael Hanselmann

1157 8d1a2a64 Michael Hanselmann
  A non-None return value means there's an error, and the return value
1158 8d1a2a64 Michael Hanselmann
  is the error message.
1159 8d1a2a64 Michael Hanselmann

1160 8d1a2a64 Michael Hanselmann
  """
1161 8d1a2a64 Michael Hanselmann
  vgsize = vglist.get(vgname, None)
1162 8d1a2a64 Michael Hanselmann
  if vgsize is None:
1163 8d1a2a64 Michael Hanselmann
    return "volume group '%s' missing" % vgname
1164 8d1a2a64 Michael Hanselmann
  elif vgsize < minsize:
1165 8d1a2a64 Michael Hanselmann
    return ("volume group '%s' too small (%s MiB required, %d MiB found)" %
1166 8d1a2a64 Michael Hanselmann
            (vgname, minsize, vgsize))
1167 8d1a2a64 Michael Hanselmann
  return None
1168 7996a135 Iustin Pop
1169 7996a135 Iustin Pop
1170 739be818 Michael Hanselmann
def SplitTime(seconds):
1171 739be818 Michael Hanselmann
  """Splits time as floating point number into a tuple.
1172 739be818 Michael Hanselmann

1173 739be818 Michael Hanselmann
  @param seconds: Time in seconds
1174 739be818 Michael Hanselmann
  @type seconds: int or float
1175 739be818 Michael Hanselmann
  @return: Tuple containing (seconds, milliseconds)
1176 739be818 Michael Hanselmann

1177 739be818 Michael Hanselmann
  """
1178 739be818 Michael Hanselmann
  (seconds, fraction) = divmod(seconds, 1.0)
1179 739be818 Michael Hanselmann
  return (int(seconds), int(round(fraction * 1000, 0)))
1180 739be818 Michael Hanselmann
1181 739be818 Michael Hanselmann
1182 739be818 Michael Hanselmann
def MergeTime(timetuple):
1183 739be818 Michael Hanselmann
  """Merges a tuple into time as a floating point number.
1184 739be818 Michael Hanselmann

1185 739be818 Michael Hanselmann
  @param timetuple: Time as tuple, (seconds, milliseconds)
1186 739be818 Michael Hanselmann
  @type timetuple: tuple
1187 739be818 Michael Hanselmann
  @return: Time as a floating point number expressed in seconds
1188 739be818 Michael Hanselmann

1189 739be818 Michael Hanselmann
  """
1190 739be818 Michael Hanselmann
  (seconds, milliseconds) = timetuple
1191 739be818 Michael Hanselmann
1192 739be818 Michael Hanselmann
  assert 0 <= seconds, "Seconds must be larger than 0"
1193 739be818 Michael Hanselmann
  assert 0 <= milliseconds <= 999, "Milliseconds must be 0-999"
1194 739be818 Michael Hanselmann
1195 739be818 Michael Hanselmann
  return float(seconds) + (float(1) / 1000 * milliseconds)
1196 739be818 Michael Hanselmann
1197 739be818 Michael Hanselmann
1198 7996a135 Iustin Pop
def LockedMethod(fn):
1199 7996a135 Iustin Pop
  """Synchronized object access decorator.
1200 7996a135 Iustin Pop

1201 7996a135 Iustin Pop
  This decorator is intended to protect access to an object using the
1202 7996a135 Iustin Pop
  object's own lock which is hardcoded to '_lock'.
1203 7996a135 Iustin Pop

1204 7996a135 Iustin Pop
  """
1205 7996a135 Iustin Pop
  def wrapper(self, *args, **kwargs):
1206 7996a135 Iustin Pop
    assert hasattr(self, '_lock')
1207 7996a135 Iustin Pop
    lock = self._lock
1208 7996a135 Iustin Pop
    lock.acquire()
1209 7996a135 Iustin Pop
    try:
1210 7996a135 Iustin Pop
      result = fn(self, *args, **kwargs)
1211 7996a135 Iustin Pop
    finally:
1212 7996a135 Iustin Pop
      lock.release()
1213 7996a135 Iustin Pop
    return result
1214 7996a135 Iustin Pop
  return wrapper
1215 eb0f0ce0 Michael Hanselmann
1216 eb0f0ce0 Michael Hanselmann
1217 eb0f0ce0 Michael Hanselmann
def LockFile(fd):
1218 eb0f0ce0 Michael Hanselmann
  """Locks a file using POSIX locks.
1219 eb0f0ce0 Michael Hanselmann

1220 eb0f0ce0 Michael Hanselmann
  """
1221 eb0f0ce0 Michael Hanselmann
  try:
1222 eb0f0ce0 Michael Hanselmann
    fcntl.flock(fd, fcntl.LOCK_EX | fcntl.LOCK_NB)
1223 eb0f0ce0 Michael Hanselmann
  except IOError, err:
1224 eb0f0ce0 Michael Hanselmann
    if err.errno == errno.EAGAIN:
1225 eb0f0ce0 Michael Hanselmann
      raise errors.LockError("File already locked")
1226 eb0f0ce0 Michael Hanselmann
    raise
1227 de499029 Michael Hanselmann
1228 de499029 Michael Hanselmann
1229 a87b4824 Michael Hanselmann
class FileLock(object):
1230 a87b4824 Michael Hanselmann
  """Utility class for file locks.
1231 a87b4824 Michael Hanselmann

1232 a87b4824 Michael Hanselmann
  """
1233 a87b4824 Michael Hanselmann
  def __init__(self, filename):
1234 a87b4824 Michael Hanselmann
    self.filename = filename
1235 a87b4824 Michael Hanselmann
    self.fd = open(self.filename, "w")
1236 a87b4824 Michael Hanselmann
1237 a87b4824 Michael Hanselmann
  def __del__(self):
1238 a87b4824 Michael Hanselmann
    self.Close()
1239 a87b4824 Michael Hanselmann
1240 a87b4824 Michael Hanselmann
  def Close(self):
1241 a87b4824 Michael Hanselmann
    if self.fd:
1242 a87b4824 Michael Hanselmann
      self.fd.close()
1243 a87b4824 Michael Hanselmann
      self.fd = None
1244 a87b4824 Michael Hanselmann
1245 a87b4824 Michael Hanselmann
  def _flock(self, flag, blocking, errmsg):
1246 a87b4824 Michael Hanselmann
    assert self.fd, "Lock was closed"
1247 a87b4824 Michael Hanselmann
1248 a87b4824 Michael Hanselmann
    if not blocking:
1249 a87b4824 Michael Hanselmann
      flag |= fcntl.LOCK_NB
1250 a87b4824 Michael Hanselmann
1251 a87b4824 Michael Hanselmann
    try:
1252 a87b4824 Michael Hanselmann
      fcntl.flock(self.fd, flag)
1253 a87b4824 Michael Hanselmann
    except IOError, err:
1254 a87b4824 Michael Hanselmann
      if err.errno in (errno.EAGAIN, ):
1255 a87b4824 Michael Hanselmann
        raise errors.LockError(errmsg)
1256 aa65ed72 Michael Hanselmann
      else:
1257 aa65ed72 Michael Hanselmann
        logging.exception("fcntl.flock failed")
1258 aa65ed72 Michael Hanselmann
        raise
1259 a87b4824 Michael Hanselmann
1260 a87b4824 Michael Hanselmann
  def Exclusive(self, blocking=False):
1261 a87b4824 Michael Hanselmann
    """Locks the file in exclusive mode.
1262 a87b4824 Michael Hanselmann

1263 a87b4824 Michael Hanselmann
    """
1264 a87b4824 Michael Hanselmann
    self._flock(fcntl.LOCK_EX, blocking,
1265 a87b4824 Michael Hanselmann
                "Failed to lock %s in exclusive mode" % self.filename)
1266 a87b4824 Michael Hanselmann
1267 a87b4824 Michael Hanselmann
  def Shared(self, blocking=False):
1268 a87b4824 Michael Hanselmann
    """Locks the file in shared mode.
1269 a87b4824 Michael Hanselmann

1270 a87b4824 Michael Hanselmann
    """
1271 a87b4824 Michael Hanselmann
    self._flock(fcntl.LOCK_SH, blocking,
1272 a87b4824 Michael Hanselmann
                "Failed to lock %s in shared mode" % self.filename)
1273 a87b4824 Michael Hanselmann
1274 a87b4824 Michael Hanselmann
  def Unlock(self, blocking=True):
1275 a87b4824 Michael Hanselmann
    """Unlocks the file.
1276 a87b4824 Michael Hanselmann

1277 a87b4824 Michael Hanselmann
    According to "man flock", unlocking can also be a nonblocking operation:
1278 a87b4824 Michael Hanselmann
    "To make a non-blocking request, include LOCK_NB with any of the above
1279 a87b4824 Michael Hanselmann
    operations"
1280 a87b4824 Michael Hanselmann

1281 a87b4824 Michael Hanselmann
    """
1282 a87b4824 Michael Hanselmann
    self._flock(fcntl.LOCK_UN, blocking,
1283 a87b4824 Michael Hanselmann
                "Failed to unlock %s" % self.filename)
1284 a87b4824 Michael Hanselmann
1285 a87b4824 Michael Hanselmann
1286 de499029 Michael Hanselmann
class SignalHandler(object):
1287 de499029 Michael Hanselmann
  """Generic signal handler class.
1288 de499029 Michael Hanselmann

1289 de499029 Michael Hanselmann
  It automatically restores the original handler when deconstructed or when
1290 de499029 Michael Hanselmann
  Reset() is called. You can either pass your own handler function in or query
1291 de499029 Michael Hanselmann
  the "called" attribute to detect whether the signal was sent.
1292 de499029 Michael Hanselmann

1293 de499029 Michael Hanselmann
  """
1294 de499029 Michael Hanselmann
  def __init__(self, signum):
1295 de499029 Michael Hanselmann
    """Constructs a new SignalHandler instance.
1296 de499029 Michael Hanselmann

1297 de499029 Michael Hanselmann
    @param signum: Single signal number or set of signal numbers
1298 de499029 Michael Hanselmann

1299 de499029 Michael Hanselmann
    """
1300 de499029 Michael Hanselmann
    if isinstance(signum, (int, long)):
1301 de499029 Michael Hanselmann
      self.signum = set([signum])
1302 de499029 Michael Hanselmann
    else:
1303 de499029 Michael Hanselmann
      self.signum = set(signum)
1304 de499029 Michael Hanselmann
1305 de499029 Michael Hanselmann
    self.called = False
1306 de499029 Michael Hanselmann
1307 de499029 Michael Hanselmann
    self._previous = {}
1308 de499029 Michael Hanselmann
    try:
1309 de499029 Michael Hanselmann
      for signum in self.signum:
1310 de499029 Michael Hanselmann
        # Setup handler
1311 de499029 Michael Hanselmann
        prev_handler = signal.signal(signum, self._HandleSignal)
1312 de499029 Michael Hanselmann
        try:
1313 de499029 Michael Hanselmann
          self._previous[signum] = prev_handler
1314 de499029 Michael Hanselmann
        except:
1315 de499029 Michael Hanselmann
          # Restore previous handler
1316 de499029 Michael Hanselmann
          signal.signal(signum, prev_handler)
1317 de499029 Michael Hanselmann
          raise
1318 de499029 Michael Hanselmann
    except:
1319 de499029 Michael Hanselmann
      # Reset all handlers
1320 de499029 Michael Hanselmann
      self.Reset()
1321 de499029 Michael Hanselmann
      # Here we have a race condition: a handler may have already been called,
1322 de499029 Michael Hanselmann
      # but there's not much we can do about it at this point.
1323 de499029 Michael Hanselmann
      raise
1324 de499029 Michael Hanselmann
1325 de499029 Michael Hanselmann
  def __del__(self):
1326 de499029 Michael Hanselmann
    self.Reset()
1327 de499029 Michael Hanselmann
1328 de499029 Michael Hanselmann
  def Reset(self):
1329 de499029 Michael Hanselmann
    """Restore previous handler.
1330 de499029 Michael Hanselmann

1331 de499029 Michael Hanselmann
    """
1332 de499029 Michael Hanselmann
    for signum, prev_handler in self._previous.items():
1333 de499029 Michael Hanselmann
      signal.signal(signum, prev_handler)
1334 de499029 Michael Hanselmann
      # If successful, remove from dict
1335 de499029 Michael Hanselmann
      del self._previous[signum]
1336 de499029 Michael Hanselmann
1337 de499029 Michael Hanselmann
  def Clear(self):
1338 de499029 Michael Hanselmann
    """Unsets "called" flag.
1339 de499029 Michael Hanselmann

1340 de499029 Michael Hanselmann
    This function can be used in case a signal may arrive several times.
1341 de499029 Michael Hanselmann

1342 de499029 Michael Hanselmann
    """
1343 de499029 Michael Hanselmann
    self.called = False
1344 de499029 Michael Hanselmann
1345 de499029 Michael Hanselmann
  def _HandleSignal(self, signum, frame):
1346 de499029 Michael Hanselmann
    """Actual signal handling function.
1347 de499029 Michael Hanselmann

1348 de499029 Michael Hanselmann
    """
1349 de499029 Michael Hanselmann
    # This is not nice and not absolutely atomic, but it appears to be the only
1350 de499029 Michael Hanselmann
    # solution in Python -- there are no atomic types.
1351 de499029 Michael Hanselmann
    self.called = True