Statistics
| Branch: | Tag: | Revision:

root / lib / utils.py @ a8083063

History | View | Annotate | Download (18.5 kB)

1 a8083063 Iustin Pop
#!/usr/bin/python
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 a8083063 Iustin Pop
"""
24 a8083063 Iustin Pop
25 a8083063 Iustin Pop
26 a8083063 Iustin Pop
import sys
27 a8083063 Iustin Pop
import os
28 a8083063 Iustin Pop
import sha
29 a8083063 Iustin Pop
import time
30 a8083063 Iustin Pop
import popen2
31 a8083063 Iustin Pop
import re
32 a8083063 Iustin Pop
import socket
33 a8083063 Iustin Pop
import tempfile
34 a8083063 Iustin Pop
import shutil
35 a8083063 Iustin Pop
from errno import ENOENT, ENOTDIR, EISDIR, EEXIST
36 a8083063 Iustin Pop
37 a8083063 Iustin Pop
from ganeti import logger
38 a8083063 Iustin Pop
from ganeti import errors
39 a8083063 Iustin Pop
40 a8083063 Iustin Pop
_locksheld = []
41 a8083063 Iustin Pop
_re_shell_unquoted = re.compile('^[-.,=:/_+@A-Za-z0-9]+$')
42 a8083063 Iustin Pop
43 a8083063 Iustin Pop
class RunResult(object):
44 a8083063 Iustin Pop
  """Simple class for holding the result of running external programs.
45 a8083063 Iustin Pop

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

57 a8083063 Iustin Pop
  """
58 a8083063 Iustin Pop
  __slots__ = ["exit_code", "signal", "stdout", "stderr",
59 a8083063 Iustin Pop
               "failed", "fail_reason", "cmd"]
60 a8083063 Iustin Pop
61 a8083063 Iustin Pop
62 a8083063 Iustin Pop
  def __init__(self, exit_code, signal, stdout, stderr, cmd):
63 a8083063 Iustin Pop
    self.cmd = cmd
64 a8083063 Iustin Pop
    self.exit_code = exit_code
65 a8083063 Iustin Pop
    self.signal = signal
66 a8083063 Iustin Pop
    self.stdout = stdout
67 a8083063 Iustin Pop
    self.stderr = stderr
68 a8083063 Iustin Pop
    self.failed = (signal is not None or exit_code != 0)
69 a8083063 Iustin Pop
70 a8083063 Iustin Pop
    if self.signal is not None:
71 a8083063 Iustin Pop
      self.fail_reason = "terminated by signal %s" % self.signal
72 a8083063 Iustin Pop
    elif self.exit_code is not None:
73 a8083063 Iustin Pop
      self.fail_reason = "exited with exit code %s" % self.exit_code
74 a8083063 Iustin Pop
    else:
75 a8083063 Iustin Pop
      self.fail_reason = "unable to determine termination reason"
76 a8083063 Iustin Pop
77 a8083063 Iustin Pop
  def _GetOutput(self):
78 a8083063 Iustin Pop
    """Returns the combined stdout and stderr for easier usage.
79 a8083063 Iustin Pop

80 a8083063 Iustin Pop
    """
81 a8083063 Iustin Pop
    return self.stdout + self.stderr
82 a8083063 Iustin Pop
83 a8083063 Iustin Pop
  output = property(_GetOutput, None, None, "Return full output")
84 a8083063 Iustin Pop
85 a8083063 Iustin Pop
86 a8083063 Iustin Pop
def _GetLockFile(subsystem):
87 a8083063 Iustin Pop
  """Compute the file name for a given lock name."""
88 a8083063 Iustin Pop
  return "/var/lock/ganeti_lock_%s" % subsystem
89 a8083063 Iustin Pop
90 a8083063 Iustin Pop
91 a8083063 Iustin Pop
def Lock(name, max_retries=None, debug=False):
92 a8083063 Iustin Pop
  """Lock a given subsystem.
93 a8083063 Iustin Pop

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

97 a8083063 Iustin Pop
  When the optional integer argument 'max_retries' is passed with a
98 a8083063 Iustin Pop
  non-zero value, the function will sleep only for this number of
99 a8083063 Iustin Pop
  times, and then it will will raise a LockError if the lock can't be
100 a8083063 Iustin Pop
  acquired. Passing in a negative number will cause only one try to
101 a8083063 Iustin Pop
  get the lock. Passing a positive number will make the function retry
102 a8083063 Iustin Pop
  for approximately that number of seconds.
103 a8083063 Iustin Pop

