PNG  IHDR* pHYs+ IDATx]n#; cdLb Ǚ[at¤_:uP}>!Usă cag޿ ֵNu`ݼTâabO7uL&y^wFٝA"l[|ŲHLN밪4*sG3|Dv}?+y߉{OuOAt4Jj.u]Gz*҉sP'VQKbA1u\`& Af;HWj hsO;ogTu uj7S3/QzUr&wS`M$X_L7r2;aE+ώ%vikDA:dR+%KzƉo>eOth$z%: :{WwaQ:wz%4foɹE[9<]#ERINƻv溂E%P1i01 |Jvҗ&{b?9g=^wζXn/lK::90KwrюO\!ջ3uzuGv^;騢wq<Iatv09:tt~hEG`v;3@MNZD.1]L:{ծI3`L(÷ba")Y.iljCɄae#I"1 `3*Bdz>j<fU40⨬%O$3cGt]j%Fߠ_twJ;ABU8vP3uEԑwQ V:h%))LfraqX-ۿX]v-\9I gl8tzX ]ecm)-cgʒ#Uw=Wlێn(0hPP/ӨtQ“&J35 $=]r1{tLuǮ*i0_;NƝ8;-vݏr8+U-kruȕYr0RnC]*ެ(M:]gE;{]tg(#ZJ9y>utRDRMdr9㪩̞zֹb<ģ&wzJM"iI( .ꮅX)Qw:9,i좜\Ԛi7&N0:asϓc];=ΗOӣ APqz93 y $)A*kVHZwBƺnWNaby>XMN*45~ղM6Nvm;A=jֲ.~1}(9`KJ/V F9[=`~[;sRuk]rєT!)iQO)Y$V ی ۤmzWz5IM Zb )ˆC`6 rRa}qNmUfDsWuˤV{ Pݝ'=Kֳbg,UҘVz2ﴻnjNgBb{? ߮tcsͻQuxVCIY۠:(V뺕 ٥2;t`@Fo{Z9`;]wMzU~%UA蛚dI vGq\r82iu +St`cR.6U/M9IENDB` REDROOM
PHP 5.6.40
Preview: cc_ansible.py Size: 8.69 KB
/lib/python3.6/site-packages/cloudinit/config/cc_ansible.py

"""ansible enables running on first boot either ansible-pull"""
import abc
import logging
import os
import re
import sys
import sysconfig
from copy import deepcopy
from textwrap import dedent
from typing import Optional

from cloudinit.cloud import Cloud
from cloudinit.config import Config
from cloudinit.config.schema import MetaSchema, get_meta_doc
from cloudinit.distros import ALL_DISTROS, Distro
from cloudinit.settings import PER_INSTANCE
from cloudinit.subp import subp, which
from cloudinit.util import Version, get_cfg_by_path

meta: MetaSchema = {
    "id": "cc_ansible",
    "name": "Ansible",
    "title": "Configure ansible for instance",
    "frequency": PER_INSTANCE,
    "distros": [ALL_DISTROS],
    "activate_by_schema_keys": ["ansible"],
    "description": dedent(
        """\
        This module provides ``ansible`` integration for
        augmenting cloud-init's configuration of the local
        node.


        This module installs ansible during boot and
        then uses ``ansible-pull`` to run the playbook
        repository at the remote URL.
        """
    ),
    "examples": [
        dedent(
            """\
            ansible:
              package_name: ansible-core
              install_method: distro
              pull:
                url: "https://github.com/holmanb/vmboot.git"
                playbook_name: ubuntu.yml
            """
        ),
        dedent(
            """\
            ansible:
              package_name: ansible-core
              install_method: pip
              pull:
                url: "https://github.com/holmanb/vmboot.git"
                playbook_name: ubuntu.yml
            """
        ),
    ],
}

__doc__ = get_meta_doc(meta)
LOG = logging.getLogger(__name__)
CFG_OVERRIDE = "ansible_config"


