Merge lp:~peter-sabaini/charm-helpers/bcache-helpers into lp:charm-helpers

Proposed by Peter Sabaini
Status: Merged
Merged at revision: 759
Proposed branch: lp:~peter-sabaini/charm-helpers/bcache-helpers
Merge into: lp:charm-helpers
Diff against target: 172 lines (+163/-0)
2 files modified
charmhelpers/contrib/storage/linux/bcache.py (+74/-0)
tests/contrib/storage/test_bcache.py (+89/-0)
To merge this branch: bzr merge lp:~peter-sabaini/charm-helpers/bcache-helpers
Reviewer Review Type Date Requested Status
Stuart Bishop (community) Approve
Review via email: mp+323616@code.launchpad.net

Description of the change

Add helper module for bcache devices

To post a comment you must log in.
Revision history for this message
Stuart Bishop (stub) wrote :

Mostly good. A few inline comments. Let me know here if I should merge.

review: Approve
741. By Peter Sabaini

Merge Tim Van Steenburgh 2017-07-05 v0.17.0

742. By Peter Sabaini

Cleanup test and some doc bits

Revision history for this message
Peter Sabaini (peter-sabaini) wrote :

Hey - apologies, this fell through the cracks.

Thanks for the valuable suggestions, I've updated the MP as requested. Please merge if this is ok

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== added file 'charmhelpers/contrib/storage/linux/bcache.py'
2--- charmhelpers/contrib/storage/linux/bcache.py 1970-01-01 00:00:00 +0000
3+++ charmhelpers/contrib/storage/linux/bcache.py 2017-07-07 10:00:50 +0000
4@@ -0,0 +1,74 @@
5+# Copyright 2017 Canonical Limited.
6+#
7+# Licensed under the Apache License, Version 2.0 (the "License");
8+# you may not use this file except in compliance with the License.
9+# You may obtain a copy of the License at
10+#
11+# http://www.apache.org/licenses/LICENSE-2.0
12+#
13+# Unless required by applicable law or agreed to in writing, software
14+# distributed under the License is distributed on an "AS IS" BASIS,
15+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16+# See the License for the specific language governing permissions and
17+# limitations under the License.
18+import os
19+import json
20+
21+from charmhelpers.core.hookenv import log
22+
23+stats_intervals = ['stats_day', 'stats_five_minute',
24+ 'stats_hour', 'stats_total']
25+
26+SYSFS = '/sys'
27+
28+
29+class Bcache(object):
30+ """Bcache behaviour
31+ """
32+
33+ def __init__(self, cachepath):
34+ self.cachepath = cachepath
35+
36+ @classmethod
37+ def fromdevice(cls, devname):
38+ return cls('{}/block/{}/bcache'.format(SYSFS, devname))
39+
40+ def __str__(self):
41+ return self.cachepath
42+
43+ def get_stats(self, interval):
44+ """Get cache stats
45+ """
46+ intervaldir = 'stats_{}'.format(interval)
47+ path = "{}/{}".format(self.cachepath, intervaldir)
48+ out = dict()
49+ for elem in os.listdir(path):
50+ out[elem] = open('{}/{}'.format(path, elem)).read().strip()
51+ return out
52+
53+
54+def get_bcache_fs():
55+ """Return all cache sets
56+ """
57+ cachesetroot = "{}/fs/bcache".format(SYSFS)
58+ try:
59+ dirs = os.listdir(cachesetroot)
60+ except OSError:
61+ log("No bcache fs found")
62+ return []
63+ cacheset = set([Bcache('{}/{}'.format(cachesetroot, d)) for d in dirs if not d.startswith('register')])
64+ return cacheset
65+
66+
67+def get_stats_action(cachespec, interval):
68+ """Action for getting bcache statistics for a given cachespec.
69+ Cachespec can either be a device name, eg. 'sdb', which will retrieve
70+ cache stats for the given device, or 'global', which will retrieve stats
71+ for all cachesets
72+ """
73+ if cachespec == 'global':
74+ caches = get_bcache_fs()
75+ else:
76+ caches = [Bcache.fromdevice(cachespec)]
77+ res = dict((c.cachepath, c.get_stats(interval)) for c in caches)
78+ return json.dumps(res, indent=4, separators=(',', ': '))
79
80=== added file 'tests/contrib/storage/test_bcache.py'
81--- tests/contrib/storage/test_bcache.py 1970-01-01 00:00:00 +0000
82+++ tests/contrib/storage/test_bcache.py 2017-07-07 10:00:50 +0000
83@@ -0,0 +1,89 @@
84+# Copyright 2017 Canonical Ltd
85+#
86+# Licensed under the Apache License, Version 2.0 (the "License");
87+# you may not use this file except in compliance with the License.
88+# You may obtain a copy of the License at
89+#
90+# http://www.apache.org/licenses/LICENSE-2.0
91+#
92+# Unless required by applicable law or agreed to in writing, software
93+# distributed under the License is distributed on an "AS IS" BASIS,
94+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
95+# See the License for the specific language governing permissions and
96+# limitations under the License.
97+
98+import os
99+import shutil
100+import json
101+from mock import patch
102+from testtools import TestCase
103+from tempfile import mkdtemp
104+from charmhelpers.contrib.storage.linux import bcache
105+
106+test_stats = {
107+ 'bypassed': '128G\n',
108+ 'cache_bypass_hits': '1132623\n',
109+ 'cache_bypass_misses': '0\n',
110+ 'cache_hit_ratio': '64\n',
111+ 'cache_hits': '12177090\n',
112+ 'cache_miss_collisions': '7091\n',
113+ 'cache_misses': '6717011\n',
114+ 'cache_readaheads': '0\n',
115+}
116+
117+tmpdir = 'bcache-stats-test.'
118+cacheset = 'abcde'
119+cachedev = 'sdfoo'
120+
121+
122+class BcacheTestCase(TestCase):
123+ def setUp(self):
124+ super(BcacheTestCase, self).setUp()
125+ self.sysfs = sysfs = mkdtemp(prefix=tmpdir)
126+ self.addCleanup(shutil.rmtree, sysfs)
127+ p = patch('charmhelpers.contrib.storage.linux.bcache.SYSFS', new=sysfs)
128+ p.start()
129+ self.addCleanup(p.stop)
130+ self.cacheset = '{}/fs/bcache/{}'.format(sysfs, cacheset)
131+ os.makedirs(self.cacheset)
132+ self.devcache = '{}/block/{}/bcache'.format(sysfs, cachedev)
133+ for n in ['register', 'register_quiet']:
134+ with open('{}/fs/bcache/{}'.format(sysfs, n), 'w') as f:
135+ f.write('foo')
136+ for kind in self.cacheset, self.devcache:
137+ for sub in bcache.stats_intervals:
138+ intvaldir = '{}/{}'.format(kind, sub)
139+ os.makedirs(intvaldir)
140+ for fn, val in test_stats.items():
141+ with open(os.path.join(intvaldir, fn), 'w') as f:
142+ f.write(val)
143+
144+ def test_get_bcache_fs(self):
145+ bcachedirs = bcache.get_bcache_fs()
146+ assert len(bcachedirs) == 1
147+ assert next(iter(bcachedirs)).cachepath.endswith('/fs/bcache/abcde')
148+
149+ @patch('charmhelpers.contrib.storage.linux.bcache.os.listdir')
150+ def test_get_bcache_fs_nobcache(self, mock_listdir):
151+ mock_listdir.side_effect = OSError(
152+ '[Errno 2] No such file or directory:...')
153+ bcachedirs = bcache.get_bcache_fs()
154+ assert bcachedirs == []
155+
156+ def test_get_stats_global(self):
157+ out = bcache.get_stats_action(
158+ 'global', 'hour')
159+ out = json.loads(out)
160+ assert len(out.keys()) == 1
161+ k = next(iter(out.keys()))
162+ assert k.endswith(cacheset)
163+ assert out[k]['bypassed'] == '128G'
164+
165+ def test_get_stats_dev(self):
166+ out = bcache.get_stats_action(
167+ cachedev, 'hour')
168+ out = json.loads(out)
169+ assert len(out.keys()) == 1
170+ k = next(iter(out.keys()))
171+ assert k.endswith('sdfoo/bcache')
172+ assert out[k]['cache_hit_ratio'] == '64'

Subscribers

People subscribed via source and target branches