Statistics
| Branch: | Tag: | Revision:

root / lib / utils.py @ 45bc5e4a

History | View | Annotate | Download (34.3 kB)

1 2f31098c Iustin Pop
#
2 a8083063 Iustin Pop
#
3 a8083063 Iustin Pop
4 a8083063 Iustin Pop
# Copyright (C) 2006, 2007 Google Inc.
5 a8083063 Iustin Pop
#
6 a8083063 Iustin Pop
# This program is free software; you can redistribute it and/or modify
7 a8083063 Iustin Pop
# it under the terms of the GNU General Public License as published by
8 a8083063 Iustin Pop
# the Free Software Foundation; either version 2 of the License, or
9 a8083063 Iustin Pop
# (at your option) any later version.
10 a8083063 Iustin Pop
#
11 a8083063 Iustin Pop
# This program is distributed in the hope that it will be useful, but
12 a8083063 Iustin Pop
# WITHOUT ANY WARRANTY; without even the implied warranty of
13 a8083063 Iustin Pop
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 a8083063 Iustin Pop
# General Public License for more details.
15 a8083063 Iustin Pop
#
16 a8083063 Iustin Pop
# You should have received a copy of the GNU General Public License
17 a8083063 Iustin Pop
# along with this program; if not, write to the Free Software
18 a8083063 Iustin Pop
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 a8083063 Iustin Pop
# 02110-1301, USA.
20 a8083063 Iustin Pop
21 a8083063 Iustin Pop
22 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 e67bd559 Michael Hanselmann
debug_locks = False
56 b74159ee Iustin Pop
no_fork = False
57 f362096f Iustin Pop
58 7c0d6283 Michael Hanselmann
59 a8083063 Iustin Pop
class RunResult(object):
60 a8083063 Iustin Pop
  """Simple class for holding the result of running external programs.
61 a8083063 Iustin Pop

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1174 45bc5e4a Michael Hanselmann
  @param value: Time in seconds
1175 45bc5e4a Michael Hanselmann
  @type value: int or float
1176 45bc5e4a Michael Hanselmann
  @return: Tuple containing (seconds, microseconds)
1177 739be818 Michael Hanselmann

1178 739be818 Michael Hanselmann
  """
1179 45bc5e4a Michael Hanselmann
  (seconds, microseconds) = divmod(int(value * 1000000), 1000000)
1180 45bc5e4a Michael Hanselmann
1181 45bc5e4a Michael Hanselmann
  assert 0 <= seconds, \
1182 45bc5e4a Michael Hanselmann
    "Seconds must be larger than or equal to 0, but are %s" % seconds
1183 45bc5e4a Michael Hanselmann
  assert 0 <= microseconds <= 999999, \
1184 45bc5e4a Michael Hanselmann
    "Microseconds must be 0-999999, but are %s" % microseconds
1185 45bc5e4a Michael Hanselmann
1186 45bc5e4a Michael Hanselmann
  return (int(seconds), int(microseconds))
1187 739be818 Michael Hanselmann
1188 739be818 Michael Hanselmann
1189 739be818 Michael Hanselmann
def MergeTime(timetuple):
1190 739be818 Michael Hanselmann
  """Merges a tuple into time as a floating point number.
1191 739be818 Michael Hanselmann

1192 45bc5e4a Michael Hanselmann
  @param timetuple: Time as tuple, (seconds, microseconds)
1193 739be818 Michael Hanselmann
  @type timetuple: tuple
1194 739be818 Michael Hanselmann
  @return: Time as a floating point number expressed in seconds
1195 739be818 Michael Hanselmann

1196 739be818 Michael Hanselmann
  """
1197 45bc5e4a Michael Hanselmann
  (seconds, microseconds) = timetuple
1198 739be818 Michael Hanselmann
1199 45bc5e4a Michael Hanselmann
  assert 0 <= seconds, \
1200 45bc5e4a Michael Hanselmann
    "Seconds must be larger than or equal to 0, but are %s" % seconds
1201 45bc5e4a Michael Hanselmann
  assert 0 <= microseconds <= 999999, \
1202 45bc5e4a Michael Hanselmann
    "Microseconds must be 0-999999, but are %s" % microseconds
1203 739be818 Michael Hanselmann
1204 45bc5e4a Michael Hanselmann
  return float(seconds) + (float(microseconds) * 0.000001)
1205 739be818 Michael Hanselmann
1206 739be818 Michael Hanselmann
1207 7996a135 Iustin Pop
def LockedMethod(fn):
1208 7996a135 Iustin Pop
  """Synchronized object access decorator.
1209 7996a135 Iustin Pop

1210 7996a135 Iustin Pop
  This decorator is intended to protect access to an object using the
1211 7996a135 Iustin Pop
  object's own lock which is hardcoded to '_lock'.
1212 7996a135 Iustin Pop

1213 7996a135 Iustin Pop
  """
1214 e67bd559 Michael Hanselmann
  def _LockDebug(*args, **kwargs):
