Skip to content

Commit cd1de5f

Browse files
committed
openstack: Allow discovery in init-local using dhclient in a sandbox.
Network has not yet been configured in the init-local stage so the openstack datasource will use dhcp-client to temporarily obtain an ipv4 address and query the metadata service at http://169.254.169.254 to get network_data.json configuration. If present, the datasource will return network_config version 1 config based on that network_data.json content. Previously OpenStack datasource only setup dhcp on the fallback interface so this represents a change in behavior to react to the full config provided by openstack. Also significant to OpenStack is the separation of a _crawl_data operation from get_data(). crawl_data walks the available metadata services and returns a dict of discovered content. get_data consumes the crawled_data,  caches it in the datasource and reacts to that data. /run/cloud-init/instance-data.json now published network_data.json or ec2_metadata key if that data is present on any datasource. The main reasons for the separation of crawl from get_data:  * Enable performance metrics of cloud-init's metadata crawls on each  * Enable cloud-init modules and scripts to query and consume metadata    content which may have updated/changed after cloud-init's initial cache    during instance boot. (Think hotplug) Also generalize common logic to base DataSource class/module:  * Move to a common UNSET variable up into base datasource module fix EC2,    ConfigDrive, OpenStack, SmartOS to use the global.  * Drop get_url_settings from Ec2, CloudStack and OpenStack and generalize    DataSource.get_url_params(). Allow subclasses to override url_max_wait,    url_timeout and url_retries params.  * Rename get_network_metadata bool to perform_dhcp_setup as it designates    whether EphemeralDHCPv4 setup is required before crawling metadata. LP: #1749717
1 parent 12799d9 commit cd1de5f

File tree

9 files changed

+416
-124
lines changed

9 files changed

+416
-124
lines changed

cloudinit/sources/DataSourceCloudStack.py

Lines changed: 10 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,10 @@ class DataSourceCloudStack(sources.DataSource):
6868

6969
dsname = 'CloudStack'
7070

71+
# Setup read_url parameters per get_url_params.
72+
url_max_wait = 120
73+
url_timeout = 50
74+
7175
def __init__(self, sys_cfg, distro, paths):
7276
sources.DataSource.__init__(self, sys_cfg, distro, paths)
7377
self.seed_dir = os.path.join(paths.seed_dir, 'cs')
@@ -80,33 +84,18 @@ def __init__(self, sys_cfg, distro, paths):
8084
self.metadata_address = "http://%s/" % (self.vr_addr,)
8185
self.cfg = {}
8286

83-
def _get_url_settings(self):
84-
mcfg = self.ds_cfg
85-
max_wait = 120
86-
try:
87-
max_wait = int(mcfg.get("max_wait", max_wait))
88-
except Exception:
89-
util.logexc(LOG, "Failed to get max wait. using %s", max_wait)
87+
def wait_for_metadata_service(self):
88+
url_params = self.get_url_params()
9089

91-
if max_wait == 0:
90+
if url_params.max_wait_seconds <= 0:
9291
return False
9392

94-
timeout = 50
95-
try:
96-
timeout = int(mcfg.get("timeout", timeout))
97-
except Exception:
98-
util.logexc(LOG, "Failed to get timeout, using %s", timeout)
99-
100-
return (max_wait, timeout)
101-
102-
def wait_for_metadata_service(self):
103-
(max_wait, timeout) = self._get_url_settings()
104-
10593
urls = [uhelp.combine_url(self.metadata_address,
10694
'latest/meta-data/instance-id')]
10795
start_time = time.time()
108-
url = uhelp.wait_for_url(urls=urls, max_wait=max_wait,
109-
timeout=timeout, status_cb=LOG.warn)
96+
url = uhelp.wait_for_url(
97+
urls=urls, max_wait=url_params.max_wait_seconds,
98+
timeout=url_params.timeout_seconds, status_cb=LOG.warn)
11099

111100
if url:
112101
LOG.debug("Using metadata source: '%s'", url)

cloudinit/sources/DataSourceConfigDrive.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ def __init__(self, sys_cfg, distro, paths):
4343
self.version = None
4444
self.ec2_metadata = None
4545
self._network_config = None
46-
self.network_json = None
46+
self.network_json = sources.UNSET
4747
self.network_eni = None
4848
self.known_macs = None
4949
self.files = {}
@@ -149,7 +149,7 @@ def check_instance_id(self, sys_cfg):
149149
@property
150150
def network_config(self):
151151
if self._network_config is None:
152-
if self.network_json is not None:
152+
if self.network_json not in (None, sources.UNSET):
153153
LOG.debug("network config provided via network_json")
154154
self._network_config = openstack.convert_net_json(
155155
self.network_json, known_macs=self.known_macs)

cloudinit/sources/DataSourceEc2.py