104 a8083063 Iustin Pop
  """
105 a8083063 Iustin Pop
  lockfile = _GetLockFile(name)
106 a8083063 Iustin Pop
107 a8083063 Iustin Pop
  if name in _locksheld:
108 a8083063 Iustin Pop
    raise errors.LockError('Lock "%s" already held!' % (name,))
109 a8083063 Iustin Pop
110 a8083063 Iustin Pop
  errcount = 0
111 a8083063 Iustin Pop
112 a8083063 Iustin Pop
  retries = 0
113 a8083063 Iustin Pop
  while True:
114 a8083063 Iustin Pop
    try:
115 a8083063 Iustin Pop
      fd = os.open(lockfile, os.O_CREAT | os.O_EXCL | os.O_RDWR | os.O_SYNC)
116 a8083063 Iustin Pop
      break
117 a8083063 Iustin Pop
    except OSError, creat_err:
118 a8083063 Iustin Pop
      if creat_err.errno != EEXIST:
119 a8083063 Iustin Pop
        raise errors.LockError, ("Can't create the lock file. Error '%s'." %
120 a8083063 Iustin Pop
                                 str(creat_err))
121 a8083063 Iustin Pop
122 a8083063 Iustin Pop
      try:
123 a8083063 Iustin Pop
        pf = open(lockfile, 'r')
124 a8083063 Iustin Pop
      except IOError, open_err:
125 a8083063 Iustin Pop
        errcount += 1
126 a8083063 Iustin Pop
        if errcount >= 5:
127 a8083063 Iustin Pop
          raise errors.LockError, ("Lock file exists but cannot be opened."
128 a8083063 Iustin Pop
                                   " Error: '%s'." % str(open_err))
129 a8083063 Iustin Pop
        time.sleep(1)
130 a8083063 Iustin Pop
        continue
131 a8083063 Iustin Pop
132 a8083063 Iustin Pop
      try:
133 a8083063 Iustin Pop
        pid = int(pf.read())
134 a8083063 Iustin Pop
      except ValueError:
135 a8083063 Iustin Pop
        raise errors.LockError('Invalid pid string in %s' %
136 a8083063 Iustin Pop
                               (lockfile,))
137 a8083063 Iustin Pop
138 a8083063 Iustin Pop
      if not IsProcessAlive(pid):
139 a8083063 Iustin Pop
        raise errors.LockError, ('Stale lockfile %s for pid %d?' %
140 a8083063 Iustin Pop
                                 (lockfile, pid))
141 a8083063 Iustin Pop
142 a8083063 Iustin Pop
      if max_retries and max_retries <= retries:
143 a8083063 Iustin Pop
        raise errors.LockError, ("Can't acquire lock during the specified"
144 a8083063 Iustin Pop
                                 " time, aborting.")
145 a8083063 Iustin Pop
      if retries == 5 and (debug or sys.stdin.isatty()):
146 a8083063 Iustin Pop
        logger.ToStderr("Waiting for '%s' lock from pid %d..." % (name, pid))
147 a8083063 Iustin Pop
148 a8083063 Iustin Pop
      time.sleep(1)
149 a8083063 Iustin Pop
      retries += 1
150 a8083063 Iustin Pop
      continue
151 a8083063 Iustin Pop
152 a8083063 Iustin Pop
  os.write(fd, '%d\n' % (os.getpid(),))
153 a8083063 Iustin Pop
  os.close(fd)
154 a8083063 Iustin Pop
155 a8083063 Iustin Pop
  _locksheld.append(name)
156 a8083063 Iustin Pop
157 a8083063 Iustin Pop
158 a8083063 Iustin Pop
def Unlock(name):
159 a8083063 Iustin Pop
  """Unlock a given subsystem."""
160 a8083063 Iustin Pop
161 a8083063 Iustin Pop
  lockfile = _GetLockFile(name)
162 a8083063 Iustin Pop
163 a8083063 Iustin Pop
  try:
164 a8083063 Iustin Pop
    fd = os.open(lockfile, os.O_RDONLY)
165 a8083063 Iustin Pop
  except OSError:
166 a8083063 Iustin Pop
    raise errors.LockError('Lock "%s" not held.' % (name,))
167 a8083063 Iustin Pop
168 a8083063 Iustin Pop
  f = os.fdopen(fd, 'r')
169 a8083063 Iustin Pop
  pid_str = f.read()
170 a8083063 Iustin Pop
171 a8083063 Iustin Pop
  try:
172 a8083063 Iustin Pop
    pid = int(pid_str)
173 a8083063 Iustin Pop
  except ValueError:
174 a8083063 Iustin Pop
    raise errors.LockError('Unable to determine PID of locking process.')
175 a8083063 Iustin Pop
176 a8083063 Iustin Pop
  if pid != os.getpid():
177 a8083063 Iustin Pop
    raise errors.LockError('Lock not held by me (%d != %d)' %
178 a8083063 Iustin Pop
                           (os.getpid(), pid,))
179 a8083063 Iustin Pop
180 a8083063 Iustin Pop
  os.unlink(lockfile)
181 a8083063 Iustin Pop
  _locksheld.remove(name)
182 a8083063 Iustin Pop
183 a8083063 Iustin Pop
184 a8083063 Iustin Pop
def LockCleanup():
185 a8083063 Iustin Pop
  """Remove all locks."""
186 a8083063 Iustin Pop
187 a8083063 Iustin Pop
  for lock in _locksheld:
188 a8083063 Iustin Pop
    Unlock(lock)
189 a8083063 Iustin Pop
190 a8083063 Iustin Pop
191 a8083063 Iustin Pop
def RunCmd(cmd):
192 a8083063 Iustin Pop
  """Execute a (shell) command.
193 a8083063 Iustin Pop

194 a8083063 Iustin Pop
  The command should not read from its standard input, as it will be
195 a8083063 Iustin Pop
  closed.
196 a8083063 Iustin Pop

197 a8083063 Iustin Pop
  Args:
198 a8083063 Iustin Pop
    cmd: command to run. (str)
199 a8083063 Iustin Pop

200 a8083063 Iustin Pop
  Returns: `RunResult` instance