class AnsiblePull(abc.ABC):
    def __init__(self, distro: Distro):
        self.cmd_pull = ["ansible-pull"]
        self.cmd_version = ["ansible-pull", "--version"]
        self.distro = distro
        self.env = os.environ
        self.run_user: Optional[str] = None

        # some ansible modules directly reference os.environ["HOME"]
        # and cloud-init might not have that set, default: /root
        self.env["HOME"] = self.env.get("HOME", "/root")

    def get_version(self) -> Optional[Version]:
        stdout, _ = self.do_as(self.cmd_version)
        first_line = stdout.splitlines().pop(0)
        matches = re.search(r"([\d\.]+)", first_line)
        if matches:
            version = matches.group(0)
            return Version.from_str(version)
        return None

    def pull(self, *args) -> str:
        stdout, _ = self.do_as([*self.cmd_pull, *args])
        return stdout

    def check_deps(self):
        if not self.is_installed():
            raise ValueError("command: ansible is not installed")

    def do_as(self, command: list, **kwargs):
        if not self.run_user:
            return self.subp(command, **kwargs)
        return self.distro.do_as(command, self.run_user, **kwargs)

    def subp(self, command, **kwargs):
        return subp(command, env=self.env, **kwargs)

    @abc.abstractmethod
    def is_installed(self):
        pass

    @abc.abstractmethod
    def install(self, pkg_name: str):
        pass


class AnsiblePullPip(AnsiblePull):
    def __init__(self, distro: Distro, user: Optional[str]):
        super().__init__(distro)
        self.run_user = user

        # Add pip install site to PATH
        user_base, _ = self.do_as(
            [sys.executable, "-c", "'import site; print(site.getuserbase())'"]
        )
        ansible_path = f"{user_base}/bin/"
        old_path = self.env.get("PATH")
        if old_path:
            self.env["PATH"] = ":".join([old_path, ansible_path])
        else:
            self.env["PATH"] = ansible_path

    def install(self, pkg_name: str):
        """should cloud-init grow an interface for non-distro package
        managers? this seems reusable
        """
        if not self.is_installed():
            # bootstrap pip if required
            try:
                import pip  # noqa: F401
            except ImportError:
                self.distro.install_packages([self.distro.pip_package_name])
            cmd = [
                sys.executable,
                "-m",
                "pip",
                "install",
            ]
            if os.path.exists(
                os.path.join(
                    sysconfig.get_path("stdlib"), "EXTERNALLY-MANAGED"
                )
            ):
                cmd.append("--break-system-packages")
            if self.run_user:
                cmd.append("--user")
            self.do_as([*cmd, "--upgrade", "pip"])
            self.do_as([*cmd, pkg_name])

    def is_installed(self) -> bool:
        stdout, _ = self.do_as([sys.executable, "-m", "pip", "list"])
        return "ansible" in stdout


class AnsiblePullDistro(AnsiblePull):
    def install(self, pkg_name: str):
        if not self.is_installed():
            self.distro.install_packages([pkg_name])

    def is_installed(self) -> bool:
        return bool(which("ansible"))


def handle(name: str, cfg: Config, cloud: Cloud, args: list) -> None:

    ansible_cfg: dict = cfg.get("ansible", {})
    ansible_user = ansible_cfg.get("run_user")
    install_method = ansible_cfg.get("install_method")
    setup_controller = ansible_cfg.get("setup_controller")

    galaxy_cfg = ansible_cfg.get("galaxy")
    pull_cfg = ansible_cfg.get("pull")
    package_name = ansible_cfg.get("package_name", "")

    if ansible_cfg:
        ansible: AnsiblePull
        validate_config(ansible_cfg)

        distro: Distro = cloud.distro
        if install_method == "pip":
            ansible = AnsiblePullPip(distro, ansible_user)
        else:
            ansible = AnsiblePullDistro(distro)
        ansible.install(package_name)
        ansible.check_deps()
        ansible_config = ansible_cfg.get("ansible_config", "")

        if ansible_config:
            ansible.env[CFG_OVERRIDE] = ansible_config

        if galaxy_cfg:
            ansible_galaxy(galaxy_cfg, ansible)

        if pull_cfg:
            run_ansible_pull(ansible, deepcopy(pull_cfg))

        if setup_controller:
            ansible_controller(setup_controller, ansible)


