Statistics
| Branch: | Tag: | Revision:

root / lib / utils.py @ f4bc1f2c

History | View | Annotate | Download (25.5 kB)

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

24 a8083063 Iustin Pop
"""
25 a8083063 Iustin Pop
26 a8083063 Iustin Pop
27 a8083063 Iustin Pop
import sys
28 a8083063 Iustin Pop
import os
29 a8083063 Iustin Pop
import sha
30 a8083063 Iustin Pop
import time
31 113b55aa Iustin Pop
import subprocess
32 a8083063 Iustin Pop
import re
33 a8083063 Iustin Pop
import socket
34 a8083063 Iustin Pop
import tempfile
35 a8083063 Iustin Pop
import shutil
36 4ca1b175 Alexander Schreiber
import errno
37 2f8b60b3 Iustin Pop
import pwd
38 78feb6fb Guido Trotter
import itertools
39 a8083063 Iustin Pop
40 a8083063 Iustin Pop
from ganeti import logger
41 a8083063 Iustin Pop
from ganeti import errors
42 3aecd2c7 Iustin Pop
from ganeti import constants
43 a8083063 Iustin Pop
44 16abfbc2 Alexander Schreiber
45 a8083063 Iustin Pop
_locksheld = []
46 a8083063 Iustin Pop
_re_shell_unquoted = re.compile('^[-.,=:/_+@A-Za-z0-9]+$')
47 a8083063 Iustin Pop
48 f362096f Iustin Pop
debug = False
49 f362096f Iustin Pop
50 a8083063 Iustin Pop
class RunResult(object):
51 a8083063 Iustin Pop
  """Simple class for holding the result of running external programs.
52 a8083063 Iustin Pop

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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