Lines changed: 15 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,6 @@
2727
STRICT_ID_PATH = ("datasource", "Ec2", "strict_id")
2828
STRICT_ID_DEFAULT = "warn"
2929

30-
_unset = "_unset"
31-
3230

3331
class Platforms(object):
3432
# TODO Rename and move to cloudinit.cloud.CloudNames
@@ -59,15 +57,16 @@ class DataSourceEc2(sources.DataSource):
5957
# for extended metadata content. IPv6 support comes in 2016-09-02
6058
extended_metadata_versions = ['2016-09-02']
6159

60+
# Setup read_url parameters per get_url_params.
61+
url_max_wait = 120
62+
url_timeout = 50
63+
6264
_cloud_platform = None
6365

64-
_network_config = _unset # Used for caching calculated network config v1
66+
_network_config = sources.UNSET # Used to cache calculated network cfg v1
6567

6668
# Whether we want to get network configuration from the metadata service.
67-
get_network_metadata = False
68-
69-
# Track the discovered fallback nic for use in configuration generation.
70-
_fallback_interface = None
69+
perform_dhcp_setup = False
7170

7271
def __init__(self, sys_cfg, distro, paths):
7372
super(DataSourceEc2, self).__init__(sys_cfg, distro, paths)
@@ -98,7 +97,7 @@ def _get_data(self):
9897
elif self.cloud_platform == Platforms.NO_EC2_METADATA:
9998
return False
10099

101-
if self.get_network_metadata: # Setup networking in init-local stage.
100+
if self.perform_dhcp_setup: # Setup networking in init-local stage.
102101
if util.is_FreeBSD():
103102
LOG.debug("FreeBSD doesn't support running dhclient with -sf")
104103
return False
@@ -158,27 +157,11 @@ def get_instance_id(self):
158157
else:
159158
return self.metadata['instance-id']
160159

161-
def _get_url_settings(self):
162-
mcfg = self.ds_cfg
163-
max_wait = 120
164-
try:
165-
max_wait = int(mcfg.get("max_wait", max_wait))
166-
except Exception:
167-
util.logexc(LOG, "Failed to get max wait. using %s", max_wait)
168-
169-
timeout = 50
170-
try:
171-
timeout = max(0, int(mcfg.get("timeout", timeout)))
172-
except Exception:
173-
util.logexc(LOG, "Failed to get timeout, using %s", timeout)
174-
175-
return (max_wait, timeout)
176-
177160
def wait_for_metadata_service(self):
178161
mcfg = self.ds_cfg
179162

180-
(max_wait, timeout) = self._get_url_settings()
181-
if max_wait <= 0:
163+
url_params = self.get_url_params()
164+
if url_params.max_wait_seconds <= 0:
182165
return False
183166

184167
# Remove addresses from the list that wont resolve.
@@ -205,7 +188,8 @@ def wait_for_metadata_service(self):
205188

206189
start_time = time.time()
207190
url = uhelp.wait_for_url(
208-
urls=urls, max_wait=max_wait, timeout=timeout, status_cb=LOG.warn)
191+
urls=urls, max_wait=url_params.max_wait_seconds,
192+
timeout=url_params.timeout_seconds, status_cb=LOG.warn)
209193

210194
if url:
211195
self.metadata_address = url2base[url]
@@ -310,11 +294,11 @@ def activate(self, cfg, is_new_instance):
310294
@property
311295
def network_config(self):
312296
"""Return a network config dict for rendering ENI or netplan files."""
313-
if self._network_config != _unset:
297+
if self._network_config != sources.UNSET:
314298
return self._network_config
315299

316300
if self.metadata is None:
317-
# this would happen if get_data hadn't been called. leave as _unset
301+
# this would happen if get_data hadn't been called. leave as UNSET
318302
LOG.warning(
319303
"Unexpected call to network_config when metadata is None.")
320304
return None
@@ -353,9 +337,7 @@ def fallback_interface(self):
353337
self._fallback_interface = _legacy_fbnic
354338
self.fallback_nic = None
355339
else:
356-
self._fallback_interface = net.find_fallback_nic()
357-
if self._fallback_interface is None:
358-
LOG.warning("Did not find a fallback interface on EC2.")
340+
return super(DataSourceEc2, self).fallback_interface
359341
return self._fallback_interface
360342

361343
def _crawl_metadata(self):
@@ -390,7 +372,7 @@ class DataSourceEc2Local(DataSourceEc2):
390372
metadata service. If the metadata service provides network configuration
391373
then render the network configuration for that instance based on metadata.
392374
"""
393-
get_network_metadata = True # Get metadata network config if present
375+
perform_dhcp_setup = True # Use dhcp before querying metadata
394376

395377
def get_data(self):
396378
supported_platforms = (Platforms.AWS,)

0 commit comments

Comments
 (0)