Statistics
| Branch: | Tag: | Revision:

root / lib / utils / __init__.py @ 651cc3e2

History | View | Annotate | Download (23.7 kB)

1 2f31098c Iustin Pop
#
2 a8083063 Iustin Pop
#
3 a8083063 Iustin Pop
4 4fd029cf Michael Hanselmann
# Copyright (C) 2006, 2007, 2010, 2011 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 58885d79 Iustin Pop
"""Ganeti utility module.
23 58885d79 Iustin Pop

24 58885d79 Iustin Pop
This module holds functions that can be used in both daemons (all) and
25 58885d79 Iustin Pop
the command line scripts.
26 899d2a81 Michael Hanselmann

27 a8083063 Iustin Pop
"""
28 a8083063 Iustin Pop
29 b459a848 Andrea Spadaccini
# Allow wildcard import in pylint: disable=W0401
30 a8083063 Iustin Pop
31 a8083063 Iustin Pop
import os
32 a8083063 Iustin Pop
import re
33 4ca1b175 Alexander Schreiber
import errno
34 2f8b60b3 Iustin Pop
import pwd
35 f8326fca Andrea Spadaccini
import time
36 78feb6fb Guido Trotter
import itertools
37 9c233417 Iustin Pop
import select
38 bb698c1f Iustin Pop
import logging
39 de499029 Michael Hanselmann
import signal
40 a8083063 Iustin Pop
41 a8083063 Iustin Pop
from ganeti import errors
42 3aecd2c7 Iustin Pop
from ganeti import constants
43 716a32cb Guido Trotter
from ganeti import compat
44 111a7d04 Michael Hanselmann
from ganeti import pathutils
45 a8083063 Iustin Pop
46 63fc4229 Michael Hanselmann
from ganeti.utils.algo import *
47 63fc4229 Michael Hanselmann
from ganeti.utils.filelock import *
48 63fc4229 Michael Hanselmann
from ganeti.utils.hash import *
49 63fc4229 Michael Hanselmann
from ganeti.utils.io import *
50 63fc4229 Michael Hanselmann
from ganeti.utils.log import *
51 59726e15 Bernardo Dal Seno
from ganeti.utils.lvm import *
52 8dc76d54 Guido Trotter
from ganeti.utils.mlock import *
53 63fc4229 Michael Hanselmann
from ganeti.utils.nodesetup import *
54 63fc4229 Michael Hanselmann
from ganeti.utils.process import *
55 63fc4229 Michael Hanselmann
from ganeti.utils.retry import *
56 63fc4229 Michael Hanselmann
from ganeti.utils.text import *
57 63fc4229 Michael Hanselmann
from ganeti.utils.wrapper import *
58 63fc4229 Michael Hanselmann
from ganeti.utils.x509 import *
59 16abfbc2 Alexander Schreiber
60 58885d79 Iustin Pop
61 28f34048 Michael Hanselmann
_VALID_SERVICE_NAME_RE = re.compile("^[-_.a-zA-Z0-9]{1,128}$")
62 28f34048 Michael Hanselmann
63 80a0546b Michele Tartara
UUID_RE = re.compile(constants.UUID_REGEX)
64 05636402 Guido Trotter
65 7c0d6283 Michael Hanselmann
66 a5728081 Guido Trotter
def ForceDictType(target, key_types, allowed_values=None):
67 a5728081 Guido Trotter
  """Force the values of a dict to have certain types.
68 a5728081 Guido Trotter

69 a5728081 Guido Trotter
  @type target: dict
70 a5728081 Guido Trotter
  @param target: the dict to update
71 a5728081 Guido Trotter
  @type key_types: dict
72 a5728081 Guido Trotter
  @param key_types: dict mapping target dict keys to types
73 a5728081 Guido Trotter
                    in constants.ENFORCEABLE_TYPES
74 a5728081 Guido Trotter
  @type allowed_values: list
75 a5728081 Guido Trotter
  @keyword allowed_values: list of specially allowed values
76 a5728081 Guido Trotter

77 a5728081 Guido Trotter
  """
78 a5728081 Guido Trotter
  if allowed_values is None:
79 a5728081 Guido Trotter
    allowed_values = []
80 a5728081 Guido Trotter
81 8b46606c Guido Trotter
  if not isinstance(target, dict):
82 8b46606c Guido Trotter
    msg = "Expected dictionary, got '%s'" % target
83 8b46606c Guido Trotter
    raise errors.TypeEnforcementError(msg)
84 8b46606c Guido Trotter
85 a5728081 Guido Trotter
  for key in target:
86 a5728081 Guido Trotter
    if key not in key_types:
87 58a59652 Iustin Pop
      msg = "Unknown parameter '%s'" % key
88 a5728081 Guido Trotter
      raise errors.TypeEnforcementError(msg)
89 a5728081 Guido Trotter
90 a5728081 Guido Trotter
    if target[key] in allowed_values:
91 a5728081 Guido Trotter
      continue
92 a5728081 Guido Trotter
93 29921401 Iustin Pop
    ktype = key_types[key]
94 29921401 Iustin Pop
    if ktype not in constants.ENFORCEABLE_TYPES:
95 29921401 Iustin Pop
      msg = "'%s' has non-enforceable type %s" % (key, ktype)
96 a5728081 Guido Trotter
      raise errors.ProgrammerError(msg)
97 a5728081 Guido Trotter
98 59525e1f Michael Hanselmann
    if ktype in (constants.VTYPE_STRING, constants.VTYPE_MAYBE_STRING):
99 59525e1f Michael Hanselmann
      if target[key] is None and ktype == constants.VTYPE_MAYBE_STRING:
100 59525e1f Michael Hanselmann
        pass
101 59525e1f Michael Hanselmann
      elif not isinstance(target[key], basestring):
102 a5728081 Guido Trotter
        if isinstance(target[key], bool) and not target[key]:
103 d0c8c01d Iustin Pop
          target[key] = ""
104 a5728081 Guido Trotter
        else:
105 a5728081 Guido Trotter
          msg = "'%s' (value %s) is not a valid string" % (key, target[key])
106 a5728081 Guido Trotter
          raise errors.TypeEnforcementError(msg)
107 29921401 Iustin Pop
    elif ktype == constants.VTYPE_BOOL:
108 a5728081 Guido Trotter
      if isinstance(target[key], basestring) and target[key]:
109 a5728081 Guido Trotter
        if target[key].lower() == constants.VALUE_FALSE:
110 a5728081 Guido Trotter
          target[key] = False
111 a5728081 Guido Trotter
        elif target[key].lower() == constants.VALUE_TRUE:
112 a5728081 Guido Trotter
          target[key] = True
113 a5728081 Guido Trotter
        else:
114 a5728081 Guido Trotter
          msg = "'%s' (value %s) is not a valid boolean" % (key, target[key])
115 a5728081 Guido Trotter
          raise errors.TypeEnforcementError(msg)
116 a5728081 Guido Trotter
      elif target[key]:
117 a5728081 Guido Trotter
        target[key] = True
118 a5728081 Guido Trotter
      else:
119 a5728081 Guido Trotter
        target[key] = False
