Merge lp:~niedbalski/charm-helpers/fix-lp-1492031 into lp:charm-helpers

Proposed by Jorge Niedbalski
Status: Merged
Merged at revision: 440
Proposed branch: lp:~niedbalski/charm-helpers/fix-lp-1492031
Merge into: lp:charm-helpers
Diff against target: 278 lines (+266/-0)
2 files modified
charmhelpers/contrib/mellanox/infiniband.py (+151/-0)
tests/contrib/mellanox/test_infiniband.py (+115/-0)
To merge this branch: bzr merge lp:~niedbalski/charm-helpers/fix-lp-1492031
Reviewer Review Type Date Requested Status
Felipe Reyes (community) Approve
Review via email: mp+270118@code.launchpad.net

Description of the change

This is an initial support for LP: #1492031

To post a comment you must log in.
Revision history for this message
Felipe Reyes (freyes) wrote :

Jorge, The tests run fine and the logic on the code is pretty straightforward, there are some things I would like you to review tough

Best,

review: Needs Fixing
441. By Jorge Niedbalski

Addressed @freyes comments

Revision history for this message
Jorge Niedbalski (niedbalski) wrote :

Thanks felipe for reviewing, I fixed your comments.

Revision history for this message
Felipe Reyes (freyes) :
review: Needs Fixing
Revision history for this message
Felipe Reyes (freyes) wrote :

