Merge lp:~bjornt/charms/trusty/block-storage-broker/multiple-units into lp:~charmers/charms/trusty/block-storage-broker/trunk

Proposed by Björn Tillenius
Status: Merged
Merged at revision: 57
Proposed branch: lp:~bjornt/charms/trusty/block-storage-broker/multiple-units
Merge into: lp:~charmers/charms/trusty/block-storage-broker/trunk
Diff against target: 168 lines (+63/-10)
3 files modified
hooks/hooks.py (+16/-0)
hooks/test_hooks.py (+38/-7)
hooks/testing.py (+9/-3)
To merge this branch: bzr merge lp:~bjornt/charms/trusty/block-storage-broker/multiple-units
Reviewer Review Type Date Requested Status
Review Queue (community) automated testing Needs Fixing
Chris Glass (community) Approve
Review via email: mp+283319@code.launchpad.net

Description of the change

Allow the block-storage-broker work with multiple storage units without race conditions.

Currently, a single block-device-path relation setting is used for all storage units. That means that if you add one unit at a time. But in practice, I always get an error when I try to add a second storage unit, since it tries to mount the device before it has been attached.

This branch adds another relation setting, block-device-path-map, which is a mapping of instance id to device path. That way the storage charm can look at that and only try to mount the device when its instance id is in that map.

This change is backwards-compatible so the old storage charm will work in the same way as it works now. I modified the storage charm to look for the new setting here: lp:~bjornt/charms/precise/storage/multiple-units/

To post a comment you must log in.
Revision history for this message
Chris Glass (tribaal) wrote :

Looks good! +1

review: Approve
Revision history for this message
Review Queue (review-queue) wrote :

This item has failed automated testing! Results available here http://juju-ci.vapour.ws:8080/job/charm-bundle-test-lxc/2278/

review: Needs Fixing (automated testing)
Revision history for this message
Review Queue (review-queue) wrote :

This item has failed automated testing! Results available here http://juju-ci.vapour.ws:8080/job/charm-bundle-test-aws/2257/

