Merge ~brtknr/cloud-init:infiniband-patch into cloud-init:master
- Git
- lp:~brtknr/cloud-init
- infiniband-patch
- Merge into master
Status: | Merged |
---|---|
Approved by: | Scott Moser |
Approved revision: | 8814039d483603ac62421b339e9e522fe0724c23 |
Merge reported by: | Server Team CI bot |
Merged at revision: | not available |
Proposed branch: | ~brtknr/cloud-init:infiniband-patch |
Merge into: | cloud-init:master |
Diff against target: |
249 lines (+147/-1) 5 files modified
cloudinit/net/__init__.py (+38/-0) cloudinit/net/network_state.py (+4/-0) cloudinit/net/sysconfig.py (+14/-0) cloudinit/sources/helpers/openstack.py (+11/-0) tests/unittests/test_net.py (+80/-1) |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Server Team CI bot | continuous-integration | Approve | |
Scott Moser | Approve | ||
Mark Goddard (community) | Approve | ||
Review via email: mp+353182@code.launchpad.net |
Commit message
Add support for Infiniband network interfaces (IPoIB).
OpenStack ironic references Infiniband interfaces via a 6 byte 'MAC
address' formed from bytes 13-15 and 18-20 of interface's hardware
address. This address is used as the ethernet_
links in network_data.json in configdrives generated by OpenStack nova.
We can use this address to map links in network_data.json to their
corresponding interface names.
When generating interface configuration files, we need to use the
interface's full hardware address as the HWADDR, rather than the 6 byte
MAC address provided by network_data.json.
This change allows IB interfaces to be referenced in this dual mode - by
MAC address and hardware address, depending on the context.
Support TYPE=InfiniBand for sysconfig configuration of IB interfaces.
Description of the change
Scott Moser (smoser) wrote : | # |
Bharat Kunwar (brtknr) wrote : | # |
I’ve just signed it. Although, I put in <email address hidden> <mailto:<email address hidden>> instead of Scott Moser as Canonical Project Manager. Sorry.
> On 17 Aug 2018, at 15:24, Scott Moser <email address hidden> wrote:
>
> Hi,
> Thank you for contributing to cloud-init.
>
> To contribute, you must sign the Canonical Contributor License Agreement (CLA) [1].
>
> If you have already signed it as an individual, your Launchpad user will be listed in the contributor-
>
> For information on how to sign, please see the HACKING document [3].
>
> Thanks again, and please feel free to reach out with any questions.
> --
> https:/
> You are the owner of ~brtknr/
Scott Moser (smoser) wrote : | # |
good enough on the CLA. thanks.
On Fri, Aug 17, 2018 at 10:36 AM Bharat Kunwar <email address hidden> wrote:
> I’ve just signed it. Although, I put in <email address hidden> <mailto:
> <email address hidden>> instead of Scott Moser as Canonical Project
> Manager. Sorry.
>
> > On 17 Aug 2018, at 15:24, Scott Moser <email address hidden> wrote:
> >
> > Hi,
> > Thank you for contributing to cloud-init.
> >
> > To contribute, you must sign the Canonical Contributor License Agreement
> (CLA) [1].
> >
> > If you have already signed it as an individual, your Launchpad user will
> be listed in the contributor-
> Unfortunately there is no easy way to check if an organization or company
> you are doing work for has signed. If you are unsure or have questions,
> email <email address hidden> or ping smoser in #cloud-init channel via
> freenode.
> >
> > For information on how to sign, please see the HACKING document [3].
> >
> > Thanks again, and please feel free to reach out with any questions.
> > --
> >
> https:/
> > You are the owner of ~brtknr/
>
>
> --
> https:/
> You are subscribed to branch cloud-init:master.
>
Server Team CI bot (server-team-bot) wrote : | # |
PASSED: Continuous integration, rev:6a3880f2b19
https:/
Executed test runs:
SUCCESS: Checkout
SUCCESS: Unit & Style Tests
SUCCESS: Ubuntu LTS: Build
SUCCESS: Ubuntu LTS: Integration
IN_PROGRESS: Declarative: Post Actions
Click here to trigger a rebuild:
https:/
Scott Moser (smoser) wrote : | # |
This only adds rendering for sysconfig, right?
We'd definitely like to have support for ENI rendering.
We'd need upstream netplan feature for netplan rendering, but I'd like to see a warning emitted should that path get hit.
Scott Moser (smoser) wrote : | # |
@Mark,
Could you respond to the above?
Mark Goddard (mgoddard) wrote : | # |
Hi Scott, sorry for not replying sooner. I spoke to Bharat about this yesterday, and adding ENI support is on his todo list. It seems that ENI config should be the same as for ethernet, but we'll need to make a small additional change to ensure that we handle the new infiniband interface type. I've not used netplan before, but we can look at adding a warning.
Bharat Kunwar (brtknr) wrote : | # |
@smoser, any progress on this patch? To summarise our discussion from IRC, I tested this on Ubuntu to see if ENI would be rendered and it appears that it does since openstack helper obtains the fixed ipv4 address from ConfigDrive which ENI already knows how to handle.
Scott Moser (smoser) wrote : | # |
2018-09-26 11:40:18 @smoser it is IPoIB right?
2018-09-26 11:40:31 @smoser sorry for being infiniband unaware.
2018-09-26 11:47:08 brtknr smoser: hmm i dont see any response. where exactly?
2018-09-26 11:48:10 @smoser brtknr: bah. sorry
2018-09-26 11:48:19 @smoser i didn't hit 'save'
2018-09-26 11:48:26 @smoser it configuers IPoIB right ?
2018-09-26 11:48:36 brtknr smoser: yes
2018-09-26 11:48:38 @smoser and in some cases... that could be problematic ?
2018-09-26 11:48:47 brtknr smoser: which cases?
2018-09-26 11:49:06 brtknr smoser: it only configures IPoIB if ConfigDrive is enabled
2018-09-26 11:49:19 brtknr smoser: as a data source
2018-09-26 11:49:25 @smoser i was under theimpression that enabling IPoIB might make be incompatible with other infiniband "modes"
2018-09-26 11:50:05 @smoser (for your info, it is config drive or openstack metadata i'm pretty sure at this point, both would work with your change)
2018-09-26 11:50:33 @smoser so what i'm thinking is possibly problematic
2018-09-26 11:51:04 @smoser a.) user had infiniband hardware on openstack cloud. they manually configured some infiniband function after deplyoing an instance.
2018-09-26 11:51:29 @smoser b.) cloud-init upgraade, new instances with new cloud-init would then cause their existing process to fail
2018-09-26 11:53:33 @smoser is that possible ?
2018-09-26 11:54:09 @smoser I'm *not* suggesting that makes the MP unacceptable. I'm just wanting to clarify if that is possible.
2018-09-26 11:54:33 brtknr smoser: users need to enable these kernel modules: ib_ipoib
2018-09-26 11:54:35 brtknr mlx5_ib
2018-09-26 11:54:42 brtknr in order for this to work
2018-09-26 11:55:06 brtknr therefore, it wouldnt automatically interfere with existing setup
2018-09-26 11:55:49 brtknr e.g. openstack cloud with infiniband hardware would need to have these modules enabled by default in the respective cloud images
2018-09-26 11:56:26 brtknr mgoddard also says it should only have an entry in the metadata/
2018-09-26 11:57:38 brtknr as far as im aware, ib_ipoib and mlx5_ib are not enabled by default on neither Centos nor Ubuntu
Scott Moser (smoser) : | # |
Server Team CI bot (server-team-bot) wrote : | # |
FAILED: Autolanding.
More details in the following jenkins job:
https:/
Executed test runs:
FAILED: Checkout
Server Team CI bot (server-team-bot) : | # |
Server Team CI bot (server-team-bot) wrote : | # |
FAILED: Autolanding.
Unapproved changes made after approval.
https:/
Executed test runs:
SUCCESS: Checkout
SUCCESS: Unit & Style Tests
SUCCESS: Ubuntu LTS: Build
SUCCESS: Ubuntu LTS: Integration
IN_PROGRESS: Declarative: Post Actions
Server Team CI bot (server-team-bot) : | # |
Preview Diff
1 | diff --git a/cloudinit/net/__init__.py b/cloudinit/net/__init__.py |
2 | index 5e87bca..f83d368 100644 |
3 | --- a/cloudinit/net/__init__.py |
4 | +++ b/cloudinit/net/__init__.py |
5 | @@ -569,6 +569,20 @@ def get_interface_mac(ifname): |
6 | return read_sys_net_safe(ifname, path) |
7 | |
8 | |
9 | +def get_ib_interface_hwaddr(ifname, ethernet_format): |
10 | + """Returns the string value of an Infiniband interface's hardware |
11 | + address. If ethernet_format is True, an Ethernet MAC-style 6 byte |
12 | + representation of the address will be returned. |
13 | + """ |
14 | + # Type 32 is Infiniband. |
15 | + if read_sys_net_safe(ifname, 'type') == '32': |
16 | + mac = get_interface_mac(ifname) |
17 | + if mac and ethernet_format: |
18 | + # Use bytes 13-15 and 18-20 of the hardware address. |
19 | + mac = mac[36:-14] + mac[51:] |
20 | + return mac |
21 | + |
22 | + |
23 | def get_interfaces_by_mac(): |
24 | """Build a dictionary of tuples {mac: name}. |
25 | |
26 | @@ -580,6 +594,15 @@ def get_interfaces_by_mac(): |
27 | "duplicate mac found! both '%s' and '%s' have mac '%s'" % |
28 | (name, ret[mac], mac)) |
29 | ret[mac] = name |
30 | + # Try to get an Infiniband hardware address (in 6 byte Ethernet format) |
31 | + # for the interface. |
32 | + ib_mac = get_ib_interface_hwaddr(name, True) |
33 | + if ib_mac: |
34 | + if ib_mac in ret: |
35 | + raise RuntimeError( |
36 | + "duplicate mac found! both '%s' and '%s' have mac '%s'" % |
37 | + (name, ret[ib_mac], ib_mac)) |
38 | + ret[ib_mac] = name |
39 | return ret |
40 | |
41 | |
42 | @@ -607,6 +630,21 @@ def get_interfaces(): |
43 | return ret |
44 | |
45 | |
46 | +def get_ib_hwaddrs_by_interface(): |
47 | + """Build a dictionary mapping Infiniband interface names to their hardware |
48 | + address.""" |
49 | + ret = {} |
50 | + for name, _, _, _ in get_interfaces(): |
51 | + ib_mac = get_ib_interface_hwaddr(name, False) |
52 | + if ib_mac: |
53 | + if ib_mac in ret: |
54 | + raise RuntimeError( |
55 | + "duplicate mac found! both '%s' and '%s' have mac '%s'" % |
56 | + (name, ret[ib_mac], ib_mac)) |
57 | + ret[name] = ib_mac |
58 | + return ret |
59 | + |
60 | + |
61 | class EphemeralIPv4Network(object): |
62 | """Context manager which sets up temporary static network configuration. |
63 | |
64 | diff --git a/cloudinit/net/network_state.py b/cloudinit/net/network_state.py |
65 | index 72c803e..f76e508 100644 |
66 | --- a/cloudinit/net/network_state.py |
67 | +++ b/cloudinit/net/network_state.py |
68 | @@ -483,6 +483,10 @@ class NetworkStateInterpreter(object): |
69 | |
70 | interfaces.update({iface['name']: iface}) |
71 | |
72 | + @ensure_command_keys(['name']) |
73 | + def handle_infiniband(self, command): |
74 | + self.handle_physical(command) |
75 | + |
76 | @ensure_command_keys(['address']) |
77 | def handle_nameserver(self, command): |
78 | dns = self._network_state.get('dns') |
79 | diff --git a/cloudinit/net/sysconfig.py b/cloudinit/net/sysconfig.py |
80 | index 66e970e..9c16d3a 100644 |
81 | --- a/cloudinit/net/sysconfig.py |
82 | +++ b/cloudinit/net/sysconfig.py |
83 | @@ -174,6 +174,7 @@ class NetInterface(ConfigMap): |
84 | 'ethernet': 'Ethernet', |
85 | 'bond': 'Bond', |
86 | 'bridge': 'Bridge', |
87 | + 'infiniband': 'InfiniBand', |
88 | } |
89 | |
90 | def __init__(self, iface_name, base_sysconf_dir, templates, |
91 | @@ -569,6 +570,18 @@ class Renderer(renderer.Renderer): |
92 | cls._render_subnet_routes(iface_cfg, route_cfg, iface_subnets) |
93 | |
94 | @classmethod |
95 | + def _render_ib_interfaces(cls, network_state, iface_contents): |
96 | + ib_filter = renderer.filter_by_type('infiniband') |
97 | + for iface in network_state.iter_interfaces(ib_filter): |
98 | + iface_name = iface['name'] |
99 | + iface_cfg = iface_contents[iface_name] |
100 | + iface_cfg.kind = 'infiniband' |
101 | + iface_subnets = iface.get("subnets", []) |
102 | + route_cfg = iface_cfg.routes |
103 | + cls._render_subnets(iface_cfg, iface_subnets) |
104 | + cls._render_subnet_routes(iface_cfg, route_cfg, iface_subnets) |
105 | + |
106 | + @classmethod |
107 | def _render_sysconfig(cls, base_sysconf_dir, network_state, |
108 | templates=None): |
109 | '''Given state, return /etc/sysconfig files + contents''' |
110 | @@ -586,6 +599,7 @@ class Renderer(renderer.Renderer): |
111 | cls._render_bond_interfaces(network_state, iface_contents) |
112 | cls._render_vlan_interfaces(network_state, iface_contents) |
113 | cls._render_bridge_interfaces(network_state, iface_contents) |
114 | + cls._render_ib_interfaces(network_state, iface_contents) |
115 | contents = {} |
116 | for iface_name, iface_cfg in iface_contents.items(): |
117 | if iface_cfg or iface_cfg.children: |
118 | diff --git a/cloudinit/sources/helpers/openstack.py b/cloudinit/sources/helpers/openstack.py |
119 | index 76a6e31..9c29cea 100644 |
120 | --- a/cloudinit/sources/helpers/openstack.py |
121 | +++ b/cloudinit/sources/helpers/openstack.py |
122 | @@ -675,6 +675,17 @@ def convert_net_json(network_json=None, known_macs=None): |
123 | else: |
124 | cfg[key] = fmt % link_id_info[target]['name'] |
125 | |
126 | + # Infiniband interfaces may be referenced in network_data.json by a 6 byte |
127 | + # Ethernet MAC-style address, and we use that address to look up the |
128 | + # interface name above. Now ensure that the hardware address is set to the |
129 | + # full 20 byte address. |
130 | + ib_known_hwaddrs = net.get_ib_hwaddrs_by_interface() |
131 | + if ib_known_hwaddrs: |
132 | + for cfg in config: |
133 | + if cfg['name'] in ib_known_hwaddrs: |
134 | + cfg['mac_address'] = ib_known_hwaddrs[cfg['name']] |
135 | + cfg['type'] = 'infiniband' |
136 | + |
137 | for service in services: |
138 | cfg = service |
139 | cfg.update({'type': 'nameserver'}) |
140 | diff --git a/tests/unittests/test_net.py b/tests/unittests/test_net.py |
141 | index f3165da..5d9c7d9 100644 |
142 | --- a/tests/unittests/test_net.py |
143 | +++ b/tests/unittests/test_net.py |
144 | @@ -3260,11 +3260,15 @@ class TestGetInterfacesByMac(CiTestCase): |
145 | def _se_interface_has_own_mac(self, name): |
146 | return name in self.data['own_macs'] |
147 | |
148 | + def _se_get_ib_interface_hwaddr(self, name, ethernet_format): |
149 | + ib_hwaddr = self.data.get('ib_hwaddr', {}) |
150 | + return ib_hwaddr.get(name, {}).get(ethernet_format) |
151 | + |
152 | def _mock_setup(self): |
153 | self.data = copy.deepcopy(self._data) |
154 | self.data['devices'] = set(list(self.data['macs'].keys())) |
155 | mocks = ('get_devicelist', 'get_interface_mac', 'is_bridge', |
156 | - 'interface_has_own_mac', 'is_vlan') |
157 | + 'interface_has_own_mac', 'is_vlan', 'get_ib_interface_hwaddr') |
158 | self.mocks = {} |
159 | for n in mocks: |
160 | m = mock.patch('cloudinit.net.' + n, |
161 | @@ -3338,6 +3342,20 @@ class TestGetInterfacesByMac(CiTestCase): |
162 | ret = net.get_interfaces_by_mac() |
163 | self.assertEqual('lo', ret[empty_mac]) |
164 | |
165 | + def test_ib(self): |
166 | + ib_addr = '80:00:00:28:fe:80:00:00:00:00:00:00:00:11:22:03:00:33:44:56' |
167 | + ib_addr_eth_format = '00:11:22:33:44:56' |
168 | + self._mock_setup() |
169 | + self.data['devices'] = ['enp0s1', 'ib0'] |
170 | + self.data['own_macs'].append('ib0') |
171 | + self.data['macs']['ib0'] = ib_addr |
172 | + self.data['ib_hwaddr'] = {'ib0': {True: ib_addr_eth_format, |
173 | + False: ib_addr}} |
174 | + result = net.get_interfaces_by_mac() |
175 | + expected = {'aa:aa:aa:aa:aa:01': 'enp0s1', |
176 | + ib_addr_eth_format: 'ib0', ib_addr: 'ib0'} |
177 | + self.assertEqual(expected, result) |
178 | + |
179 | |
180 | class TestInterfacesSorting(CiTestCase): |
181 | |
182 | @@ -3352,6 +3370,67 @@ class TestInterfacesSorting(CiTestCase): |
183 | ['enp0s3', 'enp0s8', 'enp0s13', 'enp1s2', 'enp2s0', 'enp2s3']) |
184 | |
185 | |
186 | +class TestGetIBHwaddrsByInterface(CiTestCase): |
187 | + |
188 | + _ib_addr = '80:00:00:28:fe:80:00:00:00:00:00:00:00:11:22:03:00:33:44:56' |
189 | + _ib_addr_eth_format = '00:11:22:33:44:56' |
190 | + _data = {'devices': ['enp0s1', 'enp0s2', 'bond1', 'bridge1', |
191 | + 'bridge1-nic', 'tun0', 'ib0'], |
192 | + 'bonds': ['bond1'], |
193 | + 'bridges': ['bridge1'], |
194 | + 'own_macs': ['enp0s1', 'enp0s2', 'bridge1-nic', 'bridge1', 'ib0'], |
195 | + 'macs': {'enp0s1': 'aa:aa:aa:aa:aa:01', |
196 | + 'enp0s2': 'aa:aa:aa:aa:aa:02', |
197 | + 'bond1': 'aa:aa:aa:aa:aa:01', |
198 | + 'bridge1': 'aa:aa:aa:aa:aa:03', |
199 | + 'bridge1-nic': 'aa:aa:aa:aa:aa:03', |
200 | + 'tun0': None, |
201 | + 'ib0': _ib_addr}, |
202 | + 'ib_hwaddr': {'ib0': {True: _ib_addr_eth_format, |
203 | + False: _ib_addr}}} |
204 | + data = {} |
205 | + |
206 | + def _mock_setup(self): |
207 | + self.data = copy.deepcopy(self._data) |
208 | + mocks = ('get_devicelist', 'get_interface_mac', 'is_bridge', |
209 | + 'interface_has_own_mac', 'get_ib_interface_hwaddr') |
210 | + self.mocks = {} |
211 | + for n in mocks: |
212 | + m = mock.patch('cloudinit.net.' + n, |
213 | + side_effect=getattr(self, '_se_' + n)) |
214 | + self.addCleanup(m.stop) |
215 | + self.mocks[n] = m.start() |
216 | + |
217 | + def _se_get_devicelist(self): |
218 | + return self.data['devices'] |
219 | + |
220 | + def _se_get_interface_mac(self, name): |
221 | + return self.data['macs'][name] |
222 | + |
223 | + def _se_is_bridge(self, name): |
224 | + return name in self.data['bridges'] |
225 | + |
226 | + def _se_interface_has_own_mac(self, name): |
227 | + return name in self.data['own_macs'] |
228 | + |
229 | + def _se_get_ib_interface_hwaddr(self, name, ethernet_format): |
230 | + ib_hwaddr = self.data.get('ib_hwaddr', {}) |
231 | + return ib_hwaddr.get(name, {}).get(ethernet_format) |
232 | + |
233 | + def test_ethernet(self): |
234 | + self._mock_setup() |
235 | + self.data['devices'].remove('ib0') |
236 | + result = net.get_ib_hwaddrs_by_interface() |
237 | + expected = {} |
238 | + self.assertEqual(expected, result) |
239 | + |
240 | + def test_ib(self): |
241 | + self._mock_setup() |
242 | + result = net.get_ib_hwaddrs_by_interface() |
243 | + expected = {'ib0': self._ib_addr} |
244 | + self.assertEqual(expected, result) |
245 | + |
246 | + |
247 | def _gzip_data(data): |
248 | with io.BytesIO() as iobuf: |
249 | gzfp = gzip.GzipFile(mode="wb", fileobj=iobuf) |
Hi,
Thank you for contributing to cloud-init.
To contribute, you must sign the Canonical Contributor License Agreement (CLA) [1].
If you have already signed it as an individual, your Launchpad user will be listed in the contributor- agreement- canonical launchpad group [2]. Unfortunately there is no easy way to check if an organization or company you are doing work for has signed. If you are unsure or have questions, email <email address hidden> or ping smoser in #cloud-init channel via freenode.
For information on how to sign, please see the HACKING document [3].
Thanks again, and please feel free to reach out with any questions.