summaryrefslogtreecommitdiff
path: root/cloudinit/sources
diff options
context:
space:
mode:
authorshell-skrimp <116320856+shell-skrimp@users.noreply.github.com>2022-10-26 16:18:02 -0500
committerGitHub <noreply@github.com>2022-10-26 16:18:02 -0500
commitd0cae67b2f51a37e2a019e4b8b2faa5aa0ba4928 (patch)
treef24cbfa929b90478153a0a9423a5c75990b541d1 /cloudinit/sources
parentc6d1ef89a6aca203364d0014eeb5057461600d8e (diff)
downloadcloud-init-git-d0cae67b2f51a37e2a019e4b8b2faa5aa0ba4928.tar.gz
add NWCS datasource (#1793)
I am writing a cloud platform (nwcs) and need to add support to cloud-init.
Diffstat (limited to 'cloudinit/sources')
-rw-r--r--cloudinit/sources/DataSourceNWCS.py168
1 files changed, 168 insertions, 0 deletions
diff --git a/cloudinit/sources/DataSourceNWCS.py b/cloudinit/sources/DataSourceNWCS.py
new file mode 100644
index 00000000..e21383d2
--- /dev/null
+++ b/cloudinit/sources/DataSourceNWCS.py
@@ -0,0 +1,168 @@
+# This file is part of cloud-init. See LICENSE file for license information.
+
+from requests import exceptions
+
+from cloudinit import dmi
+from cloudinit import log as logging
+from cloudinit import net, sources, subp, url_helper, util
+from cloudinit.net.dhcp import NoDHCPLeaseError
+from cloudinit.net.ephemeral import EphemeralDHCPv4
+
+LOG = logging.getLogger(__name__)
+
+BASE_URL_V1 = "http://169.254.169.254/api/v1"
+
+BUILTIN_DS_CONFIG = {
+ "metadata_url": BASE_URL_V1 + "/metadata",
+}
+
+MD_RETRIES = 30
+MD_TIMEOUT = 5
+MD_WAIT_RETRY = 5
+
+
+class DataSourceNWCS(sources.DataSource):
+
+ dsname = "NWCS"
+
+ 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", "NWCS"], {}),
+ BUILTIN_DS_CONFIG,
+ ]
+ )
+ self.metadata_address = self.ds_cfg["metadata_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 = sources.UNSET
+ self.dsmode = sources.DSMODE_NETWORK
+
+ def _get_data(self):
+ LOG.info("Detecting if machine is a NWCS instance")
+ on_nwcs = get_nwcs_data()
+
+ if not on_nwcs:
+ LOG.info("Machine is not a NWCS instance")
+ return False
+
+ LOG.info("Machine is a NWCS instance")
+
+ md = self.get_metadata()
+
+ if md is None:
+ raise Exception("failed to get metadata")
+
+ self.metadata_full = md
+
+ self.metadata["instance-id"] = md["instance-id"]
+ self.metadata["public-keys"] = md["public-keys"]
+ self.metadata["network"] = md["network"]
+ self.metadata["local-hostname"] = md["hostname"]
+
+ self.userdata_raw = md.get("userdata", None)
+
+ self.vendordata_raw = md.get("vendordata", None)
+
+ return True
+
+ def get_metadata(self):
+ try:
+ LOG.info("Attempting to get metadata via DHCP")
+
+ with EphemeralDHCPv4(
+ iface=net.find_fallback_nic(),
+ connectivity_url_data={
+ "url": BASE_URL_V1 + "/metadata/instance-id",
+ },
+ ):
+ return read_metadata(
+ self.metadata_address,
+ timeout=self.timeout,
+ sec_between=self.wait_retry,
+ retries=self.retries,
+ )
+
+ except (
+ NoDHCPLeaseError,
+ subp.ProcessExecutionError,
+ RuntimeError,
+ exceptions.RequestException,
+ ) as e:
+ LOG.error("DHCP failure: %s", e)
+ raise
+
+ @property
+ def network_config(self):
+ LOG.debug("Attempting network configuration")
+
+ if self._network_config is None:
+ LOG.warning(
+ "Found None as cached _network_config, resetting to %s",
+ sources.UNSET,
+ )
+ self._network_config = sources.UNSET
+
+ if self._network_config != sources.UNSET:
+ return self._network_config
+
+ if not self.metadata["network"]["config"]:
+ raise Exception("Unable to get metadata from server")
+
+ # metadata sends interface names, but we dont want to use them
+ for i in self.metadata["network"]["config"]:
+ iface_name = get_interface_name(i["mac_address"])
+
+ if iface_name:
+ LOG.info("Overriding %s with %s", i["name"], iface_name)
+ i["name"] = iface_name
+
+ self._network_config = self.metadata["network"]
+
+ return self._network_config
+
+
+def get_nwcs_data():
+ vendor_name = dmi.read_dmi_data("system-manufacturer")
+
+ if vendor_name != "NWCS":
+ return False
+
+ return True
+
+
+def get_interface_name(mac):
+ macs_to_nic = net.get_interfaces_by_mac()
+
+ if mac not in macs_to_nic:
+ return None
+
+ return macs_to_nic.get(mac)
+
+
+# Return a list of data sources that match this set of dependencies
+def get_datasource_list(depends):
+ return sources.list_from_depends(depends, datasources)
+
+
+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_json(response.contents.decode())
+
+
+# Used to match classes to dependencies
+datasources = [
+ (DataSourceNWCS, (sources.DEP_FILESYSTEM,)),
+]
+
+# vi: ts=4 expandtab