120 29921401 Iustin Pop
    elif ktype == constants.VTYPE_SIZE:
121 a5728081 Guido Trotter
      try:
122 a5728081 Guido Trotter
        target[key] = ParseUnit(target[key])
123 a5728081 Guido Trotter
      except errors.UnitParseError, err:
124 a5728081 Guido Trotter
        msg = "'%s' (value %s) is not a valid size. error: %s" % \
125 a5728081 Guido Trotter
              (key, target[key], err)
126 a5728081 Guido Trotter
        raise errors.TypeEnforcementError(msg)
127 29921401 Iustin Pop
    elif ktype == constants.VTYPE_INT:
128 a5728081 Guido Trotter
      try:
129 a5728081 Guido Trotter
        target[key] = int(target[key])
130 a5728081 Guido Trotter
      except (ValueError, TypeError):
131 a5728081 Guido Trotter
        msg = "'%s' (value %s) is not a valid integer" % (key, target[key])
132 a5728081 Guido Trotter
        raise errors.TypeEnforcementError(msg)
133 a5728081 Guido Trotter
134 a5728081 Guido Trotter
135 28f34048 Michael Hanselmann
def ValidateServiceName(name):
136 28f34048 Michael Hanselmann
  """Validate the given service name.
137 28f34048 Michael Hanselmann

138 28f34048 Michael Hanselmann
  @type name: number or string
139 28f34048 Michael Hanselmann
  @param name: Service name or port specification
140 28f34048 Michael Hanselmann

141 28f34048 Michael Hanselmann
  """
142 28f34048 Michael Hanselmann
  try:
143 28f34048 Michael Hanselmann
    numport = int(name)
144 28f34048 Michael Hanselmann
  except (ValueError, TypeError):
145 28f34048 Michael Hanselmann
    # Non-numeric service name
146 28f34048 Michael Hanselmann
    valid = _VALID_SERVICE_NAME_RE.match(name)
147 28f34048 Michael Hanselmann
  else:
148 28f34048 Michael Hanselmann
    # Numeric port (protocols other than TCP or UDP might need adjustments
149 28f34048 Michael Hanselmann
    # here)
150 28f34048 Michael Hanselmann
    valid = (numport >= 0 and numport < (1 << 16))
151 28f34048 Michael Hanselmann
152 28f34048 Michael Hanselmann
  if not valid:
153 28f34048 Michael Hanselmann
    raise errors.OpPrereqError("Invalid service name '%s'" % name,
154 28f34048 Michael Hanselmann
                               errors.ECODE_INVAL)
155 28f34048 Michael Hanselmann
156 28f34048 Michael Hanselmann
  return name
157 28f34048 Michael Hanselmann
158 28f34048 Michael Hanselmann
159 32be86da Renรฉ Nussbaumer
def _ComputeMissingKeys(key_path, options, defaults):
160 32be86da Renรฉ Nussbaumer
  """Helper functions to compute which keys a invalid.
161 32be86da Renรฉ Nussbaumer

162 32be86da Renรฉ Nussbaumer
  @param key_path: The current key path (if any)
163 32be86da Renรฉ Nussbaumer
  @param options: The user provided options
164 32be86da Renรฉ Nussbaumer
  @param defaults: The default dictionary
165 32be86da Renรฉ Nussbaumer
  @return: A list of invalid keys
166 32be86da Renรฉ Nussbaumer

167 32be86da Renรฉ Nussbaumer
  """
168 32be86da Renรฉ Nussbaumer
  defaults_keys = frozenset(defaults.keys())
169 32be86da Renรฉ Nussbaumer
  invalid = []
170 32be86da Renรฉ Nussbaumer
  for key, value in options.items():
171 32be86da Renรฉ Nussbaumer
    if key_path:
172 32be86da Renรฉ Nussbaumer
      new_path = "%s/%s" % (key_path, key)
173 32be86da Renรฉ Nussbaumer
    else:
174 32be86da Renรฉ Nussbaumer
      new_path = key
175 32be86da Renรฉ Nussbaumer
176 32be86da Renรฉ Nussbaumer
    if key not in defaults_keys:
177 32be86da Renรฉ Nussbaumer
      invalid.append(new_path)
178 32be86da Renรฉ Nussbaumer
    elif isinstance(value, dict):
179 32be86da Renรฉ Nussbaumer
      invalid.extend(_ComputeMissingKeys(new_path, value, defaults[key]))
180 32be86da Renรฉ Nussbaumer
181 32be86da Renรฉ Nussbaumer
  return invalid
182 32be86da Renรฉ Nussbaumer
183 32be86da Renรฉ Nussbaumer
184 32be86da Renรฉ Nussbaumer
def VerifyDictOptions(options, defaults):
185 32be86da Renรฉ Nussbaumer
  """Verify a dict has only keys set which also are in the defaults dict.
186 32be86da Renรฉ Nussbaumer

187 32be86da Renรฉ Nussbaumer
  @param options: The user provided options
188 32be86da Renรฉ Nussbaumer
  @param defaults: The default dictionary
189 32be86da Renรฉ Nussbaumer
  @raise error.OpPrereqError: If one of the keys is not supported
190 32be86da Renรฉ Nussbaumer

191 32be86da Renรฉ Nussbaumer
  """
192 32be86da Renรฉ Nussbaumer
  invalid = _ComputeMissingKeys("", options, defaults)
193 32be86da Renรฉ Nussbaumer
194 32be86da Renรฉ Nussbaumer
  if invalid:
195 32be86da Renรฉ Nussbaumer
    raise errors.OpPrereqError("Provided option keys not supported: %s" %
196 32be86da Renรฉ Nussbaumer
                               CommaJoin(invalid), errors.ECODE_INVAL)
197 32be86da Renรฉ Nussbaumer
198 32be86da Renรฉ Nussbaumer
199 a8083063 Iustin Pop
def ListVolumeGroups():
200 a8083063 Iustin Pop
  """List volume groups and their size
201 a8083063 Iustin Pop

202 58885d79 Iustin Pop
  @rtype: dict
203 58885d79 Iustin Pop
  @return:
204 58885d79 Iustin Pop
       Dictionary with keys volume name and values
205 58885d79 Iustin Pop
       the size of the volume
206 a8083063 Iustin Pop

207 a8083063 Iustin Pop
  """
208 a8083063 Iustin Pop
  command = "vgs --noheadings --units m --nosuffix -o name,size"
209 a8083063 Iustin Pop
  result = RunCmd(command)
210 a8083063 Iustin Pop
  retval = {}
211 a8083063 Iustin Pop
  if result.failed:
212 a8083063 Iustin Pop
    return retval
213 a8083063 Iustin Pop
214 a8083063 Iustin Pop
  for line in result.stdout.splitlines():
215 a8083063 Iustin Pop
    try:
216 a8083063 Iustin Pop
      name, size = line.split()
217 a8083063 Iustin Pop
      size = int(float(size))
218 a8083063 Iustin Pop
    except (IndexError, ValueError), err:
219 bb698c1f Iustin Pop
      logging.error("Invalid output from vgs (%s): %s", err, line)
220 a8083063 Iustin Pop
      continue
221 a8083063 Iustin Pop
222 a8083063 Iustin Pop
    retval[name] = size
