Blame cloudinit/sources/DataSourceHetzner.py

Packit Service a04d08
# Author: Jonas Keidel <jonas.keidel@hetzner.com>
Packit Service a04d08
# Author: Markus Schade <markus.schade@hetzner.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
"""Hetzner Cloud API Documentation.
Packit Service a04d08
   https://docs.hetzner.cloud/"""
Packit Service a04d08
Packit Service a04d08
from cloudinit import log as logging
Packit Service a04d08
from cloudinit import net as cloudnet
Packit Service a04d08
from cloudinit import sources
Packit Service a04d08
from cloudinit import util
Packit Service a04d08
Packit Service a04d08
import cloudinit.sources.helpers.hetzner as hc_helper
Packit Service a04d08
Packit Service a04d08
LOG = logging.getLogger(__name__)
Packit Service a04d08
Packit Service a04d08
BASE_URL_V1 = 'http://169.254.169.254/hetzner/v1'
Packit Service a04d08
Packit Service a04d08
BUILTIN_DS_CONFIG = {
Packit Service a04d08
    'metadata_url': BASE_URL_V1 + '/metadata',
Packit Service a04d08
    'userdata_url': BASE_URL_V1 + '/userdata',
Packit Service a04d08
}
Packit Service a04d08
Packit Service a04d08
MD_RETRIES = 60
Packit Service a04d08
MD_TIMEOUT = 2
Packit Service a04d08
MD_WAIT_RETRY = 2
Packit Service a04d08
Packit Service a04d08
Packit Service a04d08
class DataSourceHetzner(sources.DataSource):
Packit Service a04d08
Packit Service a04d08
    dsname = 'Hetzner'
Packit Service a04d08
Packit Service a04d08
    def __init__(self, sys_cfg, distro, paths):
Packit Service a04d08
        sources.DataSource.__init__(self, sys_cfg, distro, paths)
Packit Service a04d08
        self.distro = distro
Packit Service a04d08
        self.metadata = dict()
Packit Service a04d08
        self.ds_cfg = util.mergemanydict([
Packit Service a04d08
            util.get_cfg_by_path(sys_cfg, ["datasource", "Hetzner"], {}),
Packit Service a04d08
            BUILTIN_DS_CONFIG])
Packit Service a04d08
        self.metadata_address = self.ds_cfg['metadata_url']
Packit Service a04d08
        self.userdata_address = self.ds_cfg['userdata_url']
Packit Service a04d08
        self.retries = self.ds_cfg.get('retries', MD_RETRIES)
Packit Service a04d08
        self.timeout = self.ds_cfg.get('timeout', MD_TIMEOUT)
Packit Service a04d08
        self.wait_retry = self.ds_cfg.get('wait_retry', MD_WAIT_RETRY)
Packit Service a04d08
        self._network_config = None
Packit Service a04d08
        self.dsmode = sources.DSMODE_NETWORK
Packit Service a04d08
Packit Service a04d08
    def get_data(self):
Packit Service a04d08
        if not on_hetzner():
Packit Service a04d08
            return False
Packit Service a04d08
        nic = cloudnet.find_fallback_nic()
Packit Service a04d08
        with cloudnet.EphemeralIPv4Network(nic, "169.254.0.1", 16,
Packit Service a04d08
                                           "169.254.255.255"):
Packit Service a04d08
            md = hc_helper.read_metadata(
Packit Service a04d08
                self.metadata_address, timeout=self.timeout,
Packit Service a04d08
                sec_between=self.wait_retry, retries=self.retries)
Packit Service a04d08
            ud = hc_helper.read_userdata(
Packit Service a04d08
                self.userdata_address, timeout=self.timeout,
Packit Service a04d08
                sec_between=self.wait_retry, retries=self.retries)
Packit Service a04d08
Packit Service 751c4a
        # Hetzner cloud does not support binary user-data. So here, do a
Packit Service 751c4a
        # base64 decode of the data if we can. The end result being that a
Packit Service 751c4a
        # user can provide base64 encoded (possibly gzipped) data as user-data.
Packit Service 751c4a
        #
Packit Service 751c4a
        # The fallout is that in the event of b64 encoded user-data,
Packit Service 751c4a
        # /var/lib/cloud-init/cloud-config.txt will not be identical to the
Packit Service 751c4a
        # user-data provided.  It will be decoded.
Packit Service 751c4a
        self.userdata_raw = hc_helper.maybe_b64decode(ud)
Packit Service a04d08
        self.metadata_full = md
Packit Service a04d08
Packit Service 751c4a
        # hostname is name provided by user at launch.  The API enforces it is
Packit Service 751c4a
        # a valid hostname, but it is not guaranteed to be resolvable in dns or
Packit Service 751c4a
        # fully qualified.
Packit Service a04d08
        self.metadata['instance-id'] = md['instance-id']
Packit Service a04d08
        self.metadata['local-hostname'] = md['hostname']
Packit Service a04d08
        self.metadata['network-config'] = md.get('network-config', None)
Packit Service a04d08
        self.metadata['public-keys'] = md.get('public-keys', None)
Packit Service a04d08
        self.vendordata_raw = md.get("vendor_data", None)
Packit Service a04d08
Packit Service a04d08
        return True
Packit Service a04d08
Packit Service a04d08
    @property
Packit Service a04d08
    def network_config(self):
Packit Service a04d08
        """Configure the networking. This needs to be done each boot, since
Packit Service a04d08
           the IP information may have changed due to snapshot and/or
Packit Service a04d08
           migration.
Packit Service a04d08
        """
Packit Service a04d08
Packit Service a04d08
        if self._network_config:
Packit Service a04d08
            return self._network_config
Packit Service a04d08
Packit Service a04d08
        _net_config = self.metadata['network-config']
Packit Service a04d08
        if not _net_config:
Packit Service a04d08
            raise Exception("Unable to get meta-data from server....")
Packit Service a04d08
Packit Service a04d08
        self._network_config = _net_config
Packit Service a04d08
Packit Service a04d08
        return self._network_config
Packit Service a04d08
Packit Service a04d08
Packit Service a04d08
def on_hetzner():
Packit Service a04d08
    return util.read_dmi_data('system-manufacturer') == "Hetzner"
Packit Service a04d08
Packit Service a04d08
Packit Service a04d08
# Used to match classes to dependencies
Packit Service a04d08
datasources = [
Packit Service a04d08
    (DataSourceHetzner, (sources.DEP_FILESYSTEM, )),
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