# 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"])
