Revision 9c709b31

b/lib/utils/algo.py
24 24

  
25 25
import re
26 26
import time
27
import itertools
27 28

  
28 29
from ganeti import compat
29 30
from ganeti.utils import text
......
193 194
  return dict(zip(keys, seq))
194 195

  
195 196

  
197
def _MakeFlatToDict(data):
198
  """Helper function for C{FlatToDict}.
199

  
200
  This function is recursively called
201

  
202
  @param data: The input data as described in C{FlatToDict}, already splitted
203
  @returns: The so far converted dict
204

  
205
  """
206
  if not compat.fst(compat.fst(data)):
207
    assert len(data) == 1, \
208
      "not bottom most element, found %d elements, expected 1" % len(data)
209
    return compat.snd(compat.fst(data))
210

  
211
  keyfn = lambda e: compat.fst(e).pop(0)
212
  return dict([(k, _MakeFlatToDict(list(g)))
213
               for (k, g) in itertools.groupby(sorted(data), keyfn)])
214

  
215

  
216
def FlatToDict(data, field_sep="/"):
217
  """Converts a flat structure to a fully fledged dict.
218

  
219
  It accept a list of tuples in the form::
220

  
221
    [
222
      ("foo/bar", {"key1": "data1", "key2": "data2"}),
223
      ("foo/baz", {"key3" :"data3" }),
224
    ]
225

  
226
  where the first element is the key separated by C{field_sep}.
227

  
228
  This would then return::
229

  
230
    {
231
      "foo": {
232
        "bar": {"key1": "data1", "key2": "data2"},
233
        "baz": {"key3" :"data3" },
234
        },
235
    }
236

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

  
243
  """
244
  return _MakeFlatToDict([(keys.split(field_sep), value)
245
                          for (keys, value) in data])
246

  
247

  
196 248
class RunningTimeout(object):
197 249
  """Class to calculate remaining timeout when doing several operations.
198 250

  
b/test/ganeti.utils.algo_unittest.py
339 339
                      [(i, ) for i in range(200)] + [(10, )])
340 340

  
341 341

  
342
class TestFlatToDict(unittest.TestCase):
343
  def testNormal(self):
344
    data = [
345
      ("lv/xenvg", {"foo": "bar", "bar": "baz"}),
346
      ("lv/xenfoo", {"foo": "bar", "baz": "blubb"}),
347
      ("san/foo", {"ip": "127.0.0.1", "port": 1337}),
348
      ("san/blubb/blibb", 54),
349
      ]
350
    reference = {
351
      "lv": {
352
        "xenvg": {"foo": "bar", "bar": "baz"},
353
        "xenfoo": {"foo": "bar", "baz": "blubb"},
354
        },
355
      "san": {
356
        "foo": {"ip": "127.0.0.1", "port": 1337},
357
        "blubb": {"blibb": 54},
358
        },
359
      }
360
    self.assertEqual(algo.FlatToDict(data), reference)
361

  
362
  def testUnlikeDepth(self):
363
    data = [
364
      ("san/foo", {"ip": "127.0.0.1", "port": 1337}),
365
      ("san/foo/blubb", 23), # Another foo entry under san
366
      ("san/blubb/blibb", 54),
367
      ]
368
    self.assertRaises(AssertionError, algo.FlatToDict, data)
369

  
370

  
342 371
if __name__ == "__main__":
343 372
  testutils.GanetiTestProgram()

Also available in: Unified diff