def validate_config(cfg: dict):
    required_keys = (
        "install_method",
        "package_name",
    )
    for key in required_keys:
        if not get_cfg_by_path(cfg, key):
            raise ValueError(f"Missing required key '{key}' from {cfg}")
    if cfg.get("pull"):
        for key in "pull/url", "pull/playbook_name":
            if not get_cfg_by_path(cfg, key):
                raise ValueError(f"Missing required key '{key}' from {cfg}")

    controller_cfg = cfg.get("setup_controller")
    if controller_cfg:
        if not any(
            [
                controller_cfg.get("repositories"),
                controller_cfg.get("run_ansible"),
            ]
        ):
            raise ValueError(f"Missing required key from {controller_cfg}")

    install = cfg["install_method"]
    if install not in ("pip", "distro"):
        raise ValueError("Invalid install method {install}")


def filter_args(cfg: dict) -> dict:
    """remove boolean false values"""
    return {
        key.replace("_", "-"): value
        for (key, value) in cfg.items()
        if value is not False
    }


def run_ansible_pull(pull: AnsiblePull, cfg: dict):
    playbook_name: str = cfg.pop("playbook_name")

    v = pull.get_version()
    if not v:
        LOG.warning("Cannot parse ansible version")
    elif v < Version(2, 7, 0):
        # diff was added in commit edaa0b52450ade9b86b5f63097ce18ebb147f46f
        if cfg.get("diff"):
            raise ValueError(
                f"Ansible version {v.major}.{v.minor}.{v.patch}"
                "doesn't support --diff flag, exiting."
            )
    stdout = pull.pull(
        *[
            f"--{key}={value}" if value is not True else f"--{key}"
            for key, value in filter_args(cfg).items()
        ],
        playbook_name,
    )
    if stdout:
        sys.stdout.write(f"{stdout}")


def ansible_galaxy(cfg: dict, ansible: AnsiblePull):
    actions = cfg.get("actions", [])

    if not actions:
        LOG.warning("Invalid config: %s", cfg)
    for command in actions:
        ansible.do_as(command)


def ansible_controller(cfg: dict, ansible: AnsiblePull):
    for repository in cfg.get("repositories", []):
        ansible.do_as(
            ["git", "clone", repository["source"], repository["path"]]
        )
    for args in cfg.get("run_ansible", []):
        playbook_dir = args.pop("playbook_dir")
        playbook_name = args.pop("playbook_name")
        command = [
            "ansible-playbook",
            playbook_name,
            *[f"--{key}={value}" for key, value in filter_args(args).items()],
        ]
        ansible.do_as(command, cwd=playbook_dir)

Directory Contents

Dirs: 2 × Files: 64

