Merge ~oddbloke/cloud-init/+git/cloud-init:oci-vnic into cloud-init:master

Proposed by Dan Watkins
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)
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_secondary_nics` enables it:

    datasource:
      Oracle:
        configure_secondary_nics: true

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.net.cmdline generates v1 config
    and we need to integrate the secondary NICs into that configuration.

To post a comment you must log in.
Revision history for this message
Dan Watkins (oddbloke) wrote :

Marking myself as Needs Fixing because I need to write docs before this lands (but that can happen while code review is ongoing).

review: Needs Fixing
Revision history for this message
Server Team CI bot (server-team-bot) wrote :

PASSED: Continuous integration, rev:c5a8c2ff40cfc7cd563fbde0c106546a64ac600c
https://jenkins.ubuntu.com/server/job/cloud-init-ci/1033/
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://jenkins.ubuntu.com/server/job/cloud-init-ci/1033//rebuild

review: Approve (continuous-integration)
Revision history for this message
Dan Watkins (oddbloke) wrote :

Docs are now present, so switching my review to Abstain.

review: Abstain
Revision history for this message
Server Team CI bot (server-team-bot) wrote :

PASSED: Continuous integration, rev:bb32cd9fa456afa71b6955091092a0647726d2b1
https://jenkins.ubuntu.com/server/job/cloud-init-ci/1035/
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://jenkins.ubuntu.com/server/job/cloud-init-ci/1035//rebuild

review: Approve (continuous-integration)
Revision history for this message
Chad Smith (chad.smith) :
Revision history for this message
Ryan Harper (raharper) wrote :

Looks good. A few in-line questions.

Revision history for this message
Dan Watkins (oddbloke) wrote :

Thanks for the reviews! Responses inline.

Revision history for this message
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.

Revision history for this message
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.)

Revision history for this message
Chad Smith (chad.smith) wrote :

LGTM.

review: Approve
Revision history for this message
Chad Smith (chad.smith) wrote :

minor logging nit and a folowup question on _network_config_from_opc_imds mutate vs. altertive return values

8898a51... by Dan Watkins

DataSourceOracle: drop log level down to debug

Revision history for this message
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.

Revision history for this message
Server Team CI bot (server-team-bot) wrote :

PASSED: Continuous integration, rev:7e0f6891b378a14913ea36716d2ba4fe7db62333
https://jenkins.ubuntu.com/server/job/cloud-init-ci/1037/
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://jenkins.ubuntu.com/server/job/cloud-init-ci/1037//rebuild

review: Approve (continuous-integration)
Revision history for this message
Ryan Harper (raharper) :
Revision history for this message
Chad Smith (chad.smith) wrote :

Again another volley of trivial comments. Otherwise looks good to me!

Revision history for this message
Chad Smith (chad.smith) :
review: Approve
Revision history for this message
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

Revision history for this message
Dan Watkins (oddbloke) wrote :

Both comments requiring action addressed.

Revision history for this message
Server Team CI bot (server-team-bot) wrote :

FAILED: Continuous integration, rev:246c359e4804a06edf43e456f733337329adf5a9
https://jenkins.ubuntu.com/server/job/cloud-init-ci/1041/
Executed test runs:
    SUCCESS: Checkout
    FAILED: Unit & Style Tests

Click here to trigger a rebuild:
https://jenkins.ubuntu.com/server/job/cloud-init-ci/1041//rebuild

review: Needs Fixing (continuous-integration)
Revision history for this message
Chad Smith (chad.smith) wrote :

+1 on this branch once CI is fixed for pycodestyle
cloudinit/sources/tests/test_oracle.py:25:80: E501 line too long (95 > 79 characters)
cloudinit/sources/tests/test_oracle.py:33:80: E501 line too long (95 > 79 characters)

review: Approve
Revision history for this message
Server Team CI bot (server-team-bot) wrote :

PASSED: Continuous integration, rev:63e2ed22336e8a8838cf10c586b07d751c811dc2
https://jenkins.ubuntu.com/server/job/cloud-init-ci/1042/
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://jenkins.ubuntu.com/server/job/cloud-init-ci/1042//rebuild

review: Approve (continuous-integration)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
diff --git a/cloudinit/sources/DataSourceOracle.py b/cloudinit/sources/DataSourceOracle.py
index 76cfa38..086af79 100644
--- a/cloudinit/sources/DataSourceOracle.py
+++ b/cloudinit/sources/DataSourceOracle.py
@@ -16,7 +16,7 @@ Notes:
16"""16"""
1717
18from cloudinit.url_helper import combine_url, readurl, UrlError18from cloudinit.url_helper import combine_url, readurl, UrlError
19from cloudinit.net import dhcp19from cloudinit.net import dhcp, get_interfaces_by_mac
20from cloudinit import net20from cloudinit import net
21from cloudinit import sources21from cloudinit import sources
22from cloudinit import util22from cloudinit import util
@@ -28,8 +28,80 @@ import re
2828
29LOG = logging.getLogger(__name__)29LOG = logging.getLogger(__name__)
3030
31BUILTIN_DS_CONFIG = {
32 # Don't use IMDS to configure secondary NICs by default
33 'configure_secondary_nics': False,
34}
31CHASSIS_ASSET_TAG = "OracleCloud.com"35CHASSIS_ASSET_TAG = "OracleCloud.com"
32METADATA_ENDPOINT = "http://169.254.169.254/openstack/"36METADATA_ENDPOINT = "http://169.254.169.254/openstack/"
37VNIC_METADATA_URL = 'http://169.254.169.254/opc/v1/vnics/'
38# https://docs.cloud.oracle.com/iaas/Content/Network/Troubleshoot/connectionhang.htm#Overview,
39# indicates that an MTU of 9000 is used within OCI
40MTU = 9000
41
42
43def _add_network_config_from_opc_imds(network_config):
44 """
45 Fetch data from Oracle's IMDS, generate secondary NIC config, merge it.
46
47 The primary NIC configuration should not be modified based on the IMDS
48 values, as it should continue to be configured for DHCP. As such, this
49 takes an existing network_config dict which is expected to have the primary
50 NIC configuration already present. It will mutate the given dict to
51 include the secondary VNICs.
52
53 :param network_config:
54 A v1 network config dict with the primary NIC already configured. This
55 dict will be mutated.
56
57 :raises:
58 Exceptions are not handled within this function. Likely exceptions are
59 those raised by url_helper.readurl (if communicating with the IMDS
60 fails), ValueError/JSONDecodeError (if the IMDS returns invalid JSON),
61 and KeyError/IndexError (if the IMDS returns valid JSON with unexpected
62 contents).
63 """
64 resp = readurl(VNIC_METADATA_URL)
65 vnics = json.loads(str(resp))
66
67 if 'nicIndex' in vnics[0]:
68 # TODO: Once configure_secondary_nics defaults to True, lower the level
69 # of this log message. (Currently, if we're running this code at all,
70 # someone has explicitly opted-in to secondary VNIC configuration, so
71 # we should warn them that it didn't happen. Once it's default, this
72 # would be emitted on every Bare Metal Machine launch, which means INFO
73 # or DEBUG would be more appropriate.)
74 LOG.warning(
75 'VNIC metadata indicates this is a bare metal machine; skipping'
76 ' secondary VNIC configuration.'
77 )
78 return
79
80 interfaces_by_mac = get_interfaces_by_mac()
81
82 for vnic_dict in vnics[1:]:
83 # We skip the first entry in the response because the primary interface
84 # is already configured by iSCSI boot; applying configuration from the
85 # IMDS is not required.
86 mac_address = vnic_dict['macAddr'].lower()
87 if mac_address not in interfaces_by_mac:
88 LOG.debug('Interface with MAC %s not found; skipping', mac_address)
89 continue
90 name = interfaces_by_mac[mac_address]
91 subnet = {
92 'type': 'static',
93 'address': vnic_dict['privateIp'],
94 'netmask': vnic_dict['subnetCidrBlock'].split('/')[1],
95 'gateway': vnic_dict['virtualRouterIp'],
96 'control': 'manual',
97 }
98 network_config['config'].append({
99 'name': name,
100 'type': 'physical',
101 'mac_address': mac_address,
102 'mtu': MTU,
103 'subnets': [subnet],
104 })
33105
34106
35class DataSourceOracle(sources.DataSource):107class DataSourceOracle(sources.DataSource):
@@ -39,6 +111,13 @@ class DataSourceOracle(sources.DataSource):
39 vendordata_pure = None111 vendordata_pure = None
40 _network_config = sources.UNSET112 _network_config = sources.UNSET
41113
114 def __init__(self, sys_cfg, *args, **kwargs):
115 super(DataSourceOracle, self).__init__(sys_cfg, *args, **kwargs)
116
117 self.ds_cfg = util.mergemanydict([
118 util.get_cfg_by_path(sys_cfg, ['datasource', self.dsname], {}),
119 BUILTIN_DS_CONFIG])
120
42 def _is_platform_viable(self):121 def _is_platform_viable(self):
43 """Check platform environment to report if this datasource may run."""122 """Check platform environment to report if this datasource may run."""
44 return _is_platform_viable()123 return _is_platform_viable()
@@ -121,6 +200,14 @@ class DataSourceOracle(sources.DataSource):
121 self._network_config = cmdline.read_initramfs_config()200 self._network_config = cmdline.read_initramfs_config()
122 if not self._network_config:201 if not self._network_config:
123 self._network_config = self.distro.generate_fallback_config()202 self._network_config = self.distro.generate_fallback_config()
203 if self.ds_cfg.get('configure_secondary_nics'):
204 try:
205 # Mutate self._network_config to include secondary VNICs
206 _add_network_config_from_opc_imds(self._network_config)
207 except Exception:
208 util.logexc(
209 LOG,
210 "Failed to fetch secondary network configuration!")
124 return self._network_config211 return self._network_config
125212
126213
diff --git a/cloudinit/sources/tests/test_oracle.py b/cloudinit/sources/tests/test_oracle.py
index 282382c..3e14677 100644
--- a/cloudinit/sources/tests/test_oracle.py
+++ b/cloudinit/sources/tests/test_oracle.py
@@ -18,10 +18,52 @@ import uuid
18DS_PATH = "cloudinit.sources.DataSourceOracle"18DS_PATH = "cloudinit.sources.DataSourceOracle"
19MD_VER = "2013-10-17"19MD_VER = "2013-10-17"
2020
21# `curl -L http://169.254.169.254/opc/v1/vnics/` on a Oracle Bare Metal Machine
22# with a secondary VNIC attached (vnicId truncated for Python line length)
23OPC_BM_SECONDARY_VNIC_RESPONSE = """\
24[ {
25 "vnicId" : "ocid1.vnic.oc1.phx.abyhqljtyvcucqkhdqmgjszebxe4hrb!!TRUNCATED||",
26 "privateIp" : "10.0.0.8",
27 "vlanTag" : 0,
28 "macAddr" : "90:e2:ba:d4:f1:68",
29 "virtualRouterIp" : "10.0.0.1",
30 "subnetCidrBlock" : "10.0.0.0/24",
31 "nicIndex" : 0
32}, {
33 "vnicId" : "ocid1.vnic.oc1.phx.abyhqljtfmkxjdy2sqidndiwrsg63zf!!TRUNCATED||",
34 "privateIp" : "10.0.4.5",
35 "vlanTag" : 1,
36 "macAddr" : "02:00:17:05:CF:51",
37 "virtualRouterIp" : "10.0.4.1",
38 "subnetCidrBlock" : "10.0.4.0/24",
39 "nicIndex" : 0
40} ]"""
41
42# `curl -L http://169.254.169.254/opc/v1/vnics/` on a Oracle Virtual Machine
43# with a secondary VNIC attached
44OPC_VM_SECONDARY_VNIC_RESPONSE = """\
45[ {
46 "vnicId" : "ocid1.vnic.oc1.phx.abyhqljtch72z5pd76cc2636qeqh7z_truncated",
47 "privateIp" : "10.0.0.230",
48 "vlanTag" : 1039,
49 "macAddr" : "02:00:17:05:D1:DB",
50 "virtualRouterIp" : "10.0.0.1",
51 "subnetCidrBlock" : "10.0.0.0/24"
52}, {
53 "vnicId" : "ocid1.vnic.oc1.phx.abyhqljt4iew3gwmvrwrhhf3bp5drj_truncated",
54 "privateIp" : "10.0.0.231",
55 "vlanTag" : 1041,
56 "macAddr" : "00:00:17:02:2B:B1",
57 "virtualRouterIp" : "10.0.0.1",
58 "subnetCidrBlock" : "10.0.0.0/24"
59} ]"""
60
2161
22class TestDataSourceOracle(test_helpers.CiTestCase):62class TestDataSourceOracle(test_helpers.CiTestCase):
23 """Test datasource DataSourceOracle."""63 """Test datasource DataSourceOracle."""
2464
65 with_logs = True
66
25 ds_class = oracle.DataSourceOracle67 ds_class = oracle.DataSourceOracle
2668
27 my_uuid = str(uuid.uuid4())69 my_uuid = str(uuid.uuid4())
@@ -79,6 +121,16 @@ class TestDataSourceOracle(test_helpers.CiTestCase):
79 self.assertEqual(121 self.assertEqual(
80 'metadata (http://169.254.169.254/openstack/)', ds.subplatform)122 'metadata (http://169.254.169.254/openstack/)', ds.subplatform)
81123
124 def test_sys_cfg_can_enable_configure_secondary_nics(self):
125 # Confirm that behaviour is toggled by sys_cfg
126 ds, _mocks = self._get_ds()
127 self.assertFalse(ds.ds_cfg['configure_secondary_nics'])
128
129 sys_cfg = {
130 'datasource': {'Oracle': {'configure_secondary_nics': True}}}
131 ds, _mocks = self._get_ds(sys_cfg=sys_cfg)
132 self.assertTrue(ds.ds_cfg['configure_secondary_nics'])
133
82 @mock.patch(DS_PATH + "._is_iscsi_root", return_value=True)134 @mock.patch(DS_PATH + "._is_iscsi_root", return_value=True)
83 def test_without_userdata(self, m_is_iscsi_root):135 def test_without_userdata(self, m_is_iscsi_root):
84 """If no user-data is provided, it should not be in return dict."""136 """If no user-data is provided, it should not be in return dict."""
@@ -133,9 +185,12 @@ class TestDataSourceOracle(test_helpers.CiTestCase):
133 self.assertEqual(self.my_md['uuid'], ds.get_instance_id())185 self.assertEqual(self.my_md['uuid'], ds.get_instance_id())
134 self.assertEqual(my_userdata, ds.userdata_raw)186 self.assertEqual(my_userdata, ds.userdata_raw)
135187
188 @mock.patch(DS_PATH + "._add_network_config_from_opc_imds",
189 side_effect=lambda network_config: network_config)
136 @mock.patch(DS_PATH + ".cmdline.read_initramfs_config")190 @mock.patch(DS_PATH + ".cmdline.read_initramfs_config")
137 @mock.patch(DS_PATH + "._is_iscsi_root", return_value=True)191 @mock.patch(DS_PATH + "._is_iscsi_root", return_value=True)
138 def test_network_cmdline(self, m_is_iscsi_root, m_initramfs_config):192 def test_network_cmdline(self, m_is_iscsi_root, m_initramfs_config,
193 _m_add_network_config_from_opc_imds):
139 """network_config should read kernel cmdline."""194 """network_config should read kernel cmdline."""
140 distro = mock.MagicMock()195 distro = mock.MagicMock()
141 ds, _ = self._get_ds(distro=distro, patches={196 ds, _ = self._get_ds(distro=distro, patches={
@@ -151,9 +206,12 @@ class TestDataSourceOracle(test_helpers.CiTestCase):
151 self.assertEqual([mock.call()], m_initramfs_config.call_args_list)206 self.assertEqual([mock.call()], m_initramfs_config.call_args_list)
152 self.assertFalse(distro.generate_fallback_config.called)207 self.assertFalse(distro.generate_fallback_config.called)
153208
209 @mock.patch(DS_PATH + "._add_network_config_from_opc_imds",
210 side_effect=lambda network_config: network_config)
154 @mock.patch(DS_PATH + ".cmdline.read_initramfs_config")211 @mock.patch(DS_PATH + ".cmdline.read_initramfs_config")
155 @mock.patch(DS_PATH + "._is_iscsi_root", return_value=True)212 @mock.patch(DS_PATH + "._is_iscsi_root", return_value=True)
156 def test_network_fallback(self, m_is_iscsi_root, m_initramfs_config):213 def test_network_fallback(self, m_is_iscsi_root, m_initramfs_config,
214 _m_add_network_config_from_opc_imds):
157 """test that fallback network is generated if no kernel cmdline."""215 """test that fallback network is generated if no kernel cmdline."""
158 distro = mock.MagicMock()216 distro = mock.MagicMock()
159 ds, _ = self._get_ds(distro=distro, patches={217 ds, _ = self._get_ds(distro=distro, patches={
@@ -175,6 +233,76 @@ class TestDataSourceOracle(test_helpers.CiTestCase):
175 self.assertEqual(ncfg, ds.network_config)233 self.assertEqual(ncfg, ds.network_config)
176 self.assertEqual(1, m_initramfs_config.call_count)234 self.assertEqual(1, m_initramfs_config.call_count)
177235
236 @mock.patch(DS_PATH + "._add_network_config_from_opc_imds")
237 @mock.patch(DS_PATH + ".cmdline.read_initramfs_config",
238 return_value={'some': 'config'})
239 @mock.patch(DS_PATH + "._is_iscsi_root", return_value=True)
240 def test_secondary_nics_added_to_network_config_if_enabled(
241 self, _m_is_iscsi_root, _m_initramfs_config,
242 m_add_network_config_from_opc_imds):
243
244 needle = object()
245
246 def network_config_side_effect(network_config):
247 network_config['secondary_added'] = needle
248
249 m_add_network_config_from_opc_imds.side_effect = (
250 network_config_side_effect)
251
252 distro = mock.MagicMock()
253 ds, _ = self._get_ds(distro=distro, patches={
254 '_is_platform_viable': {'return_value': True},
255 'crawl_metadata': {
256 'return_value': {
257 MD_VER: {'system_uuid': self.my_uuid,
258 'meta_data': self.my_md}}}})
259 ds.ds_cfg['configure_secondary_nics'] = True
260 self.assertEqual(needle, ds.network_config['secondary_added'])
261
262 @mock.patch(DS_PATH + "._add_network_config_from_opc_imds")
263 @mock.patch(DS_PATH + ".cmdline.read_initramfs_config",
264 return_value={'some': 'config'})
265 @mock.patch(DS_PATH + "._is_iscsi_root", return_value=True)
266 def test_secondary_nics_not_added_to_network_config_by_default(
267 self, _m_is_iscsi_root, _m_initramfs_config,
268 m_add_network_config_from_opc_imds):
269
270 def network_config_side_effect(network_config):
271 network_config['secondary_added'] = True
272
273 m_add_network_config_from_opc_imds.side_effect = (
274 network_config_side_effect)
275
276 distro = mock.MagicMock()
277 ds, _ = self._get_ds(distro=distro, patches={
278 '_is_platform_viable': {'return_value': True},
279 'crawl_metadata': {
280 'return_value': {
281 MD_VER: {'system_uuid': self.my_uuid,
282 'meta_data': self.my_md}}}})
283 self.assertNotIn('secondary_added', ds.network_config)
284
285 @mock.patch(DS_PATH + "._add_network_config_from_opc_imds")
286 @mock.patch(DS_PATH + ".cmdline.read_initramfs_config")
287 @mock.patch(DS_PATH + "._is_iscsi_root", return_value=True)
288 def test_secondary_nic_failure_isnt_blocking(
289 self, _m_is_iscsi_root, m_initramfs_config,
290 m_add_network_config_from_opc_imds):
291
292 m_add_network_config_from_opc_imds.side_effect = Exception()
293
294 distro = mock.MagicMock()
295 ds, _ = self._get_ds(distro=distro, patches={
296 '_is_platform_viable': {'return_value': True},
297 'crawl_metadata': {
298 'return_value': {
299 MD_VER: {'system_uuid': self.my_uuid,
300 'meta_data': self.my_md}}}})
301 ds.ds_cfg['configure_secondary_nics'] = True
302 self.assertEqual(ds.network_config, m_initramfs_config.return_value)
303 self.assertIn('Failed to fetch secondary network configuration',
304 self.logs.getvalue())
305
178306
179@mock.patch(DS_PATH + "._read_system_uuid", return_value=str(uuid.uuid4()))307@mock.patch(DS_PATH + "._read_system_uuid", return_value=str(uuid.uuid4()))
180class TestReadMetaData(test_helpers.HttprettyTestCase):308class TestReadMetaData(test_helpers.HttprettyTestCase):
@@ -335,4 +463,86 @@ class TestLoadIndex(test_helpers.CiTestCase):
335 oracle._load_index("\n".join(["meta_data.json", "user_data"])))463 oracle._load_index("\n".join(["meta_data.json", "user_data"])))
336464
337465
466class TestNetworkConfigFromOpcImds(test_helpers.CiTestCase):
467
468 with_logs = True
469
470 def setUp(self):
471 super(TestNetworkConfigFromOpcImds, self).setUp()
472 self.add_patch(DS_PATH + '.readurl', 'm_readurl')
473 self.add_patch(DS_PATH + '.get_interfaces_by_mac',
474 'm_get_interfaces_by_mac')
475
476 def test_failure_to_readurl(self):
477 # readurl failures should just bubble out to the caller
478 self.m_readurl.side_effect = Exception('oh no')
479 with self.assertRaises(Exception) as excinfo:
480 oracle._add_network_config_from_opc_imds({})
481 self.assertEqual(str(excinfo.exception), 'oh no')
482
483 def test_empty_response(self):
484 # empty response error should just bubble out to the caller
485 self.m_readurl.return_value = ''
486 with self.assertRaises(Exception):
487 oracle._add_network_config_from_opc_imds([])
488
489 def test_invalid_json(self):
490 # invalid JSON error should just bubble out to the caller
491 self.m_readurl.return_value = '{'
492 with self.assertRaises(Exception):
493 oracle._add_network_config_from_opc_imds([])
494
495 def test_no_secondary_nics_does_not_mutate_input(self):
496 self.m_readurl.return_value = json.dumps([{}])
497 # We test this by passing in a non-dict to ensure that no dict
498 # operations are used; failure would be seen as exceptions
499 oracle._add_network_config_from_opc_imds(object())
500
501 def test_bare_metal_machine_skipped(self):
502 # nicIndex in the first entry indicates a bare metal machine
503 self.m_readurl.return_value = OPC_BM_SECONDARY_VNIC_RESPONSE
504 # We test this by passing in a non-dict to ensure that no dict
505 # operations are used
506 self.assertFalse(oracle._add_network_config_from_opc_imds(object()))
507 self.assertIn('bare metal machine', self.logs.getvalue())
508
509 def test_missing_mac_skipped(self):
510 self.m_readurl.return_value = OPC_VM_SECONDARY_VNIC_RESPONSE
511 self.m_get_interfaces_by_mac.return_value = {}
512
513 network_config = {'version': 1, 'config': [{'primary': 'nic'}]}
514 oracle._add_network_config_from_opc_imds(network_config)
515
516 self.assertEqual(1, len(network_config['config']))
517 self.assertIn(
518 'Interface with MAC 00:00:17:02:2b:b1 not found; skipping',
519 self.logs.getvalue())
520
521 def test_secondary_nic(self):
522 self.m_readurl.return_value = OPC_VM_SECONDARY_VNIC_RESPONSE
523 mac_addr, nic_name = '00:00:17:02:2b:b1', 'ens3'
524 self.m_get_interfaces_by_mac.return_value = {
525 mac_addr: nic_name,
526 }
527
528 network_config = {'version': 1, 'config': [{'primary': 'nic'}]}
529 oracle._add_network_config_from_opc_imds(network_config)
530
531 # The input is mutated
532 self.assertEqual(2, len(network_config['config']))
533
534 secondary_nic_cfg = network_config['config'][1]
535 self.assertEqual(nic_name, secondary_nic_cfg['name'])
536 self.assertEqual('physical', secondary_nic_cfg['type'])
537 self.assertEqual(mac_addr, secondary_nic_cfg['mac_address'])
538 self.assertEqual(9000, secondary_nic_cfg['mtu'])
539
540 self.assertEqual(1, len(secondary_nic_cfg['subnets']))
541 subnet_cfg = secondary_nic_cfg['subnets'][0]
542 # These values are hard-coded in OPC_VM_SECONDARY_VNIC_RESPONSE
543 self.assertEqual('10.0.0.231', subnet_cfg['address'])
544 self.assertEqual('24', subnet_cfg['netmask'])
545 self.assertEqual('10.0.0.1', subnet_cfg['gateway'])
546 self.assertEqual('manual', subnet_cfg['control'])
547
338# vi: ts=4 expandtab548# vi: ts=4 expandtab
diff --git a/doc/rtd/topics/datasources/oracle.rst b/doc/rtd/topics/datasources/oracle.rst
index f2383ce..98c4657 100644
--- a/doc/rtd/topics/datasources/oracle.rst
+++ b/doc/rtd/topics/datasources/oracle.rst
@@ -8,7 +8,7 @@ This datasource reads metadata, vendor-data and user-data from
88
9Oracle Platform9Oracle Platform
10---------------10---------------
11OCI provides bare metal and virtual machines. In both cases, 11OCI provides bare metal and virtual machines. In both cases,
12the platform identifies itself via DMI data in the chassis asset tag12the platform identifies itself via DMI data in the chassis asset tag
13with the string 'OracleCloud.com'.13with the string 'OracleCloud.com'.
1414
@@ -22,5 +22,28 @@ Cloud-init has a specific datasource for Oracle in order to:
22 implementation.22 implementation.
2323
2424
25Configuration
26-------------
27
28The following configuration can be set for the datasource in system
29configuration (in ``/etc/cloud/cloud.cfg`` or ``/etc/cloud/cloud.cfg.d/``).
30
31The settings that may be configured are:
32
33* **configure_secondary_nics**: A boolean, defaulting to False. If set
34 to True on an OCI Virtual Machine, cloud-init will fetch networking
35 metadata from Oracle's IMDS and use it to configure the non-primary
36 network interface controllers in the system. If set to True on an
37 OCI Bare Metal Machine, it will have no effect (though this may
38 change in the future).
39
40An example configuration with the default values is provided below:
41
42.. sourcecode:: yaml
43
44 datasource:
45 Oracle:
46 configure_secondary_nics: false
47
25.. _Oracle Compute Infrastructure: https://cloud.oracle.com/48.. _Oracle Compute Infrastructure: https://cloud.oracle.com/
26.. vi: textwidth=7849.. vi: textwidth=78

Subscribers

People subscribed via source and target branches