Statistics
| Branch: | Tag: | Revision:

root / lib / utils.py @ e873317a

History | View | Annotate | Download (31.5 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 a8083063 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 a8083063 Iustin Pop
    self.signal = signal
81 a8083063 Iustin Pop
    self.stdout = stdout
82 a8083063 Iustin Pop
    self.stderr = stderr
83 a8083063 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 a8083063 Iustin Pop
def RunCmd(cmd):
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 a8083063 Iustin Pop
  Args:
112 a8083063 Iustin Pop
    cmd: command to run. (str)
113 a8083063 Iustin Pop

114 a8083063 Iustin Pop
  Returns: `RunResult` instance
115 a8083063 Iustin Pop

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

182 a8083063 Iustin Pop
  Remove a file, ignoring non-existing ones or directories. Other
183 a8083063 Iustin Pop
  errors are passed.
184 a8083063 Iustin Pop

185 a8083063 Iustin Pop
  """
186 a8083063 Iustin Pop
  try:
187 a8083063 Iustin Pop
    os.unlink(filename)
188 a8083063 Iustin Pop
  except OSError, err:
189 4ca1b175 Alexander Schreiber
    if err.errno not in (errno.ENOENT, errno.EISDIR):
190 a8083063 Iustin Pop
      raise
191 a8083063 Iustin Pop
192 a8083063 Iustin Pop
193 a8083063 Iustin Pop
def _FingerprintFile(filename):
194 a8083063 Iustin Pop
  """Compute the fingerprint of a file.
195 a8083063 Iustin Pop

196 a8083063 Iustin Pop
  If the file does not exist, a None will be returned
197 a8083063 Iustin Pop
  instead.
198 a8083063 Iustin Pop

199 a8083063 Iustin Pop
  Args:
200 a8083063 Iustin Pop
    filename - Filename (str)
201 a8083063 Iustin Pop

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

222 a8083063 Iustin Pop
  Args:
223 a8083063 Iustin Pop
    files - array of filenames.  ( [str, ...] )
224 a8083063 Iustin Pop

225 a8083063 Iustin Pop
  Return value:
226 a8083063 Iustin Pop
    dictionary of filename: fingerprint for the files that exist
227 a8083063 Iustin Pop

228 a8083063 Iustin Pop
  """
229 a8083063 Iustin Pop
  ret = {}
230 a8083063 Iustin Pop
231 a8083063 Iustin Pop
  for filename in files:
232 a8083063 Iustin Pop
    cksum = _FingerprintFile(filename)
233 a8083063 Iustin Pop
    if cksum:
234 a8083063 Iustin Pop
      ret[filename] = cksum
235 a8083063 Iustin Pop
236 a8083063 Iustin Pop
  return ret
237 a8083063 Iustin Pop
238 a8083063 Iustin Pop
239 a8083063 Iustin Pop
def CheckDict(target, template, logname=None):
240 a8083063 Iustin Pop
  """Ensure a dictionary has a required set of keys.
241 a8083063 Iustin Pop

242 a8083063 Iustin Pop
  For the given dictionaries `target` and `template`, ensure target
243 a8083063 Iustin Pop
  has all the keys from template. Missing keys are added with values
244 a8083063 Iustin Pop
  from template.
245 a8083063 Iustin Pop

246 a8083063 Iustin Pop
  Args:
247 a8083063 Iustin Pop
    target   - the dictionary to check
248 a8083063 Iustin Pop
    template - template dictionary
249 a8083063 Iustin Pop
    logname  - a caller-chosen string to identify the debug log
250 a8083063 Iustin Pop
               entry; if None, no logging will be done
251 a8083063 Iustin Pop

252 a8083063 Iustin Pop
  Returns value:
253 a8083063 Iustin Pop
    None
254 a8083063 Iustin Pop

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

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

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

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

300 d9f311d7 Iustin Pop
  @param pidfile: Path to a file containing the pid to be checked
301 d9f311d7 Iustin Pop
  @type  pidfile: string (filename)
302 d9f311d7 Iustin Pop
  @return: The process id, if the file exista and contains a valid PID,
303 d9f311d7 Iustin Pop
           otherwise 0
304 d9f311d7 Iustin Pop
  @rtype: int
305 fee80e90 Guido Trotter

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

326 a8083063 Iustin Pop
  This function will try to match a name like test1 against a list
327 a8083063 Iustin Pop
  like ['test1.example.com', 'test2.example.com', ...]. Against this
328 a8083063 Iustin Pop
  list, 'test1' as well as 'test1.example' will match, but not
329 a8083063 Iustin Pop
  'test1.ex'. A multiple match will be considered as no match at all
330 a8083063 Iustin Pop
  (e.g. 'test1' against ['test1.example.com', 'test1.example.org']).
331 a8083063 Iustin Pop

332 a8083063 Iustin Pop
  Args:
333 a8083063 Iustin Pop
    key: the name to be searched
334 a8083063 Iustin Pop
    name_list: the list of strings against which to search the key
335 a8083063 Iustin Pop

336 a8083063 Iustin Pop
  Returns:
337 a8083063 Iustin Pop
    None if there is no match *or* if there are multiple matches
338 a8083063 Iustin Pop
    otherwise the element from the list which matches
339 a8083063 Iustin Pop

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

351 bcf043c9 Iustin Pop
  """
352 89e1fc26 Iustin Pop
  def __init__(self, name=None):
353 bcf043c9 Iustin Pop
    """Initialize the host name object.
354 bcf043c9 Iustin Pop

355 89e1fc26 Iustin Pop
    If the name argument is not passed, it will use this system's
356 89e1fc26 Iustin Pop
    name.
357 bcf043c9 Iustin Pop

358 bcf043c9 Iustin Pop
    """
359 89e1fc26 Iustin Pop
    if name is None:
360 89e1fc26 Iustin Pop
      name = self.SysName()
361 89e1fc26 Iustin Pop
362 89e1fc26 Iustin Pop
    self.query = name
363 89e1fc26 Iustin Pop
    self.name, self.aliases, self.ipaddrs = self.LookupHostname(name)
364 bcf043c9 Iustin Pop
    self.ip = self.ipaddrs[0]
365 bcf043c9 Iustin Pop
366 c8a0948f Michael Hanselmann
  def ShortName(self):
367 c8a0948f Michael Hanselmann
    """Returns the hostname without domain.
368 c8a0948f Michael Hanselmann

369 c8a0948f Michael Hanselmann
    """
370 c8a0948f Michael Hanselmann
    return self.name.split('.')[0]
371 c8a0948f Michael Hanselmann
372 89e1fc26 Iustin Pop
  @staticmethod
373 89e1fc26 Iustin Pop
  def SysName():
374 89e1fc26 Iustin Pop
    """Return the current system's name.
375 bcf043c9 Iustin Pop

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

378 89e1fc26 Iustin Pop
    """
379 89e1fc26 Iustin Pop
    return socket.gethostname()
380 a8083063 Iustin Pop
381 89e1fc26 Iustin Pop
  @staticmethod
382 89e1fc26 Iustin Pop
  def LookupHostname(hostname):
383 89e1fc26 Iustin Pop
    """Look up hostname
384 a8083063 Iustin Pop

385 89e1fc26 Iustin Pop
    Args:
386 89e1fc26 Iustin Pop
      hostname: hostname to look up
387 89e1fc26 Iustin Pop

388 89e1fc26 Iustin Pop
    Returns:
389 89e1fc26 Iustin Pop
      a tuple (name, aliases, ipaddrs) as returned by socket.gethostbyname_ex
390 89e1fc26 Iustin Pop
      in case of errors in resolving, we raise a ResolverError
391 89e1fc26 Iustin Pop

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

405 a8083063 Iustin Pop
  Returns:
406 a8083063 Iustin Pop
     Dictionary with keys volume name and values the size of the volume
407 a8083063 Iustin Pop

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

431 a8083063 Iustin Pop
  Returns:
432 a8083063 Iustin Pop
     True if it does, false otherwise.
433 a8083063 Iustin Pop

434 a8083063 Iustin Pop
  """
435 a8083063 Iustin Pop
  return os.path.isdir("/sys/class/net/%s/bridge" % bridge)
436 a8083063 Iustin Pop
437 a8083063 Iustin Pop
438 a8083063 Iustin Pop
def NiceSort(name_list):
439 a8083063 Iustin Pop
  """Sort a list of strings based on digit and non-digit groupings.
440 a8083063 Iustin Pop

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

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

448 a8083063 Iustin Pop
  Return value
449 a8083063 Iustin Pop
    - a copy of the list sorted according to our algorithm
450 a8083063 Iustin Pop

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

475 a8083063 Iustin Pop
  This function tries to apply function `fn` to `val`. If no
476 a8083063 Iustin Pop
  ValueError or TypeError exceptions are raised, it will return the
477 a8083063 Iustin Pop
  result, else it will return the original value. Any other exceptions
478 a8083063 Iustin Pop
  are propagated to the caller.
479 a8083063 Iustin Pop

480 a8083063 Iustin Pop
  """
481 a8083063 Iustin Pop
  try:
482 a8083063 Iustin Pop
    nv = fn(val)
483 a8083063 Iustin Pop
  except (ValueError, TypeError), err:
484 a8083063 Iustin Pop
    nv = val
485 a8083063 Iustin Pop
  return nv
486 a8083063 Iustin Pop
487 a8083063 Iustin Pop
488 a8083063 Iustin Pop
def IsValidIP(ip):
489 a8083063 Iustin Pop
  """Verifies the syntax of an IP address.
490 a8083063 Iustin Pop

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

494 a8083063 Iustin Pop
  """
495 a8083063 Iustin Pop
  unit = "(0|[1-9]\d{0,2})"
496 a8083063 Iustin Pop
  return re.match("^%s\.%s\.%s\.%s$" % (unit, unit, unit, unit), ip)
497 a8083063 Iustin Pop
498 a8083063 Iustin Pop
499 a8083063 Iustin Pop
def IsValidShellParam(word):
500 a8083063 Iustin Pop
  """Verifies is the given word is safe from the shell's p.o.v.
501 a8083063 Iustin Pop

502 a8083063 Iustin Pop
  This means that we can pass this to a command via the shell and be
503 a8083063 Iustin Pop
  sure that it doesn't alter the command line and is passed as such to
504 a8083063 Iustin Pop
  the actual command.
505 a8083063 Iustin Pop

506 a8083063 Iustin Pop
  Note that we are overly restrictive here, in order to be on the safe
507 a8083063 Iustin Pop
  side.
508 a8083063 Iustin Pop

509 a8083063 Iustin Pop
  """
510 a8083063 Iustin Pop
  return bool(re.match("^[-a-zA-Z0-9._+/:%@]+$", word))
511 a8083063 Iustin Pop
512 a8083063 Iustin Pop
513 a8083063 Iustin Pop
def BuildShellCmd(template, *args):
514 a8083063 Iustin Pop
  """Build a safe shell command line from the given arguments.
515 a8083063 Iustin Pop

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

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

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

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

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

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

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

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

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

687 d9c02ca6 Michael Hanselmann
  """
688 d9c02ca6 Michael Hanselmann
  hi = HostInfo(name=hostname)
689 d9c02ca6 Michael Hanselmann
  SetEtcHostsEntry(constants.ETC_HOSTS, hi.ip, hi.name, [hi.ShortName()])
690 d9c02ca6 Michael Hanselmann
691 d9c02ca6 Michael Hanselmann
692 899d2a81 Michael Hanselmann
def RemoveEtcHostsEntry(file_name, hostname):
693 3e1cdf9f Michael Hanselmann
  """Removes a hostname from /etc/hosts.
694 899d2a81 Michael Hanselmann

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

731 d9c02ca6 Michael Hanselmann
  """
732 d9c02ca6 Michael Hanselmann
  hi = HostInfo(name=hostname)
733 d9c02ca6 Michael Hanselmann
  RemoveEtcHostsEntry(constants.ETC_HOSTS, hi.name)
734 d9c02ca6 Michael Hanselmann
  RemoveEtcHostsEntry(constants.ETC_HOSTS, hi.ShortName())
735 d9c02ca6 Michael Hanselmann
736 d9c02ca6 Michael Hanselmann
737 a8083063 Iustin Pop
def CreateBackup(file_name):
738 a8083063 Iustin Pop
  """Creates a backup of a file.
739 a8083063 Iustin Pop

740 a8083063 Iustin Pop
  Returns: the path to the newly created backup file.
741 a8083063 Iustin Pop

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

767 a8083063 Iustin Pop
  """
768 a8083063 Iustin Pop
  if _re_shell_unquoted.match(value):
769 a8083063 Iustin Pop
    return value
770 a8083063 Iustin Pop
  else:
771 a8083063 Iustin Pop
    return "'%s'" % value.replace("'", "'\\''")
772 a8083063 Iustin Pop
773 a8083063 Iustin Pop
774 a8083063 Iustin Pop
def ShellQuoteArgs(args):
775 a8083063 Iustin Pop
  """Quotes all given shell arguments and concatenates using spaces.
776 a8083063 Iustin Pop

777 a8083063 Iustin Pop
  """
778 a8083063 Iustin Pop
  return ' '.join([ShellQuote(i) for i in args])
779 88d14415 Michael Hanselmann
780 88d14415 Michael Hanselmann
781 b15d625f Iustin Pop
def TcpPing(target, port, timeout=10, live_port_needed=False, source=None):
782 2c30e9d7 Alexander Schreiber
  """Simple ping implementation using TCP connect(2).
783 2c30e9d7 Alexander Schreiber

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

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

822 eedbda4b Michael Hanselmann
  """
823 f3299a07 Michael Hanselmann
  files = [i for i in os.listdir(path) if not i.startswith(".")]
824 f3299a07 Michael Hanselmann
  files.sort()
825 f3299a07 Michael Hanselmann
  return files
826 2f8b60b3 Iustin Pop
827 2f8b60b3 Iustin Pop
828 257f4c0a Iustin Pop
def GetHomeDir(user, default=None):
829 257f4c0a Iustin Pop
  """Try to get the homedir of the given user.
830 257f4c0a Iustin Pop

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

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

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

867 087b34fe Iustin Pop
  The file_name and either fn (a function taking one argument, the
868 087b34fe Iustin Pop
  file descriptor, and which should write the data to it) or data (the
869 087b34fe Iustin Pop
  contents of the file) must be passed. The other arguments are
870 087b34fe Iustin Pop
  optional and allow setting the file mode, owner and group, and the
871 087b34fe Iustin Pop
  mtime/atime of the file.
872 087b34fe Iustin Pop

873 087b34fe Iustin Pop
  If the function doesn't raise an exception, it has succeeded and the
874 087b34fe Iustin Pop
  target file has the new contents. If the file has raised an
875 087b34fe Iustin Pop
  exception, an existing target file should be unmodified and the
876 087b34fe Iustin Pop
  temporary file should be removed.
877 087b34fe Iustin Pop

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

891 71714516 Michael Hanselmann
  Returns:
892 71714516 Michael Hanselmann
    None if "close" parameter evaluates to True, otherwise file descriptor.
893 71714516 Michael Hanselmann

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

945 7b4126b7 Iustin Pop
  The seq argument should be a sorted list of positive integers. The
946 7b4126b7 Iustin Pop
  first time the index of an element is smaller than the element
947 7b4126b7 Iustin Pop
  value, the index will be returned.
948 7b4126b7 Iustin Pop

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

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

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

980 f7414041 Michael Hanselmann
  Element order is preserved.
981 f7414041 Michael Hanselmann
  """
982 f7414041 Michael Hanselmann
  seen = set()
983 f7414041 Michael Hanselmann
  return [i for i in seq if i not in seen and not seen.add(i)]
984 1862d460 Alexander Schreiber
985 1862d460 Alexander Schreiber
986 1862d460 Alexander Schreiber
def IsValidMac(mac):
987 1862d460 Alexander Schreiber
  """Predicate to check if a MAC address is valid.
988 1862d460 Alexander Schreiber

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

999 06009e27 Iustin Pop
  """
1000 06009e27 Iustin Pop
  if duration < 0:
1001 06009e27 Iustin Pop
    return False
1002 06009e27 Iustin Pop
  time.sleep(duration)
1003 06009e27 Iustin Pop
  return True
1004 8f765069 Iustin Pop
1005 8f765069 Iustin Pop
1006 8ff612c2 Iustin Pop
def Daemonize(logfile, noclose_fds=None):
1007 8f765069 Iustin Pop
  """Daemonize the current process.
1008 8f765069 Iustin Pop

1009 8f765069 Iustin Pop
  This detaches the current process from the controlling terminal and
1010 8f765069 Iustin Pop
  runs it in the background as a daemon.
1011 8f765069 Iustin Pop

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

1062 b330ac0b Guido Trotter
  """
1063 b330ac0b Guido Trotter
  return os.path.join(constants.RUN_GANETI_DIR, "%s.pid" % name)
1064 b330ac0b Guido Trotter
1065 b330ac0b Guido Trotter
1066 b330ac0b Guido Trotter
def WritePidFile(name):
1067 b330ac0b Guido Trotter
  """Write the current process pidfile.
1068 b330ac0b Guido Trotter

1069 b330ac0b Guido Trotter
  The file will be written to constants.RUN_GANETI_DIR/name.pid
1070 b330ac0b Guido Trotter

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

1083 b330ac0b Guido Trotter
  Any errors are ignored.
1084 b330ac0b Guido Trotter

1085 b330ac0b Guido Trotter
  """
1086 b330ac0b Guido Trotter
  pid = os.getpid()
1087 53beffbb Iustin Pop
  pidfilename = DaemonPidFileName(name)
1088 b330ac0b Guido Trotter
  # TODO: we could check here that the file contains our pid
1089 b330ac0b Guido Trotter
  try:
1090 b330ac0b Guido Trotter
    RemoveFile(pidfilename)
1091 b330ac0b Guido Trotter
  except:
1092 b330ac0b Guido Trotter
    pass
1093 b330ac0b Guido Trotter
1094 b330ac0b Guido Trotter
1095 b2a1f511 Iustin Pop
def KillProcess(pid, signal=signal.SIGTERM, timeout=30):
1096 b2a1f511 Iustin Pop
  """Kill a process given by its pid.
1097 b2a1f511 Iustin Pop

1098 b2a1f511 Iustin Pop
  @type pid: int
1099 b2a1f511 Iustin Pop
  @param pid: The PID to terminate.
1100 b2a1f511 Iustin Pop
  @type signal: int
1101 b2a1f511 Iustin Pop
  @param signal: The signal to send, by default SIGTERM
1102 b2a1f511 Iustin Pop
  @type timeout: int
1103 b2a1f511 Iustin Pop
  @param timeout: The timeout after which, if the process is still alive,
1104 b2a1f511 Iustin Pop
                  a SIGKILL will be sent. If not positive, no such checking
1105 b2a1f511 Iustin Pop
                  will be done
1106 b2a1f511 Iustin Pop

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

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

1130 57c177af Iustin Pop
  Args:
1131 57c177af Iustin Pop
    - name: the name to look for
1132 57c177af Iustin Pop
    - search_path: list of directory names
1133 57c177af Iustin Pop
    - test: the test which the full path must satisfy
1134 57c177af Iustin Pop
      (defaults to os.path.exists)
1135 57c177af Iustin Pop

1136 57c177af Iustin Pop
  Returns:
1137 57c177af Iustin Pop
    - full path to the item if found
1138 57c177af Iustin Pop
    - None otherwise
1139 57c177af Iustin Pop

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

1151 8d1a2a64 Michael Hanselmann
  A non-None return value means there's an error, and the return value
1152 8d1a2a64 Michael Hanselmann
  is the error message.
1153 8d1a2a64 Michael Hanselmann

1154 8d1a2a64 Michael Hanselmann
  """
1155 8d1a2a64 Michael Hanselmann
  vgsize = vglist.get(vgname, None)
1156 8d1a2a64 Michael Hanselmann
  if vgsize is None:
1157 8d1a2a64 Michael Hanselmann
    return "volume group '%s' missing" % vgname
1158 8d1a2a64 Michael Hanselmann
  elif vgsize < minsize:
1159 8d1a2a64 Michael Hanselmann
    return ("volume group '%s' too small (%s MiB required, %d MiB found)" %
1160 8d1a2a64 Michael Hanselmann
            (vgname, minsize, vgsize))
1161 8d1a2a64 Michael Hanselmann
  return None
1162 7996a135 Iustin Pop
1163 7996a135 Iustin Pop
1164 7996a135 Iustin Pop
def LockedMethod(fn):
1165 7996a135 Iustin Pop
  """Synchronized object access decorator.
1166 7996a135 Iustin Pop

1167 7996a135 Iustin Pop
  This decorator is intended to protect access to an object using the
1168 7996a135 Iustin Pop
  object's own lock which is hardcoded to '_lock'.
1169 7996a135 Iustin Pop

1170 7996a135 Iustin Pop
  """
1171 7996a135 Iustin Pop
  def wrapper(self, *args, **kwargs):
1172 7996a135 Iustin Pop
    assert hasattr(self, '_lock')
1173 7996a135 Iustin Pop
    lock = self._lock
1174 7996a135 Iustin Pop
    lock.acquire()
1175 7996a135 Iustin Pop
    try:
1176 7996a135 Iustin Pop
      result = fn(self, *args, **kwargs)
1177 7996a135 Iustin Pop
    finally:
1178 7996a135 Iustin Pop
      lock.release()
1179 7996a135 Iustin Pop
    return result
1180 7996a135 Iustin Pop
  return wrapper
1181 eb0f0ce0 Michael Hanselmann
1182 eb0f0ce0 Michael Hanselmann
1183 eb0f0ce0 Michael Hanselmann
def LockFile(fd):
1184 eb0f0ce0 Michael Hanselmann
  """Locks a file using POSIX locks.
1185 eb0f0ce0 Michael Hanselmann

1186 eb0f0ce0 Michael Hanselmann
  """
1187 eb0f0ce0 Michael Hanselmann
  try:
1188 eb0f0ce0 Michael Hanselmann
    fcntl.flock(fd, fcntl.LOCK_EX | fcntl.LOCK_NB)
1189 eb0f0ce0 Michael Hanselmann
  except IOError, err:
1190 eb0f0ce0 Michael Hanselmann
    if err.errno == errno.EAGAIN:
1191 eb0f0ce0 Michael Hanselmann
      raise errors.LockError("File already locked")
1192 eb0f0ce0 Michael Hanselmann
    raise
1193 de499029 Michael Hanselmann
1194 de499029 Michael Hanselmann
1195 de499029 Michael Hanselmann
class SignalHandler(object):
1196 de499029 Michael Hanselmann
  """Generic signal handler class.
1197 de499029 Michael Hanselmann

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

1202 de499029 Michael Hanselmann
  """
1203 de499029 Michael Hanselmann
  def __init__(self, signum):
1204 de499029 Michael Hanselmann
    """Constructs a new SignalHandler instance.
1205 de499029 Michael Hanselmann

1206 de499029 Michael Hanselmann
    @param signum: Single signal number or set of signal numbers
1207 de499029 Michael Hanselmann

1208 de499029 Michael Hanselmann
    """
1209 de499029 Michael Hanselmann
    if isinstance(signum, (int, long)):
1210 de499029 Michael Hanselmann
      self.signum = set([signum])
1211 de499029 Michael Hanselmann
    else:
1212 de499029 Michael Hanselmann
      self.signum = set(signum)
1213 de499029 Michael Hanselmann
1214 de499029 Michael Hanselmann
    self.called = False
1215 de499029 Michael Hanselmann
1216 de499029 Michael Hanselmann
    self._previous = {}
1217 de499029 Michael Hanselmann
    try:
1218 de499029 Michael Hanselmann
      for signum in self.signum:
1219 de499029 Michael Hanselmann
        # Setup handler
1220 de499029 Michael Hanselmann
        prev_handler = signal.signal(signum, self._HandleSignal)
1221 de499029 Michael Hanselmann
        try:
1222 de499029 Michael Hanselmann
          self._previous[signum] = prev_handler
1223 de499029 Michael Hanselmann
        except:
1224 de499029 Michael Hanselmann
          # Restore previous handler
1225 de499029 Michael Hanselmann
          signal.signal(signum, prev_handler)
1226 de499029 Michael Hanselmann
          raise
1227 de499029 Michael Hanselmann
    except:
1228 de499029 Michael Hanselmann
      # Reset all handlers
1229 de499029 Michael Hanselmann
      self.Reset()
1230 de499029 Michael Hanselmann
      # Here we have a race condition: a handler may have already been called,
1231 de499029 Michael Hanselmann
      # but there's not much we can do about it at this point.
1232 de499029 Michael Hanselmann
      raise
1233 de499029 Michael Hanselmann
1234 de499029 Michael Hanselmann
  def __del__(self):
1235 de499029 Michael Hanselmann
    self.Reset()
1236 de499029 Michael Hanselmann
1237 de499029 Michael Hanselmann
  def Reset(self):
1238 de499029 Michael Hanselmann
    """Restore previous handler.
1239 de499029 Michael Hanselmann

1240 de499029 Michael Hanselmann
    """
1241 de499029 Michael Hanselmann
    for signum, prev_handler in self._previous.items():
1242 de499029 Michael Hanselmann
      signal.signal(signum, prev_handler)
1243 de499029 Michael Hanselmann
      # If successful, remove from dict
1244 de499029 Michael Hanselmann
      del self._previous[signum]
1245 de499029 Michael Hanselmann
1246 de499029 Michael Hanselmann
  def Clear(self):
1247 de499029 Michael Hanselmann
    """Unsets "called" flag.
1248 de499029 Michael Hanselmann

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

1251 de499029 Michael Hanselmann
    """
1252 de499029 Michael Hanselmann
    self.called = False
1253 de499029 Michael Hanselmann
1254 de499029 Michael Hanselmann
  def _HandleSignal(self, signum, frame):
1255 de499029 Michael Hanselmann
    """Actual signal handling function.
1256 de499029 Michael Hanselmann

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