223 a8083063 Iustin Pop
224 a8083063 Iustin Pop
  return retval
225 a8083063 Iustin Pop
226 a8083063 Iustin Pop
227 a8083063 Iustin Pop
def BridgeExists(bridge):
228 a8083063 Iustin Pop
  """Check whether the given bridge exists in the system
229 a8083063 Iustin Pop

230 58885d79 Iustin Pop
  @type bridge: str
231 58885d79 Iustin Pop
  @param bridge: the bridge name to check
232 58885d79 Iustin Pop
  @rtype: boolean
233 58885d79 Iustin Pop
  @return: True if it does
234 a8083063 Iustin Pop

235 a8083063 Iustin Pop
  """
236 a8083063 Iustin Pop
  return os.path.isdir("/sys/class/net/%s/bridge" % bridge)
237 a8083063 Iustin Pop
238 a8083063 Iustin Pop
239 a8083063 Iustin Pop
def TryConvert(fn, val):
240 a8083063 Iustin Pop
  """Try to convert a value ignoring errors.
241 a8083063 Iustin Pop

242 58885d79 Iustin Pop
  This function tries to apply function I{fn} to I{val}. If no
243 58885d79 Iustin Pop
  C{ValueError} or C{TypeError} exceptions are raised, it will return
244 58885d79 Iustin Pop
  the result, else it will return the original value. Any other
245 58885d79 Iustin Pop
  exceptions are propagated to the caller.
246 58885d79 Iustin Pop

247 58885d79 Iustin Pop
  @type fn: callable
248 58885d79 Iustin Pop
  @param fn: function to apply to the value
249 58885d79 Iustin Pop
  @param val: the value to be converted
250 58885d79 Iustin Pop
  @return: The converted value if the conversion was successful,
251 58885d79 Iustin Pop
      otherwise the original value.
252 a8083063 Iustin Pop

253 a8083063 Iustin Pop
  """
254 a8083063 Iustin Pop
  try:
255 a8083063 Iustin Pop
    nv = fn(val)
256 7c4d6c7b Michael Hanselmann
  except (ValueError, TypeError):
257 a8083063 Iustin Pop
    nv = val
258 a8083063 Iustin Pop
  return nv
259 a8083063 Iustin Pop
260 a8083063 Iustin Pop
261 31155d60 Balazs Lecz
def ParseCpuMask(cpu_mask):
262 31155d60 Balazs Lecz
  """Parse a CPU mask definition and return the list of CPU IDs.
263 31155d60 Balazs Lecz

264 31155d60 Balazs Lecz
  CPU mask format: comma-separated list of CPU IDs
265 31155d60 Balazs Lecz
  or dash-separated ID ranges
266 31155d60 Balazs Lecz
  Example: "0-2,5" -> "0,1,2,5"
267 31155d60 Balazs Lecz

268 31155d60 Balazs Lecz
  @type cpu_mask: str
269 31155d60 Balazs Lecz
  @param cpu_mask: CPU mask definition
270 31155d60 Balazs Lecz
  @rtype: list of int
271 31155d60 Balazs Lecz
  @return: list of CPU IDs
272 31155d60 Balazs Lecz

273 31155d60 Balazs Lecz
  """
274 31155d60 Balazs Lecz
  if not cpu_mask:
275 31155d60 Balazs Lecz
    return []
276 31155d60 Balazs Lecz
  cpu_list = []
277 31155d60 Balazs Lecz
  for range_def in cpu_mask.split(","):
278 31155d60 Balazs Lecz
    boundaries = range_def.split("-")
279 31155d60 Balazs Lecz
    n_elements = len(boundaries)
280 31155d60 Balazs Lecz
    if n_elements > 2:
281 31155d60 Balazs Lecz
      raise errors.ParseError("Invalid CPU ID range definition"
282 31155d60 Balazs Lecz
                              " (only one hyphen allowed): %s" % range_def)
283 31155d60 Balazs Lecz
    try:
284 31155d60 Balazs Lecz
      lower = int(boundaries[0])
285 31155d60 Balazs Lecz
    except (ValueError, TypeError), err:
286 31155d60 Balazs Lecz
      raise errors.ParseError("Invalid CPU ID value for lower boundary of"
287 31155d60 Balazs Lecz
                              " CPU ID range: %s" % str(err))
288 31155d60 Balazs Lecz
    try:
289 31155d60 Balazs Lecz
      higher = int(boundaries[-1])
290 31155d60 Balazs Lecz
    except (ValueError, TypeError), err:
291 31155d60 Balazs Lecz
      raise errors.ParseError("Invalid CPU ID value for higher boundary of"
292 31155d60 Balazs Lecz
                              " CPU ID range: %s" % str(err))
293 31155d60 Balazs Lecz
    if lower > higher:
294 31155d60 Balazs Lecz
      raise errors.ParseError("Invalid CPU ID range definition"
295 31155d60 Balazs Lecz
                              " (%d > %d): %s" % (lower, higher, range_def))
296 31155d60 Balazs Lecz
    cpu_list.extend(range(lower, higher + 1))
297 31155d60 Balazs Lecz
  return cpu_list
298 31155d60 Balazs Lecz
299 31155d60 Balazs Lecz
300 538ca33a Tsachy Shacham
def ParseMultiCpuMask(cpu_mask):
301 538ca33a Tsachy Shacham
  """Parse a multiple CPU mask definition and return the list of CPU IDs.
302 538ca33a Tsachy Shacham

303 538ca33a Tsachy Shacham
  CPU mask format: colon-separated list of comma-separated list of CPU IDs
304 538ca33a Tsachy Shacham
  or dash-separated ID ranges, with optional "all" as CPU value
305 538ca33a Tsachy Shacham
  Example: "0-2,5:all:1,5,6:2" -> [ [ 0,1,2,5 ], [ -1 ], [ 1, 5, 6 ], [ 2 ] ]
306 538ca33a Tsachy Shacham

307 538ca33a Tsachy Shacham
  @type cpu_mask: str
308 538ca33a Tsachy Shacham
  @param cpu_mask: multiple CPU mask definition
309 538ca33a Tsachy Shacham
  @rtype: list of lists of int
310 538ca33a Tsachy Shacham
  @return: list of lists of CPU IDs
311 538ca33a Tsachy Shacham

312 538ca33a Tsachy Shacham
  """
313 538ca33a Tsachy Shacham
  if not cpu_mask:
314 538ca33a Tsachy Shacham
    return []
315 538ca33a Tsachy Shacham
  cpu_list = []
316 538ca33a Tsachy Shacham
  for range_def in cpu_mask.split(constants.CPU_PINNING_SEP):
317 538ca33a Tsachy Shacham
    if range_def == constants.CPU_PINNING_ALL:
318 538ca33a Tsachy Shacham
      cpu_list.append([constants.CPU_PINNING_ALL_VAL, ])
319 538ca33a Tsachy Shacham
    else:
320 538ca33a Tsachy Shacham
      # Uniquify and sort the list before adding
321 538ca33a Tsachy Shacham
      cpu_list.append(sorted(set(ParseCpuMask(range_def))))