Name Size Perms Modified Actions
schemas DIR
- drwxr-xr-x 2026-01-24 23:01:14
Edit Download
- drwxr-xr-x 2026-01-24 23:01:14
Edit Download
8.69 KB lrw-r--r-- 2023-12-04 11:47:40
Edit Download
5.66 KB lrw-r--r-- 2023-12-04 11:47:40
Edit Download
41.99 KB lrw-r--r-- 2023-12-04 11:47:40
Edit Download
2.71 KB lrw-r--r-- 2023-12-04 11:47:40
Edit Download
2.85 KB lrw-r--r-- 2023-12-04 11:47:40
Edit Download
3.65 KB lrw-r--r-- 2023-12-04 11:47:40
Edit Download
9.13 KB lrw-r--r-- 2026-01-23 08:56:46
Edit Download
13.77 KB lrw-r--r-- 2023-12-04 11:47:40
Edit Download
2.03 KB lrw-r--r-- 2023-12-04 11:47:40
Edit Download
32.36 KB lrw-r--r-- 2023-12-04 11:47:40
Edit Download
3.02 KB lrw-r--r-- 2023-12-04 11:47:40
Edit Download
3.39 KB lrw-r--r-- 2023-12-04 11:47:40
Edit Download
21.03 KB lrw-r--r-- 2023-12-04 11:47:40
Edit Download
6.65 KB lrw-r--r-- 2023-12-04 11:47:40
Edit Download
3.81 KB lrw-r--r-- 2023-12-04 11:47:40
Edit Download
2.38 KB lrw-r--r-- 2023-12-04 11:47:40
Edit Download
3.61 KB lrw-r--r-- 2023-12-04 11:47:40
Edit Download
5.31 KB lrw-r--r-- 2023-12-04 11:47:40
Edit Download
1.86 KB lrw-r--r-- 2023-12-04 11:47:40
Edit Download
18.14 KB lrw-r--r-- 2023-12-04 11:47:40
Edit Download
6.10 KB lrw-r--r-- 2023-12-04 11:47:40
Edit Download
3.49 KB lrw-r--r-- 2023-12-04 11:47:40
Edit Download
19.71 KB lrw-r--r-- 2023-12-04 11:47:40
Edit Download
20.84 KB lrw-r--r-- 2026-01-23 08:56:46
Edit Download
4.54 KB lrw-r--r-- 2023-12-04 11:47:40
Edit Download
5.48 KB lrw-r--r-- 2023-12-04 11:47:40
Edit Download
7.41 KB lrw-r--r-- 2023-12-04 11:47:40
Edit Download
14.10 KB lrw-r--r-- 2023-12-04 11:47:40
Edit Download
4.47 KB lrw-r--r-- 2023-12-04 11:47:40
Edit Download
10.73 KB lrw-r--r-- 2023-12-04 11:47:40
Edit Download
4.98 KB lrw-r--r-- 2026-01-23 08:56:46
Edit Download
16.97 KB lrw-r--r-- 2026-01-23 08:56:46
Edit Download
4.28 KB lrw-r--r-- 2023-12-04 11:47:40
Edit Download
13.48 KB lrw-r--r-- 2023-12-04 11:47:40
Edit Download
2.90 KB lrw-r--r-- 2023-12-04 11:47:40
Edit Download
5.88 KB lrw-r--r-- 2023-12-04 11:47:40
Edit Download
1.66 KB lrw-r--r-- 2023-12-04 11:47:40
Edit Download
1.81 KB lrw-r--r-- 2023-12-04 11:47:40
Edit Download
1.76 KB lrw-r--r-- 2023-12-04 11:47:40
Edit Download
1.85 KB lrw-r--r-- 2023-12-04 11:47:40
Edit Download
2.29 KB lrw-r--r-- 2023-12-04 11:47:40
Edit Download
4.72 KB lrw-r--r-- 2023-12-04 11:47:40
Edit Download
5.13 KB lrw-r--r-- 2023-12-04 11:47:40
Edit Download
10.97 KB lrw-r--r-- 2023-12-04 11:47:40
Edit Download
6.30 KB lrw-r--r-- 2023-12-04 11:47:40
Edit Download
3.43 KB lrw-r--r-- 2023-12-04 11:47:40
Edit Download
14.86 KB lrw-r--r-- 2023-12-04 11:47:40
Edit Download
4.22 KB lrw-r--r-- 2023-12-04 11:47:40
Edit Download
6.12 KB lrw-r--r-- 2023-12-04 11:47:40
Edit Download
1.46 KB lrw-r--r-- 2023-12-04 11:47:40
Edit Download
17.00 KB lrw-r--r-- 2023-12-04 11:47:40
Edit Download
4.50 KB lrw-r--r-- 2023-12-04 11:47:40
Edit Download
4.56 KB lrw-r--r-- 2023-12-04 11:47:40
Edit Download
5.16 KB lrw-r--r-- 2023-12-04 11:47:40
Edit Download
3.87 KB lrw-r--r-- 2023-12-04 11:47:40
Edit Download
8.57 KB lrw-r--r-- 2023-12-04 11:47:40
Edit Download
9.22 KB lrw-r--r-- 2023-12-04 11:47:40
Edit Download
6.66 KB lrw-r--r-- 2023-12-04 11:47:40
Edit Download
1.68 KB lrw-r--r-- 2023-12-04 11:47:40
Edit Download
7.45 KB lrw-r--r-- 2023-12-04 11:47:40
Edit Download
6.59 KB lrw-r--r-- 2023-12-04 11:47:40
Edit Download
11.74 KB lrw-r--r-- 2023-12-04 11:47:40
Edit Download
54.85 KB lrw-r--r-- 2023-12-04 11:47:40
Edit Download
14 B lrw-r--r-- 2023-12-04 11:47:40
Edit Download

If ZipArchive is unavailable, a .tar will be created (no compression).
© 2026 REDROOM — Secure File Manager. All rights reserved. Built with ❤️ & Red Dark UI