Merge lp:~gandelman-a/charm-helpers/openstack_storage_utils into lp:charm-helpers

Proposed by Adam Gandelman
Status: Merged
Merged at revision: 89
Proposed branch: lp:~gandelman-a/charm-helpers/openstack_storage_utils
Merge into: lp:charm-helpers
Diff against target: 217 lines (+153/-9)
2 files modified
charmhelpers/contrib/openstack/utils.py (+72/-9)
tests/contrib/openstack/test_openstack_utils.py (+81/-0)
To merge this branch: bzr merge lp:~gandelman-a/charm-helpers/openstack_storage_utils
Reviewer Review Type Date Requested Status
Adam Gandelman (community) Needs Resubmitting
James Page Needs Fixing
Review via email: mp+175972@code.launchpad.net

Commit message

contrib.openstack.utils: Add ensure_block_device() + clean_storage().

Description of the change

Adds a couple of storage helpers to be shared among cinder + swift, and possibly others. They rely on the more generic helpers in contrib.openstack.storage, but help Openstack charms setup storage in an way expected way.

To post a comment you must log in.
Revision history for this message
James Page (james-page) wrote :

Please can you rebase on trunk; generates a few conflicts now.

review: Needs Fixing
61. By Adam Gandelman

Rebase on trunk

Revision history for this message
Adam Gandelman (gandelman-a) wrote :

Rebased.