201 a8083063 Iustin Pop

202 a8083063 Iustin Pop
  """
203 a8083063 Iustin Pop
  if isinstance(cmd, list):
204 a8083063 Iustin Pop
    cmd = [str(val) for val in cmd]
205 a8083063 Iustin Pop
  child = popen2.Popen3(cmd, capturestderr=True)
206 a8083063 Iustin Pop
207 a8083063 Iustin Pop
  child.tochild.close()
208 a8083063 Iustin Pop
  out = child.fromchild.read()
209 a8083063 Iustin Pop
  err = child.childerr.read()
210 a8083063 Iustin Pop
211 a8083063 Iustin Pop
  status = child.wait()
212 a8083063 Iustin Pop
  if os.WIFSIGNALED(status):
213 a8083063 Iustin Pop
    signal = os.WTERMSIG(status)
214 a8083063 Iustin Pop
  else:
215 a8083063 Iustin Pop
    signal = None
216 a8083063 Iustin Pop
  if os.WIFEXITED(status):
217 a8083063 Iustin Pop
    exitcode = os.WEXITSTATUS(status)
218 a8083063 Iustin Pop
  else:
219 a8083063 Iustin Pop
    exitcode = None
220 a8083063 Iustin Pop
221 a8083063 Iustin Pop
  if isinstance(cmd, list):
222 a8083063 Iustin Pop
    strcmd = " ".join(cmd)
223 a8083063 Iustin Pop
  else:
224 a8083063 Iustin Pop
    strcmd = str(cmd)
225 a8083063 Iustin Pop
226 a8083063 Iustin Pop
  return RunResult(exitcode, signal, out, err, strcmd)
227 a8083063 Iustin Pop
228 a8083063 Iustin Pop
229 a8083063 Iustin Pop
def RunCmdUnlocked(cmd):
230 a8083063 Iustin Pop
  """Execute a shell command without the 'cmd' lock.
231 a8083063 Iustin Pop

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

236 a8083063 Iustin Pop
  The argument and return values are the same as for the `RunCmd()`
237 a8083063 Iustin Pop
  function.
238 a8083063 Iustin Pop

239 a8083063 Iustin Pop
  Args:
240 a8083063 Iustin Pop
    cmd - command to run. (str)
241 a8083063 Iustin Pop

242 a8083063 Iustin Pop
  Returns:
243 a8083063 Iustin Pop
    `RunResult`
244 a8083063 Iustin Pop

245 a8083063 Iustin Pop
  """
246 a8083063 Iustin Pop
  Unlock('cmd')
247 a8083063 Iustin Pop
  ret = RunCmd(cmd)
248 a8083063 Iustin Pop
  Lock('cmd')
249 a8083063 Iustin Pop
250 a8083063 Iustin Pop
  return ret
251 a8083063 Iustin Pop
252 a8083063 Iustin Pop
253 a8083063 Iustin Pop
def RemoveFile(filename):
254 a8083063 Iustin Pop
  """Remove a file ignoring some errors.
255 a8083063 Iustin Pop

256 a8083063 Iustin Pop
  Remove a file, ignoring non-existing ones or directories. Other
257 a8083063 Iustin Pop
  errors are passed.
258 a8083063 Iustin Pop

259 a8083063 Iustin Pop
  """
260 a8083063 Iustin Pop
  try:
261 a8083063 Iustin Pop
    os.unlink(filename)
262 a8083063 Iustin Pop
  except OSError, err:
263 a8083063 Iustin Pop
    if err.errno not in (ENOENT, EISDIR):
264 a8083063 Iustin Pop
      raise
265 a8083063 Iustin Pop
266 a8083063 Iustin Pop
267 a8083063 Iustin Pop
def _FingerprintFile(filename):
268 a8083063 Iustin Pop
  """Compute the fingerprint of a file.
269 a8083063 Iustin Pop

270 a8083063 Iustin Pop
  If the file does not exist, a None will be returned
271 a8083063 Iustin Pop
  instead.
272 a8083063 Iustin Pop

273 a8083063 Iustin Pop
  Args:
274 a8083063 Iustin Pop
    filename - Filename (str)
275 a8083063 Iustin Pop

276 a8083063 Iustin Pop
  """
277 a8083063 Iustin Pop
  if not (os.path.exists(filename) and os.path.isfile(filename)):
278 a8083063 Iustin Pop
    return None
279 a8083063 Iustin Pop
280 a8083063 Iustin Pop
  f = open(filename)
281 a8083063 Iustin Pop
282 a8083063 Iustin Pop
  fp = sha.sha()
283 a8083063 Iustin Pop
  while True:
284 a8083063 Iustin Pop
    data = f.read(4096)
285 a8083063 Iustin Pop
    if not data:
286 a8083063 Iustin Pop
      break
287 a8083063 Iustin Pop
288 a8083063 Iustin Pop
    fp.update(data)
289 a8083063 Iustin Pop
290 a8083063 Iustin Pop
  return fp.hexdigest()
291 a8083063 Iustin Pop
292 a8083063 Iustin Pop
293 a8083063 Iustin Pop
def FingerprintFiles(files):
294 a8083063 Iustin Pop
  """Compute fingerprints for a list of files.
295 a8083063 Iustin Pop

296 a8083063 Iustin Pop
  Args:
297 a8083063 Iustin Pop
    files - array of filenames.  ( [str, ...] )
