summaryrefslogtreecommitdiff
path: root/cloudinit/sources/DataSourceLXD.py
diff options
context:
space:
mode:
Diffstat (limited to 'cloudinit/sources/DataSourceLXD.py')
-rw-r--r--cloudinit/sources/DataSourceLXD.py75
1 files changed, 52 insertions, 23 deletions
diff --git a/cloudinit/sources/DataSourceLXD.py b/cloudinit/sources/DataSourceLXD.py
index 732b32ff..55ae52a2 100644
--- a/cloudinit/sources/DataSourceLXD.py
+++ b/cloudinit/sources/DataSourceLXD.py
@@ -10,6 +10,7 @@ Notes:
* TODO( Hotplug support using websockets API 1.0/events )
"""
+from json.decoder import JSONDecodeError
import os
import requests
@@ -41,14 +42,16 @@ LXD_SOCKET_API_VERSION = "1.0"
# Config key mappings to alias as top-level instance data keys
CONFIG_KEY_ALIASES = {
+ "cloud-init.user-data": "user-data",
+ "cloud-init.network-config": "network-config",
+ "cloud-init.vendor-data": "vendor-data",
"user.user-data": "user-data",
"user.network-config": "network-config",
- "user.network_mode": "network_mode",
"user.vendor-data": "vendor-data"
}
-def generate_fallback_network_config(network_mode: str = "") -> dict:
+def generate_fallback_network_config() -> dict:
"""Return network config V1 dict representing instance network config."""
network_v1 = {
"version": 1,
@@ -76,12 +79,6 @@ def generate_fallback_network_config(network_mode: str = "") -> dict:
network_v1["config"][0]["name"] = "enc9"
else:
network_v1["config"][0]["name"] = "enp5s0"
- if network_mode == "link-local":
- network_v1["config"][0]["subnets"][0]["control"] = "manual"
- elif network_mode not in ("", "dhcp"):
- LOG.warning(
- "Ignoring unexpected value user.network_mode: %s", network_mode
- )
return network_v1
@@ -244,10 +241,7 @@ class DataSourceLXD(sources.DataSource):
"network-config"
)
else:
- network_mode = self._crawled_metadata.get("network_mode", "")
- self._network_config = generate_fallback_network_config(
- network_mode
- )
+ self._network_config = generate_fallback_network_config()
return self._network_config
@@ -294,7 +288,7 @@ def read_metadata(
with requests.Session() as session:
session.mount(version_url, LXDSocketAdapter())
# Raw meta-data as text
- md_route = "{route}/meta-data".format(route=version_url)
+ md_route = "{route}meta-data".format(route=version_url)
response = session.get(md_route)
LOG.debug("[GET] [HTTP:%d] %s", response.status_code, md_route)
if not response.ok:
@@ -302,7 +296,7 @@ def read_metadata(
"Invalid HTTP response [{code}] from {route}: {resp}".format(
code=response.status_code,
route=md_route,
- resp=response.txt
+ resp=response.text
)
)
@@ -310,19 +304,42 @@ def read_metadata(
if metadata_only:
return md # Skip network-data, vendor-data, user-data
- config_url = version_url + "config"
- # Represent all advertized/available config routes under
- # the dict path {LXD_SOCKET_API_VERSION: {config: {...}}.
- LOG.debug("[GET] %s", config_url)
- config_routes = session.get(config_url).json()
md[LXD_SOCKET_API_VERSION] = {
"config": {},
"meta-data": md["meta-data"]
}
- for config_route in config_routes:
+
+ config_url = version_url + "config"
+ # Represent all advertized/available config routes under
+ # the dict path {LXD_SOCKET_API_VERSION: {config: {...}}.
+ response = session.get(config_url)
+ LOG.debug("[GET] [HTTP:%d] %s", response.status_code, config_url)
+ if not response.ok:
+ raise sources.InvalidMetaDataException(
+ "Invalid HTTP response [{code}] from {route}: {resp}".format(
+ code=response.status_code,
+ route=config_url,
+ resp=response.text
+ )
+ )
+ try:
+ config_routes = response.json()
+ except JSONDecodeError as exc:
+ raise sources.InvalidMetaDataException(
+ "Unable to determine cloud-init config from {route}."
+ " Expected JSON but found: {resp}".format(
+ route=config_url,
+ resp=response.text
+ )
+ ) from exc
+
+ # Sorting keys to ensure we always process in alphabetical order.
+ # cloud-init.* keys will sort before user.* keys which is preferred
+ # precedence.
+ for config_route in sorted(config_routes):
url = "http://lxd{route}".format(route=config_route)
- LOG.debug("[GET] %s", url)
response = session.get(url)
+ LOG.debug("[GET] [HTTP:%d] %s", response.status_code, url)
if response.ok:
cfg_key = config_route.rpartition("/")[-1]
# Leave raw data values/format unchanged to represent it in
@@ -331,9 +348,21 @@ def read_metadata(
md[LXD_SOCKET_API_VERSION]["config"][cfg_key] = response.text
# Promote common CONFIG_KEY_ALIASES to top-level keys.
if cfg_key in CONFIG_KEY_ALIASES:
- md[CONFIG_KEY_ALIASES[cfg_key]] = response.text
+ # Due to sort of config_routes, promote cloud-init.*
+ # aliases before user.*. This allows user.* keys to act as
+ # fallback config on old LXD, with new cloud-init images.
+ if CONFIG_KEY_ALIASES[cfg_key] not in md:
+ md[CONFIG_KEY_ALIASES[cfg_key]] = response.text
+ else:
+ LOG.warning(
+ "Ignoring LXD config %s in favor of %s value.",
+ cfg_key, cfg_key.replace("user", "cloud-init", 1)
+ )
else:
- LOG.debug("Skipping %s on invalid response", url)
+ LOG.debug(
+ "Skipping %s on [HTTP:%d]:%s",
+ url, response.status_code, response.text
+ )
return md