Statistics
| Branch: | Tag: | Revision:

root / lib / utils / algo.py @ 364c350f

History | View | Annotate | Download (7.3 kB)

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

23 4fd029cf Michael Hanselmann
"""
24 4fd029cf Michael Hanselmann
25 4fd029cf Michael Hanselmann
import re
26 7d444d59 Michael Hanselmann
import time
27 9c709b31 René Nussbaumer
import itertools
28 4fd029cf Michael Hanselmann
29 6d0accae Michael Hanselmann
from ganeti import compat
30 6d0accae Michael Hanselmann
from ganeti.utils import text
31 6d0accae Michael Hanselmann
32 4fd029cf Michael Hanselmann
33 7d4da09e Michael Hanselmann
_SORTER_GROUPS = 8
34 7d4da09e Michael Hanselmann
_SORTER_RE = re.compile("^%s(.*)$" % (_SORTER_GROUPS * "(\D+|\d+)?"))
35 4fd029cf Michael Hanselmann
36 4fd029cf Michael Hanselmann
37 4fd029cf Michael Hanselmann
def UniqueSequence(seq):
38 4fd029cf Michael Hanselmann
  """Returns a list with unique elements.
39 4fd029cf Michael Hanselmann

40 4fd029cf Michael Hanselmann
  Element order is preserved.
41 4fd029cf Michael Hanselmann

42 4fd029cf Michael Hanselmann
  @type seq: sequence
43 4fd029cf Michael Hanselmann
  @param seq: the sequence with the source elements
44 4fd029cf Michael Hanselmann
  @rtype: list
45 4fd029cf Michael Hanselmann
  @return: list of unique elements from seq
46 4fd029cf Michael Hanselmann

47 4fd029cf Michael Hanselmann
  """
48 4fd029cf Michael Hanselmann
  seen = set()
49 4fd029cf Michael Hanselmann
  return [i for i in seq if i not in seen and not seen.add(i)]
50 4fd029cf Michael Hanselmann
51 4fd029cf Michael Hanselmann
52 cdf71b12 Andrea Spadaccini
def JoinDisjointDicts(dict_a, dict_b):
53 cdf71b12 Andrea Spadaccini
  """Joins dictionaries with no conflicting keys.
54 cdf71b12 Andrea Spadaccini

55 cdf71b12 Andrea Spadaccini
  Enforces the constraint that the two key sets must be disjoint, and then
56 cdf71b12 Andrea Spadaccini
  merges the two dictionaries in a new dictionary that is returned to the
57 cdf71b12 Andrea Spadaccini
  caller.
58 cdf71b12 Andrea Spadaccini

59 cdf71b12 Andrea Spadaccini
  @type dict_a: dict
60 cdf71b12 Andrea Spadaccini
  @param dict_a: the first dictionary
61 cdf71b12 Andrea Spadaccini
  @type dict_b: dict
62 cdf71b12 Andrea Spadaccini
  @param dict_b: the second dictionary
63 cdf71b12 Andrea Spadaccini
  @rtype: dict
64 cdf71b12 Andrea Spadaccini
  @return: a new dictionary containing all the key/value pairs contained in the
65 cdf71b12 Andrea Spadaccini
  two dictionaries.
66 cdf71b12 Andrea Spadaccini

67 cdf71b12 Andrea Spadaccini
  """
68 cdf71b12 Andrea Spadaccini
  assert not (set(dict_a) & set(dict_b)), ("Duplicate keys found while joining"
69 cdf71b12 Andrea Spadaccini
                                           " %s and %s" % (dict_a, dict_b))
70 cdf71b12 Andrea Spadaccini
  result = dict_a.copy()
71 cdf71b12 Andrea Spadaccini
  result.update(dict_b)
72 cdf71b12 Andrea Spadaccini
  return result
73 cdf71b12 Andrea Spadaccini
74 cdf71b12 Andrea Spadaccini
75 4fd029cf Michael Hanselmann
def FindDuplicates(seq):
76 4fd029cf Michael Hanselmann
  """Identifies duplicates in a list.
77 4fd029cf Michael Hanselmann

78 4fd029cf Michael Hanselmann
  Does not preserve element order.
79 4fd029cf Michael Hanselmann

80 4fd029cf Michael Hanselmann
  @type seq: sequence
81 4fd029cf Michael Hanselmann
  @param seq: Sequence with source elements
82 4fd029cf Michael Hanselmann
  @rtype: list
83 4fd029cf Michael Hanselmann
  @return: List of duplicate elements from seq
84 4fd029cf Michael Hanselmann