review: Needs Resubmitting

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'charmhelpers/contrib/openstack/utils.py'
2--- charmhelpers/contrib/openstack/utils.py 2013-10-11 13:38:29 +0000
3+++ charmhelpers/contrib/openstack/utils.py 2013-10-20 20:20:48 +0000
4@@ -13,15 +13,20 @@
5 config,
6 log as juju_log,
7 charm_dir,
8-)
9-
10-from charmhelpers.core.host import (
11- lsb_release,
12-)
13-
14-from charmhelpers.fetch import (
15- apt_install,
16-)
17+ ERROR,
18+ INFO
19+)
20+
21+from charmhelpers.contrib.storage.linux.lvm import (
22+ deactivate_lvm_volume_group,
23+ is_lvm_physical_volume,
24+ remove_lvm_physical_volume,
25+)
26+
27+from charmhelpers.core.host import lsb_release, mounts, umount
28+from charmhelpers.fetch import apt_install
29+from charmhelpers.contrib.storage.linux.utils import is_block_device, zap_disk
30+from charmhelpers.contrib.storage.linux.loopback import ensure_loopback_device
31
32 CLOUD_ARCHIVE_URL = "http://ubuntu-cloud.archive.canonical.com/ubuntu"
33 CLOUD_ARCHIVE_KEY_ID = '5EDB1B62EC4926EA'
34@@ -57,6 +62,8 @@
35 ('1.9.0', 'havana'),
36 ])
37
38+DEFAULT_LOOPBACK_SIZE = '5G'
39+
40
41 def error_out(msg):
42 juju_log("FATAL ERROR: %s" % msg, level='ERROR')
43@@ -299,6 +306,62 @@
44 return apt.version_compare(available_vers, cur_vers) == 1
45
46
47+def ensure_block_device(block_device):
48+ '''
49+ Confirm block_device, create as loopback if necessary.
50+
51+ :param block_device: str: Full path of block device to ensure.
52+
53+ :returns: str: Full path of ensured block device.
54+ '''
55+ _none = ['None', 'none', None]
56+ if (block_device in _none):
57+ error_out('prepare_storage(): Missing required input: '
58+ 'block_device=%s.' % block_device, level=ERROR)
59+
60+ if block_device.startswith('/dev/'):
61+ bdev = block_device
62+ elif block_device.startswith('/'):
63+ _bd = block_device.split('|')
64+ if len(_bd) == 2:
65+ bdev, size = _bd
66+ else:
67+ bdev = block_device
68+ size = DEFAULT_LOOPBACK_SIZE
69+ bdev = ensure_loopback_device(bdev, size)
70+ else:
71+ bdev = '/dev/%s' % block_device
72+
73+ if not is_block_device(bdev):
74+ error_out('Failed to locate valid block device at %s' % bdev,
75+ level=ERROR)
76+
77+ return bdev
78+
79+
80+def clean_storage(block_device):
81+ '''
82+ Ensures a block device is clean. That is:
83+ - unmounted
84+ - any lvm volume groups are deactivated
85+ - any lvm physical device signatures removed
86+ - partition table wiped
87+
88+ :param block_device: str: Full path to block device to clean.
89+ '''
90+ for mp, d in mounts():
91+ if d == block_device:
92+ juju_log('clean_storage(): %s is mounted @ %s, unmounting.' %
93+ (d, mp), level=INFO)
94+ umount(mp, persist=True)
95+
96+ if is_lvm_physical_volume(block_device):
97+ deactivate_lvm_volume_group(block_device)
98+ remove_lvm_physical_volume(block_device)
99+ else:
100+ zap_disk(block_device)
101+
102+
103 def is_ip(address):
104 """
105 Returns True if address is a valid IP address.
106
107=== modified file 'tests/contrib/openstack/test_openstack_utils.py'
108--- tests/contrib/openstack/test_openstack_utils.py 2013-10-11 13:38:29 +0000
109+++ tests/contrib/openstack/test_openstack_utils.py 2013-10-20 20:20:48 +0000
110@@ -61,6 +61,9 @@
111 }
112 }
113
114+MOUNTS = [
115+ ['/mnt', '/dev/vdb']
116+]
117
118 url = 'deb ' + openstack.CLOUD_ARCHIVE_URL
119 UCA_SOURCES = [
120@@ -431,6 +434,8 @@
121 _exists.return_value = False
122 os.environ['JUJU_UNIT_NAME'] = 'testing-foo/0'
123 openstack.save_script_rc(setting1='foo', setting2='bar')
124+ rcdir = '/var/lib/juju/units/testing-foo-0/charm/scripts'
125+ _mkdir.assert_called_with(rcdir)
126 expected_f = '/var/lib/juju/units/testing-foo-0/charm/scripts/scriptrc'
127 _open.assert_called_with(expected_f, 'wb')
128 _mkdir.assert_called_with(os.path.dirname(expected_f))
129@@ -463,6 +468,81 @@
130 vers_pkg.return_value = '2013.1~b1'
131 self.assertFalse(openstack.openstack_upgrade_available('nova-common'))
132
133+ @patch.object(openstack, 'is_block_device')
134+ @patch.object(openstack, 'error_out')
135+ def test_ensure_block_device_bad_config(self, err, is_bd):
136+ '''Test it doesn't prepare storage with bad config'''
137+ openstack.ensure_block_device(block_device='none')
138+ self.assertTrue(err.called)
139+
140+ @patch.object(openstack, 'is_block_device')
141+ @patch.object(openstack, 'ensure_loopback_device')
142+ def test_ensure_block_device_loopback(self, ensure_loopback, is_bd):
143+ '''Test it ensures loopback device when checking block device'''
144+ defsize = openstack.DEFAULT_LOOPBACK_SIZE
145+ is_bd.return_value = True
146+
147+ ensure_loopback.return_value = '/tmp/cinder.img'
148+ result = openstack.ensure_block_device('/tmp/cinder.img')
149+ ensure_loopback.assert_called_with('/tmp/cinder.img', defsize)
150+ self.assertEquals(result, '/tmp/cinder.img')
151+
152+ ensure_loopback.return_value = '/tmp/cinder-2.img'
153+ result = openstack.ensure_block_device('/tmp/cinder-2.img|15G')
154+ ensure_loopback.assert_called_with('/tmp/cinder-2.img', '15G')
155+ self.assertEquals(result, '/tmp/cinder-2.img')
156+
157+ @patch.object(openstack, 'is_block_device')
158+ def test_ensure_standard_block_device(self, is_bd):
159+ '''Test it looks for storage at both relative and full device path'''
160+ for dev in ['vdb', '/dev/vdb']:
161+ openstack.ensure_block_device(dev)
162+ is_bd.assert_called_with('/dev/vdb')
163+
164+ @patch.object(openstack, 'is_block_device')
165+ @patch.object(openstack, 'error_out')
166+ def test_ensure_nonexistent_block_device(self, error_out, is_bd):
167+ '''Test it will not ensure a non-existant block device'''
168+ is_bd.return_value = False
169+ openstack.ensure_block_device(block_device='foo')
170+ self.assertTrue(error_out.called)
171+
172+ @patch.object(openstack, 'juju_log')
173+ @patch.object(openstack, 'umount')
174+ @patch.object(openstack, 'mounts')
175+ @patch.object(openstack, 'zap_disk')
176+ @patch.object(openstack, 'is_lvm_physical_volume')
177+ def test_clean_storage_unmount(self, is_pv, zap_disk, mounts, umount, log):
178+ '''Test it unmounts block device when cleaning storage'''
179+ is_pv.return_value = False
180+ zap_disk.return_value = True
181+ mounts.return_value = MOUNTS
182+ openstack.clean_storage('/dev/vdb')
183+ umount.called_with('/dev/vdb', True)
184+
185+ @patch.object(openstack, 'juju_log')
186+ @patch.object(openstack, 'remove_lvm_physical_volume')
187+ @patch.object(openstack, 'deactivate_lvm_volume_group')
188+ @patch.object(openstack, 'mounts')
189+ @patch.object(openstack, 'is_lvm_physical_volume')
190+ def test_clean_storage_lvm_wipe(self, is_pv, mounts, rm_lv, rm_vg, log):
191+ '''Test it removes traces of LVM when cleaning storage'''
192+ mounts.return_value = []
193+ is_pv.return_value = True
194+ openstack.clean_storage('/dev/vdb')
195+ rm_lv.assert_called_with('/dev/vdb')
196+ rm_vg .assert_called_with('/dev/vdb')
197+
198+ @patch.object(openstack, 'zap_disk')
199+ @patch.object(openstack, 'is_lvm_physical_volume')
200+ @patch.object(openstack, 'mounts')
201+ def test_clean_storage_zap_disk(self, mounts, is_pv, zap_disk):
202+ '''It removes traces of LVM when cleaning storage'''
203+ mounts.return_value = []
204+ is_pv.return_value = False
205+ openstack.clean_storage('/dev/vdb')
206+ zap_disk.assert_called_with('/dev/vdb')
207+
208 def test_is_ip(self):
209 self.assertTrue(openstack.is_ip('10.0.0.1'))
210 self.assertFalse(openstack.is_ip('www.ubuntu.com'))
211@@ -495,5 +575,6 @@
212 hn = openstack.get_hostname('www.ubuntu.com')
213 self.assertEquals(hn, 'www.ubuntu.com')
214
215+
216 if __name__ == '__main__':
217 unittest.main()

Subscribers

People subscribed via source and target branches