Blame cloudinit/sources/DataSourceEc2.py

Packit Service a04d08
# Copyright (C) 2009-2010 Canonical Ltd.
Packit Service a04d08
# Copyright (C) 2012 Hewlett-Packard Development Company, L.P.
Packit Service a04d08
# Copyright (C) 2012 Yahoo! Inc.
Packit Service a04d08
#
Packit Service a04d08
# Author: Scott Moser <scott.moser@canonical.com>
Packit Service a04d08
# Author: Juerg Hafliger <juerg.haefliger@hp.com>
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 os
Packit Service a04d08
import time
Packit Service a04d08
Packit Service a04d08
from cloudinit import ec2_utils as ec2
Packit Service a04d08
from cloudinit import log as logging
Packit Service a04d08
from cloudinit import net
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 as uhelp
Packit Service a04d08
from cloudinit import util
Packit Service a04d08
from cloudinit import warnings
Packit Service a04d08
from cloudinit.event import EventType
Packit Service a04d08
Packit Service a04d08
LOG = logging.getLogger(__name__)
Packit Service a04d08
Packit Service a04d08
SKIP_METADATA_URL_CODES = frozenset([uhelp.NOT_FOUND])
Packit Service a04d08
Packit Service a04d08
STRICT_ID_PATH = ("datasource", "Ec2", "strict_id")
Packit Service a04d08
STRICT_ID_DEFAULT = "warn"
Packit Service a04d08
Packit Service a04d08
API_TOKEN_ROUTE = 'latest/api/token'
Packit Service a04d08
AWS_TOKEN_TTL_SECONDS = '21600'
Packit Service 9bfd13
AWS_TOKEN_PUT_HEADER = 'X-aws-ec2-metadata-token'
Packit Service 9bfd13
AWS_TOKEN_REQ_HEADER = AWS_TOKEN_PUT_HEADER + '-ttl-seconds'
Packit Service 9bfd13
AWS_TOKEN_REDACT = [AWS_TOKEN_PUT_HEADER, AWS_TOKEN_REQ_HEADER]
Packit Service a04d08
Packit Service a04d08
Packit Service a04d08
class CloudNames(object):
Packit Service a04d08
    ALIYUN = "aliyun"
Packit Service a04d08
    AWS = "aws"
Packit Service a04d08
    BRIGHTBOX = "brightbox"
Packit Service a04d08
    ZSTACK = "zstack"
Packit Service a04d08
    E24CLOUD = "e24cloud"
Packit Service a04d08
    # UNKNOWN indicates no positive id.  If strict_id is 'warn' or 'false',
Packit Service a04d08
    # then an attempt at the Ec2 Metadata service will be made.
Packit Service a04d08
    UNKNOWN = "unknown"
Packit Service a04d08
    # NO_EC2_METADATA indicates this platform does not have a Ec2 metadata
Packit Service a04d08
    # service available. No attempt at the Ec2 Metadata service will be made.
Packit Service a04d08
    NO_EC2_METADATA = "no-ec2-metadata"
Packit Service a04d08
Packit Service a04d08
Packit Service a04d08
class DataSourceEc2(sources.DataSource):
Packit Service a04d08
Packit Service a04d08
    dsname = 'Ec2'
Packit Service a04d08
    # Default metadata urls that will be used if none are provided
Packit Service a04d08
    # They will be checked for 'resolveability' and some of the
Packit Service a04d08
    # following may be discarded if they do not resolve
Packit Service a04d08
    metadata_urls = ["http://169.254.169.254", "http://instance-data.:8773"]
Packit Service a04d08
Packit Service a04d08
    # The minimum supported metadata_version from the ec2 metadata apis
Packit Service a04d08
    min_metadata_version = '2009-04-04'
Packit Service a04d08
Packit Service a04d08
    # Priority ordered list of additional metadata versions which will be tried
Packit Service a04d08
    # for extended metadata content. IPv6 support comes in 2016-09-02
Packit Service 9bfd13
    extended_metadata_versions = ['2018-09-24', '2016-09-02']
Packit Service a04d08
Packit Service a04d08
    # Setup read_url parameters per get_url_params.
Packit Service a04d08
    url_max_wait = 120
Packit Service a04d08
    url_timeout = 50
Packit Service a04d08
Packit Service a04d08
    _api_token = None  # API token for accessing the metadata service
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(DataSourceEc2, self).__init__(sys_cfg, distro, paths)
Packit Service a04d08
        self.metadata_address = None
Packit Service a04d08
Packit Service a04d08
    def _get_cloud_name(self):
Packit Service a04d08
        """Return the cloud name as identified during _get_data."""
Packit Service a04d08
        return identify_platform()
Packit Service a04d08
Packit Service a04d08
    def _get_data(self):
Packit Service a04d08
        strict_mode, _sleep = read_strict_mode(
Packit Service a04d08
            util.get_cfg_by_path(self.sys_cfg, STRICT_ID_PATH,
Packit Service a04d08
                                 STRICT_ID_DEFAULT), ("warn", None))
Packit Service a04d08
Packit Service a04d08
        LOG.debug("strict_mode: %s, cloud_name=%s cloud_platform=%s",
Packit Service a04d08
                  strict_mode, self.cloud_name, self.platform)
Packit Service a04d08
        if strict_mode == "true" and self.cloud_name == CloudNames.UNKNOWN:
Packit Service a04d08
            return False
Packit Service a04d08
        elif self.cloud_name == CloudNames.NO_EC2_METADATA:
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
            if util.is_FreeBSD():
Packit Service a04d08
                LOG.debug("FreeBSD doesn't support running dhclient with -sf")
Packit Service a04d08
                return False
Packit Service a04d08
            try:
Packit Service a04d08
                with EphemeralDHCPv4(self.fallback_interface):
Packit Service a04d08
                    self._crawled_metadata = 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:
Packit Service a04d08
                return False
Packit Service a04d08
        else:
Packit Service a04d08
            self._crawled_metadata = util.log_time(
Packit Service a04d08
                logfunc=LOG.debug, msg='Crawl of metadata service',
Packit Service a04d08
                func=self.crawl_metadata)
Packit Service a04d08
        if not self._crawled_metadata:
Packit Service a04d08
            return False
Packit Service a04d08
        self.metadata = self._crawled_metadata.get('meta-data', None)
