diff options
Diffstat (limited to 'cloudinit/config/cc_install_hotplug.py')
-rw-r--r-- | cloudinit/config/cc_install_hotplug.py | 151 |
1 files changed, 151 insertions, 0 deletions
diff --git a/cloudinit/config/cc_install_hotplug.py b/cloudinit/config/cc_install_hotplug.py new file mode 100644 index 00000000..34c4557e --- /dev/null +++ b/cloudinit/config/cc_install_hotplug.py @@ -0,0 +1,151 @@ +# This file is part of cloud-init. See LICENSE file for license information. +"""Install hotplug udev rules if supported and enabled""" +import os +from textwrap import dedent + +from cloudinit import stages, subp, util +from cloudinit.config.schema import ( + MetaSchema, + get_meta_doc, + validate_cloudconfig_schema, +) +from cloudinit.distros import ALL_DISTROS +from cloudinit.event import EventScope, EventType +from cloudinit.settings import PER_INSTANCE + +frequency = PER_INSTANCE +distros = [ALL_DISTROS] + +meta: MetaSchema = { + "id": "cc_install_hotplug", + "name": "Install Hotplug", + "title": "Install hotplug if supported and enabled", + "description": dedent( + """\ + This module will install the udev rules to enable hotplug if + supported by the datasource and enabled in the userdata. The udev + rules will be installed as + ``/etc/udev/rules.d/10-cloud-init-hook-hotplug.rules``. + + When hotplug is enabled, newly added network devices will be added + to the system by cloud-init. After udev detects the event, + cloud-init will referesh the instance metadata from the datasource, + detect the device in the updated metadata, then apply the updated + network configuration. + + Currently supported datasources: Openstack, EC2 + """ + ), + "distros": distros, + "examples": [ + dedent( + """\ + # Enable hotplug of network devices + updates: + network: + when: ["hotplug"] + """ + ), + dedent( + """\ + # Enable network hotplug alongside boot event + updates: + network: + when: ["boot", "hotplug"] + """ + ), + ], + "frequency": frequency, +} + +schema = { + "type": "object", + "properties": { + "updates": { + "type": "object", + "additionalProperties": False, + "properties": { + "network": { + "type": "object", + "required": ["when"], + "additionalProperties": False, + "properties": { + "when": { + "type": "array", + "additionalProperties": False, + "items": { + "type": "string", + "additionalProperties": False, + "enum": [ + "boot-new-instance", + "boot-legacy", + "boot", + "hotplug", + ], + }, + } + }, + } + }, + } + }, +} + +__doc__ = get_meta_doc(meta, schema) + + +HOTPLUG_UDEV_PATH = "/etc/udev/rules.d/10-cloud-init-hook-hotplug.rules" +HOTPLUG_UDEV_RULES_TEMPLATE = """\ +# Installed by cloud-init due to network hotplug userdata +ACTION!="add|remove", GOTO="cloudinit_end" +LABEL="cloudinit_hook" +SUBSYSTEM=="net", RUN+="{libexecdir}/hook-hotplug" +LABEL="cloudinit_end" +""" + + +def handle(_name, cfg, cloud, log, _args): + validate_cloudconfig_schema(cfg, schema) + network_hotplug_enabled = ( + "updates" in cfg + and "network" in cfg["updates"] + and "when" in cfg["updates"]["network"] + and "hotplug" in cfg["updates"]["network"]["when"] + ) + hotplug_supported = EventType.HOTPLUG in ( + cloud.datasource.get_supported_events([EventType.HOTPLUG]).get( + EventScope.NETWORK, set() + ) + ) + hotplug_enabled = stages.update_event_enabled( + datasource=cloud.datasource, + cfg=cfg, + event_source_type=EventType.HOTPLUG, + scope=EventScope.NETWORK, + ) + if not (hotplug_supported and hotplug_enabled): + if os.path.exists(HOTPLUG_UDEV_PATH): + log.debug("Uninstalling hotplug, not enabled") + util.del_file(HOTPLUG_UDEV_PATH) + subp.subp(["udevadm", "control", "--reload-rules"]) + elif network_hotplug_enabled: + log.warning( + "Hotplug is unsupported by current datasource. " + "Udev rules will NOT be installed." + ) + else: + log.debug("Skipping hotplug install, not enabled") + return + if not subp.which("udevadm"): + log.debug("Skipping hotplug install, udevadm not found") + return + + # This may need to turn into a distro property at some point + libexecdir = "/usr/libexec/cloud-init" + if not os.path.exists(libexecdir): + libexecdir = "/usr/lib/cloud-init" + util.write_file( + filename=HOTPLUG_UDEV_PATH, + content=HOTPLUG_UDEV_RULES_TEMPLATE.format(libexecdir=libexecdir), + ) + subp.subp(["udevadm", "control", "--reload-rules"]) |