298 a8083063 Iustin Pop

299 a8083063 Iustin Pop
  Return value:
300 a8083063 Iustin Pop
    dictionary of filename: fingerprint for the files that exist
301 a8083063 Iustin Pop

302 a8083063 Iustin Pop
  """
303 a8083063 Iustin Pop
  ret = {}
304 a8083063 Iustin Pop
305 a8083063 Iustin Pop
  for filename in files:
306 a8083063 Iustin Pop
    cksum = _FingerprintFile(filename)
307 a8083063 Iustin Pop
    if cksum:
308 a8083063 Iustin Pop
      ret[filename] = cksum
309 a8083063 Iustin Pop
310 a8083063 Iustin Pop
  return ret
311 a8083063 Iustin Pop
312 a8083063 Iustin Pop
313 a8083063 Iustin Pop
def CheckDict(target, template, logname=None):
314 a8083063 Iustin Pop
  """Ensure a dictionary has a required set of keys.
315 a8083063 Iustin Pop

316 a8083063 Iustin Pop
  For the given dictionaries `target` and `template`, ensure target
317 a8083063 Iustin Pop
  has all the keys from template. Missing keys are added with values
318 a8083063 Iustin Pop
  from template.
319 a8083063 Iustin Pop

320 a8083063 Iustin Pop
  Args:
321 a8083063 Iustin Pop
    target   - the dictionary to check
322 a8083063 Iustin Pop
    template - template dictionary
323 a8083063 Iustin Pop
    logname  - a caller-chosen string to identify the debug log
324 a8083063 Iustin Pop
               entry; if None, no logging will be done
325 a8083063 Iustin Pop

326 a8083063 Iustin Pop
  Returns value:
327 a8083063 Iustin Pop
    None
328 a8083063 Iustin Pop

329 a8083063 Iustin Pop
  """
330 a8083063 Iustin Pop
  missing = []
331 a8083063 Iustin Pop
  for k in template:
332 a8083063 Iustin Pop
    if k not in target:
333 a8083063 Iustin Pop
      missing.append(k)
334 a8083063 Iustin Pop
      target[k] = template[k]
335 a8083063 Iustin Pop
336 a8083063 Iustin Pop
  if missing and logname:
337 a8083063 Iustin Pop
    logger.Debug('%s missing keys %s' %
338 a8083063 Iustin Pop
                 (logname, ', '.join(missing)))
339 a8083063 Iustin Pop
340 a8083063 Iustin Pop
341 a8083063 Iustin Pop
def IsProcessAlive(pid):
342 a8083063 Iustin Pop
  """Check if a given pid exists on the system.
343 a8083063 Iustin Pop

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

346 a8083063 Iustin Pop
  Remarks: zombie processes treated as not alive
347 a8083063 Iustin Pop

348 a8083063 Iustin Pop
  """
349 a8083063 Iustin Pop
  try:
350 a8083063 Iustin Pop
    f = open("/proc/%d/status" % pid)
351 a8083063 Iustin Pop
  except IOError, err:
352 a8083063 Iustin Pop
    if err.errno in (ENOENT, ENOTDIR):
353 a8083063 Iustin Pop
      return False
354 a8083063 Iustin Pop
355 a8083063 Iustin Pop
  alive = True
356 a8083063 Iustin Pop
  try:
357 a8083063 Iustin Pop
    data = f.readlines()
358 a8083063 Iustin Pop
    if len(data) > 1:
359 a8083063 Iustin Pop
      state = data[1].split()
360 a8083063 Iustin Pop
      if len(state) > 1 and state[1] == "Z":
361 a8083063 Iustin Pop
        alive = False
362 a8083063 Iustin Pop
  finally:
363 a8083063 Iustin Pop
    f.close()
364 a8083063 Iustin Pop
365 a8083063 Iustin Pop
  return alive
366 a8083063 Iustin Pop
367 a8083063 Iustin Pop
368 a8083063 Iustin Pop
def MatchNameComponent(key, name_list):
369 a8083063 Iustin Pop
  """Try to match a name against a list.
370 a8083063 Iustin Pop

371 a8083063 Iustin Pop
  This function will try to match a name like test1 against a list
372 a8083063 Iustin Pop
  like ['test1.example.com', 'test2.example.com', ...]. Against this
373 a8083063 Iustin Pop
  list, 'test1' as well as 'test1.example' will match, but not
374 a8083063 Iustin Pop
  'test1.ex'. A multiple match will be considered as no match at all
375 a8083063 Iustin Pop
  (e.g. 'test1' against ['test1.example.com', 'test1.example.org']).
376 a8083063 Iustin Pop

377 a8083063 Iustin Pop
  Args:
378 a8083063 Iustin Pop
    key: the name to be searched
379 a8083063 Iustin Pop
    name_list: the list of strings against which to search the key
380 a8083063 Iustin Pop

381 a8083063 Iustin Pop
  Returns:
382 a8083063 Iustin Pop
    None if there is no match *or* if there are multiple matches
383 a8083063 Iustin Pop
    otherwise the element from the list which matches
384 a8083063 Iustin Pop