Packit Service a04d08
        self.userdata_raw = self._crawled_metadata.get('user-data', None)
Packit Service a04d08
        self.identity = self._crawled_metadata.get(
Packit Service a04d08
            'dynamic', {}).get('instance-identity', {}).get('document', {})
Packit Service a04d08
        return True
Packit Service a04d08
Packit Service a04d08
    def is_classic_instance(self):
Packit Service a04d08
        """Report if this instance type is Ec2 Classic (non-vpc)."""
Packit Service a04d08
        if not self.metadata:
Packit Service a04d08
            # Can return False on inconclusive as we are also called in
Packit Service a04d08
            # network_config where metadata will be present.
Packit Service a04d08
            # Secondary call site is in packaging postinst script.
Packit Service a04d08
            return False
Packit Service a04d08
        ifaces_md = self.metadata.get('network', {}).get('interfaces', {})
Packit Service a04d08
        for _mac, mac_data in ifaces_md.get('macs', {}).items():
Packit Service a04d08
            if 'vpc-id' in mac_data:
Packit Service a04d08
                return False
Packit Service a04d08
        return True
Packit Service a04d08
Packit Service a04d08
    @property
Packit Service a04d08
    def launch_index(self):
Packit Service a04d08
        if not self.metadata:
Packit Service a04d08
            return None
Packit Service a04d08
        return self.metadata.get('ami-launch-index')
Packit Service a04d08
Packit Service a04d08
    @property
Packit Service a04d08
    def platform(self):
Packit Service a04d08
        # Handle upgrade path of pickled ds
Packit Service a04d08
        if not hasattr(self, '_platform_type'):
Packit Service a04d08
            self._platform_type = DataSourceEc2.dsname.lower()
Packit Service a04d08
        if not self._platform_type:
Packit Service a04d08
            self._platform_type = DataSourceEc2.dsname.lower()
Packit Service a04d08
        return self._platform_type
Packit Service a04d08
Packit Service a04d08
    def get_metadata_api_version(self):
Packit Service a04d08
        """Get the best supported api version from the metadata service.
Packit Service a04d08
Packit Service a04d08
        Loop through all extended support metadata versions in order and
Packit Service a04d08
        return the most-fully featured metadata api version discovered.
Packit Service a04d08
Packit Service a04d08
        If extended_metadata_versions aren't present, return the datasource's
Packit Service a04d08
        min_metadata_version.
Packit Service a04d08
        """
Packit Service a04d08
        # Assumes metadata service is already up
Packit Service a04d08
        url_tmpl = '{0}/{1}/meta-data/instance-id'
Packit Service a04d08
        headers = self._get_headers()
Packit Service a04d08
        for api_ver in self.extended_metadata_versions:
Packit Service a04d08
            url = url_tmpl.format(self.metadata_address, api_ver)
Packit Service a04d08
            try:
Packit Service 9bfd13
                resp = uhelp.readurl(url=url, headers=headers,
Packit Service 9bfd13
                                     headers_redact=AWS_TOKEN_REDACT)
Packit Service a04d08
            except uhelp.UrlError as e:
Packit Service a04d08
                LOG.debug('url %s raised exception %s', url, e)
Packit Service a04d08
            else:
Packit Service a04d08
                if resp.code == 200:
Packit Service a04d08
                    LOG.debug('Found preferred metadata version %s', api_ver)
Packit Service a04d08
                    return api_ver
Packit Service a04d08
                elif resp.code == 404:
Packit Service a04d08
                    msg = 'Metadata api version %s not present. Headers: %s'
Packit Service a04d08
                    LOG.debug(msg, api_ver, resp.headers)
Packit Service a04d08
        return self.min_metadata_version
Packit Service a04d08
Packit Service a04d08
    def get_instance_id(self):
Packit Service a04d08
        if self.cloud_name == CloudNames.AWS:
Packit Service a04d08
            # Prefer the ID from the instance identity document, but fall back
Packit Service a04d08
            if not getattr(self, 'identity', None):
Packit Service a04d08
                # If re-using cached datasource, it's get_data run didn't
Packit Service a04d08
                # setup self.identity. So we need to do that now.
Packit Service a04d08
                api_version = self.get_metadata_api_version()
Packit Service a04d08
                self.identity = ec2.get_instance_identity(
Packit Service a04d08
                    api_version, self.metadata_address,
Packit Service a04d08
                    headers_cb=self._get_headers,
Packit Service 9bfd13
                    headers_redact=AWS_TOKEN_REDACT,
Packit Service a04d08
                    exception_cb=self._refresh_stale_aws_token_cb).get(
Packit Service a04d08
                        'document', {})
Packit Service a04d08
            return self.identity.get(
Packit Service a04d08
                'instanceId', self.metadata['instance-id'])
Packit Service a04d08
        else:
Packit Service a04d08
            return self.metadata['instance-id']
Packit Service a04d08
Packit Service a04d08
    def _maybe_fetch_api_token(self, mdurls, timeout=None, max_wait=None):
Packit Service 9bfd13
        """ Get an API token for EC2 Instance Metadata Service.
Packit Service 9bfd13
Packit Service 9bfd13
        On EC2. IMDS will always answer an API token, unless
Packit Service 9bfd13
        the instance owner has disabled the IMDS HTTP endpoint or
Packit Service 9bfd13
        the network topology conflicts with the configured hop-limit.
Packit Service 9bfd13
        """
Packit Service a04d08
        if self.cloud_name != CloudNames.AWS:
Packit Service a04d08
            return
Packit Service a04d08
Packit Service a04d08
        urls = []
Packit Service a04d08
        url2base = {}
Packit Service a04d08
        url_path = API_TOKEN_ROUTE
Packit Service a04d08
        request_method = 'PUT'
Packit Service a04d08
        for url in mdurls:
Packit Service a04d08
            cur = '{0}/{1}'.format(url, url_path)
Packit Service a04d08
            urls.append(cur)
Packit Service a04d08
            url2base[cur] = url
Packit Service a04d08
Packit Service 9bfd13
        # use the self._imds_exception_cb to check for Read errors
Packit Service a04d08
        LOG.debug('Fetching Ec2 IMDSv2 API Token')
Packit Service 9bfd13
Packit Service 9bfd13
        response = None
Packit Service 9bfd13
        url = None
