Sign Up
Log In
Log In
or
Sign Up
Places
All Projects
Status Monitor
Collapse sidebar
home:bmwiedemann
cloud-init
cloud-init-hetzner.patch
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File cloud-init-hetzner.patch of Package cloud-init
diff --git a/cloudinit/sources/DataSourceHetzner.py b/cloudinit/sources/DataSourceHetzner.py new file mode 100644 index 00000000..5d93c474 --- /dev/null +++ b/cloudinit/sources/DataSourceHetzner.py @@ -0,0 +1,90 @@ +# Author: Jonas Keidel <jonas.keidel@hetzner.de> +# +# This file is part of cloud-init. See LICENSE file for license information. + +from cloudinit import log as logging +from cloudinit import sources +from cloudinit import util + +import cloudinit.sources.helpers.hetzner as ho_helper +import os.path + +LOG = logging.getLogger(__name__) + +BUILTIN_DS_CONFIG = { + 'metadata_url': 'http://169.254.169.254/hetzner/v1/metadata', + 'userdata_url': 'http://169.254.169.254/hetzner/v1/userdata', +} + +MD_RETRIES = 60 +MD_TIMEOUT = 2 +MD_WAIT_RETRY = 2 + +class DataSourceHetzner(sources.DataSource): + def __init__(self, sys_cfg, distro, paths): + sources.DataSource.__init__(self, sys_cfg, distro, paths) + self.distro = distro + self.metadata = dict() + self.ds_cfg = util.mergemanydict([ + util.get_cfg_by_path(sys_cfg, ["datasource", "Hetzner"], {}), + BUILTIN_DS_CONFIG]) + self.metadata_address = self.ds_cfg['metadata_url'] + self.userdata_address = self.ds_cfg['userdata_url'] + self.retries = self.ds_cfg.get('retries', MD_RETRIES) + self.timeout = self.ds_cfg.get('timeout', MD_TIMEOUT) + self.wait_retry = self.ds_cfg.get('wait_retry', MD_WAIT_RETRY) + self._network_config = None + self.dsmode = sources.DSMODE_NETWORK + + def get_data(self): + local_ip_nic = ho_helper.add_local_ip() + + md = ho_helper.read_metadata( + self.metadata_address, timeout=self.timeout, + sec_between=self.wait_retry, retries=self.retries) + + ud = ho_helper.read_userdata( + self.userdata_address, timeout=self.timeout, + sec_between=self.wait_retry, retries=self.retries) + self.userdata_raw = ud + + self.metadata_full = md + self.metadata['instance-id'] = md.get('instance-id', None) + self.metadata['local-hostname'] = md.get('hostname', None) + self.metadata['network-config'] = md.get('network-config', None) + self.metadata['public-keys'] = md.get('public-keys', None) + self.vendordata_raw = md.get("vendor_data", None) + + ho_helper.remove_local_ip(local_ip_nic) + + return True + + @property + def network_config(self): + """Configure the networking. This needs to be done each boot, since + the IP information may have changed due to snapshot and/or + migration. + """ + + if self._network_config: + return self._network_config + + _net_config = self.metadata['network-config'] + if not _net_config: + raise Exception("Unable to get meta-data from server....") + + self._network_config = _net_config + + return self._network_config + +# Used to match classes to dependencies +datasources = [ + (DataSourceHetzner, (sources.DEP_FILESYSTEM, )), +] + + +# Return a list of data sources that match this set of dependencies +def get_datasource_list(depends): + return sources.list_from_depends(depends, datasources) + +# vi: ts=4 expandtab diff --git a/cloudinit/sources/helpers/hetzner.py b/cloudinit/sources/helpers/hetzner.py new file mode 100644 index 00000000..1eba5e08 --- /dev/null +++ b/cloudinit/sources/helpers/hetzner.py @@ -0,0 +1,73 @@ +# Author: Jonas Keidel <jonas.keidel@hetzner.de> +# +# This file is part of cloud-init. See LICENSE file for license information. + +import json +import random + +from cloudinit import log as logging +from cloudinit import net as cloudnet +from cloudinit import url_helper +from cloudinit import util + +LOG = logging.getLogger(__name__) + +def read_metadata(url, timeout=2, sec_between=2, retries=30): + response = url_helper.readurl(url, timeout=timeout, + sec_between=sec_between, retries=retries) + if not response.ok(): + raise RuntimeError("unable to read metadata at %s" % url) + return util.load_yaml(response.contents.decode()) + +def read_userdata(url, timeout=2, sec_between=2, retries=30): + response = url_helper.readurl(url, timeout=timeout, + sec_between=sec_between, retries=retries) + if not response.ok(): + raise RuntimeError("unable to read metadata at %s" % url) + return response.contents.decode() + +def add_local_ip(nic=None): + nic = get_local_ip_nic() + LOG.debug("selected interface '%s' for reading metadata", nic) + + if not nic: + raise RuntimeError("unable to find interfaces to access the" + "meta-data server. This droplet is broken.") + + ip_addr_cmd = ['ip', 'addr', 'add', '169.254.0.1/16', 'dev', nic] + ip_link_cmd = ['ip', 'link', 'set', 'dev', nic, 'up'] + + if not util.which('ip'): + raise RuntimeError("No 'ip' command available to configure local ip " + "address") + + try: + (result, _err) = util.subp(ip_addr_cmd) + LOG.debug("assigned local ip to '%s'", nic) + + (result, _err) = util.subp(ip_link_cmd) + LOG.debug("brought device '%s' up", nic) + except Exception: + util.logexc(LOG, "local ip address assignment to '%s' failed.", nic) + raise + + return nic + + +def get_local_ip_nic(): + nics = [f for f in cloudnet.get_devicelist() if cloudnet.is_physical(f)] + if not nics: + return None + return min(nics, key=lambda d: cloudnet.read_sys_net_int(d, 'ifindex')) + +def remove_local_ip(nic=None): + ip_addr_cmd = ['ip', 'addr', 'flush', 'dev', nic] + ip_link_cmd = ['ip', 'link', 'set', 'dev', nic, 'down'] + try: + (result, _err) = util.subp(ip_addr_cmd) + LOG.debug("removed all addresses from %s", nic) + (result, _err) = util.subp(ip_link_cmd) + LOG.debug("brought device '%s' down", nic) + except Exception as e: + util.logexc(LOG, "failed to remove all address from '%s'.", nic, e) + diff --git a/tools/ds-identify b/tools/ds-identify index 5f762438..98115b81 100755 --- a/tools/ds-identify +++ b/tools/ds-identify @@ -114,7 +114,7 @@ DI_DSNAME="" # be searched if there is no setting found in config. DI_DSLIST_DEFAULT="MAAS ConfigDrive NoCloud AltCloud Azure Bigstep \ CloudSigma CloudStack DigitalOcean AliYun Ec2 GCE OpenNebula OpenStack \ -OVF SmartOS Scaleway" +OVF Hetzner SmartOS Scaleway" DI_DSLIST="" DI_MODE="" DI_ON_FOUND="" @@ -630,6 +630,12 @@ dscheck_DigitalOcean() { return ${DS_NOT_FOUND} } +dscheck_Hetzner() { + dmi_sys_vendor_is Hetzner && + dmi_product_name_matches "vServer" && return ${DS_FOUND} + return ${DS_NOT_FOUND} +} + dscheck_OpenNebula() { check_seed_dir opennebula && return ${DS_FOUND} has_fs_with_label "CONTEXT" && return ${DS_FOUND}
Locations
Projects
Search
Status Monitor
Help
OpenBuildService.org
Documentation
API Documentation
Code of Conduct
Contact
Support
@OBShq
Terms
openSUSE Build Service is sponsored by
The Open Build Service is an
openSUSE project
.
Sign Up
Log In
Places
Places
All Projects
Status Monitor