85 4fd029cf Michael Hanselmann
  """
86 4fd029cf Michael Hanselmann
  dup = set()
87 4fd029cf Michael Hanselmann
  seen = set()
88 4fd029cf Michael Hanselmann
89 4fd029cf Michael Hanselmann
  for item in seq:
90 4fd029cf Michael Hanselmann
    if item in seen:
91 4fd029cf Michael Hanselmann
      dup.add(item)
92 4fd029cf Michael Hanselmann
    else:
93 4fd029cf Michael Hanselmann
      seen.add(item)
94 4fd029cf Michael Hanselmann
95 4fd029cf Michael Hanselmann
  return list(dup)
96 4fd029cf Michael Hanselmann
97 4fd029cf Michael Hanselmann
98 4fd029cf Michael Hanselmann
def _NiceSortTryInt(val):
99 4fd029cf Michael Hanselmann
  """Attempts to convert a string to an integer.
100 4fd029cf Michael Hanselmann

101 4fd029cf Michael Hanselmann
  """
102 dd27bc21 Michael Hanselmann
  if val and val.isdigit():
103 4fd029cf Michael Hanselmann
    return int(val)
104 4fd029cf Michael Hanselmann
  else:
105 4fd029cf Michael Hanselmann
    return val
106 4fd029cf Michael Hanselmann
107 4fd029cf Michael Hanselmann
108 7d4da09e Michael Hanselmann
def NiceSortKey(value):
109 4fd029cf Michael Hanselmann
  """Extract key for sorting.
110 4fd029cf Michael Hanselmann

111 4fd029cf Michael Hanselmann
  """
112 4fd029cf Michael Hanselmann
  return [_NiceSortTryInt(grp)
113 4fd029cf Michael Hanselmann
          for grp in _SORTER_RE.match(value).groups()]
114 4fd029cf Michael Hanselmann
115 4fd029cf Michael Hanselmann
116 4fd029cf Michael Hanselmann
def NiceSort(values, key=None):
117 4fd029cf Michael Hanselmann
  """Sort a list of strings based on digit and non-digit groupings.
118 4fd029cf Michael Hanselmann

119 4fd029cf Michael Hanselmann
  Given a list of names C{['a1', 'a10', 'a11', 'a2']} this function
120 4fd029cf Michael Hanselmann
  will sort the list in the logical order C{['a1', 'a2', 'a10',
121 4fd029cf Michael Hanselmann
  'a11']}.
122 4fd029cf Michael Hanselmann

123 4fd029cf Michael Hanselmann
  The sort algorithm breaks each name in groups of either only-digits
124 4fd029cf Michael Hanselmann
  or no-digits. Only the first eight such groups are considered, and
125 4fd029cf Michael Hanselmann
  after that we just use what's left of the string.
126 4fd029cf Michael Hanselmann

127 4fd029cf Michael Hanselmann
  @type values: list
128 4fd029cf Michael Hanselmann
  @param values: the names to be sorted
129 4fd029cf Michael Hanselmann
  @type key: callable or None
130 4fd029cf Michael Hanselmann
  @param key: function of one argument to extract a comparison key from each
131 4fd029cf Michael Hanselmann
    list element, must return string
132 4fd029cf Michael Hanselmann
  @rtype: list
133 4fd029cf Michael Hanselmann
  @return: a copy of the name list sorted with our algorithm
134 4fd029cf Michael Hanselmann

135 4fd029cf Michael Hanselmann
  """
136 4fd029cf Michael Hanselmann
  if key is None:
137 7d4da09e Michael Hanselmann
    keyfunc = NiceSortKey
138 4fd029cf Michael Hanselmann
  else:
139 7d4da09e Michael Hanselmann
    keyfunc = lambda value: NiceSortKey(key(value))
140 4fd029cf Michael Hanselmann
141 4fd029cf Michael Hanselmann
  return sorted(values, key=keyfunc)
142 7d444d59 Michael Hanselmann
143 7d444d59 Michael Hanselmann
144 0a9a0e5a René Nussbaumer
def InvertDict(dict_in):
145 0a9a0e5a René Nussbaumer
  """Inverts the key/value mapping of a dict.
146 0a9a0e5a René Nussbaumer

147 0a9a0e5a René Nussbaumer
  @param dict_in: The dict to invert
148 d5fca545 Iustin Pop
  @return: the inverted dict
149 0a9a0e5a René Nussbaumer

150 0a9a0e5a René Nussbaumer
  """
151 0a9a0e5a René Nussbaumer
  return dict(zip(dict_in.values(), dict_in.keys()))
152 0a9a0e5a René Nussbaumer
153 0a9a0e5a René Nussbaumer
154 d60946d9 René Nussbaumer
def InsertAtPos(src, pos, other):
155 d60946d9 René Nussbaumer
  """Inserts C{other} at given C{pos} into C{src}.
156 d60946d9 René Nussbaumer

157 d60946d9 René Nussbaumer
  @note: This function does not modify C{src} in place but returns a new copy
