Statistics
| Branch: | Tag: | Revision:

root / lib / utils.py @ 8f765069

History | View | Annotate | Download (28.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 9c233417 Iustin Pop
43 9c233417 Iustin Pop
from cStringIO import StringIO
44 a8083063 Iustin Pop
45 a8083063 Iustin Pop
from ganeti import logger
46 a8083063 Iustin Pop
from ganeti import errors
47 3aecd2c7 Iustin Pop
from ganeti import constants
48 a8083063 Iustin Pop
49 16abfbc2 Alexander Schreiber
50 a8083063 Iustin Pop
_locksheld = []
51 a8083063 Iustin Pop
_re_shell_unquoted = re.compile('^[-.,=:/_+@A-Za-z0-9]+$')
52 a8083063 Iustin Pop
53 f362096f Iustin Pop
debug = False
54 f362096f Iustin Pop
55 7c0d6283 Michael Hanselmann
56 a8083063 Iustin Pop
class RunResult(object):
57 a8083063 Iustin Pop
  """Simple class for holding the result of running external programs.
58 a8083063 Iustin Pop

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

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

97 a8083063 Iustin Pop
    """
98 a8083063 Iustin Pop
    return self.stdout + self.stderr
99 a8083063 Iustin Pop
100 a8083063 Iustin Pop
  output = property(_GetOutput, None, None, "Return full output")
101 a8083063 Iustin Pop
102 a8083063 Iustin Pop
103 a8083063 Iustin Pop
def _GetLockFile(subsystem):
104 a8083063 Iustin Pop
  """Compute the file name for a given lock name."""
105 3aecd2c7 Iustin Pop
  return "%s/ganeti_lock_%s" % (constants.LOCK_DIR, subsystem)
106 a8083063 Iustin Pop
107 a8083063 Iustin Pop
108 a8083063 Iustin Pop
def Lock(name, max_retries=None, debug=False):
109 a8083063 Iustin Pop
  """Lock a given subsystem.
110 a8083063 Iustin Pop

111 a8083063 Iustin Pop
  In case the lock is already held by an alive process, the function
112 a8083063 Iustin Pop
  will sleep indefintely and poll with a one second interval.
113 a8083063 Iustin Pop

114 a8083063 Iustin Pop
  When the optional integer argument 'max_retries' is passed with a
115 a8083063 Iustin Pop
  non-zero value, the function will sleep only for this number of
116 a8083063 Iustin Pop
  times, and then it will will raise a LockError if the lock can't be
117 a8083063 Iustin Pop
  acquired. Passing in a negative number will cause only one try to
118 a8083063 Iustin Pop
  get the lock. Passing a positive number will make the function retry
119 a8083063 Iustin Pop
  for approximately that number of seconds.
120 a8083063 Iustin Pop

121 a8083063 Iustin Pop
  """
122 a8083063 Iustin Pop
  lockfile = _GetLockFile(name)
123 a8083063 Iustin Pop
124 a8083063 Iustin Pop
  if name in _locksheld:
125 a8083063 Iustin Pop
    raise errors.LockError('Lock "%s" already held!' % (name,))
126 a8083063 Iustin Pop
127 a8083063 Iustin Pop
  errcount = 0
128 a8083063 Iustin Pop
129 a8083063 Iustin Pop
  retries = 0
130 a8083063 Iustin Pop
  while True:
131 a8083063 Iustin Pop
    try:
132 a8083063 Iustin Pop
      fd = os.open(lockfile, os.O_CREAT | os.O_EXCL | os.O_RDWR | os.O_SYNC)
133 a8083063 Iustin Pop
      break
134 a8083063 Iustin Pop
    except OSError, creat_err:
135 4ca1b175 Alexander Schreiber
      if creat_err.errno != errno.EEXIST:
136 3ecf6786 Iustin Pop
        raise errors.LockError("Can't create the lock file. Error '%s'." %
137 3ecf6786 Iustin Pop
                               str(creat_err))
138 a8083063 Iustin Pop
139 a8083063 Iustin Pop
      try:
140 a8083063 Iustin Pop
        pf = open(lockfile, 'r')
141 a8083063 Iustin Pop
      except IOError, open_err:
142 a8083063 Iustin Pop
        errcount += 1
143 a8083063 Iustin Pop
        if errcount >= 5:
144 3ecf6786 Iustin Pop
          raise errors.LockError("Lock file exists but cannot be opened."
145 3ecf6786 Iustin Pop
                                 " Error: '%s'." % str(open_err))
146 a8083063 Iustin Pop
        time.sleep(1)
147 a8083063 Iustin Pop
        continue
148 a8083063 Iustin Pop
149 a8083063 Iustin Pop
      try:
150 a8083063 Iustin Pop
        pid = int(pf.read())
151 a8083063 Iustin Pop
      except ValueError:
152 3ecf6786 Iustin Pop
        raise errors.LockError("Invalid pid string in %s" %
153 a8083063 Iustin Pop
                               (lockfile,))
154 a8083063 Iustin Pop
155 a8083063 Iustin Pop
      if not IsProcessAlive(pid):
156 3ecf6786 Iustin Pop
        raise errors.LockError("Stale lockfile %s for pid %d?" %
157 3ecf6786 Iustin Pop
                               (lockfile, pid))
158 a8083063 Iustin Pop
159 a8083063 Iustin Pop
      if max_retries and max_retries <= retries:
160 3ecf6786 Iustin Pop
        raise errors.LockError("Can't acquire lock during the specified"
161 3ecf6786 Iustin Pop
                               " time, aborting.")
162 a8083063 Iustin Pop
      if retries == 5 and (debug or sys.stdin.isatty()):
163 a8083063 Iustin Pop
        logger.ToStderr("Waiting for '%s' lock from pid %d..." % (name, pid))
164 a8083063 Iustin Pop
165 a8083063 Iustin Pop
      time.sleep(1)
166 a8083063 Iustin Pop
      retries += 1
167 a8083063 Iustin Pop
      continue
168 a8083063 Iustin Pop
169 a8083063 Iustin Pop
  os.write(fd, '%d\n' % (os.getpid(),))
170 a8083063 Iustin Pop
  os.close(fd)
171 a8083063 Iustin Pop
172 a8083063 Iustin Pop
  _locksheld.append(name)
173 a8083063 Iustin Pop
174 a8083063 Iustin Pop
175 a8083063 Iustin Pop
def Unlock(name):
176 098c0958 Michael Hanselmann
  """Unlock a given subsystem.
177 a8083063 Iustin Pop

178 098c0958 Michael Hanselmann
  """
179 a8083063 Iustin Pop
  lockfile = _GetLockFile(name)
180 a8083063 Iustin Pop
181 a8083063 Iustin Pop
  try:
182 a8083063 Iustin Pop
    fd = os.open(lockfile, os.O_RDONLY)
183 a8083063 Iustin Pop
  except OSError:
184 a8083063 Iustin Pop
    raise errors.LockError('Lock "%s" not held.' % (name,))
185 a8083063 Iustin Pop
186 a8083063 Iustin Pop
  f = os.fdopen(fd, 'r')
187 a8083063 Iustin Pop
  pid_str = f.read()
188 a8083063 Iustin Pop
189 a8083063 Iustin Pop
  try:
190 a8083063 Iustin Pop
    pid = int(pid_str)
191 a8083063 Iustin Pop
  except ValueError:
192 a8083063 Iustin Pop
    raise errors.LockError('Unable to determine PID of locking process.')
193 a8083063 Iustin Pop
194 a8083063 Iustin Pop
  if pid != os.getpid():
195 a8083063 Iustin Pop
    raise errors.LockError('Lock not held by me (%d != %d)' %
196 a8083063 Iustin Pop
                           (os.getpid(), pid,))
197 a8083063 Iustin Pop
198 a8083063 Iustin Pop
  os.unlink(lockfile)
199 a8083063 Iustin Pop
  _locksheld.remove(name)
200 a8083063 Iustin Pop
201 a8083063 Iustin Pop
202 a8083063 Iustin Pop
def LockCleanup():
203 098c0958 Michael Hanselmann
  """Remove all locks.
204 a8083063 Iustin Pop

205 098c0958 Michael Hanselmann
  """
206 a8083063 Iustin Pop
  for lock in _locksheld:
207 a8083063 Iustin Pop
    Unlock(lock)
208 a8083063 Iustin Pop
209 a8083063 Iustin Pop
210 a8083063 Iustin Pop
def RunCmd(cmd):
211 a8083063 Iustin Pop
  """Execute a (shell) command.
212 a8083063 Iustin Pop

213 a8083063 Iustin Pop
  The command should not read from its standard input, as it will be
214 a8083063 Iustin Pop
  closed.
215 a8083063 Iustin Pop

216 a8083063 Iustin Pop
  Args:
217 a8083063 Iustin Pop
    cmd: command to run. (str)
218 a8083063 Iustin Pop

219 a8083063 Iustin Pop
  Returns: `RunResult` instance
220 a8083063 Iustin Pop

221 a8083063 Iustin Pop
  """
222 a8083063 Iustin Pop
  if isinstance(cmd, list):
223 a8083063 Iustin Pop
    cmd = [str(val) for val in cmd]
224 113b55aa Iustin Pop
    strcmd = " ".join(cmd)
225 113b55aa Iustin Pop
    shell = False
226 113b55aa Iustin Pop
  else:
227 113b55aa Iustin Pop
    strcmd = cmd
228 113b55aa Iustin Pop
    shell = True
229 23f41a3e Michael Hanselmann
  env = os.environ.copy()
230 23f41a3e Michael Hanselmann
  env["LC_ALL"] = "C"
231 9c233417 Iustin Pop
  poller = select.poll()
232 113b55aa Iustin Pop
  child = subprocess.Popen(cmd, shell=shell,
233 113b55aa Iustin Pop
                           stderr=subprocess.PIPE,
234 113b55aa Iustin Pop
                           stdout=subprocess.PIPE,
235 113b55aa Iustin Pop
                           stdin=subprocess.PIPE,
236 23f41a3e Michael Hanselmann
                           close_fds=True, env=env)
237 113b55aa Iustin Pop
238 113b55aa Iustin Pop
  child.stdin.close()
239 9c233417 Iustin Pop
  poller.register(child.stdout, select.POLLIN)
240 9c233417 Iustin Pop
  poller.register(child.stderr, select.POLLIN)
241 9c233417 Iustin Pop
  out = StringIO()
242 9c233417 Iustin Pop
  err = StringIO()
243 9c233417 Iustin Pop
  fdmap = {
244 9c233417 Iustin Pop
    child.stdout.fileno(): (out, child.stdout),
245 9c233417 Iustin Pop
    child.stderr.fileno(): (err, child.stderr),
246 9c233417 Iustin Pop
    }
247 9c233417 Iustin Pop
  for fd in fdmap:
248 9c233417 Iustin Pop
    status = fcntl.fcntl(fd, fcntl.F_GETFL)
249 9c233417 Iustin Pop
    fcntl.fcntl(fd, fcntl.F_SETFL, status | os.O_NONBLOCK)
250 9c233417 Iustin Pop
251 9c233417 Iustin Pop
  while fdmap:
252 9c233417 Iustin Pop
    for fd, event in poller.poll():
253 9c233417 Iustin Pop
      if event & select.POLLIN or event & select.POLLPRI:
254 9c233417 Iustin Pop
        data = fdmap[fd][1].read()
255 9c233417 Iustin Pop
        # no data from read signifies EOF (the same as POLLHUP)
256 9c233417 Iustin Pop
        if not data:
257 9c233417 Iustin Pop
          poller.unregister(fd)
258 9c233417 Iustin Pop
          del fdmap[fd]
259 9c233417 Iustin Pop
          continue
260 9c233417 Iustin Pop
        fdmap[fd][0].write(data)
261 9c233417 Iustin Pop
      if (event & select.POLLNVAL or event & select.POLLHUP or
262 9c233417 Iustin Pop
          event & select.POLLERR):
263 9c233417 Iustin Pop
        poller.unregister(fd)
264 9c233417 Iustin Pop
        del fdmap[fd]
265 9c233417 Iustin Pop
266 9c233417 Iustin Pop
  out = out.getvalue()
267 9c233417 Iustin Pop
  err = err.getvalue()
268 a8083063 Iustin Pop
269 a8083063 Iustin Pop
  status = child.wait()
270 113b55aa Iustin Pop
  if status >= 0:
271 113b55aa Iustin Pop
    exitcode = status
272 a8083063 Iustin Pop
    signal = None
273 a8083063 Iustin Pop
  else:
274 a8083063 Iustin Pop
    exitcode = None
275 113b55aa Iustin Pop
    signal = -status
276 a8083063 Iustin Pop
277 a8083063 Iustin Pop
  return RunResult(exitcode, signal, out, err, strcmd)
278 a8083063 Iustin Pop
279 a8083063 Iustin Pop
280 a8083063 Iustin Pop
def RunCmdUnlocked(cmd):
281 a8083063 Iustin Pop
  """Execute a shell command without the 'cmd' lock.
282 a8083063 Iustin Pop

283 a8083063 Iustin Pop
  This variant of `RunCmd()` drops the 'cmd' lock before running the
284 a8083063 Iustin Pop
  command and re-aquires it afterwards, thus it can be used to call
285 a8083063 Iustin Pop
  other ganeti commands.
286 a8083063 Iustin Pop

287 a8083063 Iustin Pop
  The argument and return values are the same as for the `RunCmd()`
288 a8083063 Iustin Pop
  function.
289 a8083063 Iustin Pop

290 a8083063 Iustin Pop
  Args:
291 a8083063 Iustin Pop
    cmd - command to run. (str)
292 a8083063 Iustin Pop

293 a8083063 Iustin Pop
  Returns:
294 a8083063 Iustin Pop
    `RunResult`
295 a8083063 Iustin Pop

296 a8083063 Iustin Pop
  """
297 a8083063 Iustin Pop
  Unlock('cmd')
298 a8083063 Iustin Pop
  ret = RunCmd(cmd)
299 a8083063 Iustin Pop
  Lock('cmd')
300 a8083063 Iustin Pop
301 a8083063 Iustin Pop
  return ret
302 a8083063 Iustin Pop
303 a8083063 Iustin Pop
304 a8083063 Iustin Pop
def RemoveFile(filename):
305 a8083063 Iustin Pop
  """Remove a file ignoring some errors.
306 a8083063 Iustin Pop

307 a8083063 Iustin Pop
  Remove a file, ignoring non-existing ones or directories. Other
308 a8083063 Iustin Pop
  errors are passed.
309 a8083063 Iustin Pop

310 a8083063 Iustin Pop
  """
311 a8083063 Iustin Pop
  try:
312 a8083063 Iustin Pop
    os.unlink(filename)
313 a8083063 Iustin Pop
  except OSError, err:
314 4ca1b175 Alexander Schreiber
    if err.errno not in (errno.ENOENT, errno.EISDIR):
315 a8083063 Iustin Pop
      raise
316 a8083063 Iustin Pop
317 a8083063 Iustin Pop
318 a8083063 Iustin Pop
def _FingerprintFile(filename):
319 a8083063 Iustin Pop
  """Compute the fingerprint of a file.
320 a8083063 Iustin Pop

321 a8083063 Iustin Pop
  If the file does not exist, a None will be returned
322 a8083063 Iustin Pop
  instead.
323 a8083063 Iustin Pop

324 a8083063 Iustin Pop
  Args:
325 a8083063 Iustin Pop
    filename - Filename (str)
326 a8083063 Iustin Pop

327 a8083063 Iustin Pop
  """
328 a8083063 Iustin Pop
  if not (os.path.exists(filename) and os.path.isfile(filename)):
329 a8083063 Iustin Pop
    return None
330 a8083063 Iustin Pop
331 a8083063 Iustin Pop
  f = open(filename)
332 a8083063 Iustin Pop
333 a8083063 Iustin Pop
  fp = sha.sha()
334 a8083063 Iustin Pop
  while True:
335 a8083063 Iustin Pop
    data = f.read(4096)
336 a8083063 Iustin Pop
    if not data:
337 a8083063 Iustin Pop
      break
338 a8083063 Iustin Pop
339 a8083063 Iustin Pop
    fp.update(data)
340 a8083063 Iustin Pop
341 a8083063 Iustin Pop
  return fp.hexdigest()
342 a8083063 Iustin Pop
343 a8083063 Iustin Pop
344 a8083063 Iustin Pop
def FingerprintFiles(files):
345 a8083063 Iustin Pop
  """Compute fingerprints for a list of files.
346 a8083063 Iustin Pop

347 a8083063 Iustin Pop
  Args:
348 a8083063 Iustin Pop
    files - array of filenames.  ( [str, ...] )
349 a8083063 Iustin Pop

350 a8083063 Iustin Pop
  Return value:
351 a8083063 Iustin Pop
    dictionary of filename: fingerprint for the files that exist
352 a8083063 Iustin Pop

353 a8083063 Iustin Pop
  """
354 a8083063 Iustin Pop
  ret = {}
355 a8083063 Iustin Pop
356 a8083063 Iustin Pop
  for filename in files:
357 a8083063 Iustin Pop
    cksum = _FingerprintFile(filename)
358 a8083063 Iustin Pop
    if cksum:
359 a8083063 Iustin Pop
      ret[filename] = cksum
360 a8083063 Iustin Pop
361 a8083063 Iustin Pop
  return ret
362 a8083063 Iustin Pop
363 a8083063 Iustin Pop
364 a8083063 Iustin Pop
def CheckDict(target, template, logname=None):
365 a8083063 Iustin Pop
  """Ensure a dictionary has a required set of keys.
366 a8083063 Iustin Pop

367 a8083063 Iustin Pop
  For the given dictionaries `target` and `template`, ensure target
368 a8083063 Iustin Pop
  has all the keys from template. Missing keys are added with values
369 a8083063 Iustin Pop
  from template.
370 a8083063 Iustin Pop

371 a8083063 Iustin Pop
  Args:
372 a8083063 Iustin Pop
    target   - the dictionary to check
373 a8083063 Iustin Pop
    template - template dictionary
374 a8083063 Iustin Pop
    logname  - a caller-chosen string to identify the debug log
375 a8083063 Iustin Pop
               entry; if None, no logging will be done
376 a8083063 Iustin Pop

377 a8083063 Iustin Pop
  Returns value:
378 a8083063 Iustin Pop
    None
379 a8083063 Iustin Pop

380 a8083063 Iustin Pop
  """
381 a8083063 Iustin Pop
  missing = []
382 a8083063 Iustin Pop
  for k in template:
383 a8083063 Iustin Pop
    if k not in target:
384 a8083063 Iustin Pop
      missing.append(k)
385 a8083063 Iustin Pop
      target[k] = template[k]
386 a8083063 Iustin Pop
387 a8083063 Iustin Pop
  if missing and logname:
388 a8083063 Iustin Pop
    logger.Debug('%s missing keys %s' %
389 a8083063 Iustin Pop
                 (logname, ', '.join(missing)))
390 a8083063 Iustin Pop
391 a8083063 Iustin Pop
392 a8083063 Iustin Pop
def IsProcessAlive(pid):
393 a8083063 Iustin Pop
  """Check if a given pid exists on the system.
394 a8083063 Iustin Pop

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

397 a8083063 Iustin Pop
  Remarks: zombie processes treated as not alive
398 a8083063 Iustin Pop

399 a8083063 Iustin Pop
  """
400 a8083063 Iustin Pop
  try:
401 a8083063 Iustin Pop
    f = open("/proc/%d/status" % pid)
402 a8083063 Iustin Pop
  except IOError, err:
403 4ca1b175 Alexander Schreiber
    if err.errno in (errno.ENOENT, errno.ENOTDIR):
404 a8083063 Iustin Pop
      return False
405 a8083063 Iustin Pop
406 a8083063 Iustin Pop
  alive = True
407 a8083063 Iustin Pop
  try:
408 a8083063 Iustin Pop
    data = f.readlines()
409 a8083063 Iustin Pop
    if len(data) > 1:
410 a8083063 Iustin Pop
      state = data[1].split()
411 a8083063 Iustin Pop
      if len(state) > 1 and state[1] == "Z":
412 a8083063 Iustin Pop
        alive = False
413 a8083063 Iustin Pop
  finally:
414 a8083063 Iustin Pop
    f.close()
415 a8083063 Iustin Pop
416 a8083063 Iustin Pop
  return alive
417 a8083063 Iustin Pop
418 a8083063 Iustin Pop
419 a8083063 Iustin Pop
def MatchNameComponent(key, name_list):
420 a8083063 Iustin Pop
  """Try to match a name against a list.
421 a8083063 Iustin Pop

422 a8083063 Iustin Pop
  This function will try to match a name like test1 against a list
423 a8083063 Iustin Pop
  like ['test1.example.com', 'test2.example.com', ...]. Against this
424 a8083063 Iustin Pop
  list, 'test1' as well as 'test1.example' will match, but not
425 a8083063 Iustin Pop
  'test1.ex'. A multiple match will be considered as no match at all
426 a8083063 Iustin Pop
  (e.g. 'test1' against ['test1.example.com', 'test1.example.org']).
427 a8083063 Iustin Pop

428 a8083063 Iustin Pop
  Args:
429 a8083063 Iustin Pop
    key: the name to be searched
430 a8083063 Iustin Pop
    name_list: the list of strings against which to search the key
431 a8083063 Iustin Pop

432 a8083063 Iustin Pop
  Returns:
433 a8083063 Iustin Pop
    None if there is no match *or* if there are multiple matches
434 a8083063 Iustin Pop
    otherwise the element from the list which matches
435 a8083063 Iustin Pop

436 a8083063 Iustin Pop
  """
437 a8083063 Iustin Pop
  mo = re.compile("^%s(\..*)?$" % re.escape(key))
438 a8083063 Iustin Pop
  names_filtered = [name for name in name_list if mo.match(name) is not None]
439 a8083063 Iustin Pop
  if len(names_filtered) != 1:
440 a8083063 Iustin Pop
    return None
441 a8083063 Iustin Pop
  return names_filtered[0]
442 a8083063 Iustin Pop
443 a8083063 Iustin Pop
444 bcf043c9 Iustin Pop
class HostInfo:
445 89e1fc26 Iustin Pop
  """Class implementing resolver and hostname functionality
446 bcf043c9 Iustin Pop

447 bcf043c9 Iustin Pop
  """
448 89e1fc26 Iustin Pop
  def __init__(self, name=None):
449 bcf043c9 Iustin Pop
    """Initialize the host name object.
450 bcf043c9 Iustin Pop

451 89e1fc26 Iustin Pop
    If the name argument is not passed, it will use this system's
452 89e1fc26 Iustin Pop
    name.
453 bcf043c9 Iustin Pop

454 bcf043c9 Iustin Pop
    """
455 89e1fc26 Iustin Pop
    if name is None:
456 89e1fc26 Iustin Pop
      name = self.SysName()
457 89e1fc26 Iustin Pop
458 89e1fc26 Iustin Pop
    self.query = name
459 89e1fc26 Iustin Pop
    self.name, self.aliases, self.ipaddrs = self.LookupHostname(name)
460 bcf043c9 Iustin Pop
    self.ip = self.ipaddrs[0]
461 bcf043c9 Iustin Pop
462 c8a0948f Michael Hanselmann
  def ShortName(self):
463 c8a0948f Michael Hanselmann
    """Returns the hostname without domain.
464 c8a0948f Michael Hanselmann

465 c8a0948f Michael Hanselmann
    """
466 c8a0948f Michael Hanselmann
    return self.name.split('.')[0]
467 c8a0948f Michael Hanselmann
468 89e1fc26 Iustin Pop
  @staticmethod
469 89e1fc26 Iustin Pop
  def SysName():
470 89e1fc26 Iustin Pop
    """Return the current system's name.
471 bcf043c9 Iustin Pop

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

474 89e1fc26 Iustin Pop
    """
475 89e1fc26 Iustin Pop
    return socket.gethostname()
476 a8083063 Iustin Pop
477 89e1fc26 Iustin Pop
  @staticmethod
478 89e1fc26 Iustin Pop
  def LookupHostname(hostname):
479 89e1fc26 Iustin Pop
    """Look up hostname
480 a8083063 Iustin Pop

481 89e1fc26 Iustin Pop
    Args:
482 89e1fc26 Iustin Pop
      hostname: hostname to look up
483 89e1fc26 Iustin Pop

484 89e1fc26 Iustin Pop
    Returns:
485 89e1fc26 Iustin Pop
      a tuple (name, aliases, ipaddrs) as returned by socket.gethostbyname_ex
486 89e1fc26 Iustin Pop
      in case of errors in resolving, we raise a ResolverError
487 89e1fc26 Iustin Pop

488 89e1fc26 Iustin Pop
    """
489 89e1fc26 Iustin Pop
    try:
490 89e1fc26 Iustin Pop
      result = socket.gethostbyname_ex(hostname)
491 89e1fc26 Iustin Pop
    except socket.gaierror, err:
492 89e1fc26 Iustin Pop
      # hostname not found in DNS
493 89e1fc26 Iustin Pop
      raise errors.ResolverError(hostname, err.args[0], err.args[1])
494 a8083063 Iustin Pop
495 89e1fc26 Iustin Pop
    return result
496 a8083063 Iustin Pop
497 a8083063 Iustin Pop
498 a8083063 Iustin Pop
def ListVolumeGroups():
499 a8083063 Iustin Pop
  """List volume groups and their size
500 a8083063 Iustin Pop

501 a8083063 Iustin Pop
  Returns:
502 a8083063 Iustin Pop
     Dictionary with keys volume name and values the size of the volume
503 a8083063 Iustin Pop

504 a8083063 Iustin Pop
  """
505 a8083063 Iustin Pop
  command = "vgs --noheadings --units m --nosuffix -o name,size"
506 a8083063 Iustin Pop
  result = RunCmd(command)
507 a8083063 Iustin Pop
  retval = {}
508 a8083063 Iustin Pop
  if result.failed:
509 a8083063 Iustin Pop
    return retval
510 a8083063 Iustin Pop
511 a8083063 Iustin Pop
  for line in result.stdout.splitlines():
512 a8083063 Iustin Pop
    try:
513 a8083063 Iustin Pop
      name, size = line.split()
514 a8083063 Iustin Pop
      size = int(float(size))
515 a8083063 Iustin Pop
    except (IndexError, ValueError), err:
516 a8083063 Iustin Pop
      logger.Error("Invalid output from vgs (%s): %s" % (err, line))
517 a8083063 Iustin Pop
      continue
518 a8083063 Iustin Pop
519 a8083063 Iustin Pop
    retval[name] = size
520 a8083063 Iustin Pop
521 a8083063 Iustin Pop
  return retval
522 a8083063 Iustin Pop
523 a8083063 Iustin Pop
524 a8083063 Iustin Pop
def BridgeExists(bridge):
525 a8083063 Iustin Pop
  """Check whether the given bridge exists in the system
526 a8083063 Iustin Pop

527 a8083063 Iustin Pop
  Returns:
528 a8083063 Iustin Pop
     True if it does, false otherwise.
529 a8083063 Iustin Pop

530 a8083063 Iustin Pop
  """
531 a8083063 Iustin Pop
  return os.path.isdir("/sys/class/net/%s/bridge" % bridge)
532 a8083063 Iustin Pop
533 a8083063 Iustin Pop
534 a8083063 Iustin Pop
def NiceSort(name_list):
535 a8083063 Iustin Pop
  """Sort a list of strings based on digit and non-digit groupings.
536 a8083063 Iustin Pop

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

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

544 a8083063 Iustin Pop
  Return value
545 a8083063 Iustin Pop
    - a copy of the list sorted according to our algorithm
546 a8083063 Iustin Pop

547 a8083063 Iustin Pop
  """
548 a8083063 Iustin Pop
  _SORTER_BASE = "(\D+|\d+)"
549 a8083063 Iustin Pop
  _SORTER_FULL = "^%s%s?%s?%s?%s?%s?%s?%s?.*$" % (_SORTER_BASE, _SORTER_BASE,
550 a8083063 Iustin Pop
                                                  _SORTER_BASE, _SORTER_BASE,
551 a8083063 Iustin Pop
                                                  _SORTER_BASE, _SORTER_BASE,
552 a8083063 Iustin Pop
                                                  _SORTER_BASE, _SORTER_BASE)
553 a8083063 Iustin Pop
  _SORTER_RE = re.compile(_SORTER_FULL)
554 a8083063 Iustin Pop
  _SORTER_NODIGIT = re.compile("^\D*$")
555 a8083063 Iustin Pop
  def _TryInt(val):
556 a8083063 Iustin Pop
    """Attempts to convert a variable to integer."""
557 a8083063 Iustin Pop
    if val is None or _SORTER_NODIGIT.match(val):
558 a8083063 Iustin Pop
      return val
559 a8083063 Iustin Pop
    rval = int(val)
560 a8083063 Iustin Pop
    return rval
561 a8083063 Iustin Pop
562 a8083063 Iustin Pop
  to_sort = [([_TryInt(grp) for grp in _SORTER_RE.match(name).groups()], name)
563 a8083063 Iustin Pop
             for name in name_list]
564 a8083063 Iustin Pop
  to_sort.sort()
565 a8083063 Iustin Pop
  return [tup[1] for tup in to_sort]
566 a8083063 Iustin Pop
567 a8083063 Iustin Pop
568 a8083063 Iustin Pop
def CheckDaemonAlive(pid_file, process_string):
569 a8083063 Iustin Pop
  """Check wether the specified daemon is alive.
570 a8083063 Iustin Pop

571 a8083063 Iustin Pop
  Args:
572 a8083063 Iustin Pop
   - pid_file: file to read the daemon pid from, the file is
573 a8083063 Iustin Pop
               expected to contain only a single line containing
574 a8083063 Iustin Pop
               only the PID
575 a8083063 Iustin Pop
   - process_string: a substring that we expect to find in
576 a8083063 Iustin Pop
                     the command line of the daemon process
577 a8083063 Iustin Pop

578 a8083063 Iustin Pop
  Returns:
579 a8083063 Iustin Pop
   - True if the daemon is judged to be alive (that is:
580 a8083063 Iustin Pop
      - the PID file exists, is readable and contains a number
581 a8083063 Iustin Pop
      - a process of the specified PID is running
582 a8083063 Iustin Pop
      - that process contains the specified string in its
583 a8083063 Iustin Pop
        command line
584 a8083063 Iustin Pop
      - the process is not in state Z (zombie))
585 a8083063 Iustin Pop
   - False otherwise
586 a8083063 Iustin Pop

587 a8083063 Iustin Pop
  """
588 a8083063 Iustin Pop
  try:
589 a8083063 Iustin Pop
    pid_file = file(pid_file, 'r')
590 a8083063 Iustin Pop
    try:
591 a8083063 Iustin Pop
      pid = int(pid_file.readline())
592 a8083063 Iustin Pop
    finally:
593 a8083063 Iustin Pop
      pid_file.close()
594 a8083063 Iustin Pop
595 a8083063 Iustin Pop
    cmdline_file_path = "/proc/%s/cmdline" % (pid)
596 a8083063 Iustin Pop
    cmdline_file = open(cmdline_file_path, 'r')
597 a8083063 Iustin Pop
    try:
598 a8083063 Iustin Pop
      cmdline = cmdline_file.readline()
599 a8083063 Iustin Pop
    finally:
600 a8083063 Iustin Pop
      cmdline_file.close()
601 a8083063 Iustin Pop
602 a8083063 Iustin Pop
    if not process_string in cmdline:
603 a8083063 Iustin Pop
      return False
604 a8083063 Iustin Pop
605 a8083063 Iustin Pop
    stat_file_path =  "/proc/%s/stat" % (pid)
606 a8083063 Iustin Pop
    stat_file = open(stat_file_path, 'r')
607 a8083063 Iustin Pop
    try:
608 a8083063 Iustin Pop
      process_state = stat_file.readline().split()[2]
609 a8083063 Iustin Pop
    finally:
610 a8083063 Iustin Pop
      stat_file.close()
611 a8083063 Iustin Pop
612 a8083063 Iustin Pop
    if process_state == 'Z':
613 a8083063 Iustin Pop
      return False
614 a8083063 Iustin Pop
615 a8083063 Iustin Pop
  except (IndexError, IOError, ValueError):
616 a8083063 Iustin Pop
    return False
617 a8083063 Iustin Pop
618 a8083063 Iustin Pop
  return True
619 a8083063 Iustin Pop
620 a8083063 Iustin Pop
621 a8083063 Iustin Pop
def TryConvert(fn, val):
622 a8083063 Iustin Pop
  """Try to convert a value ignoring errors.
623 a8083063 Iustin Pop

624 a8083063 Iustin Pop
  This function tries to apply function `fn` to `val`. If no
625 a8083063 Iustin Pop
  ValueError or TypeError exceptions are raised, it will return the
626 a8083063 Iustin Pop
  result, else it will return the original value. Any other exceptions
627 a8083063 Iustin Pop
  are propagated to the caller.
628 a8083063 Iustin Pop

629 a8083063 Iustin Pop
  """
630 a8083063 Iustin Pop
  try:
631 a8083063 Iustin Pop
    nv = fn(val)
632 a8083063 Iustin Pop
  except (ValueError, TypeError), err:
633 a8083063 Iustin Pop
    nv = val
634 a8083063 Iustin Pop
  return nv
635 a8083063 Iustin Pop
636 a8083063 Iustin Pop
637 a8083063 Iustin Pop
def IsValidIP(ip):
638 a8083063 Iustin Pop
  """Verifies the syntax of an IP address.
639 a8083063 Iustin Pop

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

643 a8083063 Iustin Pop
  """
644 a8083063 Iustin Pop
  unit = "(0|[1-9]\d{0,2})"
645 a8083063 Iustin Pop
  return re.match("^%s\.%s\.%s\.%s$" % (unit, unit, unit, unit), ip)
646 a8083063 Iustin Pop
647 a8083063 Iustin Pop
648 a8083063 Iustin Pop
def IsValidShellParam(word):
649 a8083063 Iustin Pop
  """Verifies is the given word is safe from the shell's p.o.v.
650 a8083063 Iustin Pop

651 a8083063 Iustin Pop
  This means that we can pass this to a command via the shell and be
652 a8083063 Iustin Pop
  sure that it doesn't alter the command line and is passed as such to
653 a8083063 Iustin Pop
  the actual command.
654 a8083063 Iustin Pop

655 a8083063 Iustin Pop
  Note that we are overly restrictive here, in order to be on the safe
656 a8083063 Iustin Pop
  side.
657 a8083063 Iustin Pop

658 a8083063 Iustin Pop
  """
659 a8083063 Iustin Pop
  return bool(re.match("^[-a-zA-Z0-9._+/:%@]+$", word))
660 a8083063 Iustin Pop
661 a8083063 Iustin Pop
662 a8083063 Iustin Pop
def BuildShellCmd(template, *args):
663 a8083063 Iustin Pop
  """Build a safe shell command line from the given arguments.
664 a8083063 Iustin Pop

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

670 a8083063 Iustin Pop
  """
671 a8083063 Iustin Pop
  for word in args:
672 a8083063 Iustin Pop
    if not IsValidShellParam(word):
673 3ecf6786 Iustin Pop
      raise errors.ProgrammerError("Shell argument '%s' contains"
674 3ecf6786 Iustin Pop
                                   " invalid characters" % word)
675 a8083063 Iustin Pop
  return template % args
676 a8083063 Iustin Pop
677 a8083063 Iustin Pop
678 a8083063 Iustin Pop
def FormatUnit(value):
679 a8083063 Iustin Pop
  """Formats an incoming number of MiB with the appropriate unit.
680 a8083063 Iustin Pop

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

683 a8083063 Iustin Pop
  """
684 a8083063 Iustin Pop
  if value < 1024:
685 a8083063 Iustin Pop
    return "%dM" % round(value, 0)
686 a8083063 Iustin Pop
687 a8083063 Iustin Pop
  elif value < (1024 * 1024):
688 a8083063 Iustin Pop
    return "%0.1fG" % round(float(value) / 1024, 1)
689 a8083063 Iustin Pop
690 a8083063 Iustin Pop
  else:
691 a8083063 Iustin Pop
    return "%0.1fT" % round(float(value) / 1024 / 1024, 1)
692 a8083063 Iustin Pop
693 a8083063 Iustin Pop
694 a8083063 Iustin Pop
def ParseUnit(input_string):
695 a8083063 Iustin Pop
  """Tries to extract number and scale from the given string.
696 a8083063 Iustin Pop

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

700 a8083063 Iustin Pop
  """
701 a8083063 Iustin Pop
  m = re.match('^([.\d]+)\s*([a-zA-Z]+)?$', input_string)
702 a8083063 Iustin Pop
  if not m:
703 3ecf6786 Iustin Pop
    raise errors.UnitParseError("Invalid format")
704 a8083063 Iustin Pop
705 a8083063 Iustin Pop
  value = float(m.groups()[0])
706 a8083063 Iustin Pop
707 a8083063 Iustin Pop
  unit = m.groups()[1]
708 a8083063 Iustin Pop
  if unit:
709 a8083063 Iustin Pop
    lcunit = unit.lower()
710 a8083063 Iustin Pop
  else:
711 a8083063 Iustin Pop
    lcunit = 'm'
712 a8083063 Iustin Pop
713 a8083063 Iustin Pop
  if lcunit in ('m', 'mb', 'mib'):
714 a8083063 Iustin Pop
    # Value already in MiB
715 a8083063 Iustin Pop
    pass
716 a8083063 Iustin Pop
717 a8083063 Iustin Pop
  elif lcunit in ('g', 'gb', 'gib'):
718 a8083063 Iustin Pop
    value *= 1024
719 a8083063 Iustin Pop
720 a8083063 Iustin Pop
  elif lcunit in ('t', 'tb', 'tib'):
721 a8083063 Iustin Pop
    value *= 1024 * 1024
722 a8083063 Iustin Pop
723 a8083063 Iustin Pop
  else:
724 3ecf6786 Iustin Pop
    raise errors.UnitParseError("Unknown unit: %s" % unit)
725 a8083063 Iustin Pop
726 a8083063 Iustin Pop
  # Make sure we round up
727 a8083063 Iustin Pop
  if int(value) < value:
728 a8083063 Iustin Pop
    value += 1
729 a8083063 Iustin Pop
730 a8083063 Iustin Pop
  # Round up to the next multiple of 4
731 a8083063 Iustin Pop
  value = int(value)
732 a8083063 Iustin Pop
  if value % 4:
733 a8083063 Iustin Pop
    value += 4 - value % 4
734 a8083063 Iustin Pop
735 a8083063 Iustin Pop
  return value
736 a8083063 Iustin Pop
737 a8083063 Iustin Pop
738 a8083063 Iustin Pop
def AddAuthorizedKey(file_name, key):
739 a8083063 Iustin Pop
  """Adds an SSH public key to an authorized_keys file.
740 a8083063 Iustin Pop

741 a8083063 Iustin Pop
  Args:
742 a8083063 Iustin Pop
    file_name: Path to authorized_keys file
743 a8083063 Iustin Pop
    key: String containing key
744 a8083063 Iustin Pop
  """
745 a8083063 Iustin Pop
  key_fields = key.split()
746 a8083063 Iustin Pop
747 a8083063 Iustin Pop
  f = open(file_name, 'a+')
748 a8083063 Iustin Pop
  try:
749 a8083063 Iustin Pop
    nl = True
750 a8083063 Iustin Pop
    for line in f:
751 a8083063 Iustin Pop
      # Ignore whitespace changes
752 a8083063 Iustin Pop
      if line.split() == key_fields:
753 a8083063 Iustin Pop
        break
754 a8083063 Iustin Pop
      nl = line.endswith('\n')
755 a8083063 Iustin Pop
    else:
756 a8083063 Iustin Pop
      if not nl:
757 a8083063 Iustin Pop
        f.write("\n")
758 a8083063 Iustin Pop
      f.write(key.rstrip('\r\n'))
759 a8083063 Iustin Pop
      f.write("\n")
760 a8083063 Iustin Pop
      f.flush()
761 a8083063 Iustin Pop
  finally:
762 a8083063 Iustin Pop
    f.close()
763 a8083063 Iustin Pop
764 a8083063 Iustin Pop
765 a8083063 Iustin Pop
def RemoveAuthorizedKey(file_name, key):
766 a8083063 Iustin Pop
  """Removes an SSH public key from an authorized_keys file.
767 a8083063 Iustin Pop

768 a8083063 Iustin Pop
  Args:
769 a8083063 Iustin Pop
    file_name: Path to authorized_keys file
770 a8083063 Iustin Pop
    key: String containing key
771 a8083063 Iustin Pop
  """
772 a8083063 Iustin Pop
  key_fields = key.split()
773 a8083063 Iustin Pop
774 a8083063 Iustin Pop
  fd, tmpname = tempfile.mkstemp(dir=os.path.dirname(file_name))
775 a8083063 Iustin Pop
  try:
776 59f82e3f Michael Hanselmann
    out = os.fdopen(fd, 'w')
777 a8083063 Iustin Pop
    try:
778 59f82e3f Michael Hanselmann
      f = open(file_name, 'r')
779 59f82e3f Michael Hanselmann
      try:
780 59f82e3f Michael Hanselmann
        for line in f:
781 59f82e3f Michael Hanselmann
          # Ignore whitespace changes while comparing lines
782 59f82e3f Michael Hanselmann
          if line.split() != key_fields:
783 59f82e3f Michael Hanselmann
            out.write(line)
784 899d2a81 Michael Hanselmann
785 899d2a81 Michael Hanselmann
        out.flush()
786 899d2a81 Michael Hanselmann
        os.rename(tmpname, file_name)
787 899d2a81 Michael Hanselmann
      finally:
788 899d2a81 Michael Hanselmann
        f.close()
789 899d2a81 Michael Hanselmann
    finally:
790 899d2a81 Michael Hanselmann
      out.close()
791 899d2a81 Michael Hanselmann
  except:
792 899d2a81 Michael Hanselmann
    RemoveFile(tmpname)
793 899d2a81 Michael Hanselmann
    raise
794 899d2a81 Michael Hanselmann
795 899d2a81 Michael Hanselmann
796 9440aeab Michael Hanselmann
def SetEtcHostsEntry(file_name, ip, hostname, aliases):
797 9440aeab Michael Hanselmann
  """Sets the name of an IP address and hostname in /etc/hosts.
798 899d2a81 Michael Hanselmann

799 899d2a81 Michael Hanselmann
  """
800 7fbb1f65 Michael Hanselmann
  # Ensure aliases are unique
801 7fbb1f65 Michael Hanselmann
  aliases = UniqueSequence([hostname] + aliases)[1:]
802 7fbb1f65 Michael Hanselmann
803 9440aeab Michael Hanselmann
  fd, tmpname = tempfile.mkstemp(dir=os.path.dirname(file_name))
804 899d2a81 Michael Hanselmann
  try:
805 9440aeab Michael Hanselmann
    out = os.fdopen(fd, 'w')
806 9440aeab Michael Hanselmann
    try:
807 9440aeab Michael Hanselmann
      f = open(file_name, 'r')
808 9440aeab Michael Hanselmann
      try:
809 9440aeab Michael Hanselmann
        written = False
810 9440aeab Michael Hanselmann
        for line in f:
811 9440aeab Michael Hanselmann
          fields = line.split()
812 7e3dbb94 Iustin Pop
          if fields and not fields[0].startswith('#') and ip == fields[0]:
813 9440aeab Michael Hanselmann
            continue
814 9440aeab Michael Hanselmann
          out.write(line)
815 9440aeab Michael Hanselmann
816 7e3dbb94 Iustin Pop
        out.write("%s\t%s" % (ip, hostname))
817 9440aeab Michael Hanselmann
        if aliases:
818 9440aeab Michael Hanselmann
          out.write(" %s" % ' '.join(aliases))
819 9440aeab Michael Hanselmann
        out.write('\n')
820 9440aeab Michael Hanselmann
821 9440aeab Michael Hanselmann
        out.flush()
822 2e3e75b7 Michael Hanselmann
        os.fsync(out)
823 9440aeab Michael Hanselmann
        os.rename(tmpname, file_name)
824 9440aeab Michael Hanselmann
      finally:
825 9440aeab Michael Hanselmann
        f.close()
826 9440aeab Michael Hanselmann
    finally:
827 9440aeab Michael Hanselmann
      out.close()
828 9440aeab Michael Hanselmann
  except:
829 9440aeab Michael Hanselmann
    RemoveFile(tmpname)
830 9440aeab Michael Hanselmann
    raise
831 899d2a81 Michael Hanselmann
832 899d2a81 Michael Hanselmann
833 899d2a81 Michael Hanselmann
def RemoveEtcHostsEntry(file_name, hostname):
834 3e1cdf9f Michael Hanselmann
  """Removes a hostname from /etc/hosts.
835 899d2a81 Michael Hanselmann

836 9440aeab Michael Hanselmann
  IP addresses without names are removed from the file.
837 899d2a81 Michael Hanselmann
  """
838 899d2a81 Michael Hanselmann
  fd, tmpname = tempfile.mkstemp(dir=os.path.dirname(file_name))
839 899d2a81 Michael Hanselmann
  try:
840 899d2a81 Michael Hanselmann
    out = os.fdopen(fd, 'w')
841 899d2a81 Michael Hanselmann
    try:
842 899d2a81 Michael Hanselmann
      f = open(file_name, 'r')
843 899d2a81 Michael Hanselmann
      try:
844 899d2a81 Michael Hanselmann
        for line in f:
845 899d2a81 Michael Hanselmann
          fields = line.split()
846 899d2a81 Michael Hanselmann
          if len(fields) > 1 and not fields[0].startswith('#'):
847 899d2a81 Michael Hanselmann
            names = fields[1:]
848 899d2a81 Michael Hanselmann
            if hostname in names:
849 899d2a81 Michael Hanselmann
              while hostname in names:
850 899d2a81 Michael Hanselmann
                names.remove(hostname)
851 899d2a81 Michael Hanselmann
              if names:
852 9440aeab Michael Hanselmann
                out.write("%s %s\n" % (fields[0], ' '.join(names)))
853 899d2a81 Michael Hanselmann
              continue
854 899d2a81 Michael Hanselmann
855 899d2a81 Michael Hanselmann
          out.write(line)
856 59f82e3f Michael Hanselmann
857 59f82e3f Michael Hanselmann
        out.flush()
858 2e3e75b7 Michael Hanselmann
        os.fsync(out)
859 59f82e3f Michael Hanselmann
        os.rename(tmpname, file_name)
860 59f82e3f Michael Hanselmann
      finally:
861 59f82e3f Michael Hanselmann
        f.close()
862 a8083063 Iustin Pop
    finally:
863 59f82e3f Michael Hanselmann
      out.close()
864 59f82e3f Michael Hanselmann
  except:
865 59f82e3f Michael Hanselmann
    RemoveFile(tmpname)
866 59f82e3f Michael Hanselmann
    raise
867 a8083063 Iustin Pop
868 a8083063 Iustin Pop
869 a8083063 Iustin Pop
def CreateBackup(file_name):
870 a8083063 Iustin Pop
  """Creates a backup of a file.
871 a8083063 Iustin Pop

872 a8083063 Iustin Pop
  Returns: the path to the newly created backup file.
873 a8083063 Iustin Pop

874 a8083063 Iustin Pop
  """
875 a8083063 Iustin Pop
  if not os.path.isfile(file_name):
876 3ecf6786 Iustin Pop
    raise errors.ProgrammerError("Can't make a backup of a non-file '%s'" %
877 3ecf6786 Iustin Pop
                                file_name)
878 a8083063 Iustin Pop
879 081b1e69 Michael Hanselmann
  prefix = '%s.backup-%d.' % (os.path.basename(file_name), int(time.time()))
880 65fe4693 Iustin Pop
  dir_name = os.path.dirname(file_name)
881 081b1e69 Michael Hanselmann
882 081b1e69 Michael Hanselmann
  fsrc = open(file_name, 'rb')
883 081b1e69 Michael Hanselmann
  try:
884 65fe4693 Iustin Pop
    (fd, backup_name) = tempfile.mkstemp(prefix=prefix, dir=dir_name)
885 081b1e69 Michael Hanselmann
    fdst = os.fdopen(fd, 'wb')
886 081b1e69 Michael Hanselmann
    try:
887 081b1e69 Michael Hanselmann
      shutil.copyfileobj(fsrc, fdst)
888 081b1e69 Michael Hanselmann
    finally:
889 081b1e69 Michael Hanselmann
      fdst.close()
890 081b1e69 Michael Hanselmann
  finally:
891 081b1e69 Michael Hanselmann
    fsrc.close()
892 081b1e69 Michael Hanselmann
893 a8083063 Iustin Pop
  return backup_name
894 a8083063 Iustin Pop
895 a8083063 Iustin Pop
896 a8083063 Iustin Pop
def ShellQuote(value):
897 a8083063 Iustin Pop
  """Quotes shell argument according to POSIX.
898 3ecf6786 Iustin Pop

899 a8083063 Iustin Pop
  """
900 a8083063 Iustin Pop
  if _re_shell_unquoted.match(value):
901 a8083063 Iustin Pop
    return value
902 a8083063 Iustin Pop
  else:
903 a8083063 Iustin Pop
    return "'%s'" % value.replace("'", "'\\''")
904 a8083063 Iustin Pop
905 a8083063 Iustin Pop
906 a8083063 Iustin Pop
def ShellQuoteArgs(args):
907 a8083063 Iustin Pop
  """Quotes all given shell arguments and concatenates using spaces.
908 a8083063 Iustin Pop

909 a8083063 Iustin Pop
  """
910 a8083063 Iustin Pop
  return ' '.join([ShellQuote(i) for i in args])
911 88d14415 Michael Hanselmann
912 88d14415 Michael Hanselmann
913 b15d625f Iustin Pop
def TcpPing(target, port, timeout=10, live_port_needed=False, source=None):
914 2c30e9d7 Alexander Schreiber
  """Simple ping implementation using TCP connect(2).
915 2c30e9d7 Alexander Schreiber

916 b15d625f Iustin Pop
  Try to do a TCP connect(2) from an optional source IP to the
917 b15d625f Iustin Pop
  specified target IP and the specified target port. If the optional
918 b15d625f Iustin Pop
  parameter live_port_needed is set to true, requires the remote end
919 b15d625f Iustin Pop
  to accept the connection. The timeout is specified in seconds and
920 b15d625f Iustin Pop
  defaults to 10 seconds. If the source optional argument is not
921 b15d625f Iustin Pop
  passed, the source address selection is left to the kernel,
922 b15d625f Iustin Pop
  otherwise we try to connect using the passed address (failures to
923 b15d625f Iustin Pop
  bind other than EADDRNOTAVAIL will be ignored).
924 2c30e9d7 Alexander Schreiber

925 2c30e9d7 Alexander Schreiber
  """
926 2c30e9d7 Alexander Schreiber
  sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
927 2c30e9d7 Alexander Schreiber
928 2c30e9d7 Alexander Schreiber
  sucess = False
929 2c30e9d7 Alexander Schreiber
930 b15d625f Iustin Pop
  if source is not None:
931 b15d625f Iustin Pop
    try:
932 b15d625f Iustin Pop
      sock.bind((source, 0))
933 b15d625f Iustin Pop
    except socket.error, (errcode, errstring):
934 b15d625f Iustin Pop
      if errcode == errno.EADDRNOTAVAIL:
935 b15d625f Iustin Pop
        success = False
936 2c30e9d7 Alexander Schreiber
937 2c30e9d7 Alexander Schreiber
  sock.settimeout(timeout)
938 2c30e9d7 Alexander Schreiber
939 2c30e9d7 Alexander Schreiber
  try:
940 2c30e9d7 Alexander Schreiber
    sock.connect((target, port))
941 2c30e9d7 Alexander Schreiber
    sock.close()
942 2c30e9d7 Alexander Schreiber
    success = True
943 2c30e9d7 Alexander Schreiber
  except socket.timeout:
944 2c30e9d7 Alexander Schreiber
    success = False
945 2c30e9d7 Alexander Schreiber
  except socket.error, (errcode, errstring):
946 4ca1b175 Alexander Schreiber
    success = (not live_port_needed) and (errcode == errno.ECONNREFUSED)
947 2c30e9d7 Alexander Schreiber
948 2c30e9d7 Alexander Schreiber
  return success
949 eedbda4b Michael Hanselmann
950 eedbda4b Michael Hanselmann
951 eedbda4b Michael Hanselmann
def ListVisibleFiles(path):
952 eedbda4b Michael Hanselmann
  """Returns a list of all visible files in a directory.
953 eedbda4b Michael Hanselmann

954 eedbda4b Michael Hanselmann
  """
955 f3299a07 Michael Hanselmann
  files = [i for i in os.listdir(path) if not i.startswith(".")]
956 f3299a07 Michael Hanselmann
  files.sort()
957 f3299a07 Michael Hanselmann
  return files
958 2f8b60b3 Iustin Pop
959 2f8b60b3 Iustin Pop
960 257f4c0a Iustin Pop
def GetHomeDir(user, default=None):
961 257f4c0a Iustin Pop
  """Try to get the homedir of the given user.
962 257f4c0a Iustin Pop

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

967 2f8b60b3 Iustin Pop
  """
968 2f8b60b3 Iustin Pop
  try:
969 257f4c0a Iustin Pop
    if isinstance(user, basestring):
970 257f4c0a Iustin Pop
      result = pwd.getpwnam(user)
971 257f4c0a Iustin Pop
    elif isinstance(user, (int, long)):
972 257f4c0a Iustin Pop
      result = pwd.getpwuid(user)
973 257f4c0a Iustin Pop
    else:
974 257f4c0a Iustin Pop
      raise errors.ProgrammerError("Invalid type passed to GetHomeDir (%s)" %
975 257f4c0a Iustin Pop
                                   type(user))
976 2f8b60b3 Iustin Pop
  except KeyError:
977 2f8b60b3 Iustin Pop
    return default
978 2f8b60b3 Iustin Pop
  return result.pw_dir
979 59072e7e Michael Hanselmann
980 59072e7e Michael Hanselmann
981 24818e8f Michael Hanselmann
def NewUUID():
982 59072e7e Michael Hanselmann
  """Returns a random UUID.
983 59072e7e Michael Hanselmann

984 59072e7e Michael Hanselmann
  """
985 59072e7e Michael Hanselmann
  f = open("/proc/sys/kernel/random/uuid", "r")
986 59072e7e Michael Hanselmann
  try:
987 59072e7e Michael Hanselmann
    return f.read(128).rstrip("\n")
988 59072e7e Michael Hanselmann
  finally:
989 59072e7e Michael Hanselmann
    f.close()
990 087b34fe Iustin Pop
991 087b34fe Iustin Pop
992 087b34fe Iustin Pop
def WriteFile(file_name, fn=None, data=None,
993 087b34fe Iustin Pop
              mode=None, uid=-1, gid=-1,
994 087b34fe Iustin Pop
              atime=None, mtime=None):
995 087b34fe Iustin Pop
  """(Over)write a file atomically.
996 087b34fe Iustin Pop

997 087b34fe Iustin Pop
  The file_name and either fn (a function taking one argument, the
998 087b34fe Iustin Pop
  file descriptor, and which should write the data to it) or data (the
999 087b34fe Iustin Pop
  contents of the file) must be passed. The other arguments are
1000 087b34fe Iustin Pop
  optional and allow setting the file mode, owner and group, and the
1001 087b34fe Iustin Pop
  mtime/atime of the file.
1002 087b34fe Iustin Pop

1003 087b34fe Iustin Pop
  If the function doesn't raise an exception, it has succeeded and the
1004 087b34fe Iustin Pop
  target file has the new contents. If the file has raised an
1005 087b34fe Iustin Pop
  exception, an existing target file should be unmodified and the
1006 087b34fe Iustin Pop
  temporary file should be removed.
1007 087b34fe Iustin Pop

1008 087b34fe Iustin Pop
  """
1009 087b34fe Iustin Pop
  if not os.path.isabs(file_name):
1010 087b34fe Iustin Pop
    raise errors.ProgrammerError("Path passed to WriteFile is not"
1011 087b34fe Iustin Pop
                                 " absolute: '%s'" % file_name)
1012 087b34fe Iustin Pop
1013 087b34fe Iustin Pop
  if [fn, data].count(None) != 1:
1014 087b34fe Iustin Pop
    raise errors.ProgrammerError("fn or data required")
1015 087b34fe Iustin Pop
1016 087b34fe Iustin Pop
  if [atime, mtime].count(None) == 1:
1017 087b34fe Iustin Pop
    raise errors.ProgrammerError("Both atime and mtime must be either"
1018 087b34fe Iustin Pop
                                 " set or None")
1019 087b34fe Iustin Pop
1020 087b34fe Iustin Pop
1021 087b34fe Iustin Pop
  dir_name, base_name = os.path.split(file_name)
1022 087b34fe Iustin Pop
  fd, new_name = tempfile.mkstemp('.new', base_name, dir_name)
1023 087b34fe Iustin Pop
  # here we need to make sure we remove the temp file, if any error
1024 087b34fe Iustin Pop
  # leaves it in place
1025 087b34fe Iustin Pop
  try:
1026 087b34fe Iustin Pop
    if uid != -1 or gid != -1:
1027 087b34fe Iustin Pop
      os.chown(new_name, uid, gid)
1028 087b34fe Iustin Pop
    if mode:
1029 087b34fe Iustin Pop
      os.chmod(new_name, mode)
1030 087b34fe Iustin Pop
    if data is not None:
1031 087b34fe Iustin Pop
      os.write(fd, data)
1032 087b34fe Iustin Pop
    else:
1033 087b34fe Iustin Pop
      fn(fd)
1034 087b34fe Iustin Pop
    os.fsync(fd)
1035 087b34fe Iustin Pop
    if atime is not None and mtime is not None:
1036 087b34fe Iustin Pop
      os.utime(new_name, (atime, mtime))
1037 087b34fe Iustin Pop
    os.rename(new_name, file_name)
1038 087b34fe Iustin Pop
  finally:
1039 087b34fe Iustin Pop
    os.close(fd)
1040 087b34fe Iustin Pop
    RemoveFile(new_name)
1041 78feb6fb Guido Trotter
1042 78feb6fb Guido Trotter
1043 78feb6fb Guido Trotter
def all(seq, pred=bool):
1044 78feb6fb Guido Trotter
  "Returns True if pred(x) is True for every element in the iterable"
1045 78feb6fb Guido Trotter
  for elem in itertools.ifilterfalse(pred, seq):
1046 78feb6fb Guido Trotter
    return False
1047 78feb6fb Guido Trotter
  return True
1048 78feb6fb Guido Trotter
1049 78feb6fb Guido Trotter
1050 78feb6fb Guido Trotter
def any(seq, pred=bool):
1051 78feb6fb Guido Trotter
  "Returns True if pred(x) is True for at least one element in the iterable"
1052 78feb6fb Guido Trotter
  for elem in itertools.ifilter(pred, seq):
1053 78feb6fb Guido Trotter
    return True
1054 78feb6fb Guido Trotter
  return False
1055 f7414041 Michael Hanselmann
1056 f7414041 Michael Hanselmann
1057 f7414041 Michael Hanselmann
def UniqueSequence(seq):
1058 f7414041 Michael Hanselmann
  """Returns a list with unique elements.
1059 f7414041 Michael Hanselmann

1060 f7414041 Michael Hanselmann
  Element order is preserved.
1061 f7414041 Michael Hanselmann
  """
1062 f7414041 Michael Hanselmann
  seen = set()
1063 f7414041 Michael Hanselmann
  return [i for i in seq if i not in seen and not seen.add(i)]
1064 1862d460 Alexander Schreiber
1065 1862d460 Alexander Schreiber
1066 1862d460 Alexander Schreiber
def IsValidMac(mac):
1067 1862d460 Alexander Schreiber
  """Predicate to check if a MAC address is valid.
1068 1862d460 Alexander Schreiber

1069 1862d460 Alexander Schreiber
  Checks wether the supplied MAC address is formally correct, only
1070 1862d460 Alexander Schreiber
  accepts colon separated format.
1071 1862d460 Alexander Schreiber
  """
1072 1862d460 Alexander Schreiber
  mac_check = re.compile("^([0-9a-f]{2}(:|$)){6}$")
1073 1862d460 Alexander Schreiber
  return mac_check.match(mac) is not None
1074 06009e27 Iustin Pop
1075 06009e27 Iustin Pop
1076 06009e27 Iustin Pop
def TestDelay(duration):
1077 06009e27 Iustin Pop
  """Sleep for a fixed amount of time.
1078 06009e27 Iustin Pop

1079 06009e27 Iustin Pop
  """
1080 06009e27 Iustin Pop
  if duration < 0:
1081 06009e27 Iustin Pop
    return False
1082 06009e27 Iustin Pop
  time.sleep(duration)
1083 06009e27 Iustin Pop
  return True
1084 8f765069 Iustin Pop
1085 8f765069 Iustin Pop
1086 8f765069 Iustin Pop
def Daemonize(logfile):
1087 8f765069 Iustin Pop
  """Daemonize the current process.
1088 8f765069 Iustin Pop

1089 8f765069 Iustin Pop
  This detaches the current process from the controlling terminal and
1090 8f765069 Iustin Pop
  runs it in the background as a daemon.
1091 8f765069 Iustin Pop

1092 8f765069 Iustin Pop
  """
1093 8f765069 Iustin Pop
  UMASK = 077
1094 8f765069 Iustin Pop
  WORKDIR = "/"
1095 8f765069 Iustin Pop
  # Default maximum for the number of available file descriptors.
1096 8f765069 Iustin Pop
  if 'SC_OPEN_MAX' in os.sysconf_names:
1097 8f765069 Iustin Pop
    try:
1098 8f765069 Iustin Pop
      MAXFD = os.sysconf('SC_OPEN_MAX')
1099 8f765069 Iustin Pop
      if MAXFD < 0:
1100 8f765069 Iustin Pop
        MAXFD = 1024
1101 8f765069 Iustin Pop
    except OSError:
1102 8f765069 Iustin Pop
      MAXFD = 1024
1103 8f765069 Iustin Pop
  else:
1104 8f765069 Iustin Pop
    MAXFD = 1024
1105 8f765069 Iustin Pop
1106 8f765069 Iustin Pop
  # this might fail
1107 8f765069 Iustin Pop
  pid = os.fork()
1108 8f765069 Iustin Pop
  if (pid == 0):  # The first child.
1109 8f765069 Iustin Pop
    os.setsid()
1110 8f765069 Iustin Pop
    # this might fail
1111 8f765069 Iustin Pop
    pid = os.fork() # Fork a second child.
1112 8f765069 Iustin Pop
    if (pid == 0):  # The second child.
1113 8f765069 Iustin Pop
      os.chdir(WORKDIR)
1114 8f765069 Iustin Pop
      os.umask(UMASK)
1115 8f765069 Iustin Pop
    else:
1116 8f765069 Iustin Pop
      # exit() or _exit()?  See below.
1117 8f765069 Iustin Pop
      os._exit(0) # Exit parent (the first child) of the second child.
1118 8f765069 Iustin Pop
  else:
1119 8f765069 Iustin Pop
    os._exit(0) # Exit parent of the first child.
1120 8f765069 Iustin Pop
  maxfd = resource.getrlimit(resource.RLIMIT_NOFILE)[1]
1121 8f765069 Iustin Pop
  if (maxfd == resource.RLIM_INFINITY):
1122 8f765069 Iustin Pop
    maxfd = MAXFD
1123 8f765069 Iustin Pop
1124 8f765069 Iustin Pop
  # Iterate through and close all file descriptors.
1125 8f765069 Iustin Pop
  for fd in range(0, maxfd):
1126 8f765069 Iustin Pop
    try:
1127 8f765069 Iustin Pop
      os.close(fd)
1128 8f765069 Iustin Pop
    except OSError: # ERROR, fd wasn't open to begin with (ignored)
1129 8f765069 Iustin Pop
      pass
1130 8f765069 Iustin Pop
  os.open(logfile, os.O_RDWR|os.O_CREAT|os.O_APPEND, 0600)
1131 8f765069 Iustin Pop
  # Duplicate standard input to standard output and standard error.
1132 8f765069 Iustin Pop
  os.dup2(0, 1)     # standard output (1)
1133 8f765069 Iustin Pop
  os.dup2(0, 2)     # standard error (2)
1134 8f765069 Iustin Pop
  return 0