Merge ~oddbloke/cloud-init/+git/cloud-init:oci-vnic into cloud-init:master
- Git
- lp:~oddbloke/cloud-init/+git/cloud-init
- oci-vnic
- Merge into master
Status: | Merged |
---|---|
Approved by: | Dan Watkins |
Approved revision: | 63e2ed22336e8a8838cf10c586b07d751c811dc2 |
Merge reported by: | Server Team CI bot |
Merged at revision: | not available |
Proposed branch: | ~oddbloke/cloud-init/+git/cloud-init:oci-vnic |
Merge into: | cloud-init:master |
Diff against target: |
431 lines (+324/-4) 3 files modified
cloudinit/sources/DataSourceOracle.py (+88/-1) cloudinit/sources/tests/test_oracle.py (+212/-2) doc/rtd/topics/datasources/oracle.rst (+24/-1) |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Server Team CI bot | continuous-integration | Approve | |
Chad Smith | Approve | ||
Dan Watkins | Abstain | ||
Review via email: mp+371053@code.launchpad.net |
Commit message
DataSourceOracle: configure secondary NICs on Virtual Machines
Oracle Cloud Infrastructure's Instance Metadata Service provides network
configuration information for non-primary NICs. This commit introduces
support, on Virtual Machines[0], for fetching that network metadata,
converting it to v1 network-config[1] and combining it into the network
configuration generated for the primary interface.
By default, this behaviour is not enabled. Configuring the Oracle
datasource to `configure_
datasource:
Oracle:
Failures to fetch and generate secondary NIC configuration will log a
warning, but otherwise will not affect boot.
[0] The expected use of the IMDS-provided network configuration is
substantially different on Bare Metal Machines, so support for that
will be addressed separately.
[1] This is v1 config, because cloudinit.
and we need to integrate the secondary NICs into that configuration.
Description of the change
Server Team CI bot (server-team-bot) wrote : | # |
PASSED: Continuous integration, rev:c5a8c2ff40c
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:/
Dan Watkins (oddbloke) wrote : | # |
Docs are now present, so switching my review to Abstain.
Server Team CI bot (server-team-bot) wrote : | # |
PASSED: Continuous integration, rev:bb32cd9fa45
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:/
Chad Smith (chad.smith) : | # |
Ryan Harper (raharper) wrote : | # |
Looks good. A few in-line questions.
Dan Watkins (oddbloke) wrote : | # |
Thanks for the reviews! Responses inline.
Dan Watkins (oddbloke) : | # |
- eb5dfc5... by Dan Watkins
-
DataSourceOracle: add explanatory comment about MTU 9000
And move it to a constant so the comment isn't clogging up other logic.
Dan Watkins (oddbloke) wrote : | # |
Made some changes locally, still thinking about one other. (Not pushing them up until all comments are addressed, so I don't have to go digging to find the current set of comments.)
Chad Smith (chad.smith) wrote : | # |
minor logging nit and a folowup question on _network_
- 8898a51... by Dan Watkins
-
DataSourceOracle: drop log level down to debug
Dan Watkins (oddbloke) : | # |
- f7209d5... by Dan Watkins
-
DataSourceOracle: don't return _and_ mutate network_config
Also raise log level to WARNING on Bare Metal Machines.
Server Team CI bot (server-team-bot) wrote : | # |
PASSED: Continuous integration, rev:7e0f6891b37
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:/
Ryan Harper (raharper) : | # |
Chad Smith (chad.smith) wrote : | # |
Again another volley of trivial comments. Otherwise looks good to me!
Chad Smith (chad.smith) : | # |
Dan Watkins (oddbloke) wrote : | # |
Really good feedback, Chad, thanks! Will address tomorrow.
- b950373... by Dan Watkins
-
DataSourceOracle: add explanatory comment per review
- 63e2ed2... by Dan Watkins
-
test_oracle: use a verbatim dump of OPC Bare Metal network data
Dan Watkins (oddbloke) wrote : | # |
Both comments requiring action addressed.
Server Team CI bot (server-team-bot) wrote : | # |
FAILED: Continuous integration, rev:246c359e480
https:/
Executed test runs:
SUCCESS: Checkout
FAILED: Unit & Style Tests
Click here to trigger a rebuild:
https:/
Chad Smith (chad.smith) wrote : | # |
+1 on this branch once CI is fixed for pycodestyle
cloudinit/
cloudinit/
Server Team CI bot (server-team-bot) wrote : | # |
PASSED: Continuous integration, rev:63e2ed22336
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:/
Preview Diff
1 | diff --git a/cloudinit/sources/DataSourceOracle.py b/cloudinit/sources/DataSourceOracle.py |
2 | index 76cfa38..086af79 100644 |
3 | --- a/cloudinit/sources/DataSourceOracle.py |
4 | +++ b/cloudinit/sources/DataSourceOracle.py |
5 | @@ -16,7 +16,7 @@ Notes: |
6 | """ |
7 | |
8 | from cloudinit.url_helper import combine_url, readurl, UrlError |
9 | -from cloudinit.net import dhcp |
10 | +from cloudinit.net import dhcp, get_interfaces_by_mac |
11 | from cloudinit import net |
12 | from cloudinit import sources |
13 | from cloudinit import util |
14 | @@ -28,8 +28,80 @@ import re |
15 | |
16 | LOG = logging.getLogger(__name__) |
17 | |
18 | +BUILTIN_DS_CONFIG = { |
19 | + # Don't use IMDS to configure secondary NICs by default |
20 | + 'configure_secondary_nics': False, |
21 | +} |
22 | CHASSIS_ASSET_TAG = "OracleCloud.com" |
23 | METADATA_ENDPOINT = "http://169.254.169.254/openstack/" |
24 | +VNIC_METADATA_URL = 'http://169.254.169.254/opc/v1/vnics/' |
25 | +# https://docs.cloud.oracle.com/iaas/Content/Network/Troubleshoot/connectionhang.htm#Overview, |
26 | +# indicates that an MTU of 9000 is used within OCI |
27 | +MTU = 9000 |
28 | + |
29 | + |
30 | +def _add_network_config_from_opc_imds(network_config): |
31 | + """ |
32 | + Fetch data from Oracle's IMDS, generate secondary NIC config, merge it. |
33 | + |
34 | + The primary NIC configuration should not be modified based on the IMDS |
35 | + values, as it should continue to be configured for DHCP. As such, this |
36 | + takes an existing network_config dict which is expected to have the primary |
37 | + NIC configuration already present. It will mutate the given dict to |
38 | + include the secondary VNICs. |
39 | + |
40 | + :param network_config: |
41 | + A v1 network config dict with the primary NIC already configured. This |
42 | + dict will be mutated. |
43 | + |
44 | + :raises: |
45 | + Exceptions are not handled within this function. Likely exceptions are |
46 | + those raised by url_helper.readurl (if communicating with the IMDS |
47 | + fails), ValueError/JSONDecodeError (if the IMDS returns invalid JSON), |
48 | + and KeyError/IndexError (if the IMDS returns valid JSON with unexpected |
49 | + contents). |
50 | + """ |
51 | + resp = readurl(VNIC_METADATA_URL) |
52 | + vnics = json.loads(str(resp)) |
53 | + |
54 | + if 'nicIndex' in vnics[0]: |
55 | + # TODO: Once configure_secondary_nics defaults to True, lower the level |
56 | + # of this log message. (Currently, if we're running this code at all, |
57 | + # someone has explicitly opted-in to secondary VNIC configuration, so |
58 | + # we should warn them that it didn't happen. Once it's default, this |
59 | + # would be emitted on every Bare Metal Machine launch, which means INFO |
60 | + # or DEBUG would be more appropriate.) |
61 | + LOG.warning( |
62 | + 'VNIC metadata indicates this is a bare metal machine; skipping' |
63 | + ' secondary VNIC configuration.' |
64 | + ) |
65 | + return |
66 | + |
67 | + interfaces_by_mac = get_interfaces_by_mac() |
68 | + |
69 | + for vnic_dict in vnics[1:]: |
70 | + # We skip the first entry in the response because the primary interface |
71 | + # is already configured by iSCSI boot; applying configuration from the |
72 | + # IMDS is not required. |
73 | + mac_address = vnic_dict['macAddr'].lower() |
74 | + if mac_address not in interfaces_by_mac: |
75 | + LOG.debug('Interface with MAC %s not found; skipping', mac_address) |
76 | + continue |
77 | + name = interfaces_by_mac[mac_address] |
78 | + subnet = { |
79 | + 'type': 'static', |
80 | + 'address': vnic_dict['privateIp'], |
81 | + 'netmask': vnic_dict['subnetCidrBlock'].split('/')[1], |
82 | + 'gateway': vnic_dict['virtualRouterIp'], |
83 | + 'control': 'manual', |
84 | + } |
85 | + network_config['config'].append({ |
86 | + 'name': name, |
87 | + 'type': 'physical', |
88 | + 'mac_address': mac_address, |
89 | + 'mtu': MTU, |
90 | + 'subnets': [subnet], |
91 | + }) |
92 | |
93 | |
94 | class DataSourceOracle(sources.DataSource): |
95 | @@ -39,6 +111,13 @@ class DataSourceOracle(sources.DataSource): |
96 | vendordata_pure = None |
97 | _network_config = sources.UNSET |
98 | |
99 | + def __init__(self, sys_cfg, *args, **kwargs): |
100 | + super(DataSourceOracle, self).__init__(sys_cfg, *args, **kwargs) |
101 | + |
102 | + self.ds_cfg = util.mergemanydict([ |
103 | + util.get_cfg_by_path(sys_cfg, ['datasource', self.dsname], {}), |
104 | + BUILTIN_DS_CONFIG]) |
105 | + |
106 | def _is_platform_viable(self): |
107 | """Check platform environment to report if this datasource may run.""" |
108 | return _is_platform_viable() |
109 | @@ -121,6 +200,14 @@ class DataSourceOracle(sources.DataSource): |
110 | self._network_config = cmdline.read_initramfs_config() |
111 | if not self._network_config: |
112 | self._network_config = self.distro.generate_fallback_config() |
113 | + if self.ds_cfg.get('configure_secondary_nics'): |
114 | + try: |
115 | + # Mutate self._network_config to include secondary VNICs |
116 | + _add_network_config_from_opc_imds(self._network_config) |
117 | + except Exception: |
118 | + util.logexc( |
119 | + LOG, |
120 | + "Failed to fetch secondary network configuration!") |
121 | return self._network_config |
122 | |
123 | |
124 | diff --git a/cloudinit/sources/tests/test_oracle.py b/cloudinit/sources/tests/test_oracle.py |
125 | index 282382c..3e14677 100644 |
126 | --- a/cloudinit/sources/tests/test_oracle.py |
127 | +++ b/cloudinit/sources/tests/test_oracle.py |
128 | @@ -18,10 +18,52 @@ import uuid |
129 | DS_PATH = "cloudinit.sources.DataSourceOracle" |
130 | MD_VER = "2013-10-17" |
131 | |
132 | +# `curl -L http://169.254.169.254/opc/v1/vnics/` on a Oracle Bare Metal Machine |
133 | +# with a secondary VNIC attached (vnicId truncated for Python line length) |
134 | +OPC_BM_SECONDARY_VNIC_RESPONSE = """\ |
135 | +[ { |
136 | + "vnicId" : "ocid1.vnic.oc1.phx.abyhqljtyvcucqkhdqmgjszebxe4hrb!!TRUNCATED||", |
137 | + "privateIp" : "10.0.0.8", |
138 | + "vlanTag" : 0, |
139 | + "macAddr" : "90:e2:ba:d4:f1:68", |
140 | + "virtualRouterIp" : "10.0.0.1", |
141 | + "subnetCidrBlock" : "10.0.0.0/24", |
142 | + "nicIndex" : 0 |
143 | +}, { |
144 | + "vnicId" : "ocid1.vnic.oc1.phx.abyhqljtfmkxjdy2sqidndiwrsg63zf!!TRUNCATED||", |
145 | + "privateIp" : "10.0.4.5", |
146 | + "vlanTag" : 1, |
147 | + "macAddr" : "02:00:17:05:CF:51", |
148 | + "virtualRouterIp" : "10.0.4.1", |
149 | + "subnetCidrBlock" : "10.0.4.0/24", |
150 | + "nicIndex" : 0 |
151 | +} ]""" |
152 | + |
153 | +# `curl -L http://169.254.169.254/opc/v1/vnics/` on a Oracle Virtual Machine |
154 | +# with a secondary VNIC attached |
155 | +OPC_VM_SECONDARY_VNIC_RESPONSE = """\ |
156 | +[ { |
157 | + "vnicId" : "ocid1.vnic.oc1.phx.abyhqljtch72z5pd76cc2636qeqh7z_truncated", |
158 | + "privateIp" : "10.0.0.230", |
159 | + "vlanTag" : 1039, |
160 | + "macAddr" : "02:00:17:05:D1:DB", |
161 | + "virtualRouterIp" : "10.0.0.1", |
162 | + "subnetCidrBlock" : "10.0.0.0/24" |
163 | +}, { |
164 | + "vnicId" : "ocid1.vnic.oc1.phx.abyhqljt4iew3gwmvrwrhhf3bp5drj_truncated", |
165 | + "privateIp" : "10.0.0.231", |
166 | + "vlanTag" : 1041, |
167 | + "macAddr" : "00:00:17:02:2B:B1", |
168 | + "virtualRouterIp" : "10.0.0.1", |
169 | + "subnetCidrBlock" : "10.0.0.0/24" |
170 | +} ]""" |
171 | + |
172 | |
173 | class TestDataSourceOracle(test_helpers.CiTestCase): |
174 | """Test datasource DataSourceOracle.""" |
175 | |
176 | + with_logs = True |
177 | + |
178 | ds_class = oracle.DataSourceOracle |
179 | |
180 | my_uuid = str(uuid.uuid4()) |
181 | @@ -79,6 +121,16 @@ class TestDataSourceOracle(test_helpers.CiTestCase): |
182 | self.assertEqual( |
183 | 'metadata (http://169.254.169.254/openstack/)', ds.subplatform) |
184 | |
185 | + def test_sys_cfg_can_enable_configure_secondary_nics(self): |
186 | + # Confirm that behaviour is toggled by sys_cfg |
187 | + ds, _mocks = self._get_ds() |
188 | + self.assertFalse(ds.ds_cfg['configure_secondary_nics']) |
189 | + |
190 | + sys_cfg = { |
191 | + 'datasource': {'Oracle': {'configure_secondary_nics': True}}} |
192 | + ds, _mocks = self._get_ds(sys_cfg=sys_cfg) |
193 | + self.assertTrue(ds.ds_cfg['configure_secondary_nics']) |
194 | + |
195 | @mock.patch(DS_PATH + "._is_iscsi_root", return_value=True) |
196 | def test_without_userdata(self, m_is_iscsi_root): |
197 | """If no user-data is provided, it should not be in return dict.""" |
198 | @@ -133,9 +185,12 @@ class TestDataSourceOracle(test_helpers.CiTestCase): |
199 | self.assertEqual(self.my_md['uuid'], ds.get_instance_id()) |
200 | self.assertEqual(my_userdata, ds.userdata_raw) |
201 | |
202 | + @mock.patch(DS_PATH + "._add_network_config_from_opc_imds", |
203 | + side_effect=lambda network_config: network_config) |
204 | @mock.patch(DS_PATH + ".cmdline.read_initramfs_config") |
205 | @mock.patch(DS_PATH + "._is_iscsi_root", return_value=True) |
206 | - def test_network_cmdline(self, m_is_iscsi_root, m_initramfs_config): |
207 | + def test_network_cmdline(self, m_is_iscsi_root, m_initramfs_config, |
208 | + _m_add_network_config_from_opc_imds): |
209 | """network_config should read kernel cmdline.""" |
210 | distro = mock.MagicMock() |
211 | ds, _ = self._get_ds(distro=distro, patches={ |
212 | @@ -151,9 +206,12 @@ class TestDataSourceOracle(test_helpers.CiTestCase): |
213 | self.assertEqual([mock.call()], m_initramfs_config.call_args_list) |
214 | self.assertFalse(distro.generate_fallback_config.called) |
215 | |
216 | + @mock.patch(DS_PATH + "._add_network_config_from_opc_imds", |
217 | + side_effect=lambda network_config: network_config) |
218 | @mock.patch(DS_PATH + ".cmdline.read_initramfs_config") |
219 | @mock.patch(DS_PATH + "._is_iscsi_root", return_value=True) |
220 | - def test_network_fallback(self, m_is_iscsi_root, m_initramfs_config): |
221 | + def test_network_fallback(self, m_is_iscsi_root, m_initramfs_config, |
222 | + _m_add_network_config_from_opc_imds): |
223 | """test that fallback network is generated if no kernel cmdline.""" |
224 | distro = mock.MagicMock() |
225 | ds, _ = self._get_ds(distro=distro, patches={ |
226 | @@ -175,6 +233,76 @@ class TestDataSourceOracle(test_helpers.CiTestCase): |
227 | self.assertEqual(ncfg, ds.network_config) |
228 | self.assertEqual(1, m_initramfs_config.call_count) |
229 | |
230 | + @mock.patch(DS_PATH + "._add_network_config_from_opc_imds") |
231 | + @mock.patch(DS_PATH + ".cmdline.read_initramfs_config", |
232 | + return_value={'some': 'config'}) |
233 | + @mock.patch(DS_PATH + "._is_iscsi_root", return_value=True) |
234 | + def test_secondary_nics_added_to_network_config_if_enabled( |
235 | + self, _m_is_iscsi_root, _m_initramfs_config, |
236 | + m_add_network_config_from_opc_imds): |
237 | + |
238 | + needle = object() |
239 | + |
240 | + def network_config_side_effect(network_config): |
241 | + network_config['secondary_added'] = needle |
242 | + |
243 | + m_add_network_config_from_opc_imds.side_effect = ( |
244 | + network_config_side_effect) |
245 | + |
246 | + distro = mock.MagicMock() |
247 | + ds, _ = self._get_ds(distro=distro, patches={ |
248 | + '_is_platform_viable': {'return_value': True}, |
249 | + 'crawl_metadata': { |
250 | + 'return_value': { |
251 | + MD_VER: {'system_uuid': self.my_uuid, |
252 | + 'meta_data': self.my_md}}}}) |
253 | + ds.ds_cfg['configure_secondary_nics'] = True |
254 | + self.assertEqual(needle, ds.network_config['secondary_added']) |
255 | + |
256 | + @mock.patch(DS_PATH + "._add_network_config_from_opc_imds") |
257 | + @mock.patch(DS_PATH + ".cmdline.read_initramfs_config", |
258 | + return_value={'some': 'config'}) |
259 | + @mock.patch(DS_PATH + "._is_iscsi_root", return_value=True) |
260 | + def test_secondary_nics_not_added_to_network_config_by_default( |
261 | + self, _m_is_iscsi_root, _m_initramfs_config, |
262 | + m_add_network_config_from_opc_imds): |
263 | + |
264 | + def network_config_side_effect(network_config): |
265 | + network_config['secondary_added'] = True |
266 | + |
267 | + m_add_network_config_from_opc_imds.side_effect = ( |
268 | + network_config_side_effect) |
269 | + |
270 | + distro = mock.MagicMock() |
271 | + ds, _ = self._get_ds(distro=distro, patches={ |
272 | + '_is_platform_viable': {'return_value': True}, |
273 | + 'crawl_metadata': { |
274 | + 'return_value': { |
275 | + MD_VER: {'system_uuid': self.my_uuid, |
276 | + 'meta_data': self.my_md}}}}) |
277 | + self.assertNotIn('secondary_added', ds.network_config) |
278 | + |
279 | + @mock.patch(DS_PATH + "._add_network_config_from_opc_imds") |
280 | + @mock.patch(DS_PATH + ".cmdline.read_initramfs_config") |
281 | + @mock.patch(DS_PATH + "._is_iscsi_root", return_value=True) |
282 | + def test_secondary_nic_failure_isnt_blocking( |
283 | + self, _m_is_iscsi_root, m_initramfs_config, |
284 | + m_add_network_config_from_opc_imds): |
285 | + |
286 | + m_add_network_config_from_opc_imds.side_effect = Exception() |
287 | + |
288 | + distro = mock.MagicMock() |
289 | + ds, _ = self._get_ds(distro=distro, patches={ |
290 | + '_is_platform_viable': {'return_value': True}, |
291 | + 'crawl_metadata': { |
292 | + 'return_value': { |
293 | + MD_VER: {'system_uuid': self.my_uuid, |
294 | + 'meta_data': self.my_md}}}}) |
295 | + ds.ds_cfg['configure_secondary_nics'] = True |
296 | + self.assertEqual(ds.network_config, m_initramfs_config.return_value) |
297 | + self.assertIn('Failed to fetch secondary network configuration', |
298 | + self.logs.getvalue()) |
299 | + |
300 | |
301 | @mock.patch(DS_PATH + "._read_system_uuid", return_value=str(uuid.uuid4())) |
302 | class TestReadMetaData(test_helpers.HttprettyTestCase): |
303 | @@ -335,4 +463,86 @@ class TestLoadIndex(test_helpers.CiTestCase): |
304 | oracle._load_index("\n".join(["meta_data.json", "user_data"]))) |
305 | |
306 | |
307 | +class TestNetworkConfigFromOpcImds(test_helpers.CiTestCase): |
308 | + |
309 | + with_logs = True |
310 | + |
311 | + def setUp(self): |
312 | + super(TestNetworkConfigFromOpcImds, self).setUp() |
313 | + self.add_patch(DS_PATH + '.readurl', 'm_readurl') |
314 | + self.add_patch(DS_PATH + '.get_interfaces_by_mac', |
315 | + 'm_get_interfaces_by_mac') |
316 | + |
317 | + def test_failure_to_readurl(self): |
318 | + # readurl failures should just bubble out to the caller |
319 | + self.m_readurl.side_effect = Exception('oh no') |
320 | + with self.assertRaises(Exception) as excinfo: |
321 | + oracle._add_network_config_from_opc_imds({}) |
322 | + self.assertEqual(str(excinfo.exception), 'oh no') |
323 | + |
324 | + def test_empty_response(self): |
325 | + # empty response error should just bubble out to the caller |
326 | + self.m_readurl.return_value = '' |
327 | + with self.assertRaises(Exception): |
328 | + oracle._add_network_config_from_opc_imds([]) |
329 | + |
330 | + def test_invalid_json(self): |
331 | + # invalid JSON error should just bubble out to the caller |
332 | + self.m_readurl.return_value = '{' |
333 | + with self.assertRaises(Exception): |
334 | + oracle._add_network_config_from_opc_imds([]) |
335 | + |
336 | + def test_no_secondary_nics_does_not_mutate_input(self): |
337 | + self.m_readurl.return_value = json.dumps([{}]) |
338 | + # We test this by passing in a non-dict to ensure that no dict |
339 | + # operations are used; failure would be seen as exceptions |
340 | + oracle._add_network_config_from_opc_imds(object()) |
341 | + |
342 | + def test_bare_metal_machine_skipped(self): |
343 | + # nicIndex in the first entry indicates a bare metal machine |
344 | + self.m_readurl.return_value = OPC_BM_SECONDARY_VNIC_RESPONSE |
345 | + # We test this by passing in a non-dict to ensure that no dict |
346 | + # operations are used |
347 | + self.assertFalse(oracle._add_network_config_from_opc_imds(object())) |
348 | + self.assertIn('bare metal machine', self.logs.getvalue()) |
349 | + |
350 | + def test_missing_mac_skipped(self): |
351 | + self.m_readurl.return_value = OPC_VM_SECONDARY_VNIC_RESPONSE |
352 | + self.m_get_interfaces_by_mac.return_value = {} |
353 | + |
354 | + network_config = {'version': 1, 'config': [{'primary': 'nic'}]} |
355 | + oracle._add_network_config_from_opc_imds(network_config) |
356 | + |
357 | + self.assertEqual(1, len(network_config['config'])) |
358 | + self.assertIn( |
359 | + 'Interface with MAC 00:00:17:02:2b:b1 not found; skipping', |
360 | + self.logs.getvalue()) |
361 | + |
362 | + def test_secondary_nic(self): |
363 | + self.m_readurl.return_value = OPC_VM_SECONDARY_VNIC_RESPONSE |
364 | + mac_addr, nic_name = '00:00:17:02:2b:b1', 'ens3' |
365 | + self.m_get_interfaces_by_mac.return_value = { |
366 | + mac_addr: nic_name, |
367 | + } |
368 | + |
369 | + network_config = {'version': 1, 'config': [{'primary': 'nic'}]} |
370 | + oracle._add_network_config_from_opc_imds(network_config) |
371 | + |
372 | + # The input is mutated |
373 | + self.assertEqual(2, len(network_config['config'])) |
374 | + |
375 | + secondary_nic_cfg = network_config['config'][1] |
376 | + self.assertEqual(nic_name, secondary_nic_cfg['name']) |
377 | + self.assertEqual('physical', secondary_nic_cfg['type']) |
378 | + self.assertEqual(mac_addr, secondary_nic_cfg['mac_address']) |
379 | + self.assertEqual(9000, secondary_nic_cfg['mtu']) |
380 | + |
381 | + self.assertEqual(1, len(secondary_nic_cfg['subnets'])) |
382 | + subnet_cfg = secondary_nic_cfg['subnets'][0] |
383 | + # These values are hard-coded in OPC_VM_SECONDARY_VNIC_RESPONSE |
384 | + self.assertEqual('10.0.0.231', subnet_cfg['address']) |
385 | + self.assertEqual('24', subnet_cfg['netmask']) |
386 | + self.assertEqual('10.0.0.1', subnet_cfg['gateway']) |
387 | + self.assertEqual('manual', subnet_cfg['control']) |
388 | + |
389 | # vi: ts=4 expandtab |
390 | diff --git a/doc/rtd/topics/datasources/oracle.rst b/doc/rtd/topics/datasources/oracle.rst |
391 | index f2383ce..98c4657 100644 |
392 | --- a/doc/rtd/topics/datasources/oracle.rst |
393 | +++ b/doc/rtd/topics/datasources/oracle.rst |
394 | @@ -8,7 +8,7 @@ This datasource reads metadata, vendor-data and user-data from |
395 | |
396 | Oracle Platform |
397 | --------------- |
398 | -OCI provides bare metal and virtual machines. In both cases, |
399 | +OCI provides bare metal and virtual machines. In both cases, |
400 | the platform identifies itself via DMI data in the chassis asset tag |
401 | with the string 'OracleCloud.com'. |
402 | |
403 | @@ -22,5 +22,28 @@ Cloud-init has a specific datasource for Oracle in order to: |
404 | implementation. |
405 | |
406 | |
407 | +Configuration |
408 | +------------- |
409 | + |
410 | +The following configuration can be set for the datasource in system |
411 | +configuration (in ``/etc/cloud/cloud.cfg`` or ``/etc/cloud/cloud.cfg.d/``). |
412 | + |
413 | +The settings that may be configured are: |
414 | + |
415 | +* **configure_secondary_nics**: A boolean, defaulting to False. If set |
416 | + to True on an OCI Virtual Machine, cloud-init will fetch networking |
417 | + metadata from Oracle's IMDS and use it to configure the non-primary |
418 | + network interface controllers in the system. If set to True on an |
419 | + OCI Bare Metal Machine, it will have no effect (though this may |
420 | + change in the future). |
421 | + |
422 | +An example configuration with the default values is provided below: |
423 | + |
424 | +.. sourcecode:: yaml |
425 | + |
426 | + datasource: |
427 | + Oracle: |
428 | + configure_secondary_nics: false |
429 | + |
430 | .. _Oracle Compute Infrastructure: https://cloud.oracle.com/ |
431 | .. vi: textwidth=78 |
Marking myself as Needs Fixing because I need to write docs before this lands (but that can happen while code review is ongoing).