Packit Service 9bfd13
        url_params = self.get_url_params()
Packit Service 9bfd13
        try:
Packit Service 9bfd13
            url, response = uhelp.wait_for_url(
Packit Service 9bfd13
                urls=urls, max_wait=url_params.max_wait_seconds,
Packit Service 9bfd13
                timeout=url_params.timeout_seconds, status_cb=LOG.warning,
Packit Service 9bfd13
                headers_cb=self._get_headers,
Packit Service 9bfd13
                exception_cb=self._imds_exception_cb,
Packit Service 9bfd13
                request_method=request_method,
Packit Service 9bfd13
                headers_redact=AWS_TOKEN_REDACT)
Packit Service 9bfd13
        except uhelp.UrlError:
Packit Service 9bfd13
            # We use the raised exception to interupt the retry loop.
Packit Service 9bfd13
            # Nothing else to do here.
Packit Service 9bfd13
            pass
Packit Service a04d08
Packit Service a04d08
        if url and response:
Packit Service a04d08
            self._api_token = response
Packit Service a04d08
            return url2base[url]
Packit Service a04d08
Packit Service 9bfd13
        # If we get here, then wait_for_url timed out, waiting for IMDS
Packit Service 9bfd13
        # or the IMDS HTTP endpoint is disabled
Packit Service 9bfd13
        return None
Packit Service 9bfd13
Packit Service a04d08
    def wait_for_metadata_service(self):
Packit Service a04d08
        mcfg = self.ds_cfg
Packit Service a04d08
Packit Service a04d08
        url_params = self.get_url_params()
Packit Service a04d08
        if url_params.max_wait_seconds <= 0:
Packit Service a04d08
            return False
Packit Service a04d08
Packit Service a04d08
        # Remove addresses from the list that wont resolve.
Packit Service a04d08
        mdurls = mcfg.get("metadata_urls", self.metadata_urls)
Packit Service a04d08
        filtered = [x for x in mdurls if util.is_resolvable_url(x)]
Packit Service a04d08
Packit Service a04d08
        if set(filtered) != set(mdurls):
Packit Service a04d08
            LOG.debug("Removed the following from metadata urls: %s",
Packit Service a04d08
                      list((set(mdurls) - set(filtered))))
Packit Service a04d08
Packit Service a04d08
        if len(filtered):
Packit Service a04d08
            mdurls = filtered
Packit Service a04d08
        else:
Packit Service a04d08
            LOG.warning("Empty metadata url list! using default list")
Packit Service a04d08
            mdurls = self.metadata_urls
Packit Service a04d08
Packit Service a04d08
        # try the api token path first
Packit Service a04d08
        metadata_address = self._maybe_fetch_api_token(mdurls)
Packit Service 9bfd13
        # When running on EC2, we always access IMDS with an API token.
Packit Service 9bfd13
        # If we could not get an API token, then we assume the IMDS
Packit Service 9bfd13
        # endpoint was disabled and we move on without a data source.
Packit Service 9bfd13
        # Fallback to IMDSv1 if not running on EC2
Packit Service 9bfd13
        if not metadata_address and self.cloud_name != CloudNames.AWS:
Packit Service a04d08
            # if we can't get a token, use instance-id path
Packit Service a04d08
            urls = []
Packit Service a04d08
            url2base = {}
Packit Service a04d08
            url_path = '{ver}/meta-data/instance-id'.format(
Packit Service a04d08
                ver=self.min_metadata_version)
Packit Service a04d08
            request_method = 'GET'
Packit Service a04d08
            for url in mdurls:
Packit Service a04d08
                cur = '{0}/{1}'.format(url, url_path)
Packit Service a04d08
                urls.append(cur)
Packit Service a04d08
                url2base[cur] = url
Packit Service a04d08
Packit Service a04d08
            start_time = time.time()
Packit Service a04d08
            url, _ = uhelp.wait_for_url(
Packit Service a04d08
                urls=urls, max_wait=url_params.max_wait_seconds,
Packit Service a04d08
                timeout=url_params.timeout_seconds, status_cb=LOG.warning,
Packit Service 9bfd13
                headers_redact=AWS_TOKEN_REDACT, headers_cb=self._get_headers,
Packit Service 9bfd13
                request_method=request_method)
Packit Service a04d08
Packit Service a04d08
            if url:
Packit Service a04d08
                metadata_address = url2base[url]
Packit Service a04d08
Packit Service a04d08
        if metadata_address:
Packit Service a04d08
            self.metadata_address = metadata_address
Packit Service a04d08
            LOG.debug("Using metadata source: '%s'", self.metadata_address)
Packit Service 9bfd13
        elif self.cloud_name == CloudNames.AWS:
Packit Service 9bfd13
            LOG.warning("IMDS's HTTP endpoint is probably disabled")
Packit Service a04d08
        else:
Packit Service a04d08
            LOG.critical("Giving up on md from %s after %s seconds",
Packit Service a04d08
                         urls, int(time.time() - start_time))
Packit Service a04d08
Packit Service a04d08
        return bool(metadata_address)
Packit Service a04d08
Packit Service a04d08
    def device_name_to_device(self, name):
Packit Service a04d08
        # Consult metadata service, that has
Packit Service a04d08
        #  ephemeral0: sdb
Packit Service a04d08
        # and return 'sdb' for input 'ephemeral0'
Packit Service a04d08
        if 'block-device-mapping' not in self.metadata:
Packit Service a04d08
            return None
Packit Service a04d08
Packit Service a04d08
        # Example:
Packit Service a04d08
        # 'block-device-mapping':
Packit Service a04d08
        # {'ami': '/dev/sda1',
Packit Service a04d08
        # 'ephemeral0': '/dev/sdb',
Packit Service a04d08
        # 'root': '/dev/sda1'}
Packit Service a04d08
        found = None
Packit Service a04d08
        bdm = self.metadata['block-device-mapping']
Packit Service a04d08
        if not isinstance(bdm, dict):
Packit Service a04d08
            LOG.debug("block-device-mapping not a dictionary: '%s'", bdm)
Packit Service a04d08
            return None
Packit Service a04d08
Packit Service a04d08
        for (entname, device) in bdm.items():
Packit Service a04d08
            if entname == name:
Packit Service a04d08
                found = device
Packit Service a04d08
                break
Packit Service a04d08
            # LP: #513842 mapping in Euca has 'ephemeral' not 'ephemeral0'
Packit Service a04d08
            if entname == "ephemeral" and name == "ephemeral0":
Packit Service a04d08
                found = device
Packit Service a04d08
Packit Service a04d08
        if found is None:
Packit Service a04d08
            LOG.debug("Unable to convert %s to a device", name)
Packit Service a04d08
            return None
Packit Service a04d08
Packit Service a04d08
        ofound = found
Packit Service a04d08
        if not found.startswith("/"):
Packit Service a04d08
            found = "/dev/%s" % found
Packit Service a04d08
Packit Service a04d08
        if os.path.exists(found):
Packit Service a04d08
            return found
Packit Service a04d08
Packit Service a04d08
        remapped = self._remap_device(os.path.basename(found))
Packit Service a04d08
        if remapped:
Packit Service a04d08
            LOG.debug("Remapped device name %s => %s", found, remapped)
Packit Service a04d08
            return remapped
Packit Service a04d08
Packit Service a04d08
        # On t1.micro, ephemeral0 will appear in block-device-mapping from
Packit Service a04d08
        # metadata, but it will not exist on disk (and never will)
Packit Service a04d08
        # at this point, we've verified that the path did not exist
Packit Service a04d08
        # in the special case of 'ephemeral0' return None to avoid bogus
Packit Service a04d08
        # fstab entry (LP: #744019)
Packit Service a04d08
        if name == "ephemeral0":
Packit Service a04d08
            return None
Packit Service a04d08
        return ofound
Packit Service a04d08
Packit Service a04d08
    @property
Packit Service a04d08
    def availability_zone(self):
Packit Service a04d08
        try:
Packit Service a04d08
            if self.cloud_name == CloudNames.AWS:
Packit Service a04d08
                return self.identity.get(
Packit Service a04d08
                    'availabilityZone',
Packit Service a04d08
                    self.metadata['placement']['availability-zone'])
Packit Service a04d08
            else:
Packit Service a04d08
                return self.metadata['placement']['availability-zone']
Packit Service a04d08
        except KeyError:
Packit Service a04d08
            return None
Packit Service a04d08
Packit Service a04d08
    @property
Packit Service a04d08
    def region(self):
Packit Service a04d08
        if self.cloud_name == CloudNames.AWS:
Packit Service a04d08
            region = self.identity.get('region')
Packit Service a04d08
            # Fallback to trimming the availability zone if region is missing
Packit Service a04d08
            if self.availability_zone and not region:
Packit Service a04d08
                region = self.availability_zone[:-1]
Packit Service a04d08
            return region
Packit Service a04d08
        else:
Packit Service a04d08
            az = self.availability_zone
Packit Service a04d08
            if az is not None:
Packit Service a04d08
                return az[:-1]
Packit Service a04d08
        return None
Packit Service a04d08
Packit Service a04d08
    def activate(self, cfg, is_new_instance):
Packit Service a04d08
        if not is_new_instance:
Packit Service a04d08
            return
Packit Service a04d08
        if self.cloud_name == CloudNames.UNKNOWN:
Packit Service a04d08
            warn_if_necessary(
Packit Service a04d08
                util.get_cfg_by_path(cfg, STRICT_ID_PATH, STRICT_ID_DEFAULT),
Packit Service a04d08
                cfg)
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
        if self.metadata is None:
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 metadata is None.")
Packit Service a04d08
            return None
Packit Service a04d08
Packit Service a04d08
        result = None
Packit Service a04d08
        no_network_metadata_on_aws = bool(
Packit Service a04d08
            'network' not in self.metadata and
Packit Service a04d08
            self.cloud_name == CloudNames.AWS)
Packit Service a04d08
        if no_network_metadata_on_aws:
Packit Service a04d08
            LOG.debug("Metadata 'network' not present:"
Packit Service a04d08
                      " Refreshing stale metadata from prior to upgrade.")
Packit Service a04d08
            util.log_time(
Packit Service a04d08
                logfunc=LOG.debug, msg='Re-crawl of metadata service',
Packit Service a04d08
                func=self.get_data)
Packit Service a04d08
Packit Service a04d08
        iface = self.fallback_interface
Packit Service a04d08
        net_md = self.metadata.get('network')
Packit Service a04d08
        if isinstance(net_md, dict):
Packit Service 9bfd13
            # SRU_BLOCKER: xenial, bionic and eoan should default
Packit Service 9bfd13
            # apply_full_imds_network_config to False to retain original
Packit Service 9bfd13
            # behavior on those releases.
Packit Service a04d08
            result = convert_ec2_metadata_network_config(
Packit Service 9bfd13
                net_md, fallback_nic=iface,
Packit Service 9bfd13
                full_network_config=util.get_cfg_option_bool(
Packit Service 9bfd13
                    self.ds_cfg, 'apply_full_imds_network_config', True))
Packit Service a04d08
Packit Service a04d08
            # RELEASE_BLOCKER: xenial should drop the below if statement,
Packit Service a04d08
            # because the issue being addressed doesn't exist pre-netplan.
Packit Service a04d08
            # (This datasource doesn't implement check_instance_id() so the
Packit Service a04d08
            # datasource object is recreated every boot; this means we don't
Packit Service a04d08
            # need to modify update_events on cloud-init upgrade.)
Packit Service a04d08
Packit Service a04d08
            # Non-VPC (aka Classic) Ec2 instances need to rewrite the
Packit Service a04d08
            # network config file every boot due to MAC address change.
Packit Service a04d08
            if self.is_classic_instance():
Packit Service a04d08
                self.update_events['network'].add(EventType.BOOT)
Packit Service a04d08
        else:
Packit Service a04d08
            LOG.warning("Metadata 'network' key not valid: %s.", net_md)
Packit Service a04d08
        self._network_config = result
Packit Service a04d08
Packit Service a04d08
        return self._network_config
Packit Service a04d08
Packit Service a04d08
    @property
Packit Service a04d08
    def fallback_interface(self):
Packit Service a04d08
        if self._fallback_interface is None:
Packit Service a04d08
            # fallback_nic was used at one point, so restored objects may
Packit Service a04d08
            # have an attribute there. respect that if found.
Packit Service a04d08
            _legacy_fbnic = getattr(self, 'fallback_nic', None)
