HEX
Server: LiteSpeed
System: Linux ip-172-31-76-142.ec2.internal 4.14.158-129.185.amzn2.x86_64 #1 SMP Tue Dec 24 03:15:32 UTC 2019 x86_64
User: 69b4844ae61d4e92bf26ad98af552775 (1065)
PHP: 7.2.27
Disabled: exec,passthru,shell_exec,system,eval
Upload Files
File: //proc/thread-self/root/lib/python2.7/site-packages/cloudinit/config/cc_write_metadata.py
# Copyright (C) 2012 Yahoo! Inc.
# Copyright (C) 2014 Amazon.com, Inc. or its affiliates.
#
# Author: Joshua Harlow <harlowja@yahoo-inc.com>
# Author: Iliana Weller <iweller@amazon.com>
#
# This file is part of cloud-init. See LICENSE file for license information.

from cloudinit.config import cc_write_files
from cloudinit.settings import PER_INSTANCE
from cloudinit import util

frequency = PER_INSTANCE

# list of dictionaries we expose to the user from the cloud.datasource object
EXPOSED_DICTS = ('metadata', 'identity')
# list of optional arguments not supported by write_files
OPTIONAL_ARGUMENTS = ('separator', 'allowempty')


def handle(name, cfg, cloud, log, args):
    """
    Example cloud-config that would write the AWS instance ID to
    /root/.instance-id, with a fallback of "unknown":

    write_metadata:
      - path: /root/.instance-id
        data:
          - metadata: instance-id
          - unknown

    Available places to get data from include identity and metadata.
    Data paths can include slashes as delimiters in the dictionary
    structure; a user can provide their own delimiter if needed:

    write_metadata:
      - path: /root/whatever
        data:
          - metadata: public-keys|this/has/slashes|0
            separator: "|"
          - fallback string

    write_metadata will consider an empty string from a data path an
    error, unless you specifically say that's okay with "allowempty":

    write_metadata:
      - path: /root/.could-be-blank
        data:
          - metadata: could-be-blank
            allowempty: true
          - fallback string
    """
    files = cfg.get('write_metadata')
    if not files:
        log.debug(("Skipping module named %s, no/empty "
                   "'write_metadata' key in configuration"), name)
        return
    write_metadata(name, files, cloud, log)


def write_metadata(name, files, cloud, log):
    if not files:
        return

    new_files = list()

    for i, f_info in enumerate(files):
        path = f_info.get('path')
        if not path:
            log.warn("No path provided to write for entry %s in module %s",
                     i + 1, name)
            continue
        data = f_info.get('data')
        if not data:
            log.warn("No data provided to write for entry %s in module %s",
                     i + 1, name)
            continue

        f_info['content'] = retrieve_metadata(path, data, cloud, log)
        if f_info['content'] is None:
            # if there is no content, don't write anything to the file
            # (this is not the same as empty content)
            continue

        # Use the default permissions, to suppress a warning from write-files
        f_info.setdefault('permissions', cc_write_files.DEFAULT_PERMS)

        # ensure that we don't get unexpected behavior in a future
        # version of the write_files module
        for key in EXPOSED_DICTS + OPTIONAL_ARGUMENTS:
            if key in f_info:
                del f_info[key]

        new_files.append(f_info)

    cc_write_files.write_files(name, files)


def retrieve_metadata(path, data, cloud, log):
    for datum in data:
        if isinstance(datum, (str, unicode)):
            # used for fallback data
            return datum
        elif isinstance(datum, dict):
            kwargs = dict()
            if 'separator' in datum:
                kwargs['separator'] = datum['separator']
            for dataset in EXPOSED_DICTS:
                if dataset in datum:
                    if not hasattr(cloud.datasource, dataset):
                        log.warn('there is no %s dataset', dataset)
                        continue
                    try:
                        obj = getattr(cloud.datasource, dataset)
                        value = util.dictpath(obj, datum[dataset], **kwargs)
                        # if the value is an empty string, and the
                        # configuration hasn't told us that's okay, go
                        # to the next fallback
                        if not value and \
                                util.is_false(datum.get('allowempty', False)):
                            continue
                        return value
                    except Exception as exc:
                        # don't return anything, we proceed to the next datum
                        log.warn('using path "%(path)s" against %(dataset)s '
                                 'failed: %(exctype)s: %(excmsg)s',
                                 path=datum[dataset], dataset=dataset,
                                 exctype=type(exc).__name__, excmsg=str(exc))

    # if we reached this point, all attempts to get the data we want
    # failed, and there wasn't a fallback
    log.warn('all attempts to retrieve metadata for %s failed', path)

# vi: ts=4 expandtab