322 538ca33a Tsachy Shacham
323 538ca33a Tsachy Shacham
  return cpu_list
324 538ca33a Tsachy Shacham
325 538ca33a Tsachy Shacham
326 257f4c0a Iustin Pop
def GetHomeDir(user, default=None):
327 257f4c0a Iustin Pop
  """Try to get the homedir of the given user.
328 257f4c0a Iustin Pop

329 257f4c0a Iustin Pop
  The user can be passed either as a string (denoting the name) or as
330 257f4c0a Iustin Pop
  an integer (denoting the user id). If the user is not found, the
331 3ccb3a64 Michael Hanselmann
  C{default} argument is returned, which defaults to C{None}.
332 2f8b60b3 Iustin Pop

333 2f8b60b3 Iustin Pop
  """
334 2f8b60b3 Iustin Pop
  try:
335 257f4c0a Iustin Pop
    if isinstance(user, basestring):
336 257f4c0a Iustin Pop
      result = pwd.getpwnam(user)
337 257f4c0a Iustin Pop
    elif isinstance(user, (int, long)):
338 257f4c0a Iustin Pop
      result = pwd.getpwuid(user)
339 257f4c0a Iustin Pop
    else:
340 257f4c0a Iustin Pop
      raise errors.ProgrammerError("Invalid type passed to GetHomeDir (%s)" %
341 257f4c0a Iustin Pop
                                   type(user))
342 2f8b60b3 Iustin Pop
  except KeyError:
343 2f8b60b3 Iustin Pop
    return default
344 2f8b60b3 Iustin Pop
  return result.pw_dir
345 59072e7e Michael Hanselmann
346 59072e7e Michael Hanselmann
347 7b4126b7 Iustin Pop
def FirstFree(seq, base=0):
348 7b4126b7 Iustin Pop
  """Returns the first non-existing integer from seq.
349 7b4126b7 Iustin Pop

350 7b4126b7 Iustin Pop
  The seq argument should be a sorted list of positive integers. The
351 7b4126b7 Iustin Pop
  first time the index of an element is smaller than the element
352 7b4126b7 Iustin Pop
  value, the index will be returned.
353 7b4126b7 Iustin Pop

354 7b4126b7 Iustin Pop
  The base argument is used to start at a different offset,
355 58885d79 Iustin Pop
  i.e. C{[3, 4, 6]} with I{offset=3} will return 5.
356 58885d79 Iustin Pop

357 58885d79 Iustin Pop
  Example: C{[0, 1, 3]} will return I{2}.
358 7b4126b7 Iustin Pop

359 58885d79 Iustin Pop
  @type seq: sequence
360 58885d79 Iustin Pop
  @param seq: the sequence to be analyzed.
361 58885d79 Iustin Pop
  @type base: int
362 58885d79 Iustin Pop
  @param base: use this value as the base index of the sequence
363 58885d79 Iustin Pop
  @rtype: int
364 58885d79 Iustin Pop
  @return: the first non-used index in the sequence
365 7b4126b7 Iustin Pop

366 7b4126b7 Iustin Pop
  """
367 7b4126b7 Iustin Pop
  for idx, elem in enumerate(seq):
368 7b4126b7 Iustin Pop
    assert elem >= base, "Passed element is higher than base offset"
369 7b4126b7 Iustin Pop
    if elem > idx + base:
370 7b4126b7 Iustin Pop
      # idx is not used
371 7b4126b7 Iustin Pop
      return idx + base
372 7b4126b7 Iustin Pop
  return None
373 7b4126b7 Iustin Pop
374 7b4126b7 Iustin Pop
375 dfdc4060 Guido Trotter
def SingleWaitForFdCondition(fdobj, event, timeout):
376 dcd511c8 Guido Trotter
  """Waits for a condition to occur on the socket.
377 dcd511c8 Guido Trotter

378 dfdc4060 Guido Trotter
  Immediately returns at the first interruption.
379 dfdc4060 Guido Trotter

380 dfdc4060 Guido Trotter
  @type fdobj: integer or object supporting a fileno() method
381 dfdc4060 Guido Trotter
  @param fdobj: entity to wait for events on
382 dfdc4060 Guido Trotter
  @type event: integer
383 dcd511c8 Guido Trotter
  @param event: ORed condition (see select module)
384 dcd511c8 Guido Trotter
  @type timeout: float or None
385 dcd511c8 Guido Trotter
  @param timeout: Timeout in seconds
386 dcd511c8 Guido Trotter
  @rtype: int or None
387 dcd511c8 Guido Trotter
  @return: None for timeout, otherwise occured conditions
388 dcd511c8 Guido Trotter

389 dcd511c8 Guido Trotter
  """
390 dcd511c8 Guido Trotter
  check = (event | select.POLLPRI |
391 dcd511c8 Guido Trotter
           select.POLLNVAL | select.POLLHUP | select.POLLERR)
392 dcd511c8 Guido Trotter
393 dcd511c8 Guido Trotter
  if timeout is not None:
394 dcd511c8 Guido Trotter
    # Poller object expects milliseconds
395 dcd511c8 Guido Trotter
    timeout *= 1000
396 dcd511c8 Guido Trotter
397 dcd511c8 Guido Trotter
  poller = select.poll()
398 dfdc4060 Guido Trotter
  poller.register(fdobj, event)
399 dcd511c8 Guido Trotter
  try:
400 dfdc4060 Guido Trotter
    # TODO: If the main thread receives a signal and we have no timeout, we
401 dfdc4060 Guido Trotter
    # could wait forever. This should check a global "quit" flag or something
402 dfdc4060 Guido Trotter
    # every so often.
403 dfdc4060 Guido Trotter
    io_events = poller.poll(timeout)
404 dfdc4060 Guido Trotter
  except select.error, err:
405 dfdc4060 Guido Trotter
    if err[0] != errno.EINTR:
406 dfdc4060 Guido Trotter
      raise
407 dfdc4060 Guido Trotter
    io_events = []
408 dfdc4060 Guido Trotter
  if io_events and io_events[0][1] & check:
409 dfdc4060 Guido Trotter
    return io_events[0][1]
410 dfdc4060 Guido Trotter
  else:
411 dfdc4060 Guido Trotter
    return None
412 dfdc4060 Guido Trotter
413 dfdc4060 Guido Trotter
414 dfdc4060 Guido Trotter
class FdConditionWaiterHelper(object):
415 dfdc4060 Guido Trotter
  """Retry helper for WaitForFdCondition.
416 dfdc4060 Guido Trotter

417 dfdc4060 Guido Trotter
  This class contains the retried and wait functions that make sure
418 dfdc4060 Guido Trotter
  WaitForFdCondition can continue waiting until the timeout is actually
419 dfdc4060 Guido Trotter
  expired.
420 dfdc4060 Guido Trotter

421 dfdc4060 Guido Trotter
  """
422 dfdc4060 Guido Trotter
423 dfdc4060 Guido Trotter
  def __init__(self, timeout):
424 dfdc4060 Guido Trotter
    self.timeout = timeout
425 dfdc4060 Guido Trotter
426 dfdc4060 Guido Trotter
  def Poll(self, fdobj, event):