1215 e67bd559 Michael Hanselmann
    if debug_locks:
1216 e67bd559 Michael Hanselmann
      logging.debug(*args, **kwargs)
1217 e67bd559 Michael Hanselmann
1218 7996a135 Iustin Pop
  def wrapper(self, *args, **kwargs):
1219 7996a135 Iustin Pop
    assert hasattr(self, '_lock')
1220 7996a135 Iustin Pop
    lock = self._lock
1221 e67bd559 Michael Hanselmann
    _LockDebug("Waiting for %s", lock)
1222 7996a135 Iustin Pop
    lock.acquire()
1223 7996a135 Iustin Pop
    try:
1224 e67bd559 Michael Hanselmann
      _LockDebug("Acquired %s", lock)
1225 7996a135 Iustin Pop
      result = fn(self, *args, **kwargs)
1226 7996a135 Iustin Pop
    finally:
1227 e67bd559 Michael Hanselmann
      _LockDebug("Releasing %s", lock)
1228 7996a135 Iustin Pop
      lock.release()
1229 e67bd559 Michael Hanselmann
      _LockDebug("Released %s", lock)
1230 7996a135 Iustin Pop
    return result
1231 7996a135 Iustin Pop
  return wrapper
1232 eb0f0ce0 Michael Hanselmann
1233 eb0f0ce0 Michael Hanselmann
1234 eb0f0ce0 Michael Hanselmann
def LockFile(fd):
1235 eb0f0ce0 Michael Hanselmann
  """Locks a file using POSIX locks.
1236 eb0f0ce0 Michael Hanselmann

1237 eb0f0ce0 Michael Hanselmann
  """
1238 eb0f0ce0 Michael Hanselmann
  try:
1239 eb0f0ce0 Michael Hanselmann
    fcntl.flock(fd, fcntl.LOCK_EX | fcntl.LOCK_NB)
1240 eb0f0ce0 Michael Hanselmann
  except IOError, err:
1241 eb0f0ce0 Michael Hanselmann
    if err.errno == errno.EAGAIN:
1242 eb0f0ce0 Michael Hanselmann
      raise errors.LockError("File already locked")
1243 eb0f0ce0 Michael Hanselmann
    raise
1244 de499029 Michael Hanselmann
1245 de499029 Michael Hanselmann
1246 a87b4824 Michael Hanselmann
class FileLock(object):
1247 a87b4824 Michael Hanselmann
  """Utility class for file locks.
1248 a87b4824 Michael Hanselmann

1249 a87b4824 Michael Hanselmann
  """
1250 a87b4824 Michael Hanselmann
  def __init__(self, filename):
1251 a87b4824 Michael Hanselmann
    self.filename = filename
1252 a87b4824 Michael Hanselmann
    self.fd = open(self.filename, "w")
1253 a87b4824 Michael Hanselmann
1254 a87b4824 Michael Hanselmann
  def __del__(self):
1255 a87b4824 Michael Hanselmann
    self.Close()
1256 a87b4824 Michael Hanselmann
1257 a87b4824 Michael Hanselmann
  def Close(self):
1258 a87b4824 Michael Hanselmann
    if self.fd:
1259 a87b4824 Michael Hanselmann
      self.fd.close()
1260 a87b4824 Michael Hanselmann
      self.fd = None
1261 a87b4824 Michael Hanselmann
1262 a87b4824 Michael Hanselmann
  def _flock(self, flag, blocking, errmsg):
1263 a87b4824 Michael Hanselmann
    assert self.fd, "Lock was closed"
1264 a87b4824 Michael Hanselmann
1265 a87b4824 Michael Hanselmann
    if not blocking:
1266 a87b4824 Michael Hanselmann
      flag |= fcntl.LOCK_NB
1267 a87b4824 Michael Hanselmann
1268 a87b4824 Michael Hanselmann
    try:
1269 a87b4824 Michael Hanselmann
      fcntl.flock(self.fd, flag)
1270 a87b4824 Michael Hanselmann
    except IOError, err:
1271 a87b4824 Michael Hanselmann
      if err.errno in (errno.EAGAIN, ):
1272 a87b4824 Michael Hanselmann
        raise errors.LockError(errmsg)
1273 aa65ed72 Michael Hanselmann
      else:
1274 aa65ed72 Michael Hanselmann
        logging.exception("fcntl.flock failed")
1275 aa65ed72 Michael Hanselmann
        raise
1276 a87b4824 Michael Hanselmann
1277 a87b4824 Michael Hanselmann
  def Exclusive(self, blocking=False):
1278 a87b4824 Michael Hanselmann
    """Locks the file in exclusive mode.
1279 a87b4824 Michael Hanselmann

1280 a87b4824 Michael Hanselmann
    """
1281 a87b4824 Michael Hanselmann
    self._flock(fcntl.LOCK_EX, blocking,
1282 a87b4824 Michael Hanselmann
                "Failed to lock %s in exclusive mode" % self.filename)
1283 a87b4824 Michael Hanselmann
1284 a87b4824 Michael Hanselmann
  def Shared(self, blocking=False):
