Statistics
| Branch: | Tag: | Revision:

root / lib / utils.py @ 1322c697

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1078 06009e27 Iustin Pop
  """
1079 06009e27 Iustin Pop
  if duration < 0:
1080 06009e27 Iustin Pop
    return False
1081 06009e27 Iustin Pop
  time.sleep(duration)
1082 06009e27 Iustin Pop
  return True