427 dfdc4060 Guido Trotter
    result = SingleWaitForFdCondition(fdobj, event, self.timeout)
428 dfdc4060 Guido Trotter
    if result is None:
429 dfdc4060 Guido Trotter
      raise RetryAgain()
430 dfdc4060 Guido Trotter
    else:
431 dfdc4060 Guido Trotter
      return result
432 dfdc4060 Guido Trotter
433 dfdc4060 Guido Trotter
  def UpdateTimeout(self, timeout):
434 dfdc4060 Guido Trotter
    self.timeout = timeout
435 dfdc4060 Guido Trotter
436 dfdc4060 Guido Trotter
437 dfdc4060 Guido Trotter
def WaitForFdCondition(fdobj, event, timeout):
438 dfdc4060 Guido Trotter
  """Waits for a condition to occur on the socket.
439 dfdc4060 Guido Trotter

440 dfdc4060 Guido Trotter
  Retries until the timeout is expired, even if interrupted.
441 dfdc4060 Guido Trotter

442 dfdc4060 Guido Trotter
  @type fdobj: integer or object supporting a fileno() method
443 dfdc4060 Guido Trotter
  @param fdobj: entity to wait for events on
444 dfdc4060 Guido Trotter
  @type event: integer
445 dfdc4060 Guido Trotter
  @param event: ORed condition (see select module)
446 dfdc4060 Guido Trotter
  @type timeout: float or None
447 dfdc4060 Guido Trotter
  @param timeout: Timeout in seconds
448 dfdc4060 Guido Trotter
  @rtype: int or None
449 dfdc4060 Guido Trotter
  @return: None for timeout, otherwise occured conditions
450 dfdc4060 Guido Trotter

451 dfdc4060 Guido Trotter
  """
452 dfdc4060 Guido Trotter
  if timeout is not None:
453 dfdc4060 Guido Trotter
    retrywaiter = FdConditionWaiterHelper(timeout)
454 1b429e2a Iustin Pop
    try:
455 1b429e2a Iustin Pop
      result = Retry(retrywaiter.Poll, RETRY_REMAINING_TIME, timeout,
456 1b429e2a Iustin Pop
                     args=(fdobj, event), wait_fn=retrywaiter.UpdateTimeout)
457 1b429e2a Iustin Pop
    except RetryTimeout:
458 1b429e2a Iustin Pop
      result = None
459 dfdc4060 Guido Trotter
  else:
460 dfdc4060 Guido Trotter
    result = None
461 dfdc4060 Guido Trotter
    while result is None:
462 dfdc4060 Guido Trotter
      result = SingleWaitForFdCondition(fdobj, event, timeout)
463 dfdc4060 Guido Trotter
  return result
464 2de64672 Iustin Pop
465 2de64672 Iustin Pop
466 2826b361 Guido Trotter
def EnsureDaemon(name):
467 2826b361 Guido Trotter
  """Check for and start daemon if not alive.
468 2826b361 Guido Trotter

469 2826b361 Guido Trotter
  """
470 111a7d04 Michael Hanselmann
  result = RunCmd([pathutils.DAEMON_UTIL, "check-and-start", name])
471 2826b361 Guido Trotter
  if result.failed:
472 2826b361 Guido Trotter
    logging.error("Can't start daemon '%s', failure %s, output: %s",
473 2826b361 Guido Trotter
                  name, result.fail_reason, result.output)
474 2826b361 Guido Trotter
    return False
475 2826b361 Guido Trotter
476 2826b361 Guido Trotter
  return True
477 b330ac0b Guido Trotter
478 b330ac0b Guido Trotter
479 db147305 Tom Limoncelli
def StopDaemon(name):
480 db147305 Tom Limoncelli
  """Stop daemon
481 db147305 Tom Limoncelli

482 db147305 Tom Limoncelli
  """
483 111a7d04 Michael Hanselmann
  result = RunCmd([pathutils.DAEMON_UTIL, "stop", name])
484 db147305 Tom Limoncelli
  if result.failed:
485 db147305 Tom Limoncelli
    logging.error("Can't stop daemon '%s', failure %s, output: %s",
486 db147305 Tom Limoncelli
                  name, result.fail_reason, result.output)
487 db147305 Tom Limoncelli
    return False
488 db147305 Tom Limoncelli
489 db147305 Tom Limoncelli
  return True
490 db147305 Tom Limoncelli
491 db147305 Tom Limoncelli
492 45bc5e4a Michael Hanselmann
def SplitTime(value):
493 739be818 Michael Hanselmann
  """Splits time as floating point number into a tuple.
494 739be818 Michael Hanselmann

495 45bc5e4a Michael Hanselmann
  @param value: Time in seconds
496 45bc5e4a Michael Hanselmann
  @type value: int or float
497 45bc5e4a Michael Hanselmann
  @return: Tuple containing (seconds, microseconds)
498 739be818 Michael Hanselmann

499 739be818 Michael Hanselmann
  """
500 45bc5e4a Michael Hanselmann
  (seconds, microseconds) = divmod(int(value * 1000000), 1000000)
501 45bc5e4a Michael Hanselmann
502 45bc5e4a Michael Hanselmann
  assert 0 <= seconds, \
503 45bc5e4a Michael Hanselmann
    "Seconds must be larger than or equal to 0, but are %s" % seconds
504 45bc5e4a Michael Hanselmann
  assert 0 <= microseconds <= 999999, \
505 45bc5e4a Michael Hanselmann
    "Microseconds must be 0-999999, but are %s" % microseconds
506 45bc5e4a Michael Hanselmann
507 45bc5e4a Michael Hanselmann
  return (int(seconds), int(microseconds))
508 739be818 Michael Hanselmann
509 739be818 Michael Hanselmann
510 739be818 Michael Hanselmann
def MergeTime(timetuple):
511 739be818 Michael Hanselmann
  """Merges a tuple into time as a floating point number.
512 739be818 Michael Hanselmann

513 45bc5e4a Michael Hanselmann
  @param timetuple: Time as tuple, (seconds, microseconds)
514 739be818 Michael Hanselmann
  @type timetuple: tuple
515 739be818 Michael Hanselmann
  @return: Time as a floating point number expressed in seconds
516 739be818 Michael Hanselmann

517 739be818 Michael Hanselmann
  """
518 45bc5e4a Michael Hanselmann
  (seconds, microseconds) = timetuple
519 739be818 Michael Hanselmann
520 45bc5e4a Michael Hanselmann
  assert 0 <= seconds, \
521 45bc5e4a Michael Hanselmann
    "Seconds must be larger than or equal to 0, but are %s" % seconds
522 45bc5e4a Michael Hanselmann
  assert 0 <= microseconds <= 999999, \
523 45bc5e4a Michael Hanselmann
    "Microseconds must be 0-999999, but are %s" % microseconds
524 739be818 Michael Hanselmann
525 45bc5e4a Michael Hanselmann
  return float(seconds) + (float(microseconds) * 0.000001)
