Files
Christian Manivong b5c830acfe
CI / test (3.10) (push) Successful in 30s
CI / test (3.11) (push) Successful in 26s
CI / test (3.12) (push) Successful in 28s
CI / test (3.10) (pull_request) Successful in 27s
CI / test (3.11) (pull_request) Successful in 26s
CI / test (3.12) (pull_request) Successful in 27s
feat(vm-provision): declare the guest agent per guest OS, Linux only
Follows napalm-device-types 3.0, which replaces GUEST_AGENT_PACKAGES /
GUEST_AGENT_RUNCMD with GUEST_AGENTS = {guest_os: (packages, runcmd)}.
VMware lists Linux with open-vm-tools, as before, and refuses any other
guest_os before anything is created: BSD guests on VMware are optional
for now (NetOrk/netork#801).

The NoCloud network-config moved to napalm-device-types
(`provisioning.network_config`), which Proxmox now uses as well; the seed
module's copy is gone, and the tests check that the generic one is used.
2026-10-08 07:54:13 +02:00

54 lines
1.8 KiB
Python

"""cloud-init's NoCloud seed: user-data, meta-data, network-config on an ISO.
NoCloud rather than VMware's guestinfo datasource because it needs nothing in
the guest: guestinfo is read through open-vm-tools, which a generic cloud
image does not ship -- it is one of the things the user-data installs.
"""
from __future__ import annotations
import io
from typing import Any
import pycdlib
import yaml
#: The volume label cloud-init's NoCloud datasource looks for.
_LABEL = "cidata"
def user_data(cloud_init_config: dict[str, Any], ssh_public_keys: list[str] | None) -> str:
"""The ``#cloud-config`` document, with ``ssh_public_keys`` merged in."""
config = dict(cloud_init_config)
if ssh_public_keys:
keys = list(config.get("ssh_authorized_keys") or [])
keys += [k for k in ssh_public_keys if k not in keys]
config["ssh_authorized_keys"] = keys
return "#cloud-config\n" + yaml.safe_dump(config, sort_keys=False)
def meta_data(hostname: str, instance_id: str) -> str:
return yaml.safe_dump({"instance-id": instance_id, "local-hostname": hostname}, sort_keys=False)
def nocloud_iso(user: str, meta: str, network: dict[str, Any] | None) -> bytes:
"""An ISO 9660 image (Rock Ridge + Joliet) holding the seed files."""
files = {"user-data": user, "meta-data": meta}
if network is not None:
files["network-config"] = yaml.safe_dump(network, sort_keys=False)
iso = pycdlib.PyCdlib()
iso.new(interchange_level=3, joliet=3, rock_ridge="1.09", vol_ident=_LABEL)
for index, (name, content) in enumerate(files.items()):
data = content.encode()
iso.add_fp(
io.BytesIO(data),
len(data),
f"/SEED{index}.;1",
rr_name=name,
joliet_path=f"/{name}",
)
out = io.BytesIO()
iso.write_fp(out)
iso.close()
return out.getvalue()