summaryrefslogtreecommitdiff
path: root/cloudinit/cmd
diff options
context:
space:
mode:
authorScott Moser <smoser@brickies.net>2016-10-20 22:53:17 -0400
committerScott Moser <smoser@brickies.net>2017-01-11 14:19:35 -0500
commita1b185d0cce5064e9b36b4db7b55564e2ab1d7a8 (patch)
treed3f0382f70faaa4803ee1fc9b43a6990beaf465c /cloudinit/cmd
parent7fb6f78177b5ece10ca7c54ba3958010a9987f06 (diff)
downloadvyos-cloud-init-a1b185d0cce5064e9b36b4db7b55564e2ab1d7a8.tar.gz
vyos-cloud-init-a1b185d0cce5064e9b36b4db7b55564e2ab1d7a8.zip
Get early logging logged, including failures of cmdline url.
Failures to load the kernel command line's url (cloud-config-url=) would previously get swallowed. This should make it much more obvious when that happens. With logging going to expected places at sane levels (WARN will go to stderr by default).
Diffstat (limited to 'cloudinit/cmd')
-rw-r--r--cloudinit/cmd/main.py118
1 files changed, 103 insertions, 15 deletions
diff --git a/cloudinit/cmd/main.py b/cloudinit/cmd/main.py
index c83496c5..65b15edc 100644
--- a/cloudinit/cmd/main.py
+++ b/cloudinit/cmd/main.py
@@ -26,6 +26,7 @@ from cloudinit import signal_handler
from cloudinit import sources
from cloudinit import stages
from cloudinit import templater
+from cloudinit import url_helper
from cloudinit import util
from cloudinit import version
@@ -129,23 +130,104 @@ def apply_reporting_cfg(cfg):
reporting.update_configuration(cfg.get('reporting'))
+def parse_cmdline_url(cmdline, names=('cloud-config-url', 'url')):
+ data = util.keyval_str_to_dict(cmdline)
+ for key in names:
+ if key in data:
+ return key, data[key]
+ raise KeyError("No keys (%s) found in string '%s'" %
+ (cmdline, names))
+
+
+def attempt_cmdline_url(path, network=True, cmdline=None):
+ """Write data from url referenced in command line to path.
+
+ path: a file to write content to if downloaded.
+ network: should network access be assumed.
+ cmdline: the cmdline to parse for cloud-config-url.
+
+ This is used in MAAS datasource, in "ephemeral" (read-only root)
+ environment where the instance netboots to iscsi ro root.
+ and the entity that controls the pxe config has to configure
+ the maas datasource.
+
+ An attempt is made on network urls even in local datasource
+ for case of network set up in initramfs.
+
+ Return value is a tuple of a logger function (logging.DEBUG)
+ and a message indicating what happened.
+ """
+
+ if cmdline is None:
+ cmdline = util.get_cmdline()
+
+ try:
+ cmdline_name, url = parse_cmdline_url(cmdline)
+ except KeyError:
+ return (logging.DEBUG, "No kernel command line url found.")
+
+ path_is_local = url.startswith("file://") or url.startswith("/")
+
+ if path_is_local and os.path.exists(path):
+ if network:
+ m = ("file '%s' existed, possibly from local stage download"
+ " of command line url '%s'. Not re-writing." % (path, url))
+ level = logging.INFO
+ if path_is_local:
+ level = logging.DEBUG
+ else:
+ m = ("file '%s' existed, possibly from previous boot download"
+ " of command line url '%s'. Not re-writing." % (path, url))
+ level = logging.WARN
+
+ return (level, m)
+
+ kwargs = {'url': url, 'timeout': 10, 'retries': 2}
+ if network or path_is_local:
+ level = logging.WARN
+ kwargs['sec_between'] = 1
+ else:
+ level = logging.DEBUG
+ kwargs['sec_between'] = .1
+
+ data = None
+ header = b'#cloud-config'
+ try:
+ resp = util.read_file_or_url(**kwargs)
+ if resp.ok():
+ data = resp.contents
+ if not resp.contents.startswith(header):
+ if cmdline_name == 'cloud-config-url':
+ level = logging.WARN
+ else:
+ level = logging.INFO
+ return (
+ level,
+ "contents of '%s' did not start with %s" % (url, header))
+ else:
+ return (level,
+ "url '%s' returned code %s. Ignoring." % (url, resp.code))
+
+ except url_helper.UrlError as e:
+ return (level, "retrieving url '%s' failed: %s" % (url, e))
+
+ util.write_file(path, data, mode=0o600)
+ return (logging.INFO,
+ "wrote cloud-config data from %s='%s' to %s" %
+ (cmdline_name, url, path))
+
+
def main_init(name, args):
deps = [sources.DEP_FILESYSTEM, sources.DEP_NETWORK]
if args.local:
deps = [sources.DEP_FILESYSTEM]
- if not args.local:
- # See doc/kernel-cmdline.txt
- #
- # This is used in maas datasource, in "ephemeral" (read-only root)
- # environment where the instance netboots to iscsi ro root.
- # and the entity that controls the pxe config has to configure
- # the maas datasource.
- #
- # Could be used elsewhere, only works on network based (not local).
- root_name = "%s.d" % (CLOUD_CONFIG)
- target_fn = os.path.join(root_name, "91_kernel_cmdline_url.cfg")
- util.read_write_cmdline_url(target_fn)
+ early_logs = []
+ early_logs.append(
+ attempt_cmdline_url(
+ path=os.path.join("%s.d" % CLOUD_CONFIG,
+ "91_kernel_cmdline_url.cfg"),
+ network=not args.local))
# Cloud-init 'init' stage is broken up into the following sub-stages
# 1. Ensure that the init object fetches its config without errors
@@ -171,12 +253,14 @@ def main_init(name, args):
outfmt = None
errfmt = None
try:
- LOG.debug("Closing stdin")
+ early_logs.append((logging.DEBUG, "Closing stdin."))
util.close_stdin()
(outfmt, errfmt) = util.fixup_output(init.cfg, name)
except Exception:
- util.logexc(LOG, "Failed to setup output redirection!")
- print_exc("Failed to setup output redirection!")
+ msg = "Failed to setup output redirection!"
+ util.logexc(LOG, msg)
+ print_exc(msg)
+ early_logs.append((logging.WARN, msg))
if args.debug:
# Reset so that all the debug handlers are closed out
LOG.debug(("Logging being reset, this logger may no"
@@ -190,6 +274,10 @@ def main_init(name, args):
# been redirected and log now configured.
welcome(name, msg=w_msg)
+ # re-play early log messages before logging was setup
+ for lvl, msg in early_logs:
+ LOG.log(lvl, msg)
+
# Stage 3
try:
init.initialize()