summaryrefslogtreecommitdiff
path: root/cloudinit/config/cc_install_hotplug.py
diff options
context:
space:
mode:
Diffstat (limited to 'cloudinit/config/cc_install_hotplug.py')
-rw-r--r--cloudinit/config/cc_install_hotplug.py151
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"])