385 a8083063 Iustin Pop
  """
386 a8083063 Iustin Pop
  mo = re.compile("^%s(\..*)?$" % re.escape(key))
387 a8083063 Iustin Pop
  names_filtered = [name for name in name_list if mo.match(name) is not None]
388 a8083063 Iustin Pop
  if len(names_filtered) != 1:
389 a8083063 Iustin Pop
    return None
390 a8083063 Iustin Pop
  return names_filtered[0]
391 a8083063 Iustin Pop
392 a8083063 Iustin Pop
393 a8083063 Iustin Pop
def LookupHostname(hostname):
394 a8083063 Iustin Pop
  """Look up hostname
395 a8083063 Iustin Pop

396 a8083063 Iustin Pop
  Args:
397 a8083063 Iustin Pop
    hostname: hostname to look up, can be also be a non FQDN
398 a8083063 Iustin Pop

399 a8083063 Iustin Pop
  Returns:
400 a8083063 Iustin Pop
    Dictionary with keys:
401 a8083063 Iustin Pop
    - ip: IP addr
402 a8083063 Iustin Pop
    - hostname_full: hostname fully qualified
403 a8083063 Iustin Pop
    - hostname: hostname fully qualified (historic artifact)
404 a8083063 Iustin Pop
  """
405 a8083063 Iustin Pop
406 a8083063 Iustin Pop
  try:
407 a8083063 Iustin Pop
    (fqdn, dummy, ipaddrs) = socket.gethostbyname_ex(hostname)
408 a8083063 Iustin Pop
    ipaddr = ipaddrs[0]
409 a8083063 Iustin Pop
  except socket.gaierror:
410 a8083063 Iustin Pop
    # hostname not found in DNS
411 a8083063 Iustin Pop
    return None
412 a8083063 Iustin Pop
413 a8083063 Iustin Pop
  returnhostname = {
414 a8083063 Iustin Pop
    "ip": ipaddr,
415 a8083063 Iustin Pop
    "hostname_full": fqdn,
416 a8083063 Iustin Pop
    "hostname": fqdn,
417 a8083063 Iustin Pop
    }
418 a8083063 Iustin Pop
419 a8083063 Iustin Pop
  return returnhostname
420 a8083063 Iustin Pop
421 a8083063 Iustin Pop
422 a8083063 Iustin Pop
def ListVolumeGroups():
423 a8083063 Iustin Pop
  """List volume groups and their size
424 a8083063 Iustin Pop

425 a8083063 Iustin Pop
  Returns:
426 a8083063 Iustin Pop
     Dictionary with keys volume name and values the size of the volume
427 a8083063 Iustin Pop

428 a8083063 Iustin Pop
  """
429 a8083063 Iustin Pop
  command = "vgs --noheadings --units m --nosuffix -o name,size"
430 a8083063 Iustin Pop
  result = RunCmd(command)
431 a8083063 Iustin Pop
  retval = {}
432 a8083063 Iustin Pop
  if result.failed:
433 a8083063 Iustin Pop
    return retval
434 a8083063 Iustin Pop
435 a8083063 Iustin Pop
  for line in result.stdout.splitlines():
436 a8083063 Iustin Pop
    try:
437 a8083063 Iustin Pop
      name, size = line.split()
438 a8083063 Iustin Pop
      size = int(float(size))
439 a8083063 Iustin Pop
    except (IndexError, ValueError), err:
440 a8083063 Iustin Pop
      logger.Error("Invalid output from vgs (%s): %s" % (err, line))
441 a8083063 Iustin Pop
      continue
442 a8083063 Iustin Pop
443 a8083063 Iustin Pop
    retval[name] = size
444 a8083063 Iustin Pop
445 a8083063 Iustin Pop
  return retval
446 a8083063 Iustin Pop
447 a8083063 Iustin Pop
448 a8083063 Iustin Pop
def BridgeExists(bridge):
449 a8083063 Iustin Pop
  """Check whether the given bridge exists in the system
450 a8083063 Iustin Pop

451 a8083063 Iustin Pop
  Returns:
452 a8083063 Iustin Pop
     True if it does, false otherwise.
453 a8083063 Iustin Pop

454 a8083063 Iustin Pop
  """
455 a8083063 Iustin Pop
456 a8083063 Iustin Pop
  return os.path.isdir("/sys/class/net/%s/bridge" % bridge)
457 a8083063 Iustin Pop
458 a8083063 Iustin Pop
459 a8083063 Iustin Pop
def NiceSort(name_list):
460 a8083063 Iustin Pop
  """Sort a list of strings based on digit and non-digit groupings.
461 a8083063 Iustin Pop

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

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

469 a8083063 Iustin Pop
  Return value
470 a8083063 Iustin Pop
    - a copy of the list sorted according to our algorithm
471 a8083063 Iustin Pop