526 739be818 Michael Hanselmann
527 739be818 Michael Hanselmann
528 691c81b7 Michael Hanselmann
def FindMatch(data, name):
529 691c81b7 Michael Hanselmann
  """Tries to find an item in a dictionary matching a name.
530 691c81b7 Michael Hanselmann

531 691c81b7 Michael Hanselmann
  Callers have to ensure the data names aren't contradictory (e.g. a regexp
532 691c81b7 Michael Hanselmann
  that matches a string). If the name isn't a direct key, all regular
533 691c81b7 Michael Hanselmann
  expression objects in the dictionary are matched against it.
534 691c81b7 Michael Hanselmann

535 691c81b7 Michael Hanselmann
  @type data: dict
536 691c81b7 Michael Hanselmann
  @param data: Dictionary containing data
537 691c81b7 Michael Hanselmann
  @type name: string
538 691c81b7 Michael Hanselmann
  @param name: Name to look for
539 691c81b7 Michael Hanselmann
  @rtype: tuple; (value in dictionary, matched groups as list)
540 691c81b7 Michael Hanselmann

541 691c81b7 Michael Hanselmann
  """
542 691c81b7 Michael Hanselmann
  if name in data:
543 691c81b7 Michael Hanselmann
    return (data[name], [])
544 691c81b7 Michael Hanselmann
545 691c81b7 Michael Hanselmann
  for key, value in data.items():
546 691c81b7 Michael Hanselmann
    # Regex objects
547 691c81b7 Michael Hanselmann
    if hasattr(key, "match"):
548 691c81b7 Michael Hanselmann
      m = key.match(name)
549 691c81b7 Michael Hanselmann
      if m:
550 691c81b7 Michael Hanselmann
        return (value, list(m.groups()))
551 691c81b7 Michael Hanselmann
552 691c81b7 Michael Hanselmann
  return None
553 691c81b7 Michael Hanselmann
554 691c81b7 Michael Hanselmann
555 1b045f5d Balazs Lecz
def GetMounts(filename=constants.PROC_MOUNTS):
556 1b045f5d Balazs Lecz
  """Returns the list of mounted filesystems.
557 1b045f5d Balazs Lecz

558 1b045f5d Balazs Lecz
  This function is Linux-specific.
559 1b045f5d Balazs Lecz

560 1b045f5d Balazs Lecz
  @param filename: path of mounts file (/proc/mounts by default)
561 1b045f5d Balazs Lecz
  @rtype: list of tuples
562 1b045f5d Balazs Lecz
  @return: list of mount entries (device, mountpoint, fstype, options)
563 1b045f5d Balazs Lecz

564 1b045f5d Balazs Lecz
  """
565 1b045f5d Balazs Lecz
  # TODO(iustin): investigate non-Linux options (e.g. via mount output)
566 1b045f5d Balazs Lecz
  data = []
567 1b045f5d Balazs Lecz
  mountlines = ReadFile(filename).splitlines()
568 1b045f5d Balazs Lecz
  for line in mountlines:
569 1b045f5d Balazs Lecz
    device, mountpoint, fstype, options, _ = line.split(None, 4)
570 1b045f5d Balazs Lecz
    data.append((device, mountpoint, fstype, options))
571 1b045f5d Balazs Lecz
572 1b045f5d Balazs Lecz
  return data
573 1b045f5d Balazs Lecz
574 1b045f5d Balazs Lecz
575 451575de Guido Trotter
def SignalHandled(signums):
576 451575de Guido Trotter
  """Signal Handled decoration.
577 451575de Guido Trotter

578 451575de Guido Trotter
  This special decorator installs a signal handler and then calls the target
579 451575de Guido Trotter
  function. The function must accept a 'signal_handlers' keyword argument,
580 451575de Guido Trotter
  which will contain a dict indexed by signal number, with SignalHandler
581 451575de Guido Trotter
  objects as values.
582 451575de Guido Trotter

583 451575de Guido Trotter
  The decorator can be safely stacked with iself, to handle multiple signals
584 451575de Guido Trotter
  with different handlers.
585 451575de Guido Trotter

586 451575de Guido Trotter
  @type signums: list
587 451575de Guido Trotter
  @param signums: signals to intercept
588 451575de Guido Trotter

589 451575de Guido Trotter
  """
590 451575de Guido Trotter
  def wrap(fn):
591 451575de Guido Trotter
    def sig_function(*args, **kwargs):
592 d0c8c01d Iustin Pop
      assert "signal_handlers" not in kwargs or \
593 d0c8c01d Iustin Pop
             kwargs["signal_handlers"] is None or \
594 d0c8c01d Iustin Pop
             isinstance(kwargs["signal_handlers"], dict), \
595 451575de Guido Trotter
             "Wrong signal_handlers parameter in original function call"
596 d0c8c01d Iustin Pop
      if "signal_handlers" in kwargs and kwargs["signal_handlers"] is not None:
597 d0c8c01d Iustin Pop
        signal_handlers = kwargs["signal_handlers"]
598 451575de Guido Trotter
      else:
599 451575de Guido Trotter
        signal_handlers = {}
600 d0c8c01d Iustin Pop
        kwargs["signal_handlers"] = signal_handlers
601 451575de Guido Trotter
      sighandler = SignalHandler(signums)
602 451575de Guido Trotter
      try:
603 451575de Guido Trotter
        for sig in signums:
604 451575de Guido Trotter
          signal_handlers[sig] = sighandler
605 451575de Guido Trotter
        return fn(*args, **kwargs)
606 451575de Guido Trotter
      finally:
607 451575de Guido Trotter
        sighandler.Reset()
608 451575de Guido Trotter
    return sig_function
609 451575de Guido Trotter
  return wrap
610 451575de Guido Trotter
611 451575de Guido Trotter
612 f8326fca Andrea Spadaccini
def TimeoutExpired(epoch, timeout, _time_fn=time.time):
613 f8326fca Andrea Spadaccini
  """Checks whether a timeout has expired.
614 f8326fca Andrea Spadaccini

615 f8326fca Andrea Spadaccini
  """
616 f8326fca Andrea Spadaccini
  return _time_fn() > (epoch + timeout)
617 f8326fca Andrea Spadaccini
618 f8326fca Andrea Spadaccini
619 b9768937 Michael Hanselmann
class SignalWakeupFd(object):
620 b9768937 Michael Hanselmann
  try:
621 b9768937 Michael Hanselmann
    # This is only supported in Python 2.5 and above (some distributions
622 b9768937 Michael Hanselmann
    # backported it to Python 2.4)
623 b9768937 Michael Hanselmann
    _set_wakeup_fd_fn = signal.set_wakeup_fd
624 b9768937 Michael Hanselmann
  except AttributeError:
625 b9768937 Michael Hanselmann
    # Not supported
626 3c286190 Dimitris Aragiorgis
627 b459a848 Andrea Spadaccini
    def _SetWakeupFd(self, _): # pylint: disable=R0201
628 b9768937 Michael Hanselmann
      return -1
629 b9768937 Michael Hanselmann
  else:
630 3c286190 Dimitris Aragiorgis
631 b9768937 Michael Hanselmann
    def _SetWakeupFd(self, fd):
632 b9768937 Michael Hanselmann
      return self._set_wakeup_fd_fn(fd)
633 b9768937 Michael Hanselmann
634 b9768937 Michael Hanselmann
  def __init__(self):
