| Branch: | Tag: | Revision:

root / lib / utils / @ e1a6abf9

History | View | Annotate | Download (23.9 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
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 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 59ef0f15 Helga Velroyen
from import *
57 63fc4229 Michael Hanselmann
from ganeti.utils.text import *
58 63fc4229 Michael Hanselmann
from ganeti.utils.wrapper import *
59 63fc4229 Michael Hanselmann
from ganeti.utils.x509 import *
60 16abfbc2 Alexander Schreiber
61 58885d79 Iustin Pop
62 28f34048 Michael Hanselmann
_VALID_SERVICE_NAME_RE = re.compile("^[-_.a-zA-Z0-9]{1,128}$")
63 28f34048 Michael Hanselmann
64 80a0546b Michele Tartara
UUID_RE = re.compile(constants.UUID_REGEX)
65 05636402 Guido Trotter
66 7c0d6283 Michael Hanselmann
67 a5728081 Guido Trotter
def ForceDictType(target, key_types, allowed_values=None):
68 a5728081 Guido Trotter
  """Force the values of a dict to have certain types.
69 a5728081 Guido Trotter

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

518 739be818 Michael Hanselmann
519 45bc5e4a Michael Hanselmann
  (seconds, microseconds) = timetuple
520 739be818 Michael Hanselmann
521 45bc5e4a Michael Hanselmann
  assert 0 <= seconds, \
522 45bc5e4a Michael Hanselmann
    "Seconds must be larger than or equal to 0, but are %s" % seconds
523 45bc5e4a Michael Hanselmann
  assert 0 <= microseconds <= 999999, \
524 45bc5e4a Michael Hanselmann
    "Microseconds must be 0-999999, but are %s" % microseconds
525 739be818 Michael Hanselmann
526 45bc5e4a Michael Hanselmann
  return float(seconds) + (float(microseconds) * 0.000001)
527 739be818 Michael Hanselmann
528 739be818 Michael Hanselmann
529 c28911dd Michele Tartara
def EpochNano():
530 c28911dd Michele Tartara
  """Return the current timestamp expressed as number of nanoseconds since the
531 c28911dd Michele Tartara
  unix epoch
532 c28911dd Michele Tartara

533 c28911dd Michele Tartara
  @return: nanoseconds since the Unix epoch
534 c28911dd Michele Tartara

535 c28911dd Michele Tartara
536 c28911dd Michele Tartara
  return int(time.time() * 1000000000)
537 c28911dd Michele Tartara
538 c28911dd Michele Tartara
539 691c81b7 Michael Hanselmann
def FindMatch(data, name):
540 691c81b7 Michael Hanselmann
  """Tries to find an item in a dictionary matching a name.
541 691c81b7 Michael Hanselmann

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

546 691c81b7 Michael Hanselmann
  @type data: dict
547 691c81b7 Michael Hanselmann
  @param data: Dictionary containing data
548 691c81b7 Michael Hanselmann
  @type name: string
549 691c81b7 Michael Hanselmann
  @param name: Name to look for
550 691c81b7 Michael Hanselmann
  @rtype: tuple; (value in dictionary, matched groups as list)
551 691c81b7 Michael Hanselmann

552 691c81b7 Michael Hanselmann
553 691c81b7 Michael Hanselmann
  if name in data:
554 691c81b7 Michael Hanselmann
    return (data[name], [])
555 691c81b7 Michael Hanselmann
556 691c81b7 Michael Hanselmann
  for key, value in data.items():
557 691c81b7 Michael Hanselmann
    # Regex objects
558 691c81b7 Michael Hanselmann
    if hasattr(key, "match"):
559 691c81b7 Michael Hanselmann
      m = key.match(name)
560 691c81b7 Michael Hanselmann
      if m:
561 691c81b7 Michael Hanselmann
        return (value, list(m.groups()))
562 691c81b7 Michael Hanselmann
563 691c81b7 Michael Hanselmann
  return None
564 691c81b7 Michael Hanselmann
565 691c81b7 Michael Hanselmann
566 1b045f5d Balazs Lecz
def GetMounts(filename=constants.PROC_MOUNTS):
567 1b045f5d Balazs Lecz
  """Returns the list of mounted filesystems.
568 1b045f5d Balazs Lecz

569 1b045f5d Balazs Lecz
  This function is Linux-specific.
570 1b045f5d Balazs Lecz

571 1b045f5d Balazs Lecz
  @param filename: path of mounts file (/proc/mounts by default)
572 1b045f5d Balazs Lecz
  @rtype: list of tuples
573 1b045f5d Balazs Lecz
  @return: list of mount entries (device, mountpoint, fstype, options)
574 1b045f5d Balazs Lecz

575 1b045f5d Balazs Lecz
576 1b045f5d Balazs Lecz
  # TODO(iustin): investigate non-Linux options (e.g. via mount output)
577 1b045f5d Balazs Lecz
  data = []
578 1b045f5d Balazs Lecz
  mountlines = ReadFile(filename).splitlines()
579 1b045f5d Balazs Lecz
  for line in mountlines:
580 1b045f5d Balazs Lecz
    device, mountpoint, fstype, options, _ = line.split(None, 4)
581 1b045f5d Balazs Lecz
    data.append((device, mountpoint, fstype, options))
582 1b045f5d Balazs Lecz
583 1b045f5d Balazs Lecz
  return data
584 1b045f5d Balazs Lecz
585 1b045f5d Balazs Lecz
586 451575de Guido Trotter
def SignalHandled(signums):
587 451575de Guido Trotter
  """Signal Handled decoration.
588 451575de Guido Trotter

589 451575de Guido Trotter
  This special decorator installs a signal handler and then calls the target
590 451575de Guido Trotter
  function. The function must accept a 'signal_handlers' keyword argument,
591 451575de Guido Trotter
  which will contain a dict indexed by signal number, with SignalHandler
592 451575de Guido Trotter
  objects as values.
593 451575de Guido Trotter

594 451575de Guido Trotter
  The decorator can be safely stacked with iself, to handle multiple signals
595 451575de Guido Trotter
  with different handlers.
596 451575de Guido Trotter

597 451575de Guido Trotter
  @type signums: list
598 451575de Guido Trotter
  @param signums: signals to intercept
599 451575de Guido Trotter

600 451575de Guido Trotter
601 451575de Guido Trotter
  def wrap(fn):
602 451575de Guido Trotter
    def sig_function(*args, **kwargs):
603 d0c8c01d Iustin Pop
      assert "signal_handlers" not in kwargs or \
604 d0c8c01d Iustin Pop
             kwargs["signal_handlers"] is None or \
605 d0c8c01d Iustin Pop
             isinstance(kwargs["signal_handlers"], dict), \
606 451575de Guido Trotter
             "Wrong signal_handlers parameter in original function call"
607 d0c8c01d Iustin Pop
      if "signal_handlers" in kwargs and kwargs["signal_handlers"] is not None:
608 d0c8c01d Iustin Pop
        signal_handlers = kwargs["signal_handlers"]
609 451575de Guido Trotter
610 451575de Guido Trotter
        signal_handlers = {}
611 d0c8c01d Iustin Pop
        kwargs["signal_handlers"] = signal_handlers
612 451575de Guido Trotter
      sighandler = SignalHandler(signums)
613 451575de Guido Trotter
614 451575de Guido Trotter
        for sig in signums:
615 451575de Guido Trotter
          signal_handlers[sig] = sighandler
616 451575de Guido Trotter
        return fn(*args, **kwargs)
617 451575de Guido Trotter
618 451575de Guido Trotter
619 451575de Guido Trotter
    return sig_function
620 451575de Guido Trotter
  return wrap
621 451575de Guido Trotter
622 451575de Guido Trotter
623 f8326fca Andrea Spadaccini
def TimeoutExpired(epoch, timeout, _time_fn=time.time):
624 f8326fca Andrea Spadaccini
  """Checks whether a timeout has expired.
625 f8326fca Andrea Spadaccini

626 f8326fca Andrea Spadaccini
627 f8326fca Andrea Spadaccini
  return _time_fn() > (epoch + timeout)
628 f8326fca Andrea Spadaccini
629 f8326fca Andrea Spadaccini
630 b9768937 Michael Hanselmann
class SignalWakeupFd(object):
631 b9768937 Michael Hanselmann
632 b9768937 Michael Hanselmann
    # This is only supported in Python 2.5 and above (some distributions
633 b9768937 Michael Hanselmann
    # backported it to Python 2.4)
634 b9768937 Michael Hanselmann
    _set_wakeup_fd_fn = signal.set_wakeup_fd
635 b9768937 Michael Hanselmann
  except AttributeError:
636 b9768937 Michael Hanselmann
    # Not supported
637 3c286190 Dimitris Aragiorgis
638 b459a848 Andrea Spadaccini
    def _SetWakeupFd(self, _): # pylint: disable=R0201
639 b9768937 Michael Hanselmann
      return -1
640 b9768937 Michael Hanselmann
641 3c286190 Dimitris Aragiorgis
642 b9768937 Michael Hanselmann
    def _SetWakeupFd(self, fd):
643 b9768937 Michael Hanselmann
      return self._set_wakeup_fd_fn(fd)
644 b9768937 Michael Hanselmann
645 b9768937 Michael Hanselmann
  def __init__(self):
646 b9768937 Michael Hanselmann
    """Initializes this class.
647 b9768937 Michael Hanselmann

648 b9768937 Michael Hanselmann
649 b9768937 Michael Hanselmann
    (read_fd, write_fd) = os.pipe()
650 b9768937 Michael Hanselmann
651 b9768937 Michael Hanselmann
    # Once these succeeded, the file descriptors will be closed automatically.
652 b9768937 Michael Hanselmann
    # Buffer size 0 is important, otherwise .read() with a specified length
653 b9768937 Michael Hanselmann
    # might buffer data and the file descriptors won't be marked readable.
654 b9768937 Michael Hanselmann
    self._read_fh = os.fdopen(read_fd, "r", 0)
655 b9768937 Michael Hanselmann
    self._write_fh = os.fdopen(write_fd, "w", 0)
656 b9768937 Michael Hanselmann
657 b9768937 Michael Hanselmann
    self._previous = self._SetWakeupFd(self._write_fh.fileno())
658 b9768937 Michael Hanselmann
659 b9768937 Michael Hanselmann
    # Utility functions
660 b9768937 Michael Hanselmann
    self.fileno = self._read_fh.fileno
661 b9768937 Michael Hanselmann =
662 b9768937 Michael Hanselmann
663 b9768937 Michael Hanselmann
  def Reset(self):
664 b9768937 Michael Hanselmann
    """Restores the previous wakeup file descriptor.
665 b9768937 Michael Hanselmann

666 b9768937 Michael Hanselmann
667 b9768937 Michael Hanselmann
    if hasattr(self, "_previous") and self._previous is not None:
668 b9768937 Michael Hanselmann
669 b9768937 Michael Hanselmann
      self._previous = None
670 b9768937 Michael Hanselmann
671 b9768937 Michael Hanselmann
  def Notify(self):
672 b9768937 Michael Hanselmann
    """Notifies the wakeup file descriptor.
673 b9768937 Michael Hanselmann

674 b9768937 Michael Hanselmann
675 b9768937 Michael Hanselmann
676 b9768937 Michael Hanselmann
677 b9768937 Michael Hanselmann
  def __del__(self):
678 b9768937 Michael Hanselmann
    """Called before object deletion.
679 b9768937 Michael Hanselmann

680 b9768937 Michael Hanselmann
681 b9768937 Michael Hanselmann
682 b9768937 Michael Hanselmann
683 b9768937 Michael Hanselmann
684 de499029 Michael Hanselmann
class SignalHandler(object):
685 de499029 Michael Hanselmann
  """Generic signal handler class.
686 de499029 Michael Hanselmann

687 58885d79 Iustin Pop
  It automatically restores the original handler when deconstructed or
688 58885d79 Iustin Pop
  when L{Reset} is called. You can either pass your own handler
689 58885d79 Iustin Pop
  function in or query the L{called} attribute to detect whether the
690 58885d79 Iustin Pop
  signal was sent.
691 58885d79 Iustin Pop

692 58885d79 Iustin Pop
  @type signum: list
693 58885d79 Iustin Pop
  @ivar signum: the signals we handle
694 58885d79 Iustin Pop
  @type called: boolean
695 58885d79 Iustin Pop
  @ivar called: tracks whether any of the signals have been raised
696 de499029 Michael Hanselmann

697 de499029 Michael Hanselmann
698 b9768937 Michael Hanselmann
  def __init__(self, signum, handler_fn=None, wakeup=None):
699 de499029 Michael Hanselmann
    """Constructs a new SignalHandler instance.
700 de499029 Michael Hanselmann

701 58885d79 Iustin Pop
    @type signum: int or list of ints
702 de499029 Michael Hanselmann
    @param signum: Single signal number or set of signal numbers
703 92b61ec7 Michael Hanselmann
    @type handler_fn: callable
704 92b61ec7 Michael Hanselmann
    @param handler_fn: Signal handling function
705 de499029 Michael Hanselmann

706 de499029 Michael Hanselmann
707 92b61ec7 Michael Hanselmann
    assert handler_fn is None or callable(handler_fn)
708 92b61ec7 Michael Hanselmann
709 6c52849e Guido Trotter
    self.signum = set(signum)
710 de499029 Michael Hanselmann
    self.called = False
711 de499029 Michael Hanselmann
712 92b61ec7 Michael Hanselmann
    self._handler_fn = handler_fn
713 b9768937 Michael Hanselmann
    self._wakeup = wakeup
714 92b61ec7 Michael Hanselmann
715 de499029 Michael Hanselmann
    self._previous = {}
716 de499029 Michael Hanselmann
717 de499029 Michael Hanselmann
      for signum in self.signum:
718 de499029 Michael Hanselmann
        # Setup handler
719 de499029 Michael Hanselmann
        prev_handler = signal.signal(signum, self._HandleSignal)
720 de499029 Michael Hanselmann
721 de499029 Michael Hanselmann
          self._previous[signum] = prev_handler
722 de499029 Michael Hanselmann
723 de499029 Michael Hanselmann
          # Restore previous handler
724 de499029 Michael Hanselmann
          signal.signal(signum, prev_handler)
725 de499029 Michael Hanselmann
726 de499029 Michael Hanselmann
727 de499029 Michael Hanselmann
      # Reset all handlers
728 de499029 Michael Hanselmann
729 de499029 Michael Hanselmann
      # Here we have a race condition: a handler may have already been called,
730 de499029 Michael Hanselmann
      # but there's not much we can do about it at this point.
731 de499029 Michael Hanselmann
732 de499029 Michael Hanselmann
733 de499029 Michael Hanselmann
  def __del__(self):
734 de499029 Michael Hanselmann
735 de499029 Michael Hanselmann
736 de499029 Michael Hanselmann
  def Reset(self):
737 de499029 Michael Hanselmann
    """Restore previous handler.
738 de499029 Michael Hanselmann

739 58885d79 Iustin Pop
    This will reset all the signals to their previous handlers.
740 58885d79 Iustin Pop

741 de499029 Michael Hanselmann
742 de499029 Michael Hanselmann
    for signum, prev_handler in self._previous.items():
743 de499029 Michael Hanselmann
      signal.signal(signum, prev_handler)
744 de499029 Michael Hanselmann
      # If successful, remove from dict
745 de499029 Michael Hanselmann
      del self._previous[signum]
746 de499029 Michael Hanselmann
747 de499029 Michael Hanselmann
  def Clear(self):
748 58885d79 Iustin Pop
    """Unsets the L{called} flag.
749 de499029 Michael Hanselmann

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

752 de499029 Michael Hanselmann
753 de499029 Michael Hanselmann
    self.called = False
754 de499029 Michael Hanselmann
755 92b61ec7 Michael Hanselmann
  def _HandleSignal(self, signum, frame):
756 de499029 Michael Hanselmann
    """Actual signal handling function.
757 de499029 Michael Hanselmann

758 de499029 Michael Hanselmann
759 de499029 Michael Hanselmann
    # This is not nice and not absolutely atomic, but it appears to be the only
760 de499029 Michael Hanselmann
    # solution in Python -- there are no atomic types.
761 de499029 Michael Hanselmann
    self.called = True
762 a2d2e1a7 Iustin Pop
763 b9768937 Michael Hanselmann
    if self._wakeup:
764 b9768937 Michael Hanselmann
      # Notify whoever is interested in signals
765 b9768937 Michael Hanselmann
766 b9768937 Michael Hanselmann
767 92b61ec7 Michael Hanselmann
    if self._handler_fn:
768 92b61ec7 Michael Hanselmann
      self._handler_fn(signum, frame)
769 92b61ec7 Michael Hanselmann
770 a2d2e1a7 Iustin Pop
771 a2d2e1a7 Iustin Pop
class FieldSet(object):
772 a2d2e1a7 Iustin Pop
  """A simple field set.
773 a2d2e1a7 Iustin Pop

774 a2d2e1a7 Iustin Pop
  Among the features are:
775 a2d2e1a7 Iustin Pop
    - checking if a string is among a list of static string or regex objects
776 a2d2e1a7 Iustin Pop
    - checking if a whole list of string matches
777 a2d2e1a7 Iustin Pop
    - returning the matching groups from a regex match
778 a2d2e1a7 Iustin Pop

779 a2d2e1a7 Iustin Pop
  Internally, all fields are held as regular expression objects.
780 a2d2e1a7 Iustin Pop

781 a2d2e1a7 Iustin Pop
782 a2d2e1a7 Iustin Pop
  def __init__(self, *items):
783 a2d2e1a7 Iustin Pop
    self.items = [re.compile("^%s$" % value) for value in items]
784 a2d2e1a7 Iustin Pop
785 a2d2e1a7 Iustin Pop
  def Extend(self, other_set):
786 a2d2e1a7 Iustin Pop
    """Extend the field set with the items from another one"""
787 a2d2e1a7 Iustin Pop
788 a2d2e1a7 Iustin Pop
789 a2d2e1a7 Iustin Pop
  def Matches(self, field):
790 a2d2e1a7 Iustin Pop
    """Checks if a field matches the current set
791 a2d2e1a7 Iustin Pop

792 a2d2e1a7 Iustin Pop
    @type field: str
793 a2d2e1a7 Iustin Pop
    @param field: the string to match
794 6c881c52 Iustin Pop
    @return: either None or a regular expression match object
795 a2d2e1a7 Iustin Pop

796 a2d2e1a7 Iustin Pop
797 a2d2e1a7 Iustin Pop
    for m in itertools.ifilter(None, (val.match(field) for val in self.items)):
798 a2d2e1a7 Iustin Pop
      return m
799 6c881c52 Iustin Pop
    return None
800 a2d2e1a7 Iustin Pop
801 a2d2e1a7 Iustin Pop
  def NonMatching(self, items):
802 a2d2e1a7 Iustin Pop
    """Returns the list of fields not matching the current set
803 a2d2e1a7 Iustin Pop

804 a2d2e1a7 Iustin Pop
    @type items: list
805 a2d2e1a7 Iustin Pop
    @param items: the list of fields to check
806 a2d2e1a7 Iustin Pop
    @rtype: list
807 a2d2e1a7 Iustin Pop
    @return: list of non-matching fields
808 a2d2e1a7 Iustin Pop

809 a2d2e1a7 Iustin Pop
810 a2d2e1a7 Iustin Pop
    return [val for val in items if not self.Matches(val)]
811 651cc3e2 Christos Stavrakakis
812 651cc3e2 Christos Stavrakakis
813 651cc3e2 Christos Stavrakakis
def ValidateDeviceNames(kind, container):
814 651cc3e2 Christos Stavrakakis
  """Validate instance device names.
815 651cc3e2 Christos Stavrakakis

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

818 651cc3e2 Christos Stavrakakis
  @type kind: string
819 651cc3e2 Christos Stavrakakis
  @param kind: One-word item description
820 651cc3e2 Christos Stavrakakis
  @type container: list
821 651cc3e2 Christos Stavrakakis
  @param container: Container containing the devices
822 651cc3e2 Christos Stavrakakis

823 651cc3e2 Christos Stavrakakis
824 651cc3e2 Christos Stavrakakis
825 651cc3e2 Christos Stavrakakis
  valid = []
826 651cc3e2 Christos Stavrakakis
  for device in container:
827 651cc3e2 Christos Stavrakakis
    if isinstance(device, dict):
828 651cc3e2 Christos Stavrakakis
      if kind == "NIC":
829 651cc3e2 Christos Stavrakakis
        name = device.get(constants.INIC_NAME, None)
830 651cc3e2 Christos Stavrakakis
      elif kind == "disk":
831 651cc3e2 Christos Stavrakakis
        name = device.get(constants.IDISK_NAME, None)
832 651cc3e2 Christos Stavrakakis
833 651cc3e2 Christos Stavrakakis
        raise errors.OpPrereqError("Invalid container kind '%s'" % kind,
834 651cc3e2 Christos Stavrakakis
835 651cc3e2 Christos Stavrakakis
836 651cc3e2 Christos Stavrakakis
      name =
837 651cc3e2 Christos Stavrakakis
      # Check that a device name is not the UUID of another device
838 651cc3e2 Christos Stavrakakis
839 651cc3e2 Christos Stavrakakis
840 651cc3e2 Christos Stavrakakis
841 651cc3e2 Christos Stavrakakis
842 651cc3e2 Christos Stavrakakis
    except (ValueError, TypeError):
843 651cc3e2 Christos Stavrakakis
844 651cc3e2 Christos Stavrakakis
845 651cc3e2 Christos Stavrakakis
      raise errors.OpPrereqError("Invalid name '%s'. Purely numeric %s names"
846 651cc3e2 Christos Stavrakakis
                                 " are not allowed" % (name, kind),
847 651cc3e2 Christos Stavrakakis
848 651cc3e2 Christos Stavrakakis
849 651cc3e2 Christos Stavrakakis
    if name is not None and name.lower() != constants.VALUE_NONE:
850 651cc3e2 Christos Stavrakakis
      if name in valid:
851 651cc3e2 Christos Stavrakakis
        raise errors.OpPrereqError("%s name '%s' already used" % (kind, name),
852 651cc3e2 Christos Stavrakakis
853 651cc3e2 Christos Stavrakakis
854 651cc3e2 Christos Stavrakakis