lgtm ;)

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== added directory 'charmhelpers/contrib/mellanox'
2=== added file 'charmhelpers/contrib/mellanox/__init__.py'
3=== added file 'charmhelpers/contrib/mellanox/infiniband.py'
4--- charmhelpers/contrib/mellanox/infiniband.py 1970-01-01 00:00:00 +0000
5+++ charmhelpers/contrib/mellanox/infiniband.py 2015-09-03 21:43:00 +0000
6@@ -0,0 +1,151 @@
7+#!/usr/bin/env python
8+# -*- coding: utf-8 -*-
9+
10+# Copyright 2014-2015 Canonical Limited.
11+#
12+# This file is part of charm-helpers.
13+#
14+# charm-helpers is free software: you can redistribute it and/or modify
15+# it under the terms of the GNU Lesser General Public License version 3 as
16+# published by the Free Software Foundation.
17+#
18+# charm-helpers is distributed in the hope that it will be useful,
19+# but WITHOUT ANY WARRANTY; without even the implied warranty of
20+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21+# GNU Lesser General Public License for more details.
22+#
23+# You should have received a copy of the GNU Lesser General Public License
24+# along with charm-helpers. If not, see <http://www.gnu.org/licenses/>.
25+
26+
27+__author__ = "Jorge Niedbalski <jorge.niedbalski@canonical.com>"
28+
29+from charmhelpers.fetch import (
30+ apt_install,
31+ apt_update,
32+)
33+
34+from charmhelpers.core.hookenv import (
35+ log,
36+ INFO,
37+)
38+
39+try:
40+ from netifaces import interfaces as network_interfaces
41+except ImportError:
42+ apt_install('python-netifaces')
43+ from netifaces import interfaces as network_interfaces
44+
45+import os
46+import re
47+import subprocess
48+
49+from charmhelpers.core.kernel import modprobe
50+
51+REQUIRED_MODULES = (
52+ "mlx4_ib",
53+ "mlx4_en",
54+ "mlx4_core",
55+ "ib_ipath",
56+ "ib_mthca",
57+ "ib_srpt",
58+ "ib_srp",
59+ "ib_ucm",
60+ "ib_isert",
61+ "ib_iser",
62+ "ib_ipoib",
63+ "ib_cm",
64+ "ib_uverbs"
65+ "ib_umad",
66+ "ib_sa",
67+ "ib_mad",
68+ "ib_core",
69+ "ib_addr",
70+ "rdma_ucm",
71+)
72+
73+REQUIRED_PACKAGES = (
74+ "ibutils",
75+ "infiniband-diags",
76+ "ibverbs-utils",
77+)
78+
79+IPOIB_DRIVERS = (
80+ "ib_ipoib",
81+)
82+
83+ABI_VERSION_FILE = "/sys/class/infiniband_mad/abi_version"
84+
85+
86+class DeviceInfo(object):
87+ pass
88+
89+
90+def install_packages():
91+ apt_update()
92+ apt_install(REQUIRED_PACKAGES, fatal=True)
93+
94+
95+def load_modules():
96+ for module in REQUIRED_MODULES:
97+ modprobe(module, persist=True)
98+
99+
100+def is_enabled():
101+ """Check if infiniband is loaded on the system"""
102+ return os.path.exists(ABI_VERSION_FILE)
103+
104+
105+def stat():
106+ """Return full output of ibstat"""
107+ return subprocess.check_output(["ibstat"])
108+
109+
110+def devices():
111+ """Returns a list of IB enabled devices"""
112+ return subprocess.check_output(['ibstat', '-l']).splitlines()
113+
114+
115+def device_info(device):
116+ """Returns a DeviceInfo object with the current device settings"""
117+
118+ status = subprocess.check_output([
119+ 'ibstat', device, '-s']).splitlines()
120+
121+ regexes = {
122+ "CA type: (.*)": "device_type",
123+ "Number of ports: (.*)": "num_ports",
124+ "Firmware version: (.*)": "fw_ver",
125+ "Hardware version: (.*)": "hw_ver",
126+ "Node GUID: (.*)": "node_guid",
127+ "System image GUID: (.*)": "sys_guid",
128+ }
129+
130+ device = DeviceInfo()
131+
132+ for line in status:
133+ for expression, key in regexes.items():
134+ matches = re.search(expression, line)
135+ if matches:
136+ setattr(device, key, matches.group(1))
137+
138+ return device
139+
140+
141+def ipoib_interfaces():
142+ """Return a list of IPOIB capable ethernet interfaces"""
143+ interfaces = []
144+
145+ for interface in network_interfaces():
146+ try:
147+ driver = re.search('^driver: (.+)$', subprocess.check_output([
148+ 'ethtool', '-i',
149+ interface]), re.M).group(1)
150+
151+ if driver in IPOIB_DRIVERS:
152+ interfaces.append(interface)
153+ except:
154+ log("Skipping interface %s" % interface, level=INFO)
155+ continue
156+
157+ return interfaces
158
159=== added directory 'tests/contrib/mellanox'
160=== added file 'tests/contrib/mellanox/test_infiniband.py'
161--- tests/contrib/mellanox/test_infiniband.py 1970-01-01 00:00:00 +0000
162+++ tests/contrib/mellanox/test_infiniband.py 2015-09-03 21:43:00 +0000
163@@ -0,0 +1,115 @@
164+#!/usr/bin/env python
165+
166+from charmhelpers.contrib.mellanox import infiniband
167+
168+from mock import patch, call
169+import unittest
170+
171+TO_PATCH = [
172+ "log",
173+ "INFO",
174+ "apt_install",
175+ "apt_update",
176+ "modprobe",
177+ "network_interfaces"
178+]
179+
180+NETWORK_INTERFACES = [
181+ 'lo',
182+ 'eth0',
183+ 'eth1',
184+ 'eth2',
185+ 'eth3',
186+ 'eth4',
187+ 'juju-br0',
188+ 'ib0',
189+ 'virbr0',
190+ 'ovs-system',
191+ 'br-int',
192+ 'br-ex',
193+ 'br-data',
194+ 'phy-br-data',
195+ 'int-br-data',
196+ 'br-tun'
197+]
198+
199+
200+IBSTAT_OUTPUT = """
201+CA 'mlx4_0'
202+ CA type: MT4103
203+ Number of ports: 2
204+ Firmware version: 2.33.5000
205+ Hardware version: 0
206+ Node GUID: 0xe41d2d03000a1120
207+ System image GUID: 0xe41d2d03000a1123
208+"""
209+
210+
211+class InfinibandTest(unittest.TestCase):
212+
213+ def setUp(self):
214+ for m in TO_PATCH:
215+ setattr(self, m, self._patch(m))
216+
217+ def _patch(self, method):
218+ _m = patch('charmhelpers.contrib.mellanox.infiniband.' + method)
219+ mock = _m.start()
220+ self.addCleanup(_m.stop)
221+ return mock
222+
223+ def test_load_modules(self):
224+ infiniband.load_modules()
225+
226+ self.modprobe.assert_has_calls(map(lambda x: call(x, persist=True),
227+ infiniband.REQUIRED_MODULES))
228+
229+ def test_install_packages(self):
230+ infiniband.install_packages()
231+
232+ self.apt_update.assert_is_called_once()
233+ self.apt_install.assert_is_called_once()
234+
235+ @patch("os.path.exists")
236+ def test_is_enabled(self, exists):
237+ exists.return_value = True
238+ self.assertTrue(infiniband.is_enabled())
239+
240+ @patch("subprocess.check_output")
241+ def test_stat(self, check_output):
242+ infiniband.stat()
243+
244+ check_output.assert_called_with(["ibstat"])
245+
246+ @patch("subprocess.check_output")
247+ def test_devices(self, check_output):
248+ infiniband.devices()
249+
250+ check_output.assert_called_with(["ibstat", "-l"])
251+
252+ @patch("subprocess.check_output")
253+ def test_device_info(self, check_output):
254+ check_output.return_value = IBSTAT_OUTPUT
255+
256+ info = infiniband.device_info("mlx4_0")
257+
258+ self.assertEquals(info.num_ports, "2")
259+ self.assertEquals(info.device_type, "MT4103")
260+ self.assertEquals(info.fw_ver, "2.33.5000")
261+ self.assertEquals(info.hw_ver, "0")
262+ self.assertEquals(info.node_guid, "0xe41d2d03000a1120")
263+ self.assertEquals(info.sys_guid, "0xe41d2d03000a1123")
264+
265+ @patch("subprocess.check_output")
266+ def test_ipoib_interfaces(self, check_output):
267+ self.network_interfaces.return_value = NETWORK_INTERFACES
268+
269+ ipoib_nic = "ib0"
270+
271+ def c(*args, **kwargs):
272+ if ipoib_nic in args[0]:
273+ return "driver: ib_ipoib"
274+ else:
275+ return "driver: mock"
276+
277+ check_output.side_effect = c
278+ self.assertEquals(infiniband.ipoib_interfaces(), [ipoib_nic])

Subscribers

People subscribed via source and target branches