summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHarkirat Singh <reachharkirat@gmail.com>2015-05-20 08:55:31 +0530
committerHarkirat Singh <reachharkirat@gmail.com>2015-05-20 08:55:31 +0530
commit4a3a8712a33d8b63b32d1a0d80beb03abb61653c (patch)
tree28531a28b2b55ab589c282c5c89e7ac2af3d097e
parent4a95874e49afd354f71dac69d86d4d4b5f6d152e (diff)
Add MultiDict class implementation
Notes
Notes (review): Verified+2: Jenkins Code-Review+2: Karthik Natarajan <natarajk@brocade.com> Workflow+1: Karthik Natarajan <natarajk@brocade.com> Code-Review+1: Sripriya Seetharam <sseetha@brocade.com> Submitted-by: Jenkins Submitted-at: Fri, 22 May 2015 06:09:16 +0000 Reviewed-on: https://review.openstack.org/184408 Project: stackforge/networking-brocade Branch: refs/heads/master
-rw-r--r--networking_brocade/vyatta/common/utils.py73
-rw-r--r--networking_brocade/vyatta/common/vrouter_config.py5
-rw-r--r--networking_brocade/vyatta/tests/test_utils.py98
3 files changed, 174 insertions, 2 deletions
diff --git a/networking_brocade/vyatta/common/utils.py b/networking_brocade/vyatta/common/utils.py
index 8b86e93..a12fe73 100644
--- a/networking_brocade/vyatta/common/utils.py
+++ b/networking_brocade/vyatta/common/utils.py
@@ -14,7 +14,9 @@
14# under the License. 14# under the License.
15 15
16import collections 16import collections
17
17from eventlet import greenthread 18from eventlet import greenthread
19import six
18 20
19 21
20RouteRule = collections.namedtuple('RouteRule', 'dest_cidr, next_hop') 22RouteRule = collections.namedtuple('RouteRule', 'dest_cidr, next_hop')
@@ -34,3 +36,74 @@ def retry(fn, args=None, kwargs=None, exceptions=None, limit=1, delay=0):
34 greenthread.sleep(delay) 36 greenthread.sleep(delay)
35 limit -= 1 37 limit -= 1
36 raise 38 raise
39
40
41class MultiDict(collections.MutableMapping):
42
43 def __init__(self, mapping=None):
44 self._items = {}
45
46 if isinstance(mapping, MultiDict):
47 for key, value in mapping.lists():
48 self._items[key] = value[:]
49 elif isinstance(mapping, dict):
50 for key, value in six.iteritems(mapping):
51 self._items[key] = [value]
52 elif mapping is not None:
53 for key, value in mapping:
54 self._items.setdefault(key, []).append(value)
55
56 def __getitem__(self, key):
57 return self._items[key][0]
58
59 def __setitem__(self, key, value):
60 self._items[key] = [value]
61
62 def __delitem__(self, key):
63 del self._items[key]
64
65 def __len__(self):
66 return len(self._items)
67
68 def __iter__(self):
69 return six.iterkeys(self._items)
70
71 def __repr__(self):
72 items = []
73 for key, lst in six.iteritems(self._items):
74 for item in lst:
75 items.append((key, item))
76 return 'MultiDict {0!r}'.format(items)
77
78 def add(self, key, value):
79 self._items.setdefault(key, []).append(value)
80
81 def getlist(self, key, default=None):
82 try:
83 return self._items[key]
84 except KeyError:
85 return default or []
86
87 def setlist(self, key, value):
88 self._items[key] = list(value)
89
90 def setlistdefault(self, key, default_list=None):
91 if key in self._items:
92 default_list = self._items[key]
93 else:
94 if default_list is None:
95 default_list = []
96 else:
97 default_list = list(default_list)
98 self._items[key] = default_list
99
100 return default_list
101
102 def copy(self):
103 return self.__class__(self)
104
105 def lists(self):
106 return six.iteritems(self._items)
107
108 def listvalues(self):
109 return six.itervalues(self._items)
diff --git a/networking_brocade/vyatta/common/vrouter_config.py b/networking_brocade/vyatta/common/vrouter_config.py
index 85293c0..4963df6 100644
--- a/networking_brocade/vyatta/common/vrouter_config.py
+++ b/networking_brocade/vyatta/common/vrouter_config.py
@@ -13,6 +13,7 @@
13# License for the specific language governing permissions and limitations 13# License for the specific language governing permissions and limitations
14# under the License. 14# under the License.
15 15
16from networking_brocade.vyatta.common import utils
16 17
17TOKEN_GROUP = 'group' 18TOKEN_GROUP = 'group'
18TOKEN_PARAM = 'param' 19TOKEN_PARAM = 'param'
@@ -45,13 +46,13 @@ def config_iter(config):
45 46
46 47
47def parse_group(lines): 48def parse_group(lines):
48 result = {} 49 result = utils.MultiDict()
49 50
50 for line in lines: 51 for line in lines:
51 token, key, value = parse_line(line) 52 token, key, value = parse_line(line)
52 53
53 if token == TOKEN_PARAM: 54 if token == TOKEN_PARAM:
54 result[key] = value 55 result.setlistdefault(key).append(value)
55 elif token == TOKEN_GROUP: 56 elif token == TOKEN_GROUP:
56 result[key] = parse_group(lines) 57 result[key] = parse_group(lines)
57 else: 58 else:
diff --git a/networking_brocade/vyatta/tests/test_utils.py b/networking_brocade/vyatta/tests/test_utils.py
new file mode 100644
index 0000000..b13af9f
--- /dev/null
+++ b/networking_brocade/vyatta/tests/test_utils.py
@@ -0,0 +1,98 @@
1# Copyright 2015 Brocade Communications System, Inc.
2# All Rights Reserved.
3#
4# Licensed under the Apache License, Version 2.0 (the "License"); you may
5# not use this file except in compliance with the License. You may obtain
6# a copy of the License at
7#
8# http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13# License for the specific language governing permissions and limitations
14# under the License.
15
16import collections
17
18import testtools
19
20from networking_brocade.vyatta.common import utils
21
22
23class TestMultiDict(testtools.TestCase):
24
25 def setUp(self):
26 super(TestMultiDict, self).setUp()
27 # Create from list of tuples
28 mapping = [('a', 1), ('b', 2), ('a', 2), ('d', 3),
29 ('a', 1), ('a', 3), ('d', 4), ('c', 3)]
30 self.md = utils.MultiDict(mapping)
31
32 def test_init(self):
33 md = utils.MultiDict()
34 self.assertIsInstance(md, collections.MutableMapping)
35
36 # Create from dict
37 mapping = {'a': 1, 'b': 2, 'c': 3}
38 md = utils.MultiDict(mapping)
39 self.assertEqual(md['a'], 1)
40 self.assertEqual(md.getlist('a'), [1])
41
42 def test_getitem(self):
43 # __getitem__
44 self.assertEqual(self.md['a'], 1)
45 self.assertEqual(self.md['c'], 3)
46 with testtools.ExpectedException(KeyError):
47 self.md['e']
48
49 # get
50 self.assertEqual(self.md.get('a'), 1)
51 self.assertEqual(self.md.get('e'), None)
52
53 # getlist
54 self.assertEqual(self.md.getlist('a'), [1, 2, 1, 3])
55 self.assertEqual(self.md.getlist('d'), [3, 4])
56 self.assertEqual(self.md.getlist('x'), [])
57
58 def test_setitem(self):
59 # __setitem__
60 self.md['a'] = 42
61 self.assertEqual(self.md['a'], 42)
62 self.assertEqual(self.md.getlist('a'), [42])
63
64 # setlist
65 self.md.setlist('a', [1, 2, 3])
66 self.assertEqual(self.md['a'], 1)
67 self.assertEqual(self.md.getlist('a'), [1, 2, 3])
68
69 # check that setlist does not affects initial list
70 lst = [1, 2, 3]
71 self.md.setlist('a', lst)
72 self.md.add('a', 42)
73 self.assertEqual(lst, [1, 2, 3])
74
75 # setdefault
76 # TODO(asaprykin): Check setdefault without arg
77 self.assertEqual(self.md.setdefault('u', 32), 32)
78 self.assertEqual(self.md.getlist('u'), [32])
79
80 def test_delitem(self):
81 # __delitem__
82 del self.md['a']
83 self.assertNotIn('a', self.md)
84 with testtools.ExpectedException(KeyError):
85 del self.md['x']
86
87 def test_setlistdefault(self):
88
89 self.assertEqual(self.md.setlistdefault('a'), [1, 2, 1, 3])
90 self.assertEqual(self.md.setlistdefault('d'), [3, 4])
91
92 self.assertEqual(self.md.setlistdefault('u1'), [])
93 self.assertEqual(self.md.setlistdefault('u3', [42]), [42])
94
95 with testtools.ExpectedException(TypeError):
96 self.md.setlistdefault('u2', False)
97 with testtools.ExpectedException(TypeError):
98 self.md.setlistdefault('u2', 32)