review: Needs Fixing (automated testing)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== added symlink 'hooks/block-storage-relation-joined'
2=== target is u'hooks.py'
3=== modified file 'hooks/hooks.py'
4--- hooks/hooks.py 2014-08-22 07:52:20 +0000
5+++ hooks/hooks.py 2016-01-20 15:57:09 +0000
6@@ -113,6 +113,16 @@
7 storage_util.detach_volume(volume_label)
8
9
10+@hooks.hook("block-storage-relation-joined")
11+def block_storage_relation_joined():
12+ # Initialize the block-device-path-map, so that units can expect it
13+ # to be a dict.
14+ if hookenv.relation_get(
15+ "block-device-path-map", unit=hookenv.local_unit()) is None:
16+ hookenv.relation_set(
17+ relation_settings={"block-device-path-map": json.dumps({})})
18+
19+
20 @hooks.hook("block-storage-relation-changed")
21 def block_storage_relation_changed():
22 """Attach a volume to the C{instance-id} requested by the relation
23@@ -140,6 +150,12 @@
24
25 # Volume is attached, send the path back to the remote storage unit
26 hookenv.relation_set(relation_settings={"block-device-path": device_path})
27+ device_map_json = hookenv.relation_get(
28+ "block-device-path-map", unit=hookenv.local_unit())
29+ device_map = json.loads(device_map_json) if device_map_json else {}
30+ device_map[instance_id] = device_path
31+ hookenv.relation_set(
32+ relation_settings={"block-device-path-map": json.dumps(device_map)})
33
34
35 if __name__ == '__main__':
36
37=== modified file 'hooks/test_hooks.py'
38--- hooks/test_hooks.py 2014-09-09 16:24:46 +0000
39+++ hooks/test_hooks.py 2016-01-20 15:57:09 +0000
40@@ -247,6 +247,31 @@
41
42 hooks.install(apt_install=apt_install)
43
44+ def test_block_storage_relation_joined_init_block_device_map(self):
45+ """
46+ When a unit joins the block-storage relation,
47+ block-device-path-map is initialized if it's not already there.
48+ """
49+ hooks.hookenv._outgoing_relation_data = ()
50+
51+ hooks.block_storage_relation_joined()
52+ self.assertEqual(
53+ hooks.hookenv._outgoing_relation_data,
54+ (("block-device-path-map", "{}"),))
55+
56+ def test_block_storage_relation_joined_with_block_device_map(self):
57+ """
58+ When a unit joins the block-storage relation,
59+ block-device-path-map isn't touched if it's already defined.
60+ """
61+ hooks.hookenv._outgoing_relation_data = (
62+ ("block-device-path-map", "something"),)
63+
64+ hooks.block_storage_relation_joined()
65+ self.assertEqual(
66+ hooks.hookenv._outgoing_relation_data,
67+ (("block-device-path-map", "something"),))
68+
69 def test_block_storage_relation_changed_waits_without_instance_id(self):
70 """
71 L{block_storage_relation_changed} will log a message and wait when
72@@ -268,7 +293,8 @@
73 L{block_storage_relation_changed} calls L{attach_volume} when
74 C{instance-id} is available in the relation data. To report
75 a successful device attach, it sets the relation data
76- C{block-device-path} to the attached volume's device path from nova.
77+ C{block-device-path} and C{block-device-path-map} to the
78+ attached volume's device path from nova.
79 """
80 device_path = "/dev/vdc"
81 self.addCleanup(setattr, os, "environ", os.environ)
82@@ -293,14 +319,16 @@
83 hooks.block_storage_relation_changed()
84 self.assertEqual(
85 hooks.hookenv._outgoing_relation_data,
86- (("block-device-path", device_path),))
87+ (("block-device-path", device_path),
88+ ("block-device-path-map", '{"i-123": "%s"}' % device_path)))
89
90 def test_block_storage_relation_changed_with_instance_id_volume_id(self):
91 """
92 When C{volume-id} and C{instance-id} are both present in the relation
93 data, they will both be passed to L{attach_volume}. To report a
94 successful device attach, it sets the relation data
95- C{block-device-path} to the attached volume's device path from nova.
96+ C{block-device-path} and C{block-device-path-map} to the
97+ attached volume's device path from nova.
98 """
99 device_path = "/dev/vdc"
100 volume_id = "123-123-123"
101@@ -328,14 +356,16 @@
102 hooks.block_storage_relation_changed()
103 self.assertEqual(
104 hooks.hookenv._outgoing_relation_data,
105- (("block-device-path", device_path),))
106+ (("block-device-path", device_path),
107+ ("block-device-path-map", '{"i-123": "%s"}' % device_path)))
108
109 def test_block_storage_relation_changed_with_instance_id_size(self):
110 """
111 When C{size} and C{instance-id} are both present in the relation data,
112 they will be passed to L{attach_volume}. To report a successful
113- device attach, it sets the relation data C{block-device-path} to the
114- attached volume's device path from nova.
115+ device attach, it sets the relation data C{block-device-path}
116+ and C{block-device-path-map} to the attached volume's device
117+ path from nova.
118 """
119 device_path = "/dev/vdc"
120 size = 12
121@@ -361,7 +391,8 @@
122 hooks.block_storage_relation_changed()
123 self.assertEqual(
124 hooks.hookenv._outgoing_relation_data,
125- (("block-device-path", device_path),))
126+ (("block-device-path", device_path),
127+ ("block-device-path-map", '{"i-123": "%s"}' % device_path)))
128
129 def test_block_storage_relation_departed_fails_without_instance_id(self):
130 """
131
132=== modified file 'hooks/testing.py'
133--- hooks/testing.py 2014-02-11 22:07:32 +0000
134+++ hooks/testing.py 2016-01-20 15:57:09 +0000
135@@ -17,6 +17,7 @@
136 _outgoing_relation_data = ()
137 _relation_list = ()
138 _config = ()
139+ _local_unit = "local-unit/0"
140
141 DEBUG = "DEBUG"
142 INFO = "INFO"
143@@ -68,7 +69,7 @@
144 return "localhost"
145
146 def local_unit(self):
147- return os.environ["JUJU_UNIT_NAME"]
148+ return self._local_unit
149
150 def remote_unit(self):
151 return os.environ["JUJU_REMOTE_UNIT"]
152@@ -96,9 +97,14 @@
153 return None
154 return dict((key, value) for key, value in self._config)
155
156- def relation_get(self, scope=None, unit_name=None, relation_id=None):
157+ def relation_get(self, scope=None, unit=None, relation_id=None):
158+ if unit == self._local_unit:
159+ relation_data = self._outgoing_relation_data
160+ else:
161+ relation_data = self._incoming_relation_data
162+
163 if scope:
164- for (key, value) in self._incoming_relation_data:
165+ for (key, value) in relation_data:
166 if key == scope:
167 return value
168 return None

Subscribers

People subscribed via source and target branches

to all changes: