/
usr
/
lib
/
python3
/
dist-packages
/
cloudinit
/
config
/
/usr/lib/python3/dist-packages/cloudinit/config
mkdir
upload
Name
Size
Mode
Actions
schemas/
-
0755
rm
__pycache__/
-
0755
rm
cc_ansible.py
10727
0644
edit
dl
rm
cc_apk_configure.py
4331
0644
edit
dl
rm
cc_apt_configure.py
39170
0644
edit
dl
rm
cc_apt_pipelining.py
1909
0644
edit
dl
rm
cc_bootcmd.py
1690
0644
edit
dl
rm
cc_byobu.py
2800
0644
edit
dl
rm
cc_ca_certs.py
9126
0644
edit
dl
rm
cc_chef.py
13502
0644
edit
dl
rm
cc_disable_ec2_metadata.py
1642
0644
edit
dl
rm
cc_disk_setup.py
52622
0644
edit
dl
rm
cc_fan.py
1997
0644
edit
dl
rm
cc_final_message.py
2561
0644
edit
dl
rm
cc_growpart.py
18809
0644
edit
dl
rm
cc_grub_dpkg.py
5524
0644
edit
dl
rm
cc_install_hotplug.py
3260
0644
edit
dl
rm
cc_keyboard.py
1527
0644
edit
dl
rm
cc_keys_to_console.py
2138
0644
edit
dl
rm
cc_landscape.py
3131
0644
edit
dl
rm
cc_locale.py
1211
0644
edit
dl
rm
cc_lxd.py
13761
0644
edit
dl
rm
cc_mcollective.py
4207
0644
edit
dl
rm
cc_mounts.py
19322
0644
edit
dl
rm
cc_ntp.py
19528
0644
edit
dl
rm
cc_package_update_upgrade_install.py
3909
0644
edit
dl
rm
cc_phone_home.py
3829
0644
edit
dl
rm
cc_power_state_change.py
6133
0644
edit
dl
rm
cc_puppet.py
10976
0644
edit
dl
rm
cc_raspberry_pi.py
6599
0644
edit
dl
rm
cc_reset_rmc.py
4432
0644
edit
dl
rm
cc_resizefs.py
10880
0644
edit
dl
rm
cc_resolv_conf.py
3206
0644
edit
dl
rm
cc_rh_subscription.py
16545
0644
edit
dl
rm
cc_rsyslog.py
11745
0644
edit
dl
rm
cc_runcmd.py
1640
0644
edit
dl
rm
cc_salt_minion.py
4053
0644
edit
dl
rm
cc_scripts_per_boot.py
1298
0644
edit
dl
rm
cc_scripts_per_instance.py
1295
0644
edit
dl
rm
cc_scripts_per_once.py
1271
0644
edit
dl
rm
cc_scripts_user.py
1282
0644
edit
dl
rm
cc_scripts_vendor.py
1292
0644
edit
dl
rm
cc_seed_random.py
3068
0644
edit
dl
rm
cc_set_hostname.py
3481
0644
edit
dl
rm
cc_set_passwords.py
10566
0644
edit
dl
rm
cc_snap.py
3400
0644
edit
dl
rm
cc_spacewalk.py
2708
0644
edit
dl
rm
cc_ssh.py
11224
0644
edit
dl
rm
cc_ssh_authkey_fingerprints.py
3850
0644
edit
dl
rm
cc_ssh_import_id.py
5571
0644
edit
dl
rm
cc_timezone.py
1157
0644
edit
dl
rm
cc_ubuntu_autoinstall.py
1267
0644
edit
dl
rm
cc_ubuntu_drivers.py
4174
0644
edit
dl
rm
cc_ubuntu_pro.py
13971
0644
edit
dl
rm
cc_update_etc_hosts.py
2522
0644
edit
dl
rm
cc_update_hostname.py
2230
0644
edit
dl
rm
cc_users_groups.py
2930
0644
edit
dl
rm
cc_wireguard.py
6825
0644
edit
dl
rm
cc_write_files.py
6296
0644
edit
dl
rm
cc_write_files_deferred.py
1308
0644
edit
dl
rm
cc_yum_add_repo.py
4641
0644
edit
dl
rm
cc_zypper_add_repo.py
5052
0644
edit
dl
rm
modules.py
13643
0644
edit
dl
rm
schema.py
52049
0644
edit
dl
rm
__init__.py
40
0644
edit
dl
rm
Edit:
/usr/lib/python3/dist-packages/cloudinit/config/cc_ansible.py
(10727B)
"""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 typing import List, Optional from cloudinit import lifecycle, subp from cloudinit.cloud import Cloud from cloudinit.config import Config from cloudinit.config.schema import MetaSchema from cloudinit.distros import ALL_DISTROS, Distro from cloudinit.settings import PER_INSTANCE from cloudinit.util import get_cfg_by_path meta: MetaSchema = { "id": "cc_ansible", "frequency": PER_INSTANCE, "distros": [ALL_DISTROS], "activate_by_schema_keys": ["ansible"], } 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 = {} 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"] = os.environ.get("HOME", "/root") def get_version(self) -> Optional[lifecycle.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 lifecycle.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.subp(command, update_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 self.add_pip_install_site_to_path() def add_pip_install_site_to_path(self): if self.run_user: 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 bootstrap_pip_if_required(self): try: import pip # noqa: F401 except ImportError: self.distro.install_packages([self.distro.pip_package_name]) def install(self, pkg_name: str): """should cloud-init grow an interface for non-distro package managers? this seems reusable """ self.bootstrap_pip_if_required() if not self.is_installed(): 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.__upgrade_pip(cmd) LOG.info("Installing the %s package", pkg_name) self.do_as([*cmd, pkg_name]) LOG.info("Installed the %s package", pkg_name) def __upgrade_pip(self, cmd: list): """Try to upgrade pip then not raise an exception if the pip upgrade fails""" LOG.info("Upgrading pip") try: self.do_as([*cmd, "--upgrade", "pip"]) except subp.ProcessExecutionError as e: LOG.warning( ( "Failed at upgrading pip. This is usually not critical" "so the script will skip this step.\n%s" ), e, ) else: LOG.info("Upgraded pip") def is_installed(self) -> bool: cmd = [sys.executable, "-m", "pip", "list"] if self.run_user: cmd.append("--user") stdout, _ = self.do_as(cmd) return "ansible" in stdout class AnsiblePullDistro(AnsiblePull): def __init__(self, distro: Distro, user: Optional[str]): super().__init__(distro) self.run_user = user def install(self, pkg_name: str): if not self.is_installed(): self.distro.install_packages([pkg_name]) def is_installed(self) -> bool: return bool(subp.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_user) 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: if isinstance(pull_cfg, dict): pull_cfg = [pull_cfg] for cfg in pull_cfg: run_ansible_pull(ansible, deepcopy(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}") pull_cfg = cfg.get("pull") if pull_cfg: if isinstance(pull_cfg, dict): pull_cfg = [pull_cfg] elif not isinstance(pull_cfg, list): raise ValueError( "Invalid value ansible.pull. Expected either dict of list of" f" dicts but found {pull_cfg}" ) for p_cfg in pull_cfg: if not isinstance(p_cfg, dict): raise ValueError( "Invalid value of ansible.pull. Expected dict but found" f" {p_cfg}" ) if not get_cfg_by_path(p_cfg, "url"): raise ValueError(f"Missing required key 'url' from {p_cfg}") has_playbook = get_cfg_by_path(p_cfg, "playbook_name") has_playbooks = get_cfg_by_path(p_cfg, "playbook_names") if not any([has_playbook, has_playbooks]): raise ValueError( f"Missing required key 'playbook_names' from {p_cfg}" ) elif all([has_playbooks, has_playbook]): raise ValueError( "Key 'ansible.pull.playbook_name' and" " 'ansible.pull.playbook_names' are mutually exclusive." f" Please use 'playbook_names' in {p_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", None) playbook_names: List[str] = cfg.pop("playbook_names", None) if playbook_name: playbook_names = [playbook_name] v = pull.get_version() if not v: LOG.warning("Cannot parse ansible version") elif v < lifecycle.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." ) pull_args = [ f"--{key}={value}" if value is not True else f"--{key}" for key, value in filter_args(cfg).items() ] if v and v >= lifecycle.Version(2, 12, 0): # Multiple playbook support was resolved in 2.12. # https://github.com/ansible/ansible/pull/73172 stdout = pull.pull(*pull_args, *playbook_names) if stdout: sys.stdout.write(f"{stdout}") else: # Older ansible must pull separate playbooks for playbook in playbook_names: stdout = pull.pull(*pull_args, playbook) 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)
Save
cmd:
run