Statistics
| Branch: | Tag: | Revision:

root / lib / utils / __init__.py @ 294254b1

History | View | Annotate | Download (24.1 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 59ef0f15 Helga Velroyen
from ganeti.utils.storage 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
      continue
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 7eed4433 Michele Tartara
        msg = "'None' is not a valid Maybe value. Use 'VALUE_HS_NOTHING'"
102 1cc324f0 Michele Tartara
        logging.warning(msg)
103 7eed4433 Michele Tartara
      elif (target[key] == constants.VALUE_HS_NOTHING
104 7eed4433 Michele Tartara
            and ktype == constants.VTYPE_MAYBE_STRING):
105 59525e1f Michael Hanselmann
        pass
106 59525e1f Michael Hanselmann
      elif not isinstance(target[key], basestring):
107 a5728081 Guido Trotter
        if isinstance(target[key], bool) and not target[key]:
108 d0c8c01d Iustin Pop
          target[key] = ""
109 a5728081 Guido Trotter
        else:
110 a5728081 Guido Trotter
          msg = "'%s' (value %s) is not a valid string" % (key, target[key])
111 a5728081 Guido Trotter
          raise errors.TypeEnforcementError(msg)
112 29921401 Iustin Pop
    elif ktype == constants.VTYPE_BOOL:
113 a5728081 Guido Trotter
      if isinstance(target[key], basestring) and target[key]:
114 a5728081 Guido Trotter
        if target[key].lower() == constants.VALUE_FALSE:
115 a5728081 Guido Trotter
          target[key] = False
116 a5728081 Guido Trotter
        elif target[key].lower() == constants.VALUE_TRUE:
117 a5728081 Guido Trotter
          target[key] = True
118 a5728081 Guido Trotter
        else:
119 a5728081 Guido Trotter
          msg = "'%s' (value %s) is not a valid boolean" % (key, target[key])
120 a5728081 Guido Trotter
          raise errors.TypeEnforcementError(msg)
121 a5728081 Guido Trotter
      elif target[key]:
122 a5728081 Guido Trotter
        target[key] = True
123 a5728081 Guido Trotter
      else:
124 a5728081 Guido Trotter
        target[key] = False
125 29921401 Iustin Pop
    elif ktype == constants.VTYPE_SIZE:
126 a5728081 Guido Trotter
      try:
127 a5728081 Guido Trotter
        target[key] = ParseUnit(target[key])
128 a5728081 Guido Trotter
      except errors.UnitParseError, err:
129 a5728081 Guido Trotter
        msg = "'%s' (value %s) is not a valid size. error: %s" % \
130 a5728081 Guido Trotter
              (key, target[key], err)
131 a5728081 Guido Trotter
        raise errors.TypeEnforcementError(msg)
132 29921401 Iustin Pop
    elif ktype == constants.VTYPE_INT:
133 a5728081 Guido Trotter
      try:
134 a5728081 Guido Trotter
        target[key] = int(target[key])
135 a5728081 Guido Trotter
      except (ValueError, TypeError):
136 a5728081 Guido Trotter
        msg = "'%s' (value %s) is not a valid integer" % (key, target[key])
137 a5728081 Guido Trotter
        raise errors.TypeEnforcementError(msg)
138 a5728081 Guido Trotter
139 a5728081 Guido Trotter
140 28f34048 Michael Hanselmann
def ValidateServiceName(name):
141 28f34048 Michael Hanselmann
  """Validate the given service name.
142 28f34048 Michael Hanselmann

143 28f34048 Michael Hanselmann
  @type name: number or string
144 28f34048 Michael Hanselmann
  @param name: Service name or port specification
145 28f34048 Michael Hanselmann

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

167 32be86da Renรฉ Nussbaumer
  @param key_path: The current key path (if any)
168 32be86da Renรฉ Nussbaumer
  @param options: The user provided options
169 32be86da Renรฉ Nussbaumer
  @param defaults: The default dictionary
170 32be86da Renรฉ Nussbaumer
  @return: A list of invalid keys
171 32be86da Renรฉ Nussbaumer

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

192 32be86da Renรฉ Nussbaumer
  @param options: The user provided options
193 32be86da Renรฉ Nussbaumer
  @param defaults: The default dictionary
194 32be86da Renรฉ Nussbaumer
  @raise error.OpPrereqError: If one of the keys is not supported
195 32be86da Renรฉ Nussbaumer

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

207 58885d79 Iustin Pop
  @rtype: dict
208 58885d79 Iustin Pop
  @return:
209 58885d79 Iustin Pop
       Dictionary with keys volume name and values
210 58885d79 Iustin Pop
       the size of the volume
211 a8083063 Iustin Pop

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

235 58885d79 Iustin Pop
  @type bridge: str
236 58885d79 Iustin Pop
  @param bridge: the bridge name to check
237 58885d79 Iustin Pop
  @rtype: boolean
238 58885d79 Iustin Pop
  @return: True if it does
239 a8083063 Iustin Pop

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

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

252 58885d79 Iustin Pop
  @type fn: callable
253 58885d79 Iustin Pop
  @param fn: function to apply to the value
254 58885d79 Iustin Pop
  @param val: the value to be converted
255 58885d79 Iustin Pop
  @return: The converted value if the conversion was successful,
256 58885d79 Iustin Pop
      otherwise the original value.
257 a8083063 Iustin Pop

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

269 31155d60 Balazs Lecz
  CPU mask format: comma-separated list of CPU IDs
270 31155d60 Balazs Lecz
  or dash-separated ID ranges
271 31155d60 Balazs Lecz
  Example: "0-2,5" -> "0,1,2,5"
272 31155d60 Balazs Lecz

273 31155d60 Balazs Lecz
  @type cpu_mask: str
274 31155d60 Balazs Lecz
  @param cpu_mask: CPU mask definition
275 31155d60 Balazs Lecz
  @rtype: list of int
276 31155d60 Balazs Lecz
  @return: list of CPU IDs
277 31155d60 Balazs Lecz

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

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

312 538ca33a Tsachy Shacham
  @type cpu_mask: str
313 538ca33a Tsachy Shacham
  @param cpu_mask: multiple CPU mask definition
314 538ca33a Tsachy Shacham
  @rtype: list of lists of int
315 538ca33a Tsachy Shacham
  @return: list of lists of CPU IDs
316 538ca33a Tsachy Shacham

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

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

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

355 7b4126b7 Iustin Pop
  The seq argument should be a sorted list of positive integers. The
356 7b4126b7 Iustin Pop
  first time the index of an element is smaller than the element
357 7b4126b7 Iustin Pop
  value, the index will be returned.
358 7b4126b7 Iustin Pop

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

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

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

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

383 dfdc4060 Guido Trotter
  Immediately returns at the first interruption.
384 dfdc4060 Guido Trotter

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

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

422 dfdc4060 Guido Trotter
  This class contains the retried and wait functions that make sure
423 dfdc4060 Guido Trotter
  WaitForFdCondition can continue waiting until the timeout is actually
424 dfdc4060 Guido Trotter
  expired.
425 dfdc4060 Guido Trotter

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

445 dfdc4060 Guido Trotter
  Retries until the timeout is expired, even if interrupted.
446 dfdc4060 Guido Trotter

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

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

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

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

500 45bc5e4a Michael Hanselmann
  @param value: Time in seconds
501 45bc5e4a Michael Hanselmann
  @type value: int or float
502 45bc5e4a Michael Hanselmann
  @return: Tuple containing (seconds, microseconds)
503 739be818 Michael Hanselmann

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

518 45bc5e4a Michael Hanselmann
  @param timetuple: Time as tuple, (seconds, microseconds)
519 739be818 Michael Hanselmann
  @type timetuple: tuple
520 739be818 Michael Hanselmann
  @return: Time as a floating point number expressed in seconds
521 739be818 Michael Hanselmann

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

537 c28911dd Michele Tartara
  @return: nanoseconds since the Unix epoch
538 c28911dd Michele Tartara

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

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

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

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

573 1b045f5d Balazs Lecz
  This function is Linux-specific.
574 1b045f5d Balazs Lecz

575 1b045f5d Balazs Lecz
  @param filename: path of mounts file (/proc/mounts by default)
576 1b045f5d Balazs Lecz
  @rtype: list of tuples
577 1b045f5d Balazs Lecz
  @return: list of mount entries (device, mountpoint, fstype, options)
578 1b045f5d Balazs Lecz

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

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

598 451575de Guido Trotter
  The decorator can be safely stacked with iself, to handle multiple signals
599 451575de Guido Trotter
  with different handlers.
600 451575de Guido Trotter

601 451575de Guido Trotter
  @type signums: list
602 451575de Guido Trotter
  @param signums: signals to intercept
603 451575de Guido Trotter

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

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

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

670 b9768937 Michael Hanselmann
    """
671 b9768937 Michael Hanselmann
    if hasattr(self, "_previous") and self._previous is not None:
672 b9768937 Michael Hanselmann
      self._SetWakeupFd(self._previous)
673 b9768937 Michael Hanselmann
      self._previous = None
674 b9768937 Michael Hanselmann
675 b9768937 Michael Hanselmann
  def Notify(self):
676 b9768937 Michael Hanselmann
    """Notifies the wakeup file descriptor.
677 b9768937 Michael Hanselmann

678 b9768937 Michael Hanselmann
    """
679 edada04b Michele Tartara
    self._write_fh.write(chr(0))
680 b9768937 Michael Hanselmann
681 b9768937 Michael Hanselmann
  def __del__(self):
682 b9768937 Michael Hanselmann
    """Called before object deletion.
683 b9768937 Michael Hanselmann

684 b9768937 Michael Hanselmann
    """
685 b9768937 Michael Hanselmann
    self.Reset()
686 b9768937 Michael Hanselmann
687 b9768937 Michael Hanselmann
688 de499029 Michael Hanselmann
class SignalHandler(object):
689 de499029 Michael Hanselmann
  """Generic signal handler class.
690 de499029 Michael Hanselmann

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

696 58885d79 Iustin Pop
  @type signum: list
697 58885d79 Iustin Pop
  @ivar signum: the signals we handle
698 58885d79 Iustin Pop
  @type called: boolean
699 58885d79 Iustin Pop
  @ivar called: tracks whether any of the signals have been raised
700 de499029 Michael Hanselmann

701 de499029 Michael Hanselmann
  """
702 b9768937 Michael Hanselmann
  def __init__(self, signum, handler_fn=None, wakeup=None):
703 de499029 Michael Hanselmann
    """Constructs a new SignalHandler instance.
704 de499029 Michael Hanselmann

705 58885d79 Iustin Pop
    @type signum: int or list of ints
706 de499029 Michael Hanselmann
    @param signum: Single signal number or set of signal numbers
707 92b61ec7 Michael Hanselmann
    @type handler_fn: callable
708 92b61ec7 Michael Hanselmann
    @param handler_fn: Signal handling function
709 de499029 Michael Hanselmann

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

743 58885d79 Iustin Pop
    This will reset all the signals to their previous handlers.
744 58885d79 Iustin Pop

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

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

756 de499029 Michael Hanselmann
    """
757 de499029 Michael Hanselmann
    self.called = False
758 de499029 Michael Hanselmann
759 92b61ec7 Michael Hanselmann
  def _HandleSignal(self, signum, frame):
760 de499029 Michael Hanselmann
    """Actual signal handling function.
761 de499029 Michael Hanselmann

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

778 a2d2e1a7 Iustin Pop
  Among the features are:
779 a2d2e1a7 Iustin Pop
    - checking if a string is among a list of static string or regex objects
780 a2d2e1a7 Iustin Pop
    - checking if a whole list of string matches
781 a2d2e1a7 Iustin Pop
    - returning the matching groups from a regex match
782 a2d2e1a7 Iustin Pop

783 a2d2e1a7 Iustin Pop
  Internally, all fields are held as regular expression objects.
784 a2d2e1a7 Iustin Pop

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

796 a2d2e1a7 Iustin Pop
    @type field: str
797 a2d2e1a7 Iustin Pop
    @param field: the string to match
798 6c881c52 Iustin Pop
    @return: either None or a regular expression match object
799 a2d2e1a7 Iustin Pop

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

808 a2d2e1a7 Iustin Pop
    @type items: list
809 a2d2e1a7 Iustin Pop
    @param items: the list of fields to check
810 a2d2e1a7 Iustin Pop
    @rtype: list
811 a2d2e1a7 Iustin Pop
    @return: list of non-matching fields
812 a2d2e1a7 Iustin Pop

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

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

822 651cc3e2 Christos Stavrakakis
  @type kind: string
823 651cc3e2 Christos Stavrakakis
  @param kind: One-word item description
824 651cc3e2 Christos Stavrakakis
  @type container: list
825 651cc3e2 Christos Stavrakakis
  @param container: Container containing the devices
826 651cc3e2 Christos Stavrakakis

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