635 b9768937 Michael Hanselmann
    """Initializes this class.
636 b9768937 Michael Hanselmann

637 b9768937 Michael Hanselmann
    """
638 b9768937 Michael Hanselmann
    (read_fd, write_fd) = os.pipe()
639 b9768937 Michael Hanselmann
640 b9768937 Michael Hanselmann
    # Once these succeeded, the file descriptors will be closed automatically.
641 b9768937 Michael Hanselmann
    # Buffer size 0 is important, otherwise .read() with a specified length
642 b9768937 Michael Hanselmann
    # might buffer data and the file descriptors won't be marked readable.
643 b9768937 Michael Hanselmann
    self._read_fh = os.fdopen(read_fd, "r", 0)
644 b9768937 Michael Hanselmann
    self._write_fh = os.fdopen(write_fd, "w", 0)
645 b9768937 Michael Hanselmann
646 b9768937 Michael Hanselmann
    self._previous = self._SetWakeupFd(self._write_fh.fileno())
647 b9768937 Michael Hanselmann
648 b9768937 Michael Hanselmann
    # Utility functions
649 b9768937 Michael Hanselmann
    self.fileno = self._read_fh.fileno
650 b9768937 Michael Hanselmann
    self.read = self._read_fh.read
651 b9768937 Michael Hanselmann
652 b9768937 Michael Hanselmann
  def Reset(self):
653 b9768937 Michael Hanselmann
    """Restores the previous wakeup file descriptor.
654 b9768937 Michael Hanselmann

655 b9768937 Michael Hanselmann
    """
656 b9768937 Michael Hanselmann
    if hasattr(self, "_previous") and self._previous is not None:
657 b9768937 Michael Hanselmann
      self._SetWakeupFd(self._previous)
658 b9768937 Michael Hanselmann
      self._previous = None
659 b9768937 Michael Hanselmann
660 b9768937 Michael Hanselmann
  def Notify(self):
661 b9768937 Michael Hanselmann
    """Notifies the wakeup file descriptor.
662 b9768937 Michael Hanselmann

663 b9768937 Michael Hanselmann
    """
664 b9768937 Michael Hanselmann
    self._write_fh.write("\0")
665 b9768937 Michael Hanselmann
666 b9768937 Michael Hanselmann
  def __del__(self):
667 b9768937 Michael Hanselmann
    """Called before object deletion.
668 b9768937 Michael Hanselmann

669 b9768937 Michael Hanselmann
    """
670 b9768937 Michael Hanselmann
    self.Reset()
671 b9768937 Michael Hanselmann
672 b9768937 Michael Hanselmann
673 de499029 Michael Hanselmann
class SignalHandler(object):
674 de499029 Michael Hanselmann
  """Generic signal handler class.
675 de499029 Michael Hanselmann

676 58885d79 Iustin Pop
  It automatically restores the original handler when deconstructed or
677 58885d79 Iustin Pop
  when L{Reset} is called. You can either pass your own handler
678 58885d79 Iustin Pop
  function in or query the L{called} attribute to detect whether the
679 58885d79 Iustin Pop
  signal was sent.
680 58885d79 Iustin Pop

681 58885d79 Iustin Pop
  @type signum: list
682 58885d79 Iustin Pop
  @ivar signum: the signals we handle
683 58885d79 Iustin Pop
  @type called: boolean
684 58885d79 Iustin Pop
  @ivar called: tracks whether any of the signals have been raised
685 de499029 Michael Hanselmann

686 de499029 Michael Hanselmann
  """
687 b9768937 Michael Hanselmann
  def __init__(self, signum, handler_fn=None, wakeup=None):
688 de499029 Michael Hanselmann
    """Constructs a new SignalHandler instance.
689 de499029 Michael Hanselmann

690 58885d79 Iustin Pop
    @type signum: int or list of ints
691 de499029 Michael Hanselmann
    @param signum: Single signal number or set of signal numbers
692 92b61ec7 Michael Hanselmann
    @type handler_fn: callable
693 92b61ec7 Michael Hanselmann
    @param handler_fn: Signal handling function
694 de499029 Michael Hanselmann

695 de499029 Michael Hanselmann
    """
696 92b61ec7 Michael Hanselmann
    assert handler_fn is None or callable(handler_fn)
697 92b61ec7 Michael Hanselmann
698 6c52849e Guido Trotter
    self.signum = set(signum)
699 de499029 Michael Hanselmann
    self.called = False
700 de499029 Michael Hanselmann
701 92b61ec7 Michael Hanselmann
    self._handler_fn = handler_fn
702 b9768937 Michael Hanselmann
    self._wakeup = wakeup
703 92b61ec7 Michael Hanselmann
704 de499029 Michael Hanselmann
    self._previous = {}
705 de499029 Michael Hanselmann
    try:
706 de499029 Michael Hanselmann
      for signum in self.signum:
707 de499029 Michael Hanselmann
        # Setup handler
708 de499029 Michael Hanselmann
        prev_handler = signal.signal(signum, self._HandleSignal)
709 de499029 Michael Hanselmann
        try:
710 de499029 Michael Hanselmann
          self._previous[signum] = prev_handler
711 de499029 Michael Hanselmann
        except:
712 de499029 Michael Hanselmann
          # Restore previous handler
713 de499029 Michael Hanselmann
          signal.signal(signum, prev_handler)
714 de499029 Michael Hanselmann
          raise
715 de499029 Michael Hanselmann
    except:
716 de499029 Michael Hanselmann
      # Reset all handlers
717 de499029 Michael Hanselmann
      self.Reset()
718 de499029 Michael Hanselmann
      # Here we have a race condition: a handler may have already been called,
719 de499029 Michael Hanselmann
      # but there's not much we can do about it at this point.
720 de499029 Michael Hanselmann
      raise
721 de499029 Michael Hanselmann
722 de499029 Michael Hanselmann
  def __del__(self):
723 de499029 Michael Hanselmann
    self.Reset()
724 de499029 Michael Hanselmann
725 de499029 Michael Hanselmann
  def Reset(self):
726 de499029 Michael Hanselmann
    """Restore previous handler.
727 de499029 Michael Hanselmann

728 58885d79 Iustin Pop
    This will reset all the signals to their previous handlers.
729 58885d79 Iustin Pop

730 de499029 Michael Hanselmann
    """
731 de499029 Michael Hanselmann
    for signum, prev_handler in self._previous.items():
732 de499029 Michael Hanselmann
      signal.signal(signum, prev_handler)
733 de499029 Michael Hanselmann
      # If successful, remove from dict
734 de499029 Michael Hanselmann
      del self._previous[signum]
735 de499029 Michael Hanselmann
736 de499029 Michael Hanselmann
  def Clear(self):
737 58885d79 Iustin Pop
    """Unsets the L{called} flag.
738 de499029 Michael Hanselmann

739 de499029 Michael Hanselmann
    This function can be used in case a signal may arrive several times.
740 de499029 Michael Hanselmann

741 de499029 Michael Hanselmann
    """
742 de499029 Michael Hanselmann
    self.called = False
743 de499029 Michael Hanselmann
744 92b61ec7 Michael Hanselmann
  def _HandleSignal(self, signum, frame):