472 a8083063 Iustin Pop
  """
473 a8083063 Iustin Pop
  _SORTER_BASE = "(\D+|\d+)"
474 a8083063 Iustin Pop
  _SORTER_FULL = "^%s%s?%s?%s?%s?%s?%s?%s?.*$" % (_SORTER_BASE, _SORTER_BASE,
475 a8083063 Iustin Pop
                                                  _SORTER_BASE, _SORTER_BASE,
476 a8083063 Iustin Pop
                                                  _SORTER_BASE, _SORTER_BASE,
477 a8083063 Iustin Pop
                                                  _SORTER_BASE, _SORTER_BASE)
478 a8083063 Iustin Pop
  _SORTER_RE = re.compile(_SORTER_FULL)
479 a8083063 Iustin Pop
  _SORTER_NODIGIT = re.compile("^\D*$")
480 a8083063 Iustin Pop
  def _TryInt(val):
481 a8083063 Iustin Pop
    """Attempts to convert a variable to integer."""
482 a8083063 Iustin Pop
    if val is None or _SORTER_NODIGIT.match(val):
483 a8083063 Iustin Pop
      return val
484 a8083063 Iustin Pop
    rval = int(val)
485 a8083063 Iustin Pop
    return rval
486 a8083063 Iustin Pop
487 a8083063 Iustin Pop
  to_sort = [([_TryInt(grp) for grp in _SORTER_RE.match(name).groups()], name)
488 a8083063 Iustin Pop
             for name in name_list]
489 a8083063 Iustin Pop
  to_sort.sort()
490 a8083063 Iustin Pop
  return [tup[1] for tup in to_sort]
491 a8083063 Iustin Pop
492 a8083063 Iustin Pop
493 a8083063 Iustin Pop
def CheckDaemonAlive(pid_file, process_string):
494 a8083063 Iustin Pop
  """Check wether the specified daemon is alive.
495 a8083063 Iustin Pop

496 a8083063 Iustin Pop
  Args:
497 a8083063 Iustin Pop
   - pid_file: file to read the daemon pid from, the file is
498 a8083063 Iustin Pop
               expected to contain only a single line containing
499 a8083063 Iustin Pop
               only the PID
500 a8083063 Iustin Pop
   - process_string: a substring that we expect to find in
501 a8083063 Iustin Pop
                     the command line of the daemon process
502 a8083063 Iustin Pop

503 a8083063 Iustin Pop
  Returns:
504 a8083063 Iustin Pop
   - True if the daemon is judged to be alive (that is:
505 a8083063 Iustin Pop
      - the PID file exists, is readable and contains a number
506 a8083063 Iustin Pop
      - a process of the specified PID is running
507 a8083063 Iustin Pop
      - that process contains the specified string in its
508 a8083063 Iustin Pop
        command line
509 a8083063 Iustin Pop
      - the process is not in state Z (zombie))
510 a8083063 Iustin Pop
   - False otherwise
511 a8083063 Iustin Pop

512 a8083063 Iustin Pop
  """
513 a8083063 Iustin Pop
  try:
514 a8083063 Iustin Pop
    pid_file = file(pid_file, 'r')
515 a8083063 Iustin Pop
    try:
516 a8083063 Iustin Pop
      pid = int(pid_file.readline())
517 a8083063 Iustin Pop
    finally:
518 a8083063 Iustin Pop
      pid_file.close()
519 a8083063 Iustin Pop
520 a8083063 Iustin Pop
    cmdline_file_path = "/proc/%s/cmdline" % (pid)
521 a8083063 Iustin Pop
    cmdline_file = open(cmdline_file_path, 'r')
522 a8083063 Iustin Pop
    try:
523 a8083063 Iustin Pop
      cmdline = cmdline_file.readline()
524 a8083063 Iustin Pop
    finally:
525 a8083063 Iustin Pop
      cmdline_file.close()
526 a8083063 Iustin Pop
527 a8083063 Iustin Pop
    if not process_string in cmdline:
528 a8083063 Iustin Pop
      return False
529 a8083063 Iustin Pop
530 a8083063 Iustin Pop
    stat_file_path =  "/proc/%s/stat" % (pid)
531 a8083063 Iustin Pop
    stat_file = open(stat_file_path, 'r')
532 a8083063 Iustin Pop
    try:
533 a8083063 Iustin Pop
      process_state = stat_file.readline().split()[2]
534 a8083063 Iustin Pop
    finally:
535 a8083063 Iustin Pop
      stat_file.close()
536 a8083063 Iustin Pop
537 a8083063 Iustin Pop
    if process_state == 'Z':
538 a8083063 Iustin Pop
      return False
539 a8083063 Iustin Pop
540 a8083063 Iustin Pop
  except (IndexError, IOError, ValueError):
541 a8083063 Iustin Pop
    return False
542 a8083063 Iustin Pop
543 a8083063 Iustin Pop
  return True
544 a8083063 Iustin Pop
545 a8083063 Iustin Pop
546 a8083063 Iustin Pop
def TryConvert(fn, val):
547 a8083063 Iustin Pop
  """Try to convert a value ignoring errors.
548 a8083063 Iustin Pop

549 a8083063 Iustin Pop
  This function tries to apply function `fn` to `val`. If no
550 a8083063 Iustin Pop
  ValueError or TypeError exceptions are raised, it will return the
551 a8083063 Iustin Pop
  result, else it will return the original value. Any other exceptions
552 a8083063 Iustin Pop
  are propagated to the caller.
553 a8083063 Iustin Pop

554 a8083063 Iustin Pop
  """
555 a8083063 Iustin Pop
  try:
556 a8083063 Iustin Pop
    nv = fn(val)
557 a8083063 Iustin Pop
  except (ValueError, TypeError), err:
558 a8083063 Iustin Pop
    nv = val
559 a8083063 Iustin Pop
  return nv
560 a8083063 Iustin Pop
561 a8083063 Iustin Pop
562 a8083063 Iustin Pop
def IsValidIP(ip):
563 a8083063 Iustin Pop
  """Verifies the syntax of an IP address.
