|
Packit Service |
a04d08 |
# Copyright (C) 2014 Yahoo! Inc.
|
|
Packit Service |
a04d08 |
#
|
|
Packit Service |
a04d08 |
# Author: Joshua Harlow <harlowja@yahoo-inc.com>
|
|
Packit Service |
a04d08 |
#
|
|
Packit Service |
a04d08 |
# This file is part of cloud-init. See LICENSE file for license information.
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
import time
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
from cloudinit import log as logging
|
|
Packit Service |
a04d08 |
from cloudinit.net.dhcp import EphemeralDHCPv4, NoDHCPLeaseError
|
|
Packit Service |
a04d08 |
from cloudinit import sources
|
|
Packit Service |
a04d08 |
from cloudinit import url_helper
|
|
Packit Service |
a04d08 |
from cloudinit import util
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
from cloudinit.sources.helpers import openstack
|
|
Packit Service |
a04d08 |
from cloudinit.sources import DataSourceOracle as oracle
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
LOG = logging.getLogger(__name__)
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
# Various defaults/constants...
|
|
Packit Service |
a04d08 |
DEF_MD_URL = "http://169.254.169.254"
|
|
Packit Service |
a04d08 |
DEFAULT_IID = "iid-dsopenstack"
|
|
Packit Service |
a04d08 |
DEFAULT_METADATA = {
|
|
Packit Service |
a04d08 |
"instance-id": DEFAULT_IID,
|
|
Packit Service |
a04d08 |
}
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
# OpenStack DMI constants
|
|
Packit Service |
a04d08 |
DMI_PRODUCT_NOVA = 'OpenStack Nova'
|
|
Packit Service |
a04d08 |
DMI_PRODUCT_COMPUTE = 'OpenStack Compute'
|
|
Packit Service |
a04d08 |
VALID_DMI_PRODUCT_NAMES = [DMI_PRODUCT_NOVA, DMI_PRODUCT_COMPUTE]
|
|
Packit Service |
a04d08 |
DMI_ASSET_TAG_OPENTELEKOM = 'OpenTelekomCloud'
|
|
Packit Service |
11b429 |
VALID_DMI_ASSET_TAGS = [DMI_ASSET_TAG_OPENTELEKOM]
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
class DataSourceOpenStack(openstack.SourceMixin, sources.DataSource):
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
dsname = "OpenStack"
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
_network_config = sources.UNSET # Used to cache calculated network cfg v1
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
# Whether we want to get network configuration from the metadata service.
|
|
Packit Service |
a04d08 |
perform_dhcp_setup = False
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
def __init__(self, sys_cfg, distro, paths):
|
|
Packit Service |
a04d08 |
super(DataSourceOpenStack, self).__init__(sys_cfg, distro, paths)
|
|
Packit Service |
a04d08 |
self.metadata_address = None
|
|
Packit Service |
a04d08 |
self.ssl_details = util.fetch_ssl_details(self.paths)
|
|
Packit Service |
a04d08 |
self.version = None
|
|
Packit Service |
a04d08 |
self.files = {}
|
|
Packit Service |
a04d08 |
self.ec2_metadata = sources.UNSET
|
|
Packit Service |
a04d08 |
self.network_json = sources.UNSET
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
def __str__(self):
|
|
Packit Service |
a04d08 |
root = sources.DataSource.__str__(self)
|
|
Packit Service |
a04d08 |
mstr = "%s [%s,ver=%s]" % (root, self.dsmode, self.version)
|
|
Packit Service |
a04d08 |
return mstr
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
def wait_for_metadata_service(self):
|
|
Packit Service |
a04d08 |
urls = self.ds_cfg.get("metadata_urls", [DEF_MD_URL])
|
|
Packit Service |
a04d08 |
filtered = [x for x in urls if util.is_resolvable_url(x)]
|
|
Packit Service |
a04d08 |
if set(filtered) != set(urls):
|
|
Packit Service |
a04d08 |
LOG.debug("Removed the following from metadata urls: %s",
|
|
Packit Service |
a04d08 |
list((set(urls) - set(filtered))))
|
|
Packit Service |
a04d08 |
if len(filtered):
|
|
Packit Service |
a04d08 |
urls = filtered
|
|
Packit Service |
a04d08 |
else:
|
|
Packit Service |
a04d08 |
LOG.warning("Empty metadata url list! using default list")
|
|
Packit Service |
a04d08 |
urls = [DEF_MD_URL]
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
md_urls = []
|
|
Packit Service |
a04d08 |
url2base = {}
|
|
Packit Service |
a04d08 |
for url in urls:
|
|
Packit Service |
a04d08 |
md_url = url_helper.combine_url(url, 'openstack')
|
|
Packit Service |
a04d08 |
md_urls.append(md_url)
|
|
Packit Service |
a04d08 |
url2base[md_url] = url
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
url_params = self.get_url_params()
|
|
Packit Service |
a04d08 |
start_time = time.time()
|
|
Packit Service |
a04d08 |
avail_url, _response = url_helper.wait_for_url(
|
|
Packit Service |
a04d08 |
urls=md_urls, max_wait=url_params.max_wait_seconds,
|
|
Packit Service |
a04d08 |
timeout=url_params.timeout_seconds)
|
|
Packit Service |
a04d08 |
if avail_url:
|
|
Packit Service |
a04d08 |
LOG.debug("Using metadata source: '%s'", url2base[avail_url])
|
|
Packit Service |
a04d08 |
else:
|
|
Packit Service |
a04d08 |
LOG.debug("Giving up on OpenStack md from %s after %s seconds",
|
|
Packit Service |
a04d08 |
md_urls, int(time.time() - start_time))
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
self.metadata_address = url2base.get(avail_url)
|
|
Packit Service |
a04d08 |
return bool(avail_url)
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
def check_instance_id(self, sys_cfg):
|
|
Packit Service |
a04d08 |
# quickly (local check only) if self.instance_id is still valid
|
|
Packit Service |
a04d08 |
return sources.instance_id_matches_system_uuid(self.get_instance_id())
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
@property
|
|
Packit Service |
a04d08 |
def network_config(self):
|
|
Packit Service |
a04d08 |
"""Return a network config dict for rendering ENI or netplan files."""
|
|
Packit Service |
a04d08 |
if self._network_config != sources.UNSET:
|
|
Packit Service |
a04d08 |
return self._network_config
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
# RELEASE_BLOCKER: SRU to Xenial and Artful SRU should not provide
|
|
Packit Service |
a04d08 |
# network_config by default unless configured in /etc/cloud/cloud.cfg*.
|
|
Packit Service |
a04d08 |
# Patch Xenial and Artful before release to default to False.
|
|
Packit Service |
a04d08 |
if util.is_false(self.ds_cfg.get('apply_network_config', True)):
|
|
Packit Service |
a04d08 |
self._network_config = None
|
|
Packit Service |
a04d08 |
return self._network_config
|
|
Packit Service |
a04d08 |
if self.network_json == sources.UNSET:
|
|
Packit Service |
a04d08 |
# this would happen if get_data hadn't been called. leave as UNSET
|
|
Packit Service |
a04d08 |
LOG.warning(
|
|
Packit Service |
a04d08 |
'Unexpected call to network_config when network_json is None.')
|
|
Packit Service |
a04d08 |
return None
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
LOG.debug('network config provided via network_json')
|
|
Packit Service |
a04d08 |
self._network_config = openstack.convert_net_json(
|
|
Packit Service |
a04d08 |
self.network_json, known_macs=None)
|
|
Packit Service |
a04d08 |
return self._network_config
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
def _get_data(self):
|
|
Packit Service |
a04d08 |
"""Crawl metadata, parse and persist that data for this instance.
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
@return: True when metadata discovered indicates OpenStack datasource.
|
|
Packit Service |
a04d08 |
False when unable to contact metadata service or when metadata
|
|
Packit Service |
a04d08 |
format is invalid or disabled.
|
|
Packit Service |
a04d08 |
"""
|
|
Packit Service |
a04d08 |
oracle_considered = 'Oracle' in self.sys_cfg.get('datasource_list')
|
|
Packit Service |
a04d08 |
if not detect_openstack(accept_oracle=not oracle_considered):
|
|
Packit Service |
a04d08 |
return False
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
if self.perform_dhcp_setup: # Setup networking in init-local stage.
|
|
Packit Service |
a04d08 |
try:
|
|
Packit Service |
a04d08 |
with EphemeralDHCPv4(self.fallback_interface):
|
|
Packit Service |
a04d08 |
results = util.log_time(
|
|
Packit Service |
a04d08 |
logfunc=LOG.debug, msg='Crawl of metadata service',
|
|
Packit Service |
a04d08 |
func=self._crawl_metadata)
|
|
Packit Service |
a04d08 |
except (NoDHCPLeaseError, sources.InvalidMetaDataException) as e:
|
|
Packit Service |
a04d08 |
util.logexc(LOG, str(e))
|
|
Packit Service |
a04d08 |
return False
|
|
Packit Service |
a04d08 |
else:
|
|
Packit Service |
a04d08 |
try:
|
|
Packit Service |
a04d08 |
results = self._crawl_metadata()
|
|
Packit Service |
a04d08 |
except sources.InvalidMetaDataException as e:
|
|
Packit Service |
a04d08 |
util.logexc(LOG, str(e))
|
|
Packit Service |
a04d08 |
return False
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
self.dsmode = self._determine_dsmode([results.get('dsmode')])
|
|
Packit Service |
a04d08 |
if self.dsmode == sources.DSMODE_DISABLED:
|
|
Packit Service |
a04d08 |
return False
|
|
Packit Service |
a04d08 |
md = results.get('metadata', {})
|
|
Packit Service |
a04d08 |
md = util.mergemanydict([md, DEFAULT_METADATA])
|
|
Packit Service |
a04d08 |
self.metadata = md
|
|
Packit Service |
a04d08 |
self.ec2_metadata = results.get('ec2-metadata')
|
|
Packit Service |
a04d08 |
self.network_json = results.get('networkdata')
|
|
Packit Service |
a04d08 |
self.userdata_raw = results.get('userdata')
|
|
Packit Service |
a04d08 |
self.version = results['version']
|
|
Packit Service |
a04d08 |
self.files.update(results.get('files', {}))
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
vd = results.get('vendordata')
|
|
Packit Service |
a04d08 |
self.vendordata_pure = vd
|
|
Packit Service |
a04d08 |
try:
|
|
Packit Service |
a04d08 |
self.vendordata_raw = sources.convert_vendordata(vd)
|
|
Packit Service |
a04d08 |
except ValueError as e:
|
|
Packit Service |
a04d08 |
LOG.warning("Invalid content in vendor-data: %s", e)
|
|
Packit Service |
a04d08 |
self.vendordata_raw = None
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
return True
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
def _crawl_metadata(self):
|
|
Packit Service |
a04d08 |
"""Crawl metadata service when available.
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
@returns: Dictionary with all metadata discovered for this datasource.
|
|
Packit Service |
a04d08 |
@raise: InvalidMetaDataException on unreadable or broken
|
|
Packit Service |
a04d08 |
metadata.
|
|
Packit Service |
a04d08 |
"""
|
|
Packit Service |
a04d08 |
try:
|
|
Packit Service |
a04d08 |
if not self.wait_for_metadata_service():
|
|
Packit Service |
a04d08 |
raise sources.InvalidMetaDataException(
|
|
Packit Service |
a04d08 |
'No active metadata service found')
|
|
Packit Service |
a04d08 |
except IOError as e:
|
|
Packit Service |
a04d08 |
raise sources.InvalidMetaDataException(
|
|
Packit Service |
a04d08 |
'IOError contacting metadata service: {error}'.format(
|
|
Packit Service |
a04d08 |
error=str(e)))
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
url_params = self.get_url_params()
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
try:
|
|
Packit Service |
a04d08 |
result = util.log_time(
|
|
Packit Service |
a04d08 |
LOG.debug, 'Crawl of openstack metadata service',
|
|
Packit Service |
a04d08 |
read_metadata_service, args=[self.metadata_address],
|
|
Packit Service |
a04d08 |
kwargs={'ssl_details': self.ssl_details,
|
|
Packit Service |
a04d08 |
'retries': url_params.num_retries,
|
|
Packit Service |
a04d08 |
'timeout': url_params.timeout_seconds})
|
|
Packit Service |
a04d08 |
except openstack.NonReadable as e:
|
|
Packit Service |
a04d08 |
raise sources.InvalidMetaDataException(str(e))
|
|
Packit Service |
11b429 |
except (openstack.BrokenMetadata, IOError):
|
|
Packit Service |
a04d08 |
msg = 'Broken metadata address {addr}'.format(
|
|
Packit Service |
a04d08 |
addr=self.metadata_address)
|
|
Packit Service |
11b429 |
raise sources.InvalidMetaDataException(msg)
|
|
Packit Service |
a04d08 |
return result
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
class DataSourceOpenStackLocal(DataSourceOpenStack):
|
|
Packit Service |
a04d08 |
"""Run in init-local using a dhcp discovery prior to metadata crawl.
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
In init-local, no network is available. This subclass sets up minimal
|
|
Packit Service |
a04d08 |
networking with dhclient on a viable nic so that it can talk to the
|
|
Packit Service |
a04d08 |
metadata service. If the metadata service provides network configuration
|
|
Packit Service |
a04d08 |
then render the network configuration for that instance based on metadata.
|
|
Packit Service |
a04d08 |
"""
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
perform_dhcp_setup = True # Get metadata network config if present
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
def read_metadata_service(base_url, ssl_details=None,
|
|
Packit Service |
a04d08 |
timeout=5, retries=5):
|
|
Packit Service |
a04d08 |
reader = openstack.MetadataReader(base_url, ssl_details=ssl_details,
|
|
Packit Service |
a04d08 |
timeout=timeout, retries=retries)
|
|
Packit Service |
a04d08 |
return reader.read_v2()
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
def detect_openstack(accept_oracle=False):
|
|
Packit Service |
a04d08 |
"""Return True when a potential OpenStack platform is detected."""
|
|
Packit Service |
a04d08 |
if not util.is_x86():
|
|
Packit Service |
a04d08 |
return True # Non-Intel cpus don't properly report dmi product names
|
|
Packit Service |
a04d08 |
product_name = util.read_dmi_data('system-product-name')
|
|
Packit Service |
a04d08 |
if product_name in VALID_DMI_PRODUCT_NAMES:
|
|
Packit Service |
a04d08 |
return True
|
|
Packit Service |
a04d08 |
elif util.read_dmi_data('chassis-asset-tag') in VALID_DMI_ASSET_TAGS:
|
|
Packit Service |
a04d08 |
return True
|
|
Packit Service |
a04d08 |
elif accept_oracle and oracle._is_platform_viable():
|
|
Packit Service |
a04d08 |
return True
|
|
Packit Service |
a04d08 |
elif util.get_proc_env(1).get('product_name') == DMI_PRODUCT_NOVA:
|
|
Packit Service |
a04d08 |
return True
|
|
Packit Service |
a04d08 |
return False
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
# Used to match classes to dependencies
|
|
Packit Service |
a04d08 |
datasources = [
|
|
Packit Service |
a04d08 |
(DataSourceOpenStackLocal, (sources.DEP_FILESYSTEM,)),
|
|
Packit Service |
a04d08 |
(DataSourceOpenStack, (sources.DEP_FILESYSTEM, sources.DEP_NETWORK)),
|
|
Packit Service |
a04d08 |
]
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
# Return a list of data sources that match this set of dependencies
|
|
Packit Service |
a04d08 |
def get_datasource_list(depends):
|
|
Packit Service |
a04d08 |
return sources.list_from_depends(depends, datasources)
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
# vi: ts=4 expandtab
|