158 d60946d9 René Nussbaumer

159 d60946d9 René Nussbaumer
  @type src: list
160 d60946d9 René Nussbaumer
  @param src: The source list in which we want insert elements
161 d60946d9 René Nussbaumer
  @type pos: int
162 d60946d9 René Nussbaumer
  @param pos: The position where we want to start insert C{other}
163 d60946d9 René Nussbaumer
  @type other: list
164 d60946d9 René Nussbaumer
  @param other: The other list to insert into C{src}
165 d60946d9 René Nussbaumer
  @return: A copy of C{src} with C{other} inserted at C{pos}
166 d60946d9 René Nussbaumer

167 d60946d9 René Nussbaumer
  """
168 d60946d9 René Nussbaumer
  new = src[:pos]
169 d60946d9 René Nussbaumer
  new.extend(other)
170 d60946d9 René Nussbaumer
  new.extend(src[pos:])
171 d60946d9 René Nussbaumer
172 d60946d9 René Nussbaumer
  return new
173 d60946d9 René Nussbaumer
174 d60946d9 René Nussbaumer
175 6d0accae Michael Hanselmann
def SequenceToDict(seq, key=compat.fst):
176 6d0accae Michael Hanselmann
  """Converts a sequence to a dictionary with duplicate detection.
177 6d0accae Michael Hanselmann

178 6d0accae Michael Hanselmann
  @type seq: sequen
179 6d0accae Michael Hanselmann
  @param seq: Input sequence
180 6d0accae Michael Hanselmann
  @type key: callable
181 6d0accae Michael Hanselmann
  @param key: Function for retrieving dictionary key from sequence element
182 6d0accae Michael Hanselmann
  @rtype: dict
183 6d0accae Michael Hanselmann

184 6d0accae Michael Hanselmann
  """
185 6d0accae Michael Hanselmann
  keys = map(key, seq)
186 6d0accae Michael Hanselmann
187 6d0accae Michael Hanselmann
  duplicates = FindDuplicates(keys)
188 6d0accae Michael Hanselmann
  if duplicates:
189 6d0accae Michael Hanselmann
    raise ValueError("Duplicate keys found: %s" % text.CommaJoin(duplicates))
190 6d0accae Michael Hanselmann
191 6d0accae Michael Hanselmann
  assert len(keys) == len(seq)
192 6d0accae Michael Hanselmann
193 6d0accae Michael Hanselmann
  return dict(zip(keys, seq))
194 6d0accae Michael Hanselmann
195 6d0accae Michael Hanselmann
196 9c709b31 René Nussbaumer
def _MakeFlatToDict(data):
197 9c709b31 René Nussbaumer
  """Helper function for C{FlatToDict}.
198 9c709b31 René Nussbaumer

199 9c709b31 René Nussbaumer
  This function is recursively called
200 9c709b31 René Nussbaumer

201 9c709b31 René Nussbaumer
  @param data: The input data as described in C{FlatToDict}, already splitted
202 9c709b31 René Nussbaumer
  @returns: The so far converted dict
203 9c709b31 René Nussbaumer

204 9c709b31 René Nussbaumer
  """
205 9c709b31 René Nussbaumer
  if not compat.fst(compat.fst(data)):
206 9c709b31 René Nussbaumer
    assert len(data) == 1, \
207 9c709b31 René Nussbaumer
      "not bottom most element, found %d elements, expected 1" % len(data)
208 9c709b31 René Nussbaumer
    return compat.snd(compat.fst(data))
209 9c709b31 René Nussbaumer
210 9c709b31 René Nussbaumer
  keyfn = lambda e: compat.fst(e).pop(0)
211 9c709b31 René Nussbaumer
  return dict([(k, _MakeFlatToDict(list(g)))
212 9c709b31 René Nussbaumer
               for (k, g) in itertools.groupby(sorted(data), keyfn)])
213 9c709b31 René Nussbaumer
214 9c709b31 René Nussbaumer
215 9c709b31 René Nussbaumer
def FlatToDict(data, field_sep="/"):
216 9c709b31 René Nussbaumer
  """Converts a flat structure to a fully fledged dict.
217 9c709b31 René Nussbaumer

218 9c709b31 René Nussbaumer
  It accept a list of tuples in the form::
219 9c709b31 René Nussbaumer

220 9c709b31 René Nussbaumer
    [
221 9c709b31 René Nussbaumer
      ("foo/bar", {"key1": "data1", "key2": "data2"}),
222 9c709b31 René Nussbaumer
      ("foo/baz", {"key3" :"data3" }),
223 9c709b31 René Nussbaumer
    ]
224 9c709b31 René Nussbaumer

225 9c709b31 René Nussbaumer
  where the first element is the key separated by C{field_sep}.
226 9c709b31 René Nussbaumer

