Statistics
| Branch: | Tag: | Revision:

root / lib / utils.py @ cdb08f44

History | View | Annotate | Download (26.8 kB)

1 2f31098c Iustin Pop
#
2 a8083063 Iustin Pop
#
3 a8083063 Iustin Pop
4 a8083063 Iustin Pop
# Copyright (C) 2006, 2007 Google Inc.
5 a8083063 Iustin Pop
#
6 a8083063 Iustin Pop
# This program is free software; you can redistribute it and/or modify
7 a8083063 Iustin Pop
# it under the terms of the GNU General Public License as published by
8 a8083063 Iustin Pop
# the Free Software Foundation; either version 2 of the License, or
9 a8083063 Iustin Pop
# (at your option) any later version.
10 a8083063 Iustin Pop
#
11 a8083063 Iustin Pop
# This program is distributed in the hope that it will be useful, but
12 a8083063 Iustin Pop
# WITHOUT ANY WARRANTY; without even the implied warranty of
13 a8083063 Iustin Pop
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 a8083063 Iustin Pop
# General Public License for more details.
15 a8083063 Iustin Pop
#
16 a8083063 Iustin Pop
# You should have received a copy of the GNU General Public License
17 a8083063 Iustin Pop
# along with this program; if not, write to the Free Software
18 a8083063 Iustin Pop
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 a8083063 Iustin Pop
# 02110-1301, USA.
20 a8083063 Iustin Pop
21 a8083063 Iustin Pop
22 a8083063 Iustin Pop
"""Ganeti small utilities
23 899d2a81 Michael Hanselmann

24 a8083063 Iustin Pop
"""
25 a8083063 Iustin Pop
26 a8083063 Iustin Pop
27 a8083063 Iustin Pop
import sys
28 a8083063 Iustin Pop
import os
29 a8083063 Iustin Pop
import sha
30 a8083063 Iustin Pop
import time
31 113b55aa Iustin Pop
import subprocess
32 a8083063 Iustin Pop
import re
33 a8083063 Iustin Pop
import socket
34 a8083063 Iustin Pop
import tempfile
35 a8083063 Iustin Pop
import shutil
36 4ca1b175 Alexander Schreiber
import errno
37 2f8b60b3 Iustin Pop
import pwd
38 78feb6fb Guido Trotter
import itertools
39 9c233417 Iustin Pop
import select
40 9c233417 Iustin Pop
import fcntl
41 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 a8083063 Iustin Pop
class RunResult(object):
55 a8083063 Iustin Pop
  """Simple class for holding the result of running external programs.
56 a8083063 Iustin Pop

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

915 2c30e9d7 Alexander Schreiber
  Try to do a TCP connect(2) from the specified source IP to the specified
916 2c30e9d7 Alexander Schreiber
  target IP and the specified target port. If live_port_needed is set to true,
917 2c30e9d7 Alexander Schreiber
  requires the remote end to accept the connection. The timeout is specified
918 2c30e9d7 Alexander Schreiber
  in seconds and defaults to 10 seconds
919 2c30e9d7 Alexander Schreiber

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

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

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

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

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

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

997 087b34fe Iustin Pop
  If the function doesn't raise an exception, it has succeeded and the
998 087b34fe Iustin Pop
  target file has the new contents. If the file has raised an
999 087b34fe Iustin Pop
  exception, an existing target file should be unmodified and the
1000 087b34fe Iustin Pop
  temporary file should be removed.
1001 087b34fe Iustin Pop

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

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

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

1073 06009e27 Iustin Pop
  """
1074 06009e27 Iustin Pop
  if duration < 0:
1075 06009e27 Iustin Pop
    return False
1076 06009e27 Iustin Pop
  time.sleep(duration)
1077 06009e27 Iustin Pop
  return True