Statistics
| Branch: | Tag: | Revision:

root / lib / utils.py @ f362096f

History | View | Annotate | Download (25.4 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 a8083063 Iustin Pop
40 a8083063 Iustin Pop
from ganeti import logger
41 a8083063 Iustin Pop
from ganeti import errors
42 a8083063 Iustin Pop
43 16abfbc2 Alexander Schreiber
44 a8083063 Iustin Pop
_locksheld = []
45 a8083063 Iustin Pop
_re_shell_unquoted = re.compile('^[-.,=:/_+@A-Za-z0-9]+$')
46 a8083063 Iustin Pop
47 f362096f Iustin Pop
debug = False
48 f362096f Iustin Pop
49 a8083063 Iustin Pop
class RunResult(object):
50 a8083063 Iustin Pop
  """Simple class for holding the result of running external programs.
51 a8083063 Iustin Pop

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

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

90 a8083063 Iustin Pop
    """
91 a8083063 Iustin Pop
    return self.stdout + self.stderr
92 a8083063 Iustin Pop
93 a8083063 Iustin Pop
  output = property(_GetOutput, None, None, "Return full output")
94 a8083063 Iustin Pop
95 a8083063 Iustin Pop
96 a8083063 Iustin Pop
def _GetLockFile(subsystem):
97 a8083063 Iustin Pop
  """Compute the file name for a given lock name."""
98 a8083063 Iustin Pop
  return "/var/lock/ganeti_lock_%s" % subsystem
99 a8083063 Iustin Pop
100 a8083063 Iustin Pop
101 a8083063 Iustin Pop
def Lock(name, max_retries=None, debug=False):
102 a8083063 Iustin Pop
  """Lock a given subsystem.
103 a8083063 Iustin Pop

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

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

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

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

198 098c0958 Michael Hanselmann
  """
199 a8083063 Iustin Pop
  for lock in _locksheld:
200 a8083063 Iustin Pop
    Unlock(lock)
201 a8083063 Iustin Pop
202 a8083063 Iustin Pop
203 a8083063 Iustin Pop
def RunCmd(cmd):
204 a8083063 Iustin Pop
  """Execute a (shell) command.
205 a8083063 Iustin Pop

206 a8083063 Iustin Pop
  The command should not read from its standard input, as it will be
207 a8083063 Iustin Pop
  closed.
208 a8083063 Iustin Pop

209 a8083063 Iustin Pop
  Args:
210 a8083063 Iustin Pop
    cmd: command to run. (str)
211 a8083063 Iustin Pop

212 a8083063 Iustin Pop
  Returns: `RunResult` instance
213 a8083063 Iustin Pop

214 a8083063 Iustin Pop
  """
215 a8083063 Iustin Pop
  if isinstance(cmd, list):
216 a8083063 Iustin Pop
    cmd = [str(val) for val in cmd]
217 113b55aa Iustin Pop
    strcmd = " ".join(cmd)
218 113b55aa Iustin Pop
    shell = False
219 113b55aa Iustin Pop
  else:
220 113b55aa Iustin Pop
    strcmd = cmd
221 113b55aa Iustin Pop
    shell = True
222 23f41a3e Michael Hanselmann
  env = os.environ.copy()
223 23f41a3e Michael Hanselmann
  env["LC_ALL"] = "C"
224 113b55aa Iustin Pop
  child = subprocess.Popen(cmd, shell=shell,
225 113b55aa Iustin Pop
                           stderr=subprocess.PIPE,
226 113b55aa Iustin Pop
                           stdout=subprocess.PIPE,
227 113b55aa Iustin Pop
                           stdin=subprocess.PIPE,
228 23f41a3e Michael Hanselmann
                           close_fds=True, env=env)
229 113b55aa Iustin Pop
230 113b55aa Iustin Pop
  child.stdin.close()
231 113b55aa Iustin Pop
  out = child.stdout.read()
232 113b55aa Iustin Pop
  err = child.stderr.read()
233 a8083063 Iustin Pop
234 a8083063 Iustin Pop
  status = child.wait()
235 113b55aa Iustin Pop
  if status >= 0:
236 113b55aa Iustin Pop
    exitcode = status
237 a8083063 Iustin Pop
    signal = None
238 a8083063 Iustin Pop
  else:
239 a8083063 Iustin Pop
    exitcode = None
240 113b55aa Iustin Pop
    signal = -status
241 a8083063 Iustin Pop
242 a8083063 Iustin Pop
  return RunResult(exitcode, signal, out, err, strcmd)
243 a8083063 Iustin Pop
244 a8083063 Iustin Pop
245 a8083063 Iustin Pop
def RunCmdUnlocked(cmd):
246 a8083063 Iustin Pop
  """Execute a shell command without the 'cmd' lock.
247 a8083063 Iustin Pop

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

252 a8083063 Iustin Pop
  The argument and return values are the same as for the `RunCmd()`
253 a8083063 Iustin Pop
  function.
254 a8083063 Iustin Pop

255 a8083063 Iustin Pop
  Args:
256 a8083063 Iustin Pop
    cmd - command to run. (str)
257 a8083063 Iustin Pop

258 a8083063 Iustin Pop
  Returns:
259 a8083063 Iustin Pop
    `RunResult`
260 a8083063 Iustin Pop

261 a8083063 Iustin Pop
  """
262 a8083063 Iustin Pop
  Unlock('cmd')
263 a8083063 Iustin Pop
  ret = RunCmd(cmd)
264 a8083063 Iustin Pop
  Lock('cmd')
265 a8083063 Iustin Pop
266 a8083063 Iustin Pop
  return ret
267 a8083063 Iustin Pop
268 a8083063 Iustin Pop
269 a8083063 Iustin Pop
def RemoveFile(filename):
270 a8083063 Iustin Pop
  """Remove a file ignoring some errors.
271 a8083063 Iustin Pop

272 a8083063 Iustin Pop
  Remove a file, ignoring non-existing ones or directories. Other
273 a8083063 Iustin Pop
  errors are passed.
274 a8083063 Iustin Pop

275 a8083063 Iustin Pop
  """
276 a8083063 Iustin Pop
  try:
277 a8083063 Iustin Pop
    os.unlink(filename)
278 a8083063 Iustin Pop
  except OSError, err:
279 4ca1b175 Alexander Schreiber
    if err.errno not in (errno.ENOENT, errno.EISDIR):
280 a8083063 Iustin Pop
      raise
281 a8083063 Iustin Pop
282 a8083063 Iustin Pop
283 a8083063 Iustin Pop
def _FingerprintFile(filename):
284 a8083063 Iustin Pop
  """Compute the fingerprint of a file.
285 a8083063 Iustin Pop

286 a8083063 Iustin Pop
  If the file does not exist, a None will be returned
287 a8083063 Iustin Pop
  instead.
288 a8083063 Iustin Pop

289 a8083063 Iustin Pop
  Args:
290 a8083063 Iustin Pop
    filename - Filename (str)
291 a8083063 Iustin Pop

292 a8083063 Iustin Pop
  """
293 a8083063 Iustin Pop
  if not (os.path.exists(filename) and os.path.isfile(filename)):
294 a8083063 Iustin Pop
    return None
295 a8083063 Iustin Pop
296 a8083063 Iustin Pop
  f = open(filename)
297 a8083063 Iustin Pop
298 a8083063 Iustin Pop
  fp = sha.sha()
299 a8083063 Iustin Pop
  while True:
300 a8083063 Iustin Pop
    data = f.read(4096)
301 a8083063 Iustin Pop
    if not data:
302 a8083063 Iustin Pop
      break
303 a8083063 Iustin Pop
304 a8083063 Iustin Pop
    fp.update(data)
305 a8083063 Iustin Pop
306 a8083063 Iustin Pop
  return fp.hexdigest()
307 a8083063 Iustin Pop
308 a8083063 Iustin Pop
309 a8083063 Iustin Pop
def FingerprintFiles(files):
310 a8083063 Iustin Pop
  """Compute fingerprints for a list of files.
311 a8083063 Iustin Pop

312 a8083063 Iustin Pop
  Args:
313 a8083063 Iustin Pop
    files - array of filenames.  ( [str, ...] )
314 a8083063 Iustin Pop

315 a8083063 Iustin Pop
  Return value:
316 a8083063 Iustin Pop
    dictionary of filename: fingerprint for the files that exist
317 a8083063 Iustin Pop

318 a8083063 Iustin Pop
  """
319 a8083063 Iustin Pop
  ret = {}
320 a8083063 Iustin Pop
321 a8083063 Iustin Pop
  for filename in files:
322 a8083063 Iustin Pop
    cksum = _FingerprintFile(filename)
323 a8083063 Iustin Pop
    if cksum:
324 a8083063 Iustin Pop
      ret[filename] = cksum
325 a8083063 Iustin Pop
326 a8083063 Iustin Pop
  return ret
327 a8083063 Iustin Pop
328 a8083063 Iustin Pop
329 a8083063 Iustin Pop
def CheckDict(target, template, logname=None):
330 a8083063 Iustin Pop
  """Ensure a dictionary has a required set of keys.
331 a8083063 Iustin Pop

332 a8083063 Iustin Pop
  For the given dictionaries `target` and `template`, ensure target
333 a8083063 Iustin Pop
  has all the keys from template. Missing keys are added with values
334 a8083063 Iustin Pop
  from template.
335 a8083063 Iustin Pop

336 a8083063 Iustin Pop
  Args:
337 a8083063 Iustin Pop
    target   - the dictionary to check
338 a8083063 Iustin Pop
    template - template dictionary
339 a8083063 Iustin Pop
    logname  - a caller-chosen string to identify the debug log
340 a8083063 Iustin Pop
               entry; if None, no logging will be done
341 a8083063 Iustin Pop

342 a8083063 Iustin Pop
  Returns value:
343 a8083063 Iustin Pop
    None
344 a8083063 Iustin Pop

345 a8083063 Iustin Pop
  """
346 a8083063 Iustin Pop
  missing = []
347 a8083063 Iustin Pop
  for k in template:
348 a8083063 Iustin Pop
    if k not in target:
349 a8083063 Iustin Pop
      missing.append(k)
350 a8083063 Iustin Pop
      target[k] = template[k]
351 a8083063 Iustin Pop
352 a8083063 Iustin Pop
  if missing and logname:
353 a8083063 Iustin Pop
    logger.Debug('%s missing keys %s' %
354 a8083063 Iustin Pop
                 (logname, ', '.join(missing)))
355 a8083063 Iustin Pop
356 a8083063 Iustin Pop
357 a8083063 Iustin Pop
def IsProcessAlive(pid):
358 a8083063 Iustin Pop
  """Check if a given pid exists on the system.
359 a8083063 Iustin Pop

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

362 a8083063 Iustin Pop
  Remarks: zombie processes treated as not alive
363 a8083063 Iustin Pop

364 a8083063 Iustin Pop
  """
365 a8083063 Iustin Pop
  try:
366 a8083063 Iustin Pop
    f = open("/proc/%d/status" % pid)
367 a8083063 Iustin Pop
  except IOError, err:
368 4ca1b175 Alexander Schreiber
    if err.errno in (errno.ENOENT, errno.ENOTDIR):
369 a8083063 Iustin Pop
      return False
370 a8083063 Iustin Pop
371 a8083063 Iustin Pop
  alive = True
372 a8083063 Iustin Pop
  try:
373 a8083063 Iustin Pop
    data = f.readlines()
374 a8083063 Iustin Pop
    if len(data) > 1:
375 a8083063 Iustin Pop
      state = data[1].split()
376 a8083063 Iustin Pop
      if len(state) > 1 and state[1] == "Z":
377 a8083063 Iustin Pop
        alive = False
378 a8083063 Iustin Pop
  finally:
379 a8083063 Iustin Pop
    f.close()
380 a8083063 Iustin Pop
381 a8083063 Iustin Pop
  return alive
382 a8083063 Iustin Pop
383 a8083063 Iustin Pop
384 a8083063 Iustin Pop
def MatchNameComponent(key, name_list):
385 a8083063 Iustin Pop
  """Try to match a name against a list.
386 a8083063 Iustin Pop

387 a8083063 Iustin Pop
  This function will try to match a name like test1 against a list
388 a8083063 Iustin Pop
  like ['test1.example.com', 'test2.example.com', ...]. Against this
389 a8083063 Iustin Pop
  list, 'test1' as well as 'test1.example' will match, but not
390 a8083063 Iustin Pop
  'test1.ex'. A multiple match will be considered as no match at all
391 a8083063 Iustin Pop
  (e.g. 'test1' against ['test1.example.com', 'test1.example.org']).
392 a8083063 Iustin Pop

393 a8083063 Iustin Pop
  Args:
394 a8083063 Iustin Pop
    key: the name to be searched
395 a8083063 Iustin Pop
    name_list: the list of strings against which to search the key
396 a8083063 Iustin Pop

397 a8083063 Iustin Pop
  Returns:
398 a8083063 Iustin Pop
    None if there is no match *or* if there are multiple matches
399 a8083063 Iustin Pop
    otherwise the element from the list which matches
400 a8083063 Iustin Pop

401 a8083063 Iustin Pop
  """
402 a8083063 Iustin Pop
  mo = re.compile("^%s(\..*)?$" % re.escape(key))
403 a8083063 Iustin Pop
  names_filtered = [name for name in name_list if mo.match(name) is not None]
404 a8083063 Iustin Pop
  if len(names_filtered) != 1:
405 a8083063 Iustin Pop
    return None
406 a8083063 Iustin Pop
  return names_filtered[0]
407 a8083063 Iustin Pop
408 a8083063 Iustin Pop
409 bcf043c9 Iustin Pop
class HostInfo:
410 89e1fc26 Iustin Pop
  """Class implementing resolver and hostname functionality
411 bcf043c9 Iustin Pop

412 bcf043c9 Iustin Pop
  """
413 89e1fc26 Iustin Pop
  def __init__(self, name=None):
414 bcf043c9 Iustin Pop
    """Initialize the host name object.
415 bcf043c9 Iustin Pop

416 89e1fc26 Iustin Pop
    If the name argument is not passed, it will use this system's
417 89e1fc26 Iustin Pop
    name.
418 bcf043c9 Iustin Pop

419 bcf043c9 Iustin Pop
    """
420 89e1fc26 Iustin Pop
    if name is None:
421 89e1fc26 Iustin Pop
      name = self.SysName()
422 89e1fc26 Iustin Pop
423 89e1fc26 Iustin Pop
    self.query = name
424 89e1fc26 Iustin Pop
    self.name, self.aliases, self.ipaddrs = self.LookupHostname(name)
425 bcf043c9 Iustin Pop
    self.ip = self.ipaddrs[0]
426 bcf043c9 Iustin Pop
427 c8a0948f Michael Hanselmann
  def ShortName(self):
428 c8a0948f Michael Hanselmann
    """Returns the hostname without domain.
429 c8a0948f Michael Hanselmann

430 c8a0948f Michael Hanselmann
    """
431 c8a0948f Michael Hanselmann
    return self.name.split('.')[0]
432 c8a0948f Michael Hanselmann
433 89e1fc26 Iustin Pop
  @staticmethod
434 89e1fc26 Iustin Pop
  def SysName():
435 89e1fc26 Iustin Pop
    """Return the current system's name.
436 bcf043c9 Iustin Pop

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

439 89e1fc26 Iustin Pop
    """
440 89e1fc26 Iustin Pop
    return socket.gethostname()
441 a8083063 Iustin Pop
442 89e1fc26 Iustin Pop
  @staticmethod
443 89e1fc26 Iustin Pop
  def LookupHostname(hostname):
444 89e1fc26 Iustin Pop
    """Look up hostname
445 a8083063 Iustin Pop

446 89e1fc26 Iustin Pop
    Args:
447 89e1fc26 Iustin Pop
      hostname: hostname to look up
448 89e1fc26 Iustin Pop

449 89e1fc26 Iustin Pop
    Returns:
450 89e1fc26 Iustin Pop
      a tuple (name, aliases, ipaddrs) as returned by socket.gethostbyname_ex
451 89e1fc26 Iustin Pop
      in case of errors in resolving, we raise a ResolverError
452 89e1fc26 Iustin Pop

453 89e1fc26 Iustin Pop
    """
454 89e1fc26 Iustin Pop
    try:
455 89e1fc26 Iustin Pop
      result = socket.gethostbyname_ex(hostname)
456 89e1fc26 Iustin Pop
    except socket.gaierror, err:
457 89e1fc26 Iustin Pop
      # hostname not found in DNS
458 89e1fc26 Iustin Pop
      raise errors.ResolverError(hostname, err.args[0], err.args[1])
459 a8083063 Iustin Pop
460 89e1fc26 Iustin Pop
    return result
461 a8083063 Iustin Pop
462 a8083063 Iustin Pop
463 a8083063 Iustin Pop
def ListVolumeGroups():
464 a8083063 Iustin Pop
  """List volume groups and their size
465 a8083063 Iustin Pop

466 a8083063 Iustin Pop
  Returns:
467 a8083063 Iustin Pop
     Dictionary with keys volume name and values the size of the volume
468 a8083063 Iustin Pop

469 a8083063 Iustin Pop
  """
470 a8083063 Iustin Pop
  command = "vgs --noheadings --units m --nosuffix -o name,size"
471 a8083063 Iustin Pop
  result = RunCmd(command)
472 a8083063 Iustin Pop
  retval = {}
473 a8083063 Iustin Pop
  if result.failed:
474 a8083063 Iustin Pop
    return retval
475 a8083063 Iustin Pop
476 a8083063 Iustin Pop
  for line in result.stdout.splitlines():
477 a8083063 Iustin Pop
    try:
478 a8083063 Iustin Pop
      name, size = line.split()
479 a8083063 Iustin Pop
      size = int(float(size))
480 a8083063 Iustin Pop
    except (IndexError, ValueError), err:
481 a8083063 Iustin Pop
      logger.Error("Invalid output from vgs (%s): %s" % (err, line))
482 a8083063 Iustin Pop
      continue
483 a8083063 Iustin Pop
484 a8083063 Iustin Pop
    retval[name] = size
485 a8083063 Iustin Pop
486 a8083063 Iustin Pop
  return retval
487 a8083063 Iustin Pop
488 a8083063 Iustin Pop
489 a8083063 Iustin Pop
def BridgeExists(bridge):
490 a8083063 Iustin Pop
  """Check whether the given bridge exists in the system
491 a8083063 Iustin Pop

492 a8083063 Iustin Pop
  Returns:
493 a8083063 Iustin Pop
     True if it does, false otherwise.
494 a8083063 Iustin Pop

495 a8083063 Iustin Pop
  """
496 a8083063 Iustin Pop
  return os.path.isdir("/sys/class/net/%s/bridge" % bridge)
497 a8083063 Iustin Pop
498 a8083063 Iustin Pop
499 a8083063 Iustin Pop
def NiceSort(name_list):
500 a8083063 Iustin Pop
  """Sort a list of strings based on digit and non-digit groupings.
501 a8083063 Iustin Pop

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

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

509 a8083063 Iustin Pop
  Return value
510 a8083063 Iustin Pop
    - a copy of the list sorted according to our algorithm
511 a8083063 Iustin Pop

512 a8083063 Iustin Pop
  """
513 a8083063 Iustin Pop
  _SORTER_BASE = "(\D+|\d+)"
514 a8083063 Iustin Pop
  _SORTER_FULL = "^%s%s?%s?%s?%s?%s?%s?%s?.*$" % (_SORTER_BASE, _SORTER_BASE,
515 a8083063 Iustin Pop
                                                  _SORTER_BASE, _SORTER_BASE,
516 a8083063 Iustin Pop
                                                  _SORTER_BASE, _SORTER_BASE,
517 a8083063 Iustin Pop
                                                  _SORTER_BASE, _SORTER_BASE)
518 a8083063 Iustin Pop
  _SORTER_RE = re.compile(_SORTER_FULL)
519 a8083063 Iustin Pop
  _SORTER_NODIGIT = re.compile("^\D*$")
520 a8083063 Iustin Pop
  def _TryInt(val):
521 a8083063 Iustin Pop
    """Attempts to convert a variable to integer."""
522 a8083063 Iustin Pop
    if val is None or _SORTER_NODIGIT.match(val):
523 a8083063 Iustin Pop
      return val
524 a8083063 Iustin Pop
    rval = int(val)
525 a8083063 Iustin Pop
    return rval
526 a8083063 Iustin Pop
527 a8083063 Iustin Pop
  to_sort = [([_TryInt(grp) for grp in _SORTER_RE.match(name).groups()], name)
528 a8083063 Iustin Pop
             for name in name_list]
529 a8083063 Iustin Pop
  to_sort.sort()
530 a8083063 Iustin Pop
  return [tup[1] for tup in to_sort]
531 a8083063 Iustin Pop
532 a8083063 Iustin Pop
533 a8083063 Iustin Pop
def CheckDaemonAlive(pid_file, process_string):
534 a8083063 Iustin Pop
  """Check wether the specified daemon is alive.
535 a8083063 Iustin Pop

536 a8083063 Iustin Pop
  Args:
537 a8083063 Iustin Pop
   - pid_file: file to read the daemon pid from, the file is
538 a8083063 Iustin Pop
               expected to contain only a single line containing
539 a8083063 Iustin Pop
               only the PID
540 a8083063 Iustin Pop
   - process_string: a substring that we expect to find in
541 a8083063 Iustin Pop
                     the command line of the daemon process
542 a8083063 Iustin Pop

543 a8083063 Iustin Pop
  Returns:
544 a8083063 Iustin Pop
   - True if the daemon is judged to be alive (that is:
545 a8083063 Iustin Pop
      - the PID file exists, is readable and contains a number
546 a8083063 Iustin Pop
      - a process of the specified PID is running
547 a8083063 Iustin Pop
      - that process contains the specified string in its
548 a8083063 Iustin Pop
        command line
549 a8083063 Iustin Pop
      - the process is not in state Z (zombie))
550 a8083063 Iustin Pop
   - False otherwise
551 a8083063 Iustin Pop

552 a8083063 Iustin Pop
  """
553 a8083063 Iustin Pop
  try:
554 a8083063 Iustin Pop
    pid_file = file(pid_file, 'r')
555 a8083063 Iustin Pop
    try:
556 a8083063 Iustin Pop
      pid = int(pid_file.readline())
557 a8083063 Iustin Pop
    finally:
558 a8083063 Iustin Pop
      pid_file.close()
559 a8083063 Iustin Pop
560 a8083063 Iustin Pop
    cmdline_file_path = "/proc/%s/cmdline" % (pid)
561 a8083063 Iustin Pop
    cmdline_file = open(cmdline_file_path, 'r')
562 a8083063 Iustin Pop
    try:
563 a8083063 Iustin Pop
      cmdline = cmdline_file.readline()
564 a8083063 Iustin Pop
    finally:
565 a8083063 Iustin Pop
      cmdline_file.close()
566 a8083063 Iustin Pop
567 a8083063 Iustin Pop
    if not process_string in cmdline:
568 a8083063 Iustin Pop
      return False
569 a8083063 Iustin Pop
570 a8083063 Iustin Pop
    stat_file_path =  "/proc/%s/stat" % (pid)
571 a8083063 Iustin Pop
    stat_file = open(stat_file_path, 'r')
572 a8083063 Iustin Pop
    try:
573 a8083063 Iustin Pop
      process_state = stat_file.readline().split()[2]
574 a8083063 Iustin Pop
    finally:
575 a8083063 Iustin Pop
      stat_file.close()
576 a8083063 Iustin Pop
577 a8083063 Iustin Pop
    if process_state == 'Z':
578 a8083063 Iustin Pop
      return False
579 a8083063 Iustin Pop
580 a8083063 Iustin Pop
  except (IndexError, IOError, ValueError):
581 a8083063 Iustin Pop
    return False
582 a8083063 Iustin Pop
583 a8083063 Iustin Pop
  return True
584 a8083063 Iustin Pop
585 a8083063 Iustin Pop
586 a8083063 Iustin Pop
def TryConvert(fn, val):
587 a8083063 Iustin Pop
  """Try to convert a value ignoring errors.
588 a8083063 Iustin Pop

589 a8083063 Iustin Pop
  This function tries to apply function `fn` to `val`. If no
590 a8083063 Iustin Pop
  ValueError or TypeError exceptions are raised, it will return the
591 a8083063 Iustin Pop
  result, else it will return the original value. Any other exceptions
592 a8083063 Iustin Pop
  are propagated to the caller.
593 a8083063 Iustin Pop

594 a8083063 Iustin Pop
  """
595 a8083063 Iustin Pop
  try:
596 a8083063 Iustin Pop
    nv = fn(val)
597 a8083063 Iustin Pop
  except (ValueError, TypeError), err:
598 a8083063 Iustin Pop
    nv = val
599 a8083063 Iustin Pop
  return nv
600 a8083063 Iustin Pop
601 a8083063 Iustin Pop
602 a8083063 Iustin Pop
def IsValidIP(ip):
603 a8083063 Iustin Pop
  """Verifies the syntax of an IP address.
604 a8083063 Iustin Pop

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

608 a8083063 Iustin Pop
  """
609 a8083063 Iustin Pop
  unit = "(0|[1-9]\d{0,2})"
610 a8083063 Iustin Pop
  return re.match("^%s\.%s\.%s\.%s$" % (unit, unit, unit, unit), ip)
611 a8083063 Iustin Pop
612 a8083063 Iustin Pop
613 a8083063 Iustin Pop
def IsValidShellParam(word):
614 a8083063 Iustin Pop
  """Verifies is the given word is safe from the shell's p.o.v.
615 a8083063 Iustin Pop

616 a8083063 Iustin Pop
  This means that we can pass this to a command via the shell and be
617 a8083063 Iustin Pop
  sure that it doesn't alter the command line and is passed as such to
618 a8083063 Iustin Pop
  the actual command.
619 a8083063 Iustin Pop

620 a8083063 Iustin Pop
  Note that we are overly restrictive here, in order to be on the safe
621 a8083063 Iustin Pop
  side.
622 a8083063 Iustin Pop

623 a8083063 Iustin Pop
  """
624 a8083063 Iustin Pop
  return bool(re.match("^[-a-zA-Z0-9._+/:%@]+$", word))
625 a8083063 Iustin Pop
626 a8083063 Iustin Pop
627 a8083063 Iustin Pop
def BuildShellCmd(template, *args):
628 a8083063 Iustin Pop
  """Build a safe shell command line from the given arguments.
629 a8083063 Iustin Pop

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

635 a8083063 Iustin Pop
  """
636 a8083063 Iustin Pop
  for word in args:
637 a8083063 Iustin Pop
    if not IsValidShellParam(word):
638 3ecf6786 Iustin Pop
      raise errors.ProgrammerError("Shell argument '%s' contains"
639 3ecf6786 Iustin Pop
                                   " invalid characters" % word)
640 a8083063 Iustin Pop
  return template % args
641 a8083063 Iustin Pop
642 a8083063 Iustin Pop
643 a8083063 Iustin Pop
def FormatUnit(value):
644 a8083063 Iustin Pop
  """Formats an incoming number of MiB with the appropriate unit.
645 a8083063 Iustin Pop

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

648 a8083063 Iustin Pop
  """
649 a8083063 Iustin Pop
  if value < 1024:
650 a8083063 Iustin Pop
    return "%dM" % round(value, 0)
651 a8083063 Iustin Pop
652 a8083063 Iustin Pop
  elif value < (1024 * 1024):
653 a8083063 Iustin Pop
    return "%0.1fG" % round(float(value) / 1024, 1)
654 a8083063 Iustin Pop
655 a8083063 Iustin Pop
  else:
656 a8083063 Iustin Pop
    return "%0.1fT" % round(float(value) / 1024 / 1024, 1)
657 a8083063 Iustin Pop
658 a8083063 Iustin Pop
659 a8083063 Iustin Pop
def ParseUnit(input_string):
660 a8083063 Iustin Pop
  """Tries to extract number and scale from the given string.
661 a8083063 Iustin Pop

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

665 a8083063 Iustin Pop
  """
666 a8083063 Iustin Pop
  m = re.match('^([.\d]+)\s*([a-zA-Z]+)?$', input_string)
667 a8083063 Iustin Pop
  if not m:
668 3ecf6786 Iustin Pop
    raise errors.UnitParseError("Invalid format")
669 a8083063 Iustin Pop
670 a8083063 Iustin Pop
  value = float(m.groups()[0])
671 a8083063 Iustin Pop
672 a8083063 Iustin Pop
  unit = m.groups()[1]
673 a8083063 Iustin Pop
  if unit:
674 a8083063 Iustin Pop
    lcunit = unit.lower()
675 a8083063 Iustin Pop
  else:
676 a8083063 Iustin Pop
    lcunit = 'm'
677 a8083063 Iustin Pop
678 a8083063 Iustin Pop
  if lcunit in ('m', 'mb', 'mib'):
679 a8083063 Iustin Pop
    # Value already in MiB
680 a8083063 Iustin Pop
    pass
681 a8083063 Iustin Pop
682 a8083063 Iustin Pop
  elif lcunit in ('g', 'gb', 'gib'):
683 a8083063 Iustin Pop
    value *= 1024
684 a8083063 Iustin Pop
685 a8083063 Iustin Pop
  elif lcunit in ('t', 'tb', 'tib'):
686 a8083063 Iustin Pop
    value *= 1024 * 1024
687 a8083063 Iustin Pop
688 a8083063 Iustin Pop
  else:
689 3ecf6786 Iustin Pop
    raise errors.UnitParseError("Unknown unit: %s" % unit)
690 a8083063 Iustin Pop
691 a8083063 Iustin Pop
  # Make sure we round up
692 a8083063 Iustin Pop
  if int(value) < value:
693 a8083063 Iustin Pop
    value += 1
694 a8083063 Iustin Pop
695 a8083063 Iustin Pop
  # Round up to the next multiple of 4
696 a8083063 Iustin Pop
  value = int(value)
697 a8083063 Iustin Pop
  if value % 4:
698 a8083063 Iustin Pop
    value += 4 - value % 4
699 a8083063 Iustin Pop
700 a8083063 Iustin Pop
  return value
701 a8083063 Iustin Pop
702 a8083063 Iustin Pop
703 a8083063 Iustin Pop
def AddAuthorizedKey(file_name, key):
704 a8083063 Iustin Pop
  """Adds an SSH public key to an authorized_keys file.
705 a8083063 Iustin Pop

706 a8083063 Iustin Pop
  Args:
707 a8083063 Iustin Pop
    file_name: Path to authorized_keys file
708 a8083063 Iustin Pop
    key: String containing key
709 a8083063 Iustin Pop
  """
710 a8083063 Iustin Pop
  key_fields = key.split()
711 a8083063 Iustin Pop
712 a8083063 Iustin Pop
  f = open(file_name, 'a+')
713 a8083063 Iustin Pop
  try:
714 a8083063 Iustin Pop
    nl = True
715 a8083063 Iustin Pop
    for line in f:
716 a8083063 Iustin Pop
      # Ignore whitespace changes
717 a8083063 Iustin Pop
      if line.split() == key_fields:
718 a8083063 Iustin Pop
        break
719 a8083063 Iustin Pop
      nl = line.endswith('\n')
720 a8083063 Iustin Pop
    else:
721 a8083063 Iustin Pop
      if not nl:
722 a8083063 Iustin Pop
        f.write("\n")
723 a8083063 Iustin Pop
      f.write(key.rstrip('\r\n'))
724 a8083063 Iustin Pop
      f.write("\n")
725 a8083063 Iustin Pop
      f.flush()
726 a8083063 Iustin Pop
  finally:
727 a8083063 Iustin Pop
    f.close()
728 a8083063 Iustin Pop
729 a8083063 Iustin Pop
730 a8083063 Iustin Pop
def RemoveAuthorizedKey(file_name, key):
731 a8083063 Iustin Pop
  """Removes an SSH public key from an authorized_keys file.
732 a8083063 Iustin Pop

733 a8083063 Iustin Pop
  Args:
734 a8083063 Iustin Pop
    file_name: Path to authorized_keys file
735 a8083063 Iustin Pop
    key: String containing key
736 a8083063 Iustin Pop
  """
737 a8083063 Iustin Pop
  key_fields = key.split()
738 a8083063 Iustin Pop
739 a8083063 Iustin Pop
  fd, tmpname = tempfile.mkstemp(dir=os.path.dirname(file_name))
740 a8083063 Iustin Pop
  try:
741 59f82e3f Michael Hanselmann
    out = os.fdopen(fd, 'w')
742 a8083063 Iustin Pop
    try:
743 59f82e3f Michael Hanselmann
      f = open(file_name, 'r')
744 59f82e3f Michael Hanselmann
      try:
745 59f82e3f Michael Hanselmann
        for line in f:
746 59f82e3f Michael Hanselmann
          # Ignore whitespace changes while comparing lines
747 59f82e3f Michael Hanselmann
          if line.split() != key_fields:
748 59f82e3f Michael Hanselmann
            out.write(line)
749 899d2a81 Michael Hanselmann
750 899d2a81 Michael Hanselmann
        out.flush()
751 899d2a81 Michael Hanselmann
        os.rename(tmpname, file_name)
752 899d2a81 Michael Hanselmann
      finally:
753 899d2a81 Michael Hanselmann
        f.close()
754 899d2a81 Michael Hanselmann
    finally:
755 899d2a81 Michael Hanselmann
      out.close()
756 899d2a81 Michael Hanselmann
  except:
757 899d2a81 Michael Hanselmann
    RemoveFile(tmpname)
758 899d2a81 Michael Hanselmann
    raise
759 899d2a81 Michael Hanselmann
760 899d2a81 Michael Hanselmann
761 9440aeab Michael Hanselmann
def SetEtcHostsEntry(file_name, ip, hostname, aliases):
762 9440aeab Michael Hanselmann
  """Sets the name of an IP address and hostname in /etc/hosts.
763 899d2a81 Michael Hanselmann

764 899d2a81 Michael Hanselmann
  """
765 7fbb1f65 Michael Hanselmann
  # Ensure aliases are unique
766 7fbb1f65 Michael Hanselmann
  aliases = UniqueSequence([hostname] + aliases)[1:]
767 7fbb1f65 Michael Hanselmann
768 9440aeab Michael Hanselmann
  fd, tmpname = tempfile.mkstemp(dir=os.path.dirname(file_name))
769 899d2a81 Michael Hanselmann
  try:
770 9440aeab Michael Hanselmann
    out = os.fdopen(fd, 'w')
771 9440aeab Michael Hanselmann
    try:
772 9440aeab Michael Hanselmann
      f = open(file_name, 'r')
773 9440aeab Michael Hanselmann
      try:
774 9440aeab Michael Hanselmann
        written = False
775 9440aeab Michael Hanselmann
        for line in f:
776 9440aeab Michael Hanselmann
          fields = line.split()
777 7e3dbb94 Iustin Pop
          if fields and not fields[0].startswith('#') and ip == fields[0]:
778 9440aeab Michael Hanselmann
            continue
779 9440aeab Michael Hanselmann
          out.write(line)
780 9440aeab Michael Hanselmann
781 7e3dbb94 Iustin Pop
        out.write("%s\t%s" % (ip, hostname))
782 9440aeab Michael Hanselmann
        if aliases:
783 9440aeab Michael Hanselmann
          out.write(" %s" % ' '.join(aliases))
784 9440aeab Michael Hanselmann
        out.write('\n')
785 9440aeab Michael Hanselmann
786 9440aeab Michael Hanselmann
        out.flush()
787 2e3e75b7 Michael Hanselmann
        os.fsync(out)
788 9440aeab Michael Hanselmann
        os.rename(tmpname, file_name)
789 9440aeab Michael Hanselmann
      finally:
790 9440aeab Michael Hanselmann
        f.close()
791 9440aeab Michael Hanselmann
    finally:
792 9440aeab Michael Hanselmann
      out.close()
793 9440aeab Michael Hanselmann
  except:
794 9440aeab Michael Hanselmann
    RemoveFile(tmpname)
795 9440aeab Michael Hanselmann
    raise
796 899d2a81 Michael Hanselmann
797 899d2a81 Michael Hanselmann
798 899d2a81 Michael Hanselmann
def RemoveEtcHostsEntry(file_name, hostname):
799 3e1cdf9f Michael Hanselmann
  """Removes a hostname from /etc/hosts.
800 899d2a81 Michael Hanselmann

801 9440aeab Michael Hanselmann
  IP addresses without names are removed from the file.
802 899d2a81 Michael Hanselmann
  """
803 899d2a81 Michael Hanselmann
  fd, tmpname = tempfile.mkstemp(dir=os.path.dirname(file_name))
804 899d2a81 Michael Hanselmann
  try:
805 899d2a81 Michael Hanselmann
    out = os.fdopen(fd, 'w')
806 899d2a81 Michael Hanselmann
    try:
807 899d2a81 Michael Hanselmann
      f = open(file_name, 'r')
808 899d2a81 Michael Hanselmann
      try:
809 899d2a81 Michael Hanselmann
        for line in f:
810 899d2a81 Michael Hanselmann
          fields = line.split()
811 899d2a81 Michael Hanselmann
          if len(fields) > 1 and not fields[0].startswith('#'):
812 899d2a81 Michael Hanselmann
            names = fields[1:]
813 899d2a81 Michael Hanselmann
            if hostname in names:
814 899d2a81 Michael Hanselmann
              while hostname in names:
815 899d2a81 Michael Hanselmann
                names.remove(hostname)
816 899d2a81 Michael Hanselmann
              if names:
817 9440aeab Michael Hanselmann
                out.write("%s %s\n" % (fields[0], ' '.join(names)))
818 899d2a81 Michael Hanselmann
              continue
819 899d2a81 Michael Hanselmann
820 899d2a81 Michael Hanselmann
          out.write(line)
821 59f82e3f Michael Hanselmann
822 59f82e3f Michael Hanselmann
        out.flush()
823 2e3e75b7 Michael Hanselmann
        os.fsync(out)
824 59f82e3f Michael Hanselmann
        os.rename(tmpname, file_name)
825 59f82e3f Michael Hanselmann
      finally:
826 59f82e3f Michael Hanselmann
        f.close()
827 a8083063 Iustin Pop
    finally:
828 59f82e3f Michael Hanselmann
      out.close()
829 59f82e3f Michael Hanselmann
  except:
830 59f82e3f Michael Hanselmann
    RemoveFile(tmpname)
831 59f82e3f Michael Hanselmann
    raise
832 a8083063 Iustin Pop
833 a8083063 Iustin Pop
834 a8083063 Iustin Pop
def CreateBackup(file_name):
835 a8083063 Iustin Pop
  """Creates a backup of a file.
836 a8083063 Iustin Pop

837 a8083063 Iustin Pop
  Returns: the path to the newly created backup file.
838 a8083063 Iustin Pop

839 a8083063 Iustin Pop
  """
840 a8083063 Iustin Pop
  if not os.path.isfile(file_name):
841 3ecf6786 Iustin Pop
    raise errors.ProgrammerError("Can't make a backup of a non-file '%s'" %
842 3ecf6786 Iustin Pop
                                file_name)
843 a8083063 Iustin Pop
844 081b1e69 Michael Hanselmann
  prefix = '%s.backup-%d.' % (os.path.basename(file_name), int(time.time()))
845 65fe4693 Iustin Pop
  dir_name = os.path.dirname(file_name)
846 081b1e69 Michael Hanselmann
847 081b1e69 Michael Hanselmann
  fsrc = open(file_name, 'rb')
848 081b1e69 Michael Hanselmann
  try:
849 65fe4693 Iustin Pop
    (fd, backup_name) = tempfile.mkstemp(prefix=prefix, dir=dir_name)
850 081b1e69 Michael Hanselmann
    fdst = os.fdopen(fd, 'wb')
851 081b1e69 Michael Hanselmann
    try:
852 081b1e69 Michael Hanselmann
      shutil.copyfileobj(fsrc, fdst)
853 081b1e69 Michael Hanselmann
    finally:
854 081b1e69 Michael Hanselmann
      fdst.close()
855 081b1e69 Michael Hanselmann
  finally:
856 081b1e69 Michael Hanselmann
    fsrc.close()
857 081b1e69 Michael Hanselmann
858 a8083063 Iustin Pop
  return backup_name
859 a8083063 Iustin Pop
860 a8083063 Iustin Pop
861 a8083063 Iustin Pop
def ShellQuote(value):
862 a8083063 Iustin Pop
  """Quotes shell argument according to POSIX.
863 3ecf6786 Iustin Pop

864 a8083063 Iustin Pop
  """
865 a8083063 Iustin Pop
  if _re_shell_unquoted.match(value):
866 a8083063 Iustin Pop
    return value
867 a8083063 Iustin Pop
  else:
868 a8083063 Iustin Pop
    return "'%s'" % value.replace("'", "'\\''")
869 a8083063 Iustin Pop
870 a8083063 Iustin Pop
871 a8083063 Iustin Pop
def ShellQuoteArgs(args):
872 a8083063 Iustin Pop
  """Quotes all given shell arguments and concatenates using spaces.
873 a8083063 Iustin Pop

874 a8083063 Iustin Pop
  """
875 a8083063 Iustin Pop
  return ' '.join([ShellQuote(i) for i in args])
876 88d14415 Michael Hanselmann
877 88d14415 Michael Hanselmann
878 2c30e9d7 Alexander Schreiber
879 16abfbc2 Alexander Schreiber
def TcpPing(source, target, port, timeout=10, live_port_needed=False):
880 2c30e9d7 Alexander Schreiber
  """Simple ping implementation using TCP connect(2).
881 2c30e9d7 Alexander Schreiber

882 2c30e9d7 Alexander Schreiber
  Try to do a TCP connect(2) from the specified source IP to the specified
883 2c30e9d7 Alexander Schreiber
  target IP and the specified target port. If live_port_needed is set to true,
884 2c30e9d7 Alexander Schreiber
  requires the remote end to accept the connection. The timeout is specified
885 2c30e9d7 Alexander Schreiber
  in seconds and defaults to 10 seconds
886 2c30e9d7 Alexander Schreiber

887 2c30e9d7 Alexander Schreiber
  """
888 2c30e9d7 Alexander Schreiber
  sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
889 2c30e9d7 Alexander Schreiber
890 2c30e9d7 Alexander Schreiber
  sucess = False
891 2c30e9d7 Alexander Schreiber
892 2c30e9d7 Alexander Schreiber
  try:
893 2c30e9d7 Alexander Schreiber
    sock.bind((source, 0))
894 2c30e9d7 Alexander Schreiber
  except socket.error, (errcode, errstring):
895 4ca1b175 Alexander Schreiber
    if errcode == errno.EADDRNOTAVAIL:
896 2c30e9d7 Alexander Schreiber
      success = False
897 2c30e9d7 Alexander Schreiber
898 2c30e9d7 Alexander Schreiber
  sock.settimeout(timeout)
899 2c30e9d7 Alexander Schreiber
900 2c30e9d7 Alexander Schreiber
  try:
901 2c30e9d7 Alexander Schreiber
    sock.connect((target, port))
902 2c30e9d7 Alexander Schreiber
    sock.close()
903 2c30e9d7 Alexander Schreiber
    success = True
904 2c30e9d7 Alexander Schreiber
  except socket.timeout:
905 2c30e9d7 Alexander Schreiber
    success = False
906 2c30e9d7 Alexander Schreiber
  except socket.error, (errcode, errstring):
907 4ca1b175 Alexander Schreiber
    success = (not live_port_needed) and (errcode == errno.ECONNREFUSED)
908 2c30e9d7 Alexander Schreiber
909 2c30e9d7 Alexander Schreiber
  return success
910 eedbda4b Michael Hanselmann
911 eedbda4b Michael Hanselmann
912 eedbda4b Michael Hanselmann
def ListVisibleFiles(path):
913 eedbda4b Michael Hanselmann
  """Returns a list of all visible files in a directory.
914 eedbda4b Michael Hanselmann

915 eedbda4b Michael Hanselmann
  """
916 f3299a07 Michael Hanselmann
  files = [i for i in os.listdir(path) if not i.startswith(".")]
917 f3299a07 Michael Hanselmann
  files.sort()
918 f3299a07 Michael Hanselmann
  return files
919 2f8b60b3 Iustin Pop
920 2f8b60b3 Iustin Pop
921 257f4c0a Iustin Pop
def GetHomeDir(user, default=None):
922 257f4c0a Iustin Pop
  """Try to get the homedir of the given user.
923 257f4c0a Iustin Pop

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

928 2f8b60b3 Iustin Pop
  """
929 2f8b60b3 Iustin Pop
  try:
930 257f4c0a Iustin Pop
    if isinstance(user, basestring):
931 257f4c0a Iustin Pop
      result = pwd.getpwnam(user)
932 257f4c0a Iustin Pop
    elif isinstance(user, (int, long)):
933 257f4c0a Iustin Pop
      result = pwd.getpwuid(user)
934 257f4c0a Iustin Pop
    else:
935 257f4c0a Iustin Pop
      raise errors.ProgrammerError("Invalid type passed to GetHomeDir (%s)" %
936 257f4c0a Iustin Pop
                                   type(user))
937 2f8b60b3 Iustin Pop
  except KeyError:
938 2f8b60b3 Iustin Pop
    return default
939 2f8b60b3 Iustin Pop
  return result.pw_dir
940 59072e7e Michael Hanselmann
941 59072e7e Michael Hanselmann
942 24818e8f Michael Hanselmann
def NewUUID():
943 59072e7e Michael Hanselmann
  """Returns a random UUID.
944 59072e7e Michael Hanselmann

945 59072e7e Michael Hanselmann
  """
946 59072e7e Michael Hanselmann
  f = open("/proc/sys/kernel/random/uuid", "r")
947 59072e7e Michael Hanselmann
  try:
948 59072e7e Michael Hanselmann
    return f.read(128).rstrip("\n")
949 59072e7e Michael Hanselmann
  finally:
950 59072e7e Michael Hanselmann
    f.close()
951 087b34fe Iustin Pop
952 087b34fe Iustin Pop
953 087b34fe Iustin Pop
def WriteFile(file_name, fn=None, data=None,
954 087b34fe Iustin Pop
              mode=None, uid=-1, gid=-1,
955 087b34fe Iustin Pop
              atime=None, mtime=None):
956 087b34fe Iustin Pop
  """(Over)write a file atomically.
957 087b34fe Iustin Pop

958 087b34fe Iustin Pop
  The file_name and either fn (a function taking one argument, the
959 087b34fe Iustin Pop
  file descriptor, and which should write the data to it) or data (the
960 087b34fe Iustin Pop
  contents of the file) must be passed. The other arguments are
961 087b34fe Iustin Pop
  optional and allow setting the file mode, owner and group, and the
962 087b34fe Iustin Pop
  mtime/atime of the file.
963 087b34fe Iustin Pop

964 087b34fe Iustin Pop
  If the function doesn't raise an exception, it has succeeded and the
965 087b34fe Iustin Pop
  target file has the new contents. If the file has raised an
966 087b34fe Iustin Pop
  exception, an existing target file should be unmodified and the
967 087b34fe Iustin Pop
  temporary file should be removed.
968 087b34fe Iustin Pop

969 087b34fe Iustin Pop
  """
970 087b34fe Iustin Pop
  if not os.path.isabs(file_name):
971 087b34fe Iustin Pop
    raise errors.ProgrammerError("Path passed to WriteFile is not"
972 087b34fe Iustin Pop
                                 " absolute: '%s'" % file_name)
973 087b34fe Iustin Pop
974 087b34fe Iustin Pop
  if [fn, data].count(None) != 1:
975 087b34fe Iustin Pop
    raise errors.ProgrammerError("fn or data required")
976 087b34fe Iustin Pop
977 087b34fe Iustin Pop
  if [atime, mtime].count(None) == 1:
978 087b34fe Iustin Pop
    raise errors.ProgrammerError("Both atime and mtime must be either"
979 087b34fe Iustin Pop
                                 " set or None")
980 087b34fe Iustin Pop
981 087b34fe Iustin Pop
982 087b34fe Iustin Pop
  dir_name, base_name = os.path.split(file_name)
983 087b34fe Iustin Pop
  fd, new_name = tempfile.mkstemp('.new', base_name, dir_name)
984 087b34fe Iustin Pop
  # here we need to make sure we remove the temp file, if any error
985 087b34fe Iustin Pop
  # leaves it in place
986 087b34fe Iustin Pop
  try:
987 087b34fe Iustin Pop
    if uid != -1 or gid != -1:
988 087b34fe Iustin Pop
      os.chown(new_name, uid, gid)
989 087b34fe Iustin Pop
    if mode:
990 087b34fe Iustin Pop
      os.chmod(new_name, mode)
991 087b34fe Iustin Pop
    if data is not None:
992 087b34fe Iustin Pop
      os.write(fd, data)
993 087b34fe Iustin Pop
    else:
994 087b34fe Iustin Pop
      fn(fd)
995 087b34fe Iustin Pop
    os.fsync(fd)
996 087b34fe Iustin Pop
    if atime is not None and mtime is not None:
997 087b34fe Iustin Pop
      os.utime(new_name, (atime, mtime))
998 087b34fe Iustin Pop
    os.rename(new_name, file_name)
999 087b34fe Iustin Pop
  finally:
1000 087b34fe Iustin Pop
    os.close(fd)
1001 087b34fe Iustin Pop
    RemoveFile(new_name)
1002 78feb6fb Guido Trotter
1003 78feb6fb Guido Trotter
1004 78feb6fb Guido Trotter
def all(seq, pred=bool):
1005 78feb6fb Guido Trotter
  "Returns True if pred(x) is True for every element in the iterable"
1006 78feb6fb Guido Trotter
  for elem in itertools.ifilterfalse(pred, seq):
1007 78feb6fb Guido Trotter
    return False
1008 78feb6fb Guido Trotter
  return True
1009 78feb6fb Guido Trotter
1010 78feb6fb Guido Trotter
1011 78feb6fb Guido Trotter
def any(seq, pred=bool):
1012 78feb6fb Guido Trotter
  "Returns True if pred(x) is True for at least one element in the iterable"
1013 78feb6fb Guido Trotter
  for elem in itertools.ifilter(pred, seq):
1014 78feb6fb Guido Trotter
    return True
1015 78feb6fb Guido Trotter
  return False
1016 f7414041 Michael Hanselmann
1017 f7414041 Michael Hanselmann
1018 f7414041 Michael Hanselmann
def UniqueSequence(seq):
1019 f7414041 Michael Hanselmann
  """Returns a list with unique elements.
1020 f7414041 Michael Hanselmann

1021 f7414041 Michael Hanselmann
  Element order is preserved.
1022 f7414041 Michael Hanselmann
  """
1023 f7414041 Michael Hanselmann
  seen = set()
1024 f7414041 Michael Hanselmann
  return [i for i in seq if i not in seen and not seen.add(i)]