564 a8083063 Iustin Pop

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

568 a8083063 Iustin Pop
  """
569 a8083063 Iustin Pop
  unit = "(0|[1-9]\d{0,2})"
570 a8083063 Iustin Pop
  return re.match("^%s\.%s\.%s\.%s$" % (unit, unit, unit, unit), ip)
571 a8083063 Iustin Pop
572 a8083063 Iustin Pop
573 a8083063 Iustin Pop
def IsValidShellParam(word):
574 a8083063 Iustin Pop
  """Verifies is the given word is safe from the shell's p.o.v.
575 a8083063 Iustin Pop

576 a8083063 Iustin Pop
  This means that we can pass this to a command via the shell and be
577 a8083063 Iustin Pop
  sure that it doesn't alter the command line and is passed as such to
578 a8083063 Iustin Pop
  the actual command.
579 a8083063 Iustin Pop

580 a8083063 Iustin Pop
  Note that we are overly restrictive here, in order to be on the safe
581 a8083063 Iustin Pop
  side.
582 a8083063 Iustin Pop

583 a8083063 Iustin Pop
  """
584 a8083063 Iustin Pop
  return bool(re.match("^[-a-zA-Z0-9._+/:%@]+$", word))
585 a8083063 Iustin Pop
586 a8083063 Iustin Pop
587 a8083063 Iustin Pop
def BuildShellCmd(template, *args):
588 a8083063 Iustin Pop
  """Build a safe shell command line from the given arguments.
589 a8083063 Iustin Pop

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

595 a8083063 Iustin Pop
  """
596 a8083063 Iustin Pop
  for word in args:
597 a8083063 Iustin Pop
    if not IsValidShellParam(word):
598 a8083063 Iustin Pop
      raise errors.ProgrammerError, ("Shell argument '%s' contains"
599 a8083063 Iustin Pop
                                     " invalid characters" % word)
600 a8083063 Iustin Pop
  return template % args
601 a8083063 Iustin Pop
602 a8083063 Iustin Pop
603 a8083063 Iustin Pop
def FormatUnit(value):
604 a8083063 Iustin Pop
  """Formats an incoming number of MiB with the appropriate unit.
605 a8083063 Iustin Pop

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

608 a8083063 Iustin Pop
  """
609 a8083063 Iustin Pop
  if value < 1024:
610 a8083063 Iustin Pop
    return "%dM" % round(value, 0)
611 a8083063 Iustin Pop
612 a8083063 Iustin Pop
  elif value < (1024 * 1024):
613 a8083063 Iustin Pop
    return "%0.1fG" % round(float(value) / 1024, 1)
614 a8083063 Iustin Pop
615 a8083063 Iustin Pop
  else:
616 a8083063 Iustin Pop
    return "%0.1fT" % round(float(value) / 1024 / 1024, 1)
617 a8083063 Iustin Pop
618 a8083063 Iustin Pop
619 a8083063 Iustin Pop
def ParseUnit(input_string):
620 a8083063 Iustin Pop
  """Tries to extract number and scale from the given string.
621 a8083063 Iustin Pop

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

625 a8083063 Iustin Pop
  """
626 a8083063 Iustin Pop
  m = re.match('^([.\d]+)\s*([a-zA-Z]+)?$', input_string)
627 a8083063 Iustin Pop
  if not m:
628 a8083063 Iustin Pop
    raise errors.UnitParseError, ("Invalid format")
629 a8083063 Iustin Pop
630 a8083063 Iustin Pop
  value = float(m.groups()[0])
631 a8083063 Iustin Pop
632 a8083063 Iustin Pop
  unit = m.groups()[1]
633 a8083063 Iustin Pop
  if unit:
634 a8083063 Iustin Pop
    lcunit = unit.lower()
635 a8083063 Iustin Pop
  else:
636 a8083063 Iustin Pop
    lcunit = 'm'
637 a8083063 Iustin Pop
638 a8083063 Iustin Pop
  if lcunit in ('m', 'mb', 'mib'):
639 a8083063 Iustin Pop
    # Value already in MiB
640 a8083063 Iustin Pop
    pass
641 a8083063 Iustin Pop
642 a8083063 Iustin Pop
  elif lcunit in ('g', 'gb', 'gib'):
643 a8083063 Iustin Pop
    value *= 1024
644 a8083063 Iustin Pop
645 a8083063 Iustin Pop
  elif lcunit in ('t', 'tb', 'tib'):
646 a8083063 Iustin Pop
    value *= 1024 * 1024
647 a8083063 Iustin Pop
648 a8083063 Iustin Pop
  else:
649 a8083063 Iustin Pop
    raise errors.UnitParseError, ("Unknown unit: %s" % unit)
650 a8083063 Iustin Pop
651 a8083063 Iustin Pop
  # Make sure we round up
652 a8083063 Iustin Pop
  if int(value) < value:
653 a8083063 Iustin Pop
    value += 1
654 a8083063 Iustin Pop
655 a8083063 Iustin Pop
  # Round up to the next multiple of 4
656 a8083063 Iustin Pop
  value = int(value)
657 a8083063 Iustin Pop
  if value % 4:
658 a8083063 Iustin Pop
    value += 4 - value % 4
659 a8083063 Iustin Pop
660 a8083063 Iustin Pop
  return value
661 a8083063 Iustin Pop
662 a8083063 Iustin Pop
663 a8083063 Iustin Pop
def AddAuthorizedKey(file_name, key):
664 a8083063 Iustin Pop
  """Adds an SSH public key to an authorized_keys file.