1285 a87b4824 Michael Hanselmann
    """Locks the file in shared mode.
1286 a87b4824 Michael Hanselmann

1287 a87b4824 Michael Hanselmann
    """
1288 a87b4824 Michael Hanselmann
    self._flock(fcntl.LOCK_SH, blocking,
1289 a87b4824 Michael Hanselmann
                "Failed to lock %s in shared mode" % self.filename)
1290 a87b4824 Michael Hanselmann
1291 a87b4824 Michael Hanselmann
  def Unlock(self, blocking=True):
1292 a87b4824 Michael Hanselmann
    """Unlocks the file.
1293 a87b4824 Michael Hanselmann

1294 a87b4824 Michael Hanselmann
    According to "man flock", unlocking can also be a nonblocking operation:
1295 a87b4824 Michael Hanselmann
    "To make a non-blocking request, include LOCK_NB with any of the above
1296 a87b4824 Michael Hanselmann
    operations"
1297 a87b4824 Michael Hanselmann

1298 a87b4824 Michael Hanselmann
    """
1299 a87b4824 Michael Hanselmann
    self._flock(fcntl.LOCK_UN, blocking,
1300 a87b4824 Michael Hanselmann
                "Failed to unlock %s" % self.filename)
1301 a87b4824 Michael Hanselmann
1302 a87b4824 Michael Hanselmann
1303 de499029 Michael Hanselmann
class SignalHandler(object):
1304 de499029 Michael Hanselmann
  """Generic signal handler class.
1305 de499029 Michael Hanselmann

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

1310 de499029 Michael Hanselmann
  """
1311 de499029 Michael Hanselmann
  def __init__(self, signum):
1312 de499029 Michael Hanselmann
    """Constructs a new SignalHandler instance.
1313 de499029 Michael Hanselmann

1314 de499029 Michael Hanselmann
    @param signum: Single signal number or set of signal numbers
1315 de499029 Michael Hanselmann

1316 de499029 Michael Hanselmann
    """
1317 de499029 Michael Hanselmann
    if isinstance(signum, (int, long)):
1318 de499029 Michael Hanselmann
      self.signum = set([signum])
1319 de499029 Michael Hanselmann
    else:
1320 de499029 Michael Hanselmann
      self.signum = set(signum)
1321 de499029 Michael Hanselmann
1322 de499029 Michael Hanselmann
    self.called = False
1323 de499029 Michael Hanselmann
1324 de499029 Michael Hanselmann
    self._previous = {}
1325 de499029 Michael Hanselmann
    try:
1326 de499029 Michael Hanselmann
      for signum in self.signum:
1327 de499029 Michael Hanselmann
        # Setup handler
1328 de499029 Michael Hanselmann
        prev_handler = signal.signal(signum, self._HandleSignal)
1329 de499029 Michael Hanselmann
        try:
1330 de499029 Michael Hanselmann
          self._previous[signum] = prev_handler
1331 de499029 Michael Hanselmann
        except:
1332 de499029 Michael Hanselmann
          # Restore previous handler
1333 de499029 Michael Hanselmann
          signal.signal(signum, prev_handler)
1334 de499029 Michael Hanselmann
          raise
1335 de499029 Michael Hanselmann
    except:
1336 de499029 Michael Hanselmann
      # Reset all handlers
1337 de499029 Michael Hanselmann
      self.Reset()
1338 de499029 Michael Hanselmann
      # Here we have a race condition: a handler may have already been called,
1339 de499029 Michael Hanselmann
      # but there's not much we can do about it at this point.
1340 de499029 Michael Hanselmann
      raise
1341 de499029 Michael Hanselmann
1342 de499029 Michael Hanselmann
  def __del__(self):
1343 de499029 Michael Hanselmann
    self.Reset()
1344 de499029 Michael Hanselmann
1345 de499029 Michael Hanselmann
  def Reset(self):
1346 de499029 Michael Hanselmann
    """Restore previous handler.
1347 de499029 Michael Hanselmann

1348 de499029 Michael Hanselmann
    """
1349 de499029 Michael Hanselmann
    for signum, prev_handler in self._previous.items():
1350 de499029 Michael Hanselmann
      signal.signal(signum, prev_handler)
1351 de499029 Michael Hanselmann
      # If successful, remove from dict
1352 de499029 Michael Hanselmann
      del self._previous[signum]
1353 de499029 Michael Hanselmann
1354 de499029 Michael Hanselmann
  def Clear(self):
1355 de499029 Michael Hanselmann
    """Unsets "called" flag.
1356 de499029 Michael Hanselmann

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

1359 de499029 Michael Hanselmann
    """
1360 de499029 Michael Hanselmann
    self.called = False
1361 de499029 Michael Hanselmann
1362 de499029 Michael Hanselmann
  def _HandleSignal(self, signum, frame):
1363 de499029 Michael Hanselmann
    """Actual signal handling function.
1364 de499029 Michael Hanselmann

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