Packit Service a04d08
            if _legacy_fbnic:
Packit Service a04d08
                self._fallback_interface = _legacy_fbnic
Packit Service a04d08
                self.fallback_nic = None
Packit Service a04d08
            else:
Packit Service a04d08
                return super(DataSourceEc2, self).fallback_interface
Packit Service a04d08
        return self._fallback_interface
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 of crawled metadata content containing the keys:
Packit Service a04d08
          meta-data, user-data and dynamic.
Packit Service a04d08
        """
Packit Service a04d08
        if not self.wait_for_metadata_service():
Packit Service a04d08
            return {}
Packit Service a04d08
        api_version = self.get_metadata_api_version()
Packit Service 9bfd13
        redact = AWS_TOKEN_REDACT
Packit Service a04d08
        crawled_metadata = {}
Packit Service a04d08
        if self.cloud_name == CloudNames.AWS:
Packit Service a04d08
            exc_cb = self._refresh_stale_aws_token_cb
Packit Service a04d08
            exc_cb_ud = self._skip_or_refresh_stale_aws_token_cb
Packit Service a04d08
        else:
Packit Service a04d08
            exc_cb = exc_cb_ud = None
Packit Service a04d08
        try:
Packit Service a04d08
            crawled_metadata['user-data'] = ec2.get_instance_userdata(
Packit Service a04d08
                api_version, self.metadata_address,
Packit Service 9bfd13
                headers_cb=self._get_headers, headers_redact=redact,
Packit Service 9bfd13
                exception_cb=exc_cb_ud)
Packit Service a04d08
            crawled_metadata['meta-data'] = ec2.get_instance_metadata(
Packit Service a04d08
                api_version, self.metadata_address,
Packit Service 9bfd13
                headers_cb=self._get_headers, headers_redact=redact,
Packit Service 9bfd13
                exception_cb=exc_cb)
Packit Service a04d08
            if self.cloud_name == CloudNames.AWS:
Packit Service a04d08
                identity = ec2.get_instance_identity(
Packit Service a04d08
                    api_version, self.metadata_address,
Packit Service 9bfd13
                    headers_cb=self._get_headers, headers_redact=redact,
Packit Service 9bfd13
                    exception_cb=exc_cb)
Packit Service a04d08
                crawled_metadata['dynamic'] = {'instance-identity': identity}
Packit Service a04d08
        except Exception:
Packit Service a04d08
            util.logexc(
Packit Service a04d08
                LOG, "Failed reading from metadata address %s",
Packit Service a04d08
                self.metadata_address)
Packit Service a04d08
            return {}
Packit Service a04d08
        crawled_metadata['_metadata_api_version'] = api_version
Packit Service a04d08
        return crawled_metadata
Packit Service a04d08
Packit Service a04d08
    def _refresh_api_token(self, seconds=AWS_TOKEN_TTL_SECONDS):
Packit Service a04d08
        """Request new metadata API token.
Packit Service a04d08
        @param seconds: The lifetime of the token in seconds
Packit Service a04d08
Packit Service a04d08
        @return: The API token or None if unavailable.
Packit Service a04d08
        """
Packit Service a04d08
        if self.cloud_name != CloudNames.AWS:
Packit Service a04d08
            return None
Packit Service a04d08
        LOG.debug("Refreshing Ec2 metadata API token")
Packit Service 9bfd13
        request_header = {AWS_TOKEN_REQ_HEADER: seconds}
Packit Service a04d08
        token_url = '{}/{}'.format(self.metadata_address, API_TOKEN_ROUTE)
Packit Service a04d08
        try:
Packit Service 9bfd13
            response = uhelp.readurl(token_url, headers=request_header,
Packit Service 9bfd13
                                     headers_redact=AWS_TOKEN_REDACT,
Packit Service 9bfd13
                                     request_method="PUT")
Packit Service a04d08
        except uhelp.UrlError as e:
Packit Service a04d08
            LOG.warning(
Packit Service a04d08
                'Unable to get API token: %s raised exception %s',
Packit Service a04d08
                token_url, e)
Packit Service a04d08
            return None
Packit Service a04d08
        return response.contents
Packit Service a04d08
Packit Service a04d08
    def _skip_or_refresh_stale_aws_token_cb(self, msg, exception):
Packit Service a04d08
        """Callback will not retry on SKIP_USERDATA_CODES or if no token
Packit Service a04d08
           is available."""
Packit Service a04d08
        retry = ec2.skip_retry_on_codes(
Packit Service a04d08
            ec2.SKIP_USERDATA_CODES, msg, exception)
Packit Service a04d08
        if not retry:
Packit Service a04d08
            return False  # False raises exception
Packit Service a04d08
        return self._refresh_stale_aws_token_cb(msg, exception)
Packit Service a04d08
Packit Service a04d08
    def _refresh_stale_aws_token_cb(self, msg, exception):
Packit Service a04d08
        """Exception handler for Ec2 to refresh token if token is stale."""
Packit Service a04d08
        if isinstance(exception, uhelp.UrlError) and exception.code == 401:
Packit Service a04d08
            # With _api_token as None, _get_headers will _refresh_api_token.
Packit Service a04d08
            LOG.debug("Clearing cached Ec2 API token due to expiry")
Packit Service a04d08
            self._api_token = None
Packit Service a04d08
        return True  # always retry
Packit Service a04d08
Packit Service 9bfd13
    def _imds_exception_cb(self, msg, exception=None):
Packit Service 9bfd13
        """Fail quickly on proper AWS if IMDSv2 rejects API token request
Packit Service 9bfd13
Packit Service 9bfd13
        Guidance from Amazon is that if IMDSv2 had disabled token requests
Packit Service 9bfd13
        by returning a 403, or cloud-init malformed requests resulting in
Packit Service 9bfd13
        other 40X errors, we want the datasource detection to fail quickly
Packit Service 9bfd13
        without retries as those symptoms will likely not be resolved by
Packit Service 9bfd13
        retries.
Packit Service 9bfd13
Packit Service 9bfd13
        Exceptions such as requests.ConnectionError due to IMDS being
Packit Service 9bfd13
        temporarily unroutable or unavailable will still retry due to the
Packit Service 9bfd13
        callsite wait_for_url.
Packit Service 9bfd13
        """
Packit Service 9bfd13
        if isinstance(exception, uhelp.UrlError):
Packit Service 9bfd13
            # requests.ConnectionError will have exception.code == None
Packit Service 9bfd13
            if exception.code and exception.code >= 400:
Packit Service 9bfd13
                if exception.code == 403:
Packit Service 9bfd13
                    LOG.warning('Ec2 IMDS endpoint returned a 403 error. '
Packit Service 9bfd13
                                'HTTP endpoint is disabled. Aborting.')
Packit Service 9bfd13
                else:
Packit Service 9bfd13
                    LOG.warning('Fatal error while requesting '
Packit Service 9bfd13
                                'Ec2 IMDSv2 API tokens')
Packit Service 9bfd13
                raise exception
Packit Service a04d08
Packit Service a04d08
    def _get_headers(self, url=''):
Packit Service a04d08
        """Return a dict of headers for accessing a url.
Packit Service a04d08
Packit Service a04d08
        If _api_token is unset on AWS, attempt to refresh the token via a PUT
Packit Service a04d08
        and then return the updated token header.
Packit Service a04d08
        """
Packit Service 9bfd13
        if self.cloud_name != CloudNames.AWS:
Packit Service a04d08
            return {}
Packit Service a04d08
        # Request a 6 hour token if URL is API_TOKEN_ROUTE
Packit Service 9bfd13
        request_token_header = {AWS_TOKEN_REQ_HEADER: AWS_TOKEN_TTL_SECONDS}
Packit Service a04d08
        if API_TOKEN_ROUTE in url:
Packit Service a04d08
            return request_token_header
Packit Service a04d08
        if not self._api_token:
Packit Service a04d08
            # If we don't yet have an API token, get one via a PUT against
Packit Service a04d08
            # API_TOKEN_ROUTE. This _api_token may get unset by a 403 due
Packit Service a04d08
            # to an invalid or expired token
Packit Service a04d08
            self._api_token = self._refresh_api_token()
Packit Service a04d08
            if not self._api_token:
Packit Service a04d08
                return {}
Packit Service 9bfd13
        return {AWS_TOKEN_PUT_HEADER: self._api_token}
Packit Service a04d08
Packit Service a04d08
Packit Service a04d08
class DataSourceEc2Local(DataSourceEc2):
Packit Service a04d08
    """Datasource run at init-local which sets up network to query metadata.
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
    perform_dhcp_setup = True  # Use dhcp before querying metadata
Packit Service a04d08
Packit Service a04d08
    def get_data(self):
Packit Service a04d08
        supported_platforms = (CloudNames.AWS,)
Packit Service a04d08
        if self.cloud_name not in supported_platforms:
Packit Service a04d08
            LOG.debug("Local Ec2 mode only supported on %s, not %s",
Packit Service a04d08
                      supported_platforms, self.cloud_name)
Packit Service a04d08
            return False
Packit Service a04d08
        return super(DataSourceEc2Local, self).get_data()
Packit Service a04d08
Packit Service a04d08
Packit Service a04d08
def read_strict_mode(cfgval, default):
Packit Service a04d08
    try:
Packit Service a04d08
        return parse_strict_mode(cfgval)
Packit Service a04d08
    except ValueError as e:
Packit Service a04d08
        LOG.warning(e)
Packit Service a04d08
        return default
Packit Service a04d08
Packit Service a04d08
Packit Service a04d08
def parse_strict_mode(cfgval):
Packit Service a04d08
    # given a mode like:
Packit Service a04d08
    #    true, false, warn,[sleep]
Packit Service a04d08
    # return tuple with string mode (true|false|warn) and sleep.
Packit Service a04d08
    if cfgval is True:
Packit Service a04d08
        return 'true', None
Packit Service a04d08
    if cfgval is False:
Packit Service a04d08
        return 'false', None
Packit Service a04d08
Packit Service a04d08
    if not cfgval:
Packit Service a04d08
        return 'warn', 0
Packit Service a04d08
Packit Service a04d08
    mode, _, sleep = cfgval.partition(",")
Packit Service a04d08
    if mode not in ('true', 'false', 'warn'):
Packit Service a04d08
        raise ValueError(
Packit Service a04d08
            "Invalid mode '%s' in strict_id setting '%s': "
Packit Service a04d08
            "Expected one of 'true', 'false', 'warn'." % (mode, cfgval))
Packit Service a04d08
Packit Service a04d08
    if sleep:
Packit Service a04d08
        try:
Packit Service a04d08
            sleep = int(sleep)
Packit Service 9bfd13
        except ValueError as e:
Packit Service 9bfd13
            raise ValueError(
Packit Service 9bfd13
                "Invalid sleep '%s' in strict_id setting '%s': not an integer"
Packit Service 9bfd13
                % (sleep, cfgval)
Packit Service 9bfd13
            ) from e
Packit Service a04d08
    else:
Packit Service a04d08
        sleep = None
Packit Service a04d08
Packit Service a04d08
    return mode, sleep
Packit Service a04d08
Packit Service a04d08
Packit Service a04d08
def warn_if_necessary(cfgval, cfg):
Packit Service a04d08
    try:
Packit Service a04d08
        mode, sleep = parse_strict_mode(cfgval)
Packit Service a04d08
    except ValueError as e:
Packit Service a04d08
        LOG.warning(e)
Packit Service a04d08
        return
Packit Service a04d08
Packit Service a04d08
    if mode == "false":
Packit Service a04d08
        return
Packit Service a04d08
Packit Service a04d08
    warnings.show_warning('non_ec2_md', cfg, mode=True, sleep=sleep)
Packit Service a04d08
Packit Service a04d08
Packit Service a04d08
def identify_aws(data):
Packit Service a04d08
    # data is a dictionary returned by _collect_platform_data.
Packit Service a04d08
    if (data['uuid'].startswith('ec2') and
Packit Service a04d08
            (data['uuid_source'] == 'hypervisor' or
Packit Service a04d08
             data['uuid'] == data['serial'])):
Packit Service a04d08
        return CloudNames.AWS
Packit Service a04d08
Packit Service a04d08
    return None
Packit Service a04d08
Packit Service a04d08
Packit Service a04d08
def identify_brightbox(data):
Packit Service a04d08
    if data['serial'].endswith('.brightbox.com'):
Packit Service a04d08
        return CloudNames.BRIGHTBOX
Packit Service a04d08
Packit Service a04d08
Packit Service a04d08
def identify_zstack(data):
Packit Service a04d08
    if data['asset_tag'].endswith('.zstack.io'):
Packit Service a04d08
        return CloudNames.ZSTACK
Packit Service a04d08
Packit Service a04d08
Packit Service a04d08
def identify_e24cloud(data):
Packit Service a04d08
    if data['vendor'] == 'e24cloud':
Packit Service a04d08
        return CloudNames.E24CLOUD
Packit Service a04d08
Packit Service a04d08
Packit Service a04d08
def identify_platform():
Packit Service a04d08
    # identify the platform and return an entry in CloudNames.
Packit Service a04d08
    data = _collect_platform_data()
Packit Service a04d08
    checks = (identify_aws, identify_brightbox, identify_zstack,
Packit Service a04d08
              identify_e24cloud, lambda x: CloudNames.UNKNOWN)
Packit Service a04d08
    for checker in checks:
Packit Service a04d08
        try:
Packit Service a04d08
            result = checker(data)
Packit Service a04d08
            if result:
Packit Service a04d08
                return result
Packit Service a04d08
        except Exception as e:
Packit Service a04d08
            LOG.warning("calling %s with %s raised exception: %s",
Packit Service a04d08
                        checker, data, e)
Packit Service a04d08
Packit Service a04d08
Packit Service a04d08
def _collect_platform_data():
Packit Service a04d08
    """Returns a dictionary of platform info from dmi or /sys/hypervisor.
Packit Service a04d08
Packit Service a04d08
    Keys in the dictionary are as follows:
Packit Service a04d08
       uuid: system-uuid from dmi or /sys/hypervisor
Packit Service a04d08
       uuid_source: 'hypervisor' (/sys/hypervisor/uuid) or 'dmi'
Packit Service a04d08
       serial: dmi 'system-serial-number' (/sys/.../product_serial)
Packit Service a04d08
       asset_tag: 'dmidecode -s chassis-asset-tag'
Packit Service a04d08
       vendor: dmi 'system-manufacturer' (/sys/.../sys_vendor)
Packit Service a04d08
Packit Service a04d08
    On Ec2 instances experimentation is that product_serial is upper case,
Packit Service a04d08
    and product_uuid is lower case.  This returns lower case values for both.
Packit Service a04d08
    """
Packit Service a04d08
    data = {}
Packit Service a04d08
    try:
Packit Service a04d08
        uuid = util.load_file("/sys/hypervisor/uuid").strip()
Packit Service a04d08
        data['uuid_source'] = 'hypervisor'
Packit Service a04d08
    except Exception:
Packit Service a04d08
        uuid = util.read_dmi_data('system-uuid')
Packit Service a04d08
        data['uuid_source'] = 'dmi'
Packit Service a04d08
Packit Service a04d08
    if uuid is None:
Packit Service a04d08
        uuid = ''
Packit Service a04d08
    data['uuid'] = uuid.lower()
Packit Service a04d08
Packit Service a04d08
    serial = util.read_dmi_data('system-serial-number')
Packit Service a04d08
    if serial is None:
Packit Service a04d08
        serial = ''
Packit Service a04d08
Packit Service a04d08
    data['serial'] = serial.lower()
Packit Service a04d08
Packit Service a04d08
    asset_tag = util.read_dmi_data('chassis-asset-tag')
Packit Service a04d08
    if asset_tag is None:
Packit Service a04d08
        asset_tag = ''
Packit Service a04d08
Packit Service a04d08
    data['asset_tag'] = asset_tag.lower()
Packit Service a04d08
Packit Service a04d08
    vendor = util.read_dmi_data('system-manufacturer')
Packit Service a04d08
    data['vendor'] = (vendor if vendor else '').lower()
Packit Service a04d08
Packit Service a04d08
    return data
Packit Service a04d08
Packit Service a04d08
Packit Service 9bfd13
def convert_ec2_metadata_network_config(
Packit Service 9bfd13
        network_md, macs_to_nics=None, fallback_nic=None,
Packit Service 9bfd13
        full_network_config=True):
Packit Service 9bfd13
    """Convert ec2 metadata to network config version 2 data dict.
Packit Service a04d08
Packit Service a04d08
    @param: network_md: 'network' portion of EC2 metadata.
Packit Service a04d08
       generally formed as {"interfaces": {"macs": {}} where
Packit Service a04d08
       'macs' is a dictionary with mac address as key and contents like:
Packit Service a04d08
       {"device-number": "0", "interface-id": "...", "local-ipv4s": ...}
Packit Service a04d08
    @param: macs_to_nics: Optional dict of mac addresses and nic names. If
Packit Service a04d08
       not provided, get_interfaces_by_mac is called to get it from the OS.
Packit Service a04d08
    @param: fallback_nic: Optionally provide the primary nic interface name.
Packit Service a04d08
       This nic will be guaranteed to minimally have a dhcp4 configuration.
Packit Service 9bfd13
    @param: full_network_config: Boolean set True to configure all networking
Packit Service 9bfd13
       presented by IMDS. This includes rendering secondary IPv4 and IPv6
Packit Service 9bfd13
       addresses on all NICs and rendering network config on secondary NICs.
Packit Service 9bfd13
       If False, only the primary nic will be configured and only with dhcp
Packit Service 9bfd13
       (IPv4/IPv6).
Packit Service a04d08
Packit Service 9bfd13
    @return A dict of network config version 2 based on the metadata and macs.
Packit Service a04d08
    """
Packit Service 9bfd13
    netcfg = {'version': 2, 'ethernets': {}}
Packit Service a04d08
    if not macs_to_nics:
Packit Service a04d08
        macs_to_nics = net.get_interfaces_by_mac()
Packit Service a04d08
    macs_metadata = network_md['interfaces']['macs']
Packit Service 9bfd13
Packit Service 9bfd13
    if not full_network_config:
Packit Service 9bfd13
        for mac, nic_name in macs_to_nics.items():
Packit Service 9bfd13
            if nic_name == fallback_nic:
Packit Service 9bfd13
                break