227 9c709b31 René Nussbaumer
  This would then return::
228 9c709b31 René Nussbaumer

229 9c709b31 René Nussbaumer
    {
230 9c709b31 René Nussbaumer
      "foo": {
231 9c709b31 René Nussbaumer
        "bar": {"key1": "data1", "key2": "data2"},
232 9c709b31 René Nussbaumer
        "baz": {"key3" :"data3" },
233 9c709b31 René Nussbaumer
        },
234 9c709b31 René Nussbaumer
    }
235 9c709b31 René Nussbaumer

236 9c709b31 René Nussbaumer
  @type data: list of tuple
237 9c709b31 René Nussbaumer
  @param data: Input list to convert
238 9c709b31 René Nussbaumer
  @type field_sep: str
239 9c709b31 René Nussbaumer
  @param field_sep: The separator for the first field of the tuple
240 9c709b31 René Nussbaumer
  @returns: A dict based on the input list
241 9c709b31 René Nussbaumer

242 9c709b31 René Nussbaumer
  """
243 9c709b31 René Nussbaumer
  return _MakeFlatToDict([(keys.split(field_sep), value)
244 9c709b31 René Nussbaumer
                          for (keys, value) in data])
245 9c709b31 René Nussbaumer
246 9c709b31 René Nussbaumer
247 7d444d59 Michael Hanselmann
class RunningTimeout(object):
248 7d444d59 Michael Hanselmann
  """Class to calculate remaining timeout when doing several operations.
249 7d444d59 Michael Hanselmann

250 7d444d59 Michael Hanselmann
  """
251 7d444d59 Michael Hanselmann
  __slots__ = [
252 7d444d59 Michael Hanselmann
    "_allow_negative",
253 7d444d59 Michael Hanselmann
    "_start_time",
254 7d444d59 Michael Hanselmann
    "_time_fn",
255 7d444d59 Michael Hanselmann
    "_timeout",
256 7d444d59 Michael Hanselmann
    ]
257 7d444d59 Michael Hanselmann
258 7d444d59 Michael Hanselmann
  def __init__(self, timeout, allow_negative, _time_fn=time.time):
259 7d444d59 Michael Hanselmann
    """Initializes this class.
260 7d444d59 Michael Hanselmann

261 7d444d59 Michael Hanselmann
    @type timeout: float
262 7d444d59 Michael Hanselmann
    @param timeout: Timeout duration
263 7d444d59 Michael Hanselmann
    @type allow_negative: bool
264 7d444d59 Michael Hanselmann
    @param allow_negative: Whether to return values below zero
265 7d444d59 Michael Hanselmann
    @param _time_fn: Time function for unittests
266 7d444d59 Michael Hanselmann

267 7d444d59 Michael Hanselmann
    """
268 7d444d59 Michael Hanselmann
    object.__init__(self)
269 7d444d59 Michael Hanselmann
270 7d444d59 Michael Hanselmann
    if timeout is not None and timeout < 0.0:
271 7d444d59 Michael Hanselmann
      raise ValueError("Timeout must not be negative")
272 7d444d59 Michael Hanselmann
273 7d444d59 Michael Hanselmann
    self._timeout = timeout
274 7d444d59 Michael Hanselmann
    self._allow_negative = allow_negative
275 7d444d59 Michael Hanselmann
    self._time_fn = _time_fn
276 7d444d59 Michael Hanselmann
277 7d444d59 Michael Hanselmann
    self._start_time = None
278 7d444d59 Michael Hanselmann
279 7d444d59 Michael Hanselmann
  def Remaining(self):
280 7d444d59 Michael Hanselmann
    """Returns the remaining timeout.
281 7d444d59 Michael Hanselmann

282 7d444d59 Michael Hanselmann
    """
283 7d444d59 Michael Hanselmann
    if self._timeout is None:
284 7d444d59 Michael Hanselmann
      return None
285 7d444d59 Michael Hanselmann
286 7d444d59 Michael Hanselmann
    # Get start time on first calculation
287 7d444d59 Michael Hanselmann
    if self._start_time is None:
288 7d444d59 Michael Hanselmann
      self._start_time = self._time_fn()
289 7d444d59 Michael Hanselmann
290 7d444d59 Michael Hanselmann
    # Calculate remaining time
291 7d444d59 Michael Hanselmann
    remaining_timeout = self._start_time + self._timeout - self._time_fn()
292 7d444d59 Michael Hanselmann
293 7d444d59 Michael Hanselmann
    if not self._allow_negative:
294 7d444d59 Michael Hanselmann
      # Ensure timeout is always >= 0
295 7d444d59 Michael Hanselmann
      return max(0.0, remaining_timeout)
296 7d444d59 Michael Hanselmann
297 7d444d59 Michael Hanselmann
    return remaining_timeout