745 de499029 Michael Hanselmann
    """Actual signal handling function.
746 de499029 Michael Hanselmann

747 de499029 Michael Hanselmann
    """
748 de499029 Michael Hanselmann
    # This is not nice and not absolutely atomic, but it appears to be the only
749 de499029 Michael Hanselmann
    # solution in Python -- there are no atomic types.
750 de499029 Michael Hanselmann
    self.called = True
751 a2d2e1a7 Iustin Pop
752 b9768937 Michael Hanselmann
    if self._wakeup:
753 b9768937 Michael Hanselmann
      # Notify whoever is interested in signals
754 b9768937 Michael Hanselmann
      self._wakeup.Notify()
755 b9768937 Michael Hanselmann
756 92b61ec7 Michael Hanselmann
    if self._handler_fn:
757 92b61ec7 Michael Hanselmann
      self._handler_fn(signum, frame)
758 92b61ec7 Michael Hanselmann
759 a2d2e1a7 Iustin Pop
760 a2d2e1a7 Iustin Pop
class FieldSet(object):
761 a2d2e1a7 Iustin Pop
  """A simple field set.
762 a2d2e1a7 Iustin Pop

763 a2d2e1a7 Iustin Pop
  Among the features are:
764 a2d2e1a7 Iustin Pop
    - checking if a string is among a list of static string or regex objects
765 a2d2e1a7 Iustin Pop
    - checking if a whole list of string matches
766 a2d2e1a7 Iustin Pop
    - returning the matching groups from a regex match
767 a2d2e1a7 Iustin Pop

768 a2d2e1a7 Iustin Pop
  Internally, all fields are held as regular expression objects.
769 a2d2e1a7 Iustin Pop

770 a2d2e1a7 Iustin Pop
  """
771 a2d2e1a7 Iustin Pop
  def __init__(self, *items):
772 a2d2e1a7 Iustin Pop
    self.items = [re.compile("^%s$" % value) for value in items]
773 a2d2e1a7 Iustin Pop
774 a2d2e1a7 Iustin Pop
  def Extend(self, other_set):
775 a2d2e1a7 Iustin Pop
    """Extend the field set with the items from another one"""
776 a2d2e1a7 Iustin Pop
    self.items.extend(other_set.items)
777 a2d2e1a7 Iustin Pop
778 a2d2e1a7 Iustin Pop
  def Matches(self, field):
779 a2d2e1a7 Iustin Pop
    """Checks if a field matches the current set
780 a2d2e1a7 Iustin Pop

781 a2d2e1a7 Iustin Pop
    @type field: str
782 a2d2e1a7 Iustin Pop
    @param field: the string to match
783 6c881c52 Iustin Pop
    @return: either None or a regular expression match object
784 a2d2e1a7 Iustin Pop

785 a2d2e1a7 Iustin Pop
    """
786 a2d2e1a7 Iustin Pop
    for m in itertools.ifilter(None, (val.match(field) for val in self.items)):
787 a2d2e1a7 Iustin Pop
      return m
788 6c881c52 Iustin Pop
    return None
789 a2d2e1a7 Iustin Pop
790 a2d2e1a7 Iustin Pop
  def NonMatching(self, items):
791 a2d2e1a7 Iustin Pop
    """Returns the list of fields not matching the current set
792 a2d2e1a7 Iustin Pop

793 a2d2e1a7 Iustin Pop
    @type items: list
794 a2d2e1a7 Iustin Pop
    @param items: the list of fields to check
795 a2d2e1a7 Iustin Pop
    @rtype: list
796 a2d2e1a7 Iustin Pop
    @return: list of non-matching fields
797 a2d2e1a7 Iustin Pop

798 a2d2e1a7 Iustin Pop
    """
799 a2d2e1a7 Iustin Pop
    return [val for val in items if not self.Matches(val)]
800 651cc3e2 Christos Stavrakakis
801 651cc3e2 Christos Stavrakakis
802 651cc3e2 Christos Stavrakakis
def ValidateDeviceNames(kind, container):
803 651cc3e2 Christos Stavrakakis
  """Validate instance device names.
804 651cc3e2 Christos Stavrakakis

805 651cc3e2 Christos Stavrakakis
  Check that a device container contains only unique and valid names.
806 651cc3e2 Christos Stavrakakis

807 651cc3e2 Christos Stavrakakis
  @type kind: string
808 651cc3e2 Christos Stavrakakis
  @param kind: One-word item description
809 651cc3e2 Christos Stavrakakis
  @type container: list
810 651cc3e2 Christos Stavrakakis
  @param container: Container containing the devices
811 651cc3e2 Christos Stavrakakis

812 651cc3e2 Christos Stavrakakis
  """
813 651cc3e2 Christos Stavrakakis
814 651cc3e2 Christos Stavrakakis
  valid = []
815 651cc3e2 Christos Stavrakakis
  for device in container:
816 651cc3e2 Christos Stavrakakis
    if isinstance(device, dict):
817 651cc3e2 Christos Stavrakakis
      if kind == "NIC":
818 651cc3e2 Christos Stavrakakis
        name = device.get(constants.INIC_NAME, None)
819 651cc3e2 Christos Stavrakakis
      elif kind == "disk":
820 651cc3e2 Christos Stavrakakis
        name = device.get(constants.IDISK_NAME, None)
821 651cc3e2 Christos Stavrakakis
      else:
822 651cc3e2 Christos Stavrakakis
        raise errors.OpPrereqError("Invalid container kind '%s'" % kind,
823 651cc3e2 Christos Stavrakakis
                                   errors.ECODE_INVAL)
824 651cc3e2 Christos Stavrakakis
    else:
825 651cc3e2 Christos Stavrakakis
      name = device.name
826 651cc3e2 Christos Stavrakakis
      # Check that a device name is not the UUID of another device
827 651cc3e2 Christos Stavrakakis
      valid.append(device.uuid)
828 651cc3e2 Christos Stavrakakis
829 651cc3e2 Christos Stavrakakis
    try:
830 651cc3e2 Christos Stavrakakis
      int(name)
831 651cc3e2 Christos Stavrakakis
    except (ValueError, TypeError):
832 651cc3e2 Christos Stavrakakis
      pass
833 651cc3e2 Christos Stavrakakis
    else:
834 651cc3e2 Christos Stavrakakis
      raise errors.OpPrereqError("Invalid name '%s'. Purely numeric %s names"
835 651cc3e2 Christos Stavrakakis
                                 " are not allowed" % (name, kind),
836 651cc3e2 Christos Stavrakakis
                                 errors.ECODE_INVAL)
837 651cc3e2 Christos Stavrakakis
838 651cc3e2 Christos Stavrakakis
    if name is not None and name.lower() != constants.VALUE_NONE:
839 651cc3e2 Christos Stavrakakis
      if name in valid:
840 651cc3e2 Christos Stavrakakis
        raise errors.OpPrereqError("%s name '%s' already used" % (kind, name),
841 651cc3e2 Christos Stavrakakis
                                   errors.ECODE_NOTUNIQUE)
842 651cc3e2 Christos Stavrakakis
      else:
843 651cc3e2 Christos Stavrakakis
        valid.append(name)