Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 52 additions & 23 deletions cloudinit/sources/DataSourceLXD.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
* TODO( Hotplug support using websockets API 1.0/events )
"""

from json.decoder import JSONDecodeError
import os

import requests
Expand Down Expand Up @@ -41,14 +42,16 @@

# 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,
Expand Down Expand Up @@ -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


Expand Down Expand Up @@ -244,10 +241,7 @@ def network_config(self) -> dict:
"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


Expand Down Expand Up @@ -294,35 +288,58 @@ 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:
raise sources.InvalidMetaDataException(
"Invalid HTTP response [{code}] from {route}: {resp}".format(
code=response.status_code,
route=md_route,
resp=response.txt
resp=response.text
)
)

md["meta-data"] = response.text
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:
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice additional error checking in this section

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
Expand All @@ -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:
Comment thread
TheRealFalcon marked this conversation as resolved.
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:
Comment thread
blackboxsw marked this conversation as resolved.
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


Expand Down
Loading