summaryrefslogtreecommitdiff
path: root/cloudinit/sources
diff options
context:
space:
mode:
Diffstat (limited to 'cloudinit/sources')
-rw-r--r--cloudinit/sources/DataSourceConfigDrive.py14
-rw-r--r--cloudinit/sources/DataSourceOpenStack.py27
-rw-r--r--cloudinit/sources/helpers/openstack.py68
3 files changed, 73 insertions, 36 deletions
diff --git a/cloudinit/sources/DataSourceConfigDrive.py b/cloudinit/sources/DataSourceConfigDrive.py
index 0c35f83a..4e5d90de 100644
--- a/cloudinit/sources/DataSourceConfigDrive.py
+++ b/cloudinit/sources/DataSourceConfigDrive.py
@@ -125,7 +125,15 @@ class DataSourceConfigDrive(openstack.SourceMixin, sources.DataSource):
self.userdata_raw = results.get('userdata')
self.version = results['version']
self.files.update(results.get('files', {}))
- self.vendordata_raw = results.get('vendordata')
+
+ vd = results.get('vendordata')
+ self.vendordata_pure = vd
+ try:
+ self.vendordata_raw = openstack.convert_vendordata_json(vd)
+ except ValueError as e:
+ LOG.warn("Invalid content in vendor-data: %s", e)
+ self.vendordata_raw = None
+
return True
@@ -160,10 +168,10 @@ def get_ds_mode(cfgdrv_ver, ds_cfg=None, user=None):
return "net"
-def read_config_drive(source_dir, version="2012-08-10"):
+def read_config_drive(source_dir):
reader = openstack.ConfigDriveReader(source_dir)
finders = [
- (reader.read_v2, [], {'version': version}),
+ (reader.read_v2, [], {}),
(reader.read_v1, [], {}),
]
excps = []
diff --git a/cloudinit/sources/DataSourceOpenStack.py b/cloudinit/sources/DataSourceOpenStack.py
index 0970d07b..469c2e2a 100644
--- a/cloudinit/sources/DataSourceOpenStack.py
+++ b/cloudinit/sources/DataSourceOpenStack.py
@@ -88,11 +88,9 @@ class DataSourceOpenStack(openstack.SourceMixin, sources.DataSource):
md_urls = []
url2base = {}
for url in urls:
- for version in openstack.OS_VERSIONS + (openstack.OS_LATEST,):
- md_url = url_helper.combine_url(url, 'openstack',
- version, 'meta_data.json')
- md_urls.append(md_url)
- url2base[md_url] = url
+ md_url = url_helper.combine_url(url, 'openstack')
+ md_urls.append(md_url)
+ url2base[md_url] = url
(max_wait, timeout) = self._get_url_settings()
start_time = time.time()
@@ -119,8 +117,7 @@ class DataSourceOpenStack(openstack.SourceMixin, sources.DataSource):
'Crawl of openstack metadata service',
read_metadata_service,
args=[self.metadata_address],
- kwargs={'ssl_details': self.ssl_details,
- 'version': openstack.OS_HAVANA})
+ kwargs={'ssl_details': self.ssl_details})
except openstack.NonReadable:
return False
except (openstack.BrokenMetadata, IOError):
@@ -143,20 +140,20 @@ class DataSourceOpenStack(openstack.SourceMixin, sources.DataSource):
self.version = results['version']
self.files.update(results.get('files', {}))
- # if vendordata includes 'cloud-init', then read that explicitly
- # for cloud-init (for namespacing).
vd = results.get('vendordata')
- if isinstance(vd, dict) and 'cloud-init' in vd:
- self.vendordata_raw = vd['cloud-init']
- else:
- self.vendordata_raw = vd
+ self.vendordata_pure = vd
+ try:
+ self.vendordata_raw = openstack.convert_vendordata_json(vd)
+ except ValueError as e:
+ LOG.warn("Invalid content in vendor-data: %s", e)
+ self.vendordata_raw = None
return True
-def read_metadata_service(base_url, version=None, ssl_details=None):
+def read_metadata_service(base_url, ssl_details=None):
reader = openstack.MetadataReader(base_url, ssl_details=ssl_details)
- return reader.read_v2(version=version)
+ return reader.read_v2()
# Used to match classes to dependencies
diff --git a/cloudinit/sources/helpers/openstack.py b/cloudinit/sources/helpers/openstack.py
index 3c6bb6aa..b7e19314 100644
--- a/cloudinit/sources/helpers/openstack.py
+++ b/cloudinit/sources/helpers/openstack.py
@@ -21,6 +21,7 @@
import abc
import base64
import copy
+import functools
import os
from cloudinit import ec2_utils
@@ -48,6 +49,7 @@ OS_LATEST = 'latest'
OS_FOLSOM = '2012-08-10'
OS_GRIZZLY = '2013-04-04'
OS_HAVANA = '2013-10-17'
+# keep this in chronological order. new supported versions go at the end.
OS_VERSIONS = (
OS_FOLSOM,
OS_GRIZZLY,
@@ -161,25 +163,27 @@ class BaseReader(object):
def _read_ec2_metadata(self):
pass
- def _find_working_version(self, version):
+ def _find_working_version(self):
try:
- versions_available = self._fetch_available_versions(self)
+ versions_available = self._fetch_available_versions()
except Exception as e:
- LOG.warn("Unable to read openstack versions from %s due to: %s",
- self.base_path, e)
+ LOG.debug("Unable to read openstack versions from %s due to: %s",
+ self.base_path, e)
versions_available = []
- search_versions = [version] + list(OS_VERSIONS)
+ # openstack.OS_VERSIONS is stored in chronological order, so
+ # reverse it to check newest first.
+ supported = [v for v in reversed(list(OS_VERSIONS))]
selected_version = OS_LATEST
- for potential_version in search_versions:
+
+ for potential_version in supported:
if potential_version not in versions_available:
continue
selected_version = potential_version
break
- if selected_version != version:
- LOG.warn("Version '%s' not available, attempting to use"
- " version '%s' instead", version, selected_version)
+ LOG.debug("Selected version '%s' from %s", selected_version,
+ versions_available)
return selected_version
def _read_content_path(self, item):
@@ -191,7 +195,7 @@ class BaseReader(object):
path = self._path_join(self.base_path, "openstack", *path_pieces)
return self._path_read(path)
- def read_v2(self, version=None):
+ def read_v2(self):
"""Reads a version 2 formatted location.
Return a dict with metadata, userdata, ec2-metadata, dsmode,
@@ -200,6 +204,9 @@ class BaseReader(object):
If not a valid location, raise a NonReadable exception.
"""
+ load_json_anytype = functools.partial(
+ util.load_json, root_types=(dict, basestring, list))
+
def datafiles(version):
files = {}
files['metadata'] = (
@@ -218,16 +225,15 @@ class BaseReader(object):
files['vendordata'] = (
self._path_join("openstack", version, 'vendor_data.json'),
False,
- util.load_json,
+ load_json_anytype,
)
return files
- version = self._find_working_version(version)
results = {
'userdata': '',
'version': 2,
}
- data = datafiles(version)
+ data = datafiles(self._find_working_version())
for (name, (path, required, translator)) in data.iteritems():
path = self._path_join(self.base_path, path)
data = None
@@ -239,7 +245,8 @@ class BaseReader(object):
LOG.debug("Failed reading optional path %s due"
" to: %s", path, e)
else:
- LOG.exception("Failed reading mandatory path %s", path)
+ LOG.debug("Failed reading mandatory path %s due"
+ " to: %s", path, e)
else:
found = True
if required and not found:
@@ -325,7 +332,7 @@ class ConfigDriveReader(BaseReader):
path = self._path_join(self.base_path, 'openstack')
found = [d for d in os.listdir(path)
if os.path.isdir(os.path.join(path))]
- self._versions = tuple(found)
+ self._versions = found
return self._versions
def _read_ec2_metadata(self):
@@ -418,18 +425,18 @@ class MetadataReader(BaseReader):
def _fetch_available_versions(self):
# <baseurl>/openstack/ returns a newline separated list of versions
if self._versions is not None:
- return self.os_versions
+ return self._versions
found = []
+ version_path = self._path_join(self.base_path, "openstack")
content = self._path_read(version_path)
for line in content.splitlines():
line = line.strip()
if not line:
continue
found.append(line)
- self._versions = tuple(found)
+ self._versions = found
return self._versions
-
def _path_read(self, path):
def should_retry_cb(_request_args, cause):
@@ -456,3 +463,28 @@ class MetadataReader(BaseReader):
return ec2_utils.get_instance_metadata(ssl_details=self.ssl_details,
timeout=self.timeout,
retries=self.retries)
+
+
+def convert_vendordata_json(data, recurse=True):
+ """ data: a loaded json *object* (strings, arrays, dicts).
+ return something suitable for cloudinit vendordata_raw.
+
+ if data is:
+ None: return None
+ string: return string
+ list: return data
+ the list is then processed in UserDataProcessor
+ dict: return convert_vendordata_json(data.get('cloud-init'))
+ """
+ if not data:
+ return None
+ if isinstance(data, (str, unicode, basestring)):
+ return data
+ if isinstance(data, list):
+ return copy.deepcopy(data)
+ if isinstance(data, dict):
+ if recurse is True:
+ return convert_vendordata_json(data.get('cloud-init'),
+ recurse=False)
+ raise ValueError("vendordata['cloud-init'] cannot be dict")
+ raise ValueError("Unknown data type for vendordata: %s" % type(data))