summaryrefslogtreecommitdiff
path: root/cloudinit/sources
diff options
context:
space:
mode:
Diffstat (limited to 'cloudinit/sources')
-rw-r--r--cloudinit/sources/DataSourceAliYun.py4
-rw-r--r--cloudinit/sources/DataSourceEc2.py147
-rw-r--r--cloudinit/sources/DataSourceOVF.py37
-rw-r--r--cloudinit/sources/DataSourceOpenStack.py15
-rw-r--r--cloudinit/sources/helpers/vmware/imc/config_nic.py24
5 files changed, 200 insertions, 27 deletions
diff --git a/cloudinit/sources/DataSourceAliYun.py b/cloudinit/sources/DataSourceAliYun.py
index 2d00255c..9debe947 100644
--- a/cloudinit/sources/DataSourceAliYun.py
+++ b/cloudinit/sources/DataSourceAliYun.py
@@ -22,6 +22,10 @@ class DataSourceAliYun(EC2.DataSourceEc2):
def get_public_ssh_keys(self):
return parse_public_keys(self.metadata.get('public-keys', {}))
+ @property
+ def cloud_platform(self):
+ return EC2.Platforms.ALIYUN
+
def parse_public_keys(public_keys):
keys = []
diff --git a/cloudinit/sources/DataSourceEc2.py b/cloudinit/sources/DataSourceEc2.py
index c657fd09..6f01a139 100644
--- a/cloudinit/sources/DataSourceEc2.py
+++ b/cloudinit/sources/DataSourceEc2.py
@@ -16,18 +16,31 @@ from cloudinit import log as logging
from cloudinit import sources
from cloudinit import url_helper as uhelp
from cloudinit import util
+from cloudinit import warnings
LOG = logging.getLogger(__name__)
# Which version we are requesting of the ec2 metadata apis
DEF_MD_VERSION = '2009-04-04'
+STRICT_ID_PATH = ("datasource", "Ec2", "strict_id")
+STRICT_ID_DEFAULT = "warn"
+
+
+class Platforms(object):
+ ALIYUN = "AliYun"
+ AWS = "AWS"
+ BRIGHTBOX = "Brightbox"
+ SEEDED = "Seeded"
+ UNKNOWN = "Unknown"
+
class DataSourceEc2(sources.DataSource):
# Default metadata urls that will be used if none are provided
# They will be checked for 'resolveability' and some of the
# following may be discarded if they do not resolve
metadata_urls = ["http://169.254.169.254", "http://instance-data.:8773"]
+ _cloud_platform = None
def __init__(self, sys_cfg, distro, paths):
sources.DataSource.__init__(self, sys_cfg, distro, paths)
@@ -41,8 +54,18 @@ class DataSourceEc2(sources.DataSource):
self.userdata_raw = seed_ret['user-data']
self.metadata = seed_ret['meta-data']
LOG.debug("Using seeded ec2 data from %s", self.seed_dir)
+ self._cloud_platform = Platforms.SEEDED
return True
+ strict_mode, _sleep = read_strict_mode(
+ util.get_cfg_by_path(self.sys_cfg, STRICT_ID_PATH,
+ STRICT_ID_DEFAULT), ("warn", None))
+
+ LOG.debug("strict_mode: %s, cloud_platform=%s",
+ strict_mode, self.cloud_platform)
+ if strict_mode == "true" and self.cloud_platform == Platforms.UNKNOWN:
+ return False
+
try:
if not self.wait_for_metadata_service():
return False
@@ -51,8 +74,8 @@ class DataSourceEc2(sources.DataSource):
ec2.get_instance_userdata(self.api_ver, self.metadata_address)
self.metadata = ec2.get_instance_metadata(self.api_ver,
self.metadata_address)
- LOG.debug("Crawl of metadata service took %s seconds",
- int(time.time() - start_time))
+ LOG.debug("Crawl of metadata service took %.3f seconds",
+ time.time() - start_time)
return True
except Exception:
util.logexc(LOG, "Failed reading from metadata address %s",
@@ -190,6 +213,126 @@ class DataSourceEc2(sources.DataSource):
return az[:-1]
return None
+ @property
+ def cloud_platform(self):
+ if self._cloud_platform is None:
+ self._cloud_platform = identify_platform()
+ return self._cloud_platform
+
+ def activate(self, cfg, is_new_instance):
+ if not is_new_instance:
+ return
+ if self.cloud_platform == Platforms.UNKNOWN:
+ warn_if_necessary(
+ util.get_cfg_by_path(cfg, STRICT_ID_PATH, STRICT_ID_DEFAULT),
+ cfg)
+
+
+def read_strict_mode(cfgval, default):
+ try:
+ return parse_strict_mode(cfgval)
+ except ValueError as e:
+ LOG.warn(e)
+ return default
+
+
+def parse_strict_mode(cfgval):
+ # given a mode like:
+ # true, false, warn,[sleep]
+ # return tuple with string mode (true|false|warn) and sleep.
+ if cfgval is True:
+ return 'true', None
+ if cfgval is False:
+ return 'false', None
+
+ if not cfgval:
+ return 'warn', 0
+
+ mode, _, sleep = cfgval.partition(",")
+ if mode not in ('true', 'false', 'warn'):
+ raise ValueError(
+ "Invalid mode '%s' in strict_id setting '%s': "
+ "Expected one of 'true', 'false', 'warn'." % (mode, cfgval))
+
+ if sleep:
+ try:
+ sleep = int(sleep)
+ except ValueError:
+ raise ValueError("Invalid sleep '%s' in strict_id setting '%s': "
+ "not an integer" % (sleep, cfgval))
+ else:
+ sleep = None
+
+ return mode, sleep
+
+
+def warn_if_necessary(cfgval, cfg):
+ try:
+ mode, sleep = parse_strict_mode(cfgval)
+ except ValueError as e:
+ LOG.warn(e)
+ return
+
+ if mode == "false":
+ return
+
+ warnings.show_warning('non_ec2_md', cfg, mode=True, sleep=sleep)
+
+
+def identify_aws(data):
+ # data is a dictionary returned by _collect_platform_data.
+ if (data['uuid'].startswith('ec2') and
+ (data['uuid_source'] == 'hypervisor' or
+ data['uuid'] == data['serial'])):
+ return Platforms.AWS
+
+ return None
+
+
+def identify_brightbox(data):
+ if data['serial'].endswith('brightbox.com'):
+ return Platforms.BRIGHTBOX
+
+
+def identify_platform():
+ # identify the platform and return an entry in Platforms.
+ data = _collect_platform_data()
+ checks = (identify_aws, identify_brightbox, lambda x: Platforms.UNKNOWN)
+ for checker in checks:
+ try:
+ result = checker(data)
+ if result:
+ return result
+ except Exception as e:
+ LOG.warn("calling %s with %s raised exception: %s",
+ checker, data, e)
+
+
+def _collect_platform_data():
+ # returns a dictionary with all lower case values:
+ # uuid: system-uuid from dmi or /sys/hypervisor
+ # uuid_source: 'hypervisor' (/sys/hypervisor/uuid) or 'dmi'
+ # serial: dmi 'system-serial-number' (/sys/.../product_serial)
+ data = {}
+ try:
+ uuid = util.load_file("/sys/hypervisor/uuid").strip()
+ data['uuid_source'] = 'hypervisor'
+ except Exception:
+ uuid = util.read_dmi_data('system-uuid')
+ data['uuid_source'] = 'dmi'
+
+ if uuid is None:
+ uuid = ''
+ data['uuid'] = uuid.lower()
+
+ serial = util.read_dmi_data('system-serial-number')
+ if serial is None:
+ serial = ''
+
+ data['serial'] = serial.lower()
+
+ return data
+
# Used to match classes to dependencies
datasources = [
diff --git a/cloudinit/sources/DataSourceOVF.py b/cloudinit/sources/DataSourceOVF.py
index 78928c77..d70784ac 100644
--- a/cloudinit/sources/DataSourceOVF.py
+++ b/cloudinit/sources/DataSourceOVF.py
@@ -48,6 +48,7 @@ class DataSourceOVF(sources.DataSource):
self.environment = None
self.cfg = {}
self.supported_seed_starts = ("/", "file://")
+ self.vmware_customization_supported = True
def __str__(self):
root = sources.DataSource.__str__(self)
@@ -78,7 +79,10 @@ class DataSourceOVF(sources.DataSource):
found.append(seed)
elif system_type and 'vmware' in system_type.lower():
LOG.debug("VMware Virtualization Platform found")
- if not util.get_cfg_option_bool(
+ if not self.vmware_customization_supported:
+ LOG.debug("Skipping the check for "
+ "VMware Customization support")
+ elif not util.get_cfg_option_bool(
self.sys_cfg, "disable_vmware_customization", True):
deployPkgPluginPath = search_file("/usr/lib/vmware-tools",
"libdeployPkgPlugin.so")
@@ -90,17 +94,18 @@ class DataSourceOVF(sources.DataSource):
# copies the customization specification file to
# /var/run/vmware-imc directory. cloud-init code needs
# to search for the file in that directory.
+ max_wait = get_max_wait_from_cfg(self.ds_cfg)
vmwareImcConfigFilePath = util.log_time(
logfunc=LOG.debug,
msg="waiting for configuration file",
func=wait_for_imc_cfg_file,
- args=("/var/run/vmware-imc", "cust.cfg"))
+ args=("/var/run/vmware-imc", "cust.cfg", max_wait))
if vmwareImcConfigFilePath:
- LOG.debug("Found VMware DeployPkg Config File at %s" %
+ LOG.debug("Found VMware Customization Config File at %s",
vmwareImcConfigFilePath)
else:
- LOG.debug("Did not find VMware DeployPkg Config File Path")
+ LOG.debug("Did not find VMware Customization Config File")
else:
LOG.debug("Customization for VMware platform is disabled.")
@@ -206,6 +211,29 @@ class DataSourceOVFNet(DataSourceOVF):
DataSourceOVF.__init__(self, sys_cfg, distro, paths)
self.seed_dir = os.path.join(paths.seed_dir, 'ovf-net')
self.supported_seed_starts = ("http://", "https://", "ftp://")
+ self.vmware_customization_supported = False
+
+
+def get_max_wait_from_cfg(cfg):
+ default_max_wait = 90
+ max_wait_cfg_option = 'vmware_cust_file_max_wait'
+ max_wait = default_max_wait
+
+ if not cfg:
+ return max_wait
+
+ try:
+ max_wait = int(cfg.get(max_wait_cfg_option, default_max_wait))
+ except ValueError:
+ LOG.warn("Failed to get '%s', using %s",
+ max_wait_cfg_option, default_max_wait)
+
+ if max_wait <= 0:
+ LOG.warn("Invalid value '%s' for '%s', using '%s' instead",
+ max_wait, max_wait_cfg_option, default_max_wait)
+ max_wait = default_max_wait
+
+ return max_wait
def wait_for_imc_cfg_file(dirpath, filename, maxwait=180, naplen=5):
@@ -215,6 +243,7 @@ def wait_for_imc_cfg_file(dirpath, filename, maxwait=180, naplen=5):
fileFullPath = search_file(dirpath, filename)
if fileFullPath:
return fileFullPath
+ LOG.debug("Waiting for VMware Customization Config File")
time.sleep(naplen)
waited += naplen
return None
diff --git a/cloudinit/sources/DataSourceOpenStack.py b/cloudinit/sources/DataSourceOpenStack.py
index 2a58f1cd..e1ea21f8 100644
--- a/cloudinit/sources/DataSourceOpenStack.py
+++ b/cloudinit/sources/DataSourceOpenStack.py
@@ -45,6 +45,7 @@ class DataSourceOpenStack(openstack.SourceMixin, sources.DataSource):
# max_wait < 0 indicates do not wait
max_wait = -1
timeout = 10
+ retries = 5
try:
max_wait = int(self.ds_cfg.get("max_wait", max_wait))
@@ -55,7 +56,13 @@ class DataSourceOpenStack(openstack.SourceMixin, sources.DataSource):
timeout = max(0, int(self.ds_cfg.get("timeout", timeout)))
except Exception:
util.logexc(LOG, "Failed to get timeout, using %s", timeout)
- return (max_wait, timeout)
+
+ try:
+ retries = int(self.ds_cfg.get("retries", retries))
+ except Exception:
+ util.logexc(LOG, "Failed to get max wait. using %s", retries)
+
+ return (max_wait, timeout, retries)
def wait_for_metadata_service(self):
urls = self.ds_cfg.get("metadata_urls", [DEF_MD_URL])
@@ -76,7 +83,7 @@ class DataSourceOpenStack(openstack.SourceMixin, sources.DataSource):
md_urls.append(md_url)
url2base[md_url] = url
- (max_wait, timeout) = self._get_url_settings()
+ (max_wait, timeout, retries) = self._get_url_settings()
start_time = time.time()
avail_url = url_helper.wait_for_url(urls=md_urls, max_wait=max_wait,
timeout=timeout)
@@ -89,13 +96,15 @@ class DataSourceOpenStack(openstack.SourceMixin, sources.DataSource):
self.metadata_address = url2base.get(avail_url)
return bool(avail_url)
- def get_data(self, retries=5, timeout=5):
+ def get_data(self):
try:
if not self.wait_for_metadata_service():
return False
except IOError:
return False
+ (max_wait, timeout, retries) = self._get_url_settings()
+
try:
results = util.log_time(LOG.debug,
'Crawl of openstack metadata service',
diff --git a/cloudinit/sources/helpers/vmware/imc/config_nic.py b/cloudinit/sources/helpers/vmware/imc/config_nic.py
index d5a7c346..67ac21db 100644
--- a/cloudinit/sources/helpers/vmware/imc/config_nic.py
+++ b/cloudinit/sources/helpers/vmware/imc/config_nic.py
@@ -101,7 +101,11 @@ class NicConfigurator(object):
return lines
# Static Ipv4
- v4 = nic.staticIpv4
+ addrs = nic.staticIpv4
+ if not addrs:
+ return lines
+
+ v4 = addrs[0]
if v4.ip:
lines.append(' address %s' % v4.ip)
if v4.netmask:
@@ -197,22 +201,6 @@ class NicConfigurator(object):
util.subp(["pkill", "dhclient"], rcs=[0, 1])
util.subp(["rm", "-f", "/var/lib/dhcp/*"])
- def if_down_up(self):
- names = []
- for nic in self.nics:
- name = self.mac2Name.get(nic.mac.lower())
- names.append(name)
-
- for name in names:
- logger.info('Bring down interface %s' % name)
- util.subp(["ifdown", "%s" % name])
-
- self.clear_dhcp()
-
- for name in names:
- logger.info('Bring up interface %s' % name)
- util.subp(["ifup", "%s" % name])
-
def configure(self):
"""
Configure the /etc/network/intefaces
@@ -232,6 +220,6 @@ class NicConfigurator(object):
for line in lines:
fp.write('%s\n' % line)
- self.if_down_up()
+ self.clear_dhcp()
# vi: ts=4 expandtab