665 a8083063 Iustin Pop

666 a8083063 Iustin Pop
  Args:
667 a8083063 Iustin Pop
    file_name: Path to authorized_keys file
668 a8083063 Iustin Pop
    key: String containing key
669 a8083063 Iustin Pop
  """
670 a8083063 Iustin Pop
  key_fields = key.split()
671 a8083063 Iustin Pop
672 a8083063 Iustin Pop
  f = open(file_name, 'a+')
673 a8083063 Iustin Pop
  try:
674 a8083063 Iustin Pop
    nl = True
675 a8083063 Iustin Pop
    for line in f:
676 a8083063 Iustin Pop
      # Ignore whitespace changes
677 a8083063 Iustin Pop
      if line.split() == key_fields:
678 a8083063 Iustin Pop
        break
679 a8083063 Iustin Pop
      nl = line.endswith('\n')
680 a8083063 Iustin Pop
    else:
681 a8083063 Iustin Pop
      if not nl:
682 a8083063 Iustin Pop
        f.write("\n")
683 a8083063 Iustin Pop
      f.write(key.rstrip('\r\n'))
684 a8083063 Iustin Pop
      f.write("\n")
685 a8083063 Iustin Pop
      f.flush()
686 a8083063 Iustin Pop
  finally:
687 a8083063 Iustin Pop
    f.close()
688 a8083063 Iustin Pop
689 a8083063 Iustin Pop
690 a8083063 Iustin Pop
def RemoveAuthorizedKey(file_name, key):
691 a8083063 Iustin Pop
  """Removes an SSH public key from an authorized_keys file.
692 a8083063 Iustin Pop

693 a8083063 Iustin Pop
  Args:
694 a8083063 Iustin Pop
    file_name: Path to authorized_keys file
695 a8083063 Iustin Pop
    key: String containing key
696 a8083063 Iustin Pop
  """
697 a8083063 Iustin Pop
  key_fields = key.split()
698 a8083063 Iustin Pop
699 a8083063 Iustin Pop
  fd, tmpname = tempfile.mkstemp(dir=os.path.dirname(file_name))
700 a8083063 Iustin Pop
  out = os.fdopen(fd, 'w')
701 a8083063 Iustin Pop
  try:
702 a8083063 Iustin Pop
    f = open(file_name, 'r')
703 a8083063 Iustin Pop
    try:
704 a8083063 Iustin Pop
      for line in f:
705 a8083063 Iustin Pop
        # Ignore whitespace changes while comparing lines
706 a8083063 Iustin Pop
        if line.split() != key_fields:
707 a8083063 Iustin Pop
          out.write(line)
708 a8083063 Iustin Pop
709 a8083063 Iustin Pop
      out.flush()
710 a8083063 Iustin Pop
      os.rename(tmpname, file_name)
711 a8083063 Iustin Pop
    finally:
712 a8083063 Iustin Pop
      f.close()
713 a8083063 Iustin Pop
  finally:
714 a8083063 Iustin Pop
    out.close()
715 a8083063 Iustin Pop
716 a8083063 Iustin Pop
717 a8083063 Iustin Pop
def CreateBackup(file_name):
718 a8083063 Iustin Pop
  """Creates a backup of a file.
719 a8083063 Iustin Pop

720 a8083063 Iustin Pop
  Returns: the path to the newly created backup file.
721 a8083063 Iustin Pop

722 a8083063 Iustin Pop
  """
723 a8083063 Iustin Pop
  if not os.path.isfile(file_name):
724 a8083063 Iustin Pop
    raise errors.ProgrammerError, ("Can't make a backup of a non-file '%s'" %
725 a8083063 Iustin Pop
                                   file_name)
726 a8083063 Iustin Pop
727 a8083063 Iustin Pop
  # Warning: the following code contains a race condition when we create more
728 a8083063 Iustin Pop
  # than one backup of the same file in a second.
729 a8083063 Iustin Pop
  backup_name = file_name + '.backup-%d' % int(time.time())
730 a8083063 Iustin Pop
  shutil.copyfile(file_name, backup_name)
731 a8083063 Iustin Pop
  return backup_name
732 a8083063 Iustin Pop
733 a8083063 Iustin Pop
734 a8083063 Iustin Pop
def ShellQuote(value):
735 a8083063 Iustin Pop
  """Quotes shell argument according to POSIX.
736 a8083063 Iustin Pop
  
737 a8083063 Iustin Pop
  """
738 a8083063 Iustin Pop
  if _re_shell_unquoted.match(value):
739 a8083063 Iustin Pop
    return value
740 a8083063 Iustin Pop
  else:
741 a8083063 Iustin Pop
    return "'%s'" % value.replace("'", "'\\''")
742 a8083063 Iustin Pop
743 a8083063 Iustin Pop
744 a8083063 Iustin Pop
def ShellQuoteArgs(args):
745 a8083063 Iustin Pop
  """Quotes all given shell arguments and concatenates using spaces.
746 a8083063 Iustin Pop

747 a8083063 Iustin Pop
  """
748 a8083063 Iustin Pop
  return ' '.join([ShellQuote(i) for i in args])