Packit Service 9bfd13
        dev_config = {'dhcp4': True,
Packit Service 9bfd13
                      'dhcp6': False,
Packit Service 9bfd13
                      'match': {'macaddress': mac.lower()},
Packit Service 9bfd13
                      'set-name': nic_name}
Packit Service 9bfd13
        nic_metadata = macs_metadata.get(mac)
Packit Service 9bfd13
        if nic_metadata.get('ipv6s'):  # Any IPv6 addresses configured
Packit Service 9bfd13
            dev_config['dhcp6'] = True
Packit Service 9bfd13
        netcfg['ethernets'][nic_name] = dev_config
Packit Service 9bfd13
        return netcfg
Packit Service 9bfd13
    # Apply network config for all nics and any secondary IPv4/v6 addresses
Packit Service 9bfd13
    for mac, nic_name in sorted(macs_to_nics.items()):
Packit Service a04d08
        nic_metadata = macs_metadata.get(mac)
Packit Service a04d08
        if not nic_metadata:
Packit Service a04d08
            continue  # Not a physical nic represented in metadata
Packit Service 9bfd13
        # device-number is zero-indexed, we want it 1-indexed for the
Packit Service 9bfd13
        # multiplication on the following line
Packit Service 9bfd13
        nic_idx = int(nic_metadata['device-number']) + 1
Packit Service 9bfd13
        dhcp_override = {'route-metric': nic_idx * 100}
Packit Service 9bfd13
        dev_config = {'dhcp4': True, 'dhcp4-overrides': dhcp_override,
Packit Service 9bfd13
                      'dhcp6': False,
Packit Service 9bfd13
                      'match': {'macaddress': mac.lower()},
Packit Service 9bfd13
                      'set-name': nic_name}
Packit Service 9bfd13
        if nic_metadata.get('ipv6s'):  # Any IPv6 addresses configured
Packit Service 9bfd13
            dev_config['dhcp6'] = True
Packit Service 9bfd13
            dev_config['dhcp6-overrides'] = dhcp_override
Packit Service 9bfd13
        dev_config['addresses'] = get_secondary_addresses(nic_metadata, mac)
Packit Service 9bfd13
        if not dev_config['addresses']:
Packit Service 9bfd13
            dev_config.pop('addresses')  # Since we found none configured
Packit Service 9bfd13
        netcfg['ethernets'][nic_name] = dev_config
Packit Service 9bfd13
    # Remove route-metric dhcp overrides if only one nic configured
Packit Service 9bfd13
    if len(netcfg['ethernets']) == 1:
Packit Service 9bfd13
        for nic_name in netcfg['ethernets'].keys():
Packit Service 9bfd13
            netcfg['ethernets'][nic_name].pop('dhcp4-overrides')
Packit Service 9bfd13
            netcfg['ethernets'][nic_name].pop('dhcp6-overrides', None)
Packit Service a04d08
    return netcfg
Packit Service a04d08
Packit Service a04d08
Packit Service 9bfd13
def get_secondary_addresses(nic_metadata, mac):
Packit Service 9bfd13
    """Parse interface-specific nic metadata and return any secondary IPs
Packit Service 9bfd13
Packit Service 9bfd13
    :return: List of secondary IPv4 or IPv6 addresses to configure on the
Packit Service 9bfd13
    interface
Packit Service 9bfd13
    """
Packit Service 9bfd13
    ipv4s = nic_metadata.get('local-ipv4s')
Packit Service 9bfd13
    ipv6s = nic_metadata.get('ipv6s')
Packit Service 9bfd13
    addresses = []
Packit Service 9bfd13
    # In version < 2018-09-24 local_ipv4s or ipv6s is a str with one IP
Packit Service 9bfd13
    if bool(isinstance(ipv4s, list) and len(ipv4s) > 1):
Packit Service 9bfd13
        addresses.extend(
Packit Service 9bfd13
            _get_secondary_addresses(
Packit Service 9bfd13
                nic_metadata, 'subnet-ipv4-cidr-block', mac, ipv4s, '24'))
Packit Service 9bfd13
    if bool(isinstance(ipv6s, list) and len(ipv6s) > 1):
Packit Service 9bfd13
        addresses.extend(
Packit Service 9bfd13
            _get_secondary_addresses(
Packit Service 9bfd13
                nic_metadata, 'subnet-ipv6-cidr-block', mac, ipv6s, '128'))
Packit Service 9bfd13
    return sorted(addresses)
Packit Service 9bfd13
Packit Service 9bfd13
Packit Service 9bfd13
def _get_secondary_addresses(nic_metadata, cidr_key, mac, ips, default_prefix):
Packit Service 9bfd13
    """Return list of IP addresses as CIDRs for secondary IPs
Packit Service 9bfd13
Packit Service 9bfd13
    The CIDR prefix will be default_prefix if cidr_key is absent or not
Packit Service 9bfd13
    parseable in nic_metadata.
Packit Service 9bfd13
    """
Packit Service 9bfd13
    addresses = []
Packit Service 9bfd13
    cidr = nic_metadata.get(cidr_key)
Packit Service 9bfd13
    prefix = default_prefix
Packit Service 9bfd13
    if not cidr or len(cidr.split('/')) != 2:
Packit Service 9bfd13
        ip_type = 'ipv4' if 'ipv4' in cidr_key else 'ipv6'
Packit Service 9bfd13
        LOG.warning(
Packit Service 9bfd13
            'Could not parse %s %s for mac %s. %s network'
Packit Service 9bfd13
            ' config prefix defaults to /%s',
Packit Service 9bfd13
            cidr_key, cidr, mac, ip_type, prefix)
Packit Service 9bfd13
    else:
Packit Service 9bfd13
        prefix = cidr.split('/')[1]
Packit Service 9bfd13
    # We know we have > 1 ips for in metadata for this IP type
Packit Service 9bfd13
    for ip in ips[1:]:
Packit Service 9bfd13
        addresses.append(
Packit Service 9bfd13
            '{ip}/{prefix}'.format(ip=ip, prefix=prefix))
Packit Service 9bfd13
    return addresses
Packit Service 9bfd13
Packit Service 9bfd13
Packit Service a04d08
# Used to match classes to dependencies
Packit Service a04d08
datasources = [
Packit Service a04d08
    (DataSourceEc2Local, (sources.DEP_FILESYSTEM,)),  # Run at init-local
Packit Service a04d08
    (DataSourceEc2, (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