def service( service, running=True, restarted=False, command=None, ): """ Manage the state of systemd managed services. + service: name of the service to manage + running: whether the service should be running + restarted: whether the service should be restarted + command: custom command to pass like: ``launchctl <command> <service>`` + enabled: whether this service should be enabled/disabled on boot """ was_running = host.get_fact(LaunchdStatus).get(service, None) yield from handle_service_control( host, service, host.get_fact(LaunchdStatus), "launchctl {1} {0}", # No support for restart/reload/command running, None, None, None, ) # No restart command, so just stop/start if restarted and was_running: yield "launchctl stop {0}".format(service) yield "launchctl start {0}".format(service)
def get( src, dest, add_deploy_dir=True, create_local_dir=False, force=False, ): """ Download a file from the remote system. + src: the remote filename to download + dest: the local filename to download the file to + add_deploy_dir: dest is relative to the deploy directory + create_local_dir: create the local directory if it doesn't exist + force: always download the file, even if the local copy matches Note: This operation is not suitable for large files as it may involve copying the remote file before downloading it. **Example:** .. code:: python files.get( name="Download a file from a remote", src="/etc/centos-release", dest="/tmp/whocares", ) """ if add_deploy_dir and state.cwd: dest = os.path.join(state.cwd, dest) if create_local_dir: local_pathname = os.path.dirname(dest) if not os.path.exists(local_pathname): os.makedirs(local_pathname) remote_file = host.get_fact(File, path=src) # No remote file, so assume exists and download it "blind" if not remote_file or force: yield FileDownloadCommand( src, dest, remote_temp_filename=state.get_temp_filename(dest)) # No local file, so always download elif not os.path.exists(dest): yield FileDownloadCommand( src, dest, remote_temp_filename=state.get_temp_filename(dest)) # Remote file exists - check if it matches our local else: local_sum = get_file_sha1(dest) remote_sum = host.get_fact(Sha1File, path=src) # Check sha1sum, upload if needed if local_sum != remote_sum: yield FileDownloadCommand( src, dest, remote_temp_filename=state.get_temp_filename(dest))
def packages( packages, present=True, ): """ Add or remove system packages. This command checks for the presence of all the system package managers ``pyinfra`` can handle and executes the relevant operation. + packages: list of packages to ensure + present: whether the packages should be installed **Example:** .. code:: python server.packages( name="Install Vim and vimpager", packages=["vimpager", "vim"], ) """ # TODO: improve this - use LinuxDistribution fact + mapping with fallback below? # Here to be preferred on openSUSE which also provides aptitude # See: https://github.com/Fizzadar/pyinfra/issues/799 if host.get_fact(Which, command="zypper"): package_operation = zypper.packages elif host.get_fact(Which, command="apk"): package_operation = apk.packages elif host.get_fact(Which, command="apt"): package_operation = apt.packages elif host.get_fact(Which, command="brew"): package_operation = brew.packages elif host.get_fact(Which, command="dnf"): package_operation = dnf.packages elif host.get_fact(Which, command="pacman"): package_operation = pacman.packages elif host.get_fact(Which, command="xbps"): package_operation = xbps.packages elif host.get_fact(Which, command="yum"): package_operation = yum.packages elif host.get_fact(Which, command="pkg") or host.get_fact( Which, command="pkg_add"): package_operation = pkg.packages else: raise OperationError( ("No system package manager found " "(no apk, apt, brew, dnf, pacman, pkg, xbps, yum or zypper found)" ), ) yield from package_operation(packages=packages, present=present)
def keyscan(hostname, force=False, port=22): """ Check/add hosts to the ``~/.ssh/known_hosts`` file. + hostname: hostname that should have a key in ``known_hosts`` + force: if the key already exists, remove and rescan **Example:** .. code:: python ssh.keyscan( name="Set add server two to known_hosts on one", hostname="two.example.com", ) """ homedir = host.get_fact(Home) yield from files.directory( "{0}/.ssh".format(homedir), mode=700, ) hostname_present = host.get_fact( FindInFile, path="{0}/.ssh/known_hosts".format(homedir), pattern=hostname, ) did_keyscan = False keyscan_command = "ssh-keyscan -p {0} {1} >> {2}/.ssh/known_hosts".format( port, hostname, homedir, ) if not hostname_present: yield keyscan_command did_keyscan = True elif force: yield "ssh-keygen -R {0}".format(hostname) yield keyscan_command did_keyscan = True else: host.noop("host key for {0} already exists".format(hostname)) if did_keyscan: host.create_fact( FindInFile, kwargs={ "path": "{0}/.ssh/known_hosts".format(homedir), "pattern": hostname }, data=["{0} unknown unknown".format(hostname)], )
def service( service, running=True, restarted=False, reloaded=False, command=None, enabled=None, ): """ Manage the state of services. This command checks for the presence of all the Linux init systems ``pyinfra`` can handle and executes the relevant operation. + service: name of the service to manage + running: whether the service should be running + restarted: whether the service should be restarted + reloaded: whether the service should be reloaded + command: custom command execute + enabled: whether this service should be enabled/disabled on boot **Example:** .. code:: python server.service( name="Enable open-vm-tools service", service="open-vm-tools", enabled=True, ) """ if host.get_fact(Which, command="systemctl"): service_operation = systemd.service elif host.get_fact(Which, command="rc-service"): service_operation = openrc.service elif host.get_fact(Which, command="initctl"): service_operation = upstart.service elif host.get_fact(Directory, path="/etc/init.d"): service_operation = sysvinit.service elif host.get_fact(Directory, path="/etc/rc.d"): service_operation = bsdinit.service else: raise OperationError( ("No init system found " "(no systemctl, initctl, /etc/init.d or /etc/rc.d found)"), ) yield from service_operation( service, running=running, restarted=restarted, reloaded=reloaded, command=command, enabled=enabled, )
def hostname(hostname, hostname_file=None): """ Set the system hostname using ``hostnamectl`` or ``hostname`` on older systems. + hostname: the hostname that should be set + hostname_file: the file that permanently sets the hostname Hostname file: The hostname file only matters no systems that do not have ``hostnamectl``, which is part of ``systemd``. By default pyinfra will auto detect this by targeting ``/etc/hostname`` on Linux and ``/etc/myname`` on OpenBSD. To completely disable writing the hostname file, set ``hostname_file=False``. **Example:** .. code:: python server.hostname( name="Set the hostname", hostname="server1.example.com", ) """ current_hostname = host.get_fact(Hostname) if host.get_fact(Which, command="hostnamectl"): if current_hostname != hostname: yield "hostnamectl set-hostname {0}".format(hostname) host.create_fact(Hostname, data=hostname) else: host.noop("hostname is set") return if hostname_file is None: os = host.get_fact(Os) if os == "Linux": hostname_file = "/etc/hostname" elif os == "OpenBSD": hostname_file = "/etc/myname" if current_hostname != hostname: yield "hostname {0}".format(hostname) host.create_fact(Hostname, data=hostname) else: host.noop("hostname is set") if hostname_file: # Create a whole new hostname file file = StringIO("{0}\n".format(hostname)) # And ensure it exists yield from files.put(file, hostname_file)
def group(group, present=True, system=False, gid=None): """ Add/remove system groups. + group: name of the group to ensure + present: whether the group should be present or not + system: whether to create a system group System users: System users don't exist on BSD, so the argument is ignored for BSD targets. **Examples:** .. code:: python server.group( name="Create docker group", group="docker", ) # multiple groups for group in ["wheel", "lusers"]: server.group( name=f"Create the group {group}", group=group, ) """ groups = host.get_fact(Groups) is_present = group in groups # Group exists but we don't want them? if not present and is_present: yield "groupdel {0}".format(group) groups.remove(group) # Group doesn't exist and we want it? elif present and not is_present: args = [] # BSD doesn't do system users if system and "BSD" not in host.get_fact(Os): args.append("-r") args.append(group) if gid: args.append("--gid {0}".format(gid)) # Groups are often added by other operations (package installs), so check # for the group at runtime before adding. yield "grep '^{0}:' /etc/group || groupadd {1}".format( group, " ".join(args), ) groups.append(group)
def packages(packages=None, present=True, latest=False): """ Add/remove/update gem packages. + packages: list of packages to ensure + present: whether the packages should be installed + latest: whether to upgrade packages without a specified version Versions: Package versions can be pinned like gem: ``<pkg>:<version>``. **Example:** .. code:: python # Note: Assumes that 'gem' is installed. gem.packages( name="Install rspec", packages=["rspec"], ) """ yield from ensure_packages( host, packages, host.get_fact(GemPackages), present, install_command="gem install", uninstall_command="gem uninstall", upgrade_command="gem update", version_join=":", latest=latest, )
def packages(packages=None, present=True, latest=False): """ Add/remove/update ``choco`` packages. + packages: list of packages to ensure + present: whether the packages should be installed + latest: whether to upgrade packages without a specified version Versions: Package versions can be pinned like gem: ``<pkg>:<version>``. **Example:** .. code:: python # Note: Assumes that 'choco' is installed and # user has Administrator permission. choco.packages( name="Install Notepad++", packages=["notepadplusplus"], ) """ yield from ensure_packages( host, packages, host.get_fact(ChocoPackages), present, install_command="choco install -y", uninstall_command="choco uninstall -y -x", upgrade_command="choco update -y", version_join=":", latest=latest, )
def repo(src, present=True, filename=None): """ Add/remove apt repositories. + src: apt source string eg ``deb http://X hardy main`` + present: whether the repo should exist on the system + filename: optional filename to use ``/etc/apt/sources.list.d/<filename>.list``. By default uses ``/etc/apt/sources.list``. **Example:** .. code:: python apt.repo( name="Install VirtualBox repo", src="deb https://download.virtualbox.org/virtualbox/debian bionic contrib", ) """ # Get the target .list file to manage if filename: filename = "/etc/apt/sources.list.d/{0}.list".format(filename) else: filename = "/etc/apt/sources.list" # Work out if the repo exists already apt_sources = host.get_fact(AptSources) is_present = False repo = parse_apt_repo(src) if repo and repo in apt_sources: is_present = True # Doesn't exist and we want it if not is_present and present: yield from files.line( filename, src, escape_regex_characters=True, ) apt_sources.append(repo) # Exists and we don't want it elif is_present and not present: yield from files.line( filename, src, present=False, assume_present=True, escape_regex_characters=True, ) apt_sources.remove(repo) else: host.noop( 'apt repo "{0}" {1}'.format( src, "exists" if present else "does not exist", ), )
def packages( packages=None, present=True, update=False, upgrade=False, ): """ Add/remove pacman packages. + packages: list of packages to ensure + present: whether the packages should be installed + update: run ``pacman -Sy`` before installing packages + upgrade: run ``pacman -Su`` before installing packages Versions: Package versions can be pinned like pacman: ``<pkg>=<version>``. **Example:** .. code:: python pacman.packages( name="Install Vim and a plugin", packages=["vim-fugitive", "vim"], update=True, ) """ if update: yield from _update() if upgrade: yield from _upgrade() yield from ensure_packages( host, packages, host.get_fact(PacmanPackages), present, install_command="pacman --noconfirm -S", uninstall_command="pacman --noconfirm -R", expand_package_fact=lambda package: host.get_fact( PacmanUnpackGroup, name=package, ), )
def chain( chain, present=True, table="filter", policy=None, version=4, ): """ Add/remove/update iptables chains. + chain: the name of the chain + present: whether the chain should exist + table: the iptables table this chain should belong to + policy: the policy this table should have + version: whether to target iptables or ip6tables Policy: These can only be applied to system chains (FORWARD, INPUT, OUTPUT, etc). """ chains = (host.get_fact(IptablesChains, table=table) if version == 4 else host.get_fact(Ip6tablesChains, table=table)) command = "iptables" if version == 4 else "ip6tables" command = "{0} -t {1}".format(command, table) if not present: if chain in chains: yield "{0} -X {1}".format(command, chain) chains.pop(chain) else: host.noop("iptables chain {0} does not exist".format(chain)) return if present: if chain not in chains: yield "{0} -N {1}".format(command, chain) chains[chain] = None # policy will be set below else: host.noop("iptables chain {0} exists".format(chain)) if policy: if chain not in chains or chains[chain] != policy: yield "{0} -P {1} {2}".format(command, chain, policy) chains[chain] = policy
def update(cache_time=None): """ Updates apt repositories. + cache_time: cache updates for this many seconds **Example:** .. code:: python apt.update( name="Update apt repositories", cache_time=3600, ) """ # If cache_time check when apt was last updated, prevent updates if within time if cache_time: # Ubuntu provides this handy file cache_info = host.get_fact(File, path=APT_UPDATE_FILENAME) # Time on files is not tz-aware, and will be the same tz as the server's time, # so we can safely remove the tzinfo from the Date fact before comparison. host_cache_time = host.get_fact(Date).replace(tzinfo=None) - timedelta( seconds=cache_time) if cache_info and cache_info[ "mtime"] and cache_info["mtime"] > host_cache_time: host.noop("apt is already up to date") return yield "apt-get update" # Some apt systems (Debian) have the /var/lib/apt/periodic directory, but # don't bother touching anything in there - so pyinfra does it, enabling # cache_time to work. if cache_time: yield "touch {0}".format(APT_UPDATE_FILENAME) if cache_info is None: host.create_fact( File, kwargs={"path": APT_UPDATE_FILENAME}, data={"mtime": datetime.utcnow()}, ) else: cache_info["mtime"] = datetime.utcnow()
def mount( path, mounted=True, options=None, # TODO: do we want to manage fstab here? # update_fstab=False, device=None, fs_type=None, ): """ Manage mounted filesystems. + path: the path of the mounted filesystem + mounted: whether the filesystem should be mounted + options: the mount options Options: If the currently mounted filesystem does not have all of the provided options it will be remounted with the options provided. ``/etc/fstab``: This operation does not attempt to modify the on disk fstab file - for that you should use the `files.line operation <./files.html#files-line>`_. """ options = options or [] options_string = ",".join(options) mounts = host.get_fact(Mounts) is_mounted = path in mounts # Want mount but don't have? if mounted and not is_mounted: yield "mount{0} {1}".format( " -o {0}".format(options_string) if options_string else "", path, ) mounts[path] = {"options": options} # Want no mount but mounted? elif mounted is False and is_mounted: yield "umount {0}".format(path) mounts.pop(path) # Want mount and is mounted! Check the options elif is_mounted and mounted and options: mounted_options = mounts[path]["options"] needed_options = set(options) - set(mounted_options) if needed_options: yield "mount -o remount,{0} {1}".format(options_string, path) mounts[path]["options"] = options else: host.noop( "filesystem {0} is {1}".format( path, "mounted" if mounted else "not mounted", ), )
def service( service, running=True, restarted=False, reloaded=False, command=None, enabled=None, runlevel="default", ): """ Manage the state of OpenRC services. + service: name of the service to manage + running: whether the service should be running + restarted: whether the service should be restarted + reloaded: whether the service should be reloaded + command: custom command to pass like: ``rc-service <service> <command>`` + enabled: whether this service should be enabled/disabled on boot + runlevel: runlevel to manage services for """ yield from handle_service_control( host, service, host.get_fact(OpenrcStatus, runlevel=runlevel), "rc-service {0} {1}", running, restarted, reloaded, command, ) if isinstance(enabled, bool): openrc_enabled = host.get_fact(OpenrcEnabled, runlevel=runlevel) is_enabled = openrc_enabled.get(service, False) if enabled and not is_enabled: yield "rc-update add {0}".format(service) openrc_enabled[service] = True if not enabled and is_enabled: yield "rc-update del {0}".format(service) openrc_enabled[service] = False
def service( service, running=True, restarted=False, reloaded=False, command=None, enabled=None, ): """ Manage the state of BSD init services. + service: name of the service to manage + running: whether the service should be running + restarted: whether the service should be restarted + reloaded: whether the service should be reloaded + command: custom command to pass like: ``/etc/rc.d/<service> <command>`` + enabled: whether this service should be enabled/disabled on boot """ status_argument = "status" if host.get_fact(Os) == "OpenBSD": status_argument = "check" yield from handle_service_control( host, service, host.get_fact(RcdStatus), "test -e /etc/rc.d/{0} && /etc/rc.d/{0} {1} || /usr/local/etc/rc.d/{0} {1}", running, restarted, reloaded, command, status_argument=status_argument, ) # BSD init is simple, just add/remove <service>_enabled="YES" if isinstance(enabled, bool): yield from files.line( "/etc/rc.conf.local", "^{0}_enable=".format(service), replace='{0}_enable="YES"'.format(service), present=enabled, )
def packages( packages=None, present=True, latest=False, update=False, upgrade=False, ): """ Add/remove/update apk packages. + packages: list of packages to ensure + present: whether the packages should be installed + latest: whether to upgrade packages without a specified version + update: run ``apk update`` before installing packages + upgrade: run ``apk upgrade`` before installing packages Versions: Package versions can be pinned like apk: ``<pkg>=<version>``. **Examples:** .. code:: python # Update package list and install packages apk.packages( name="Install Asterisk and Vim", packages=["asterisk", "vim"], update=True, ) # Install the latest versions of packages (always check) apk.packages( name="Install latest Vim", packages=["vim"], latest=True, ) """ if update: yield from _update() if upgrade: yield from _upgrade() yield from ensure_packages( host, packages, host.get_fact(ApkPackages), present, install_command="apk add", uninstall_command="apk del", upgrade_command="apk upgrade", version_join="=", latest=latest, )
def packages( packages=None, present=True, latest=False, update=False, upgrade=False, ): """ Add/remove/update brew packages. + packages: list of packages to ensure + present: whether the packages should be installed + latest: whether to upgrade packages without a specified version + update: run ``brew update`` before installing packages + upgrade: run ``brew upgrade`` before installing packages Versions: Package versions can be pinned like brew: ``<pkg>@<version>``. **Examples:** .. code:: python # Update package list and install packages brew.packages( name='Install Vim and vimpager', packages=["vimpager", "vim"], update=True, ) # Install the latest versions of packages (always check) brew.packages( name="Install latest Vim", packages=["vim"], latest=True, ) """ if update: yield from _update() if upgrade: yield from _upgrade() yield from ensure_packages( host, packages, host.get_fact(BrewPackages), present, install_command="brew install", uninstall_command="brew uninstall", upgrade_command="brew upgrade", version_join="@", latest=latest, )
def container( id, present=True, image="ubuntu:16.04", ): """ Add/remove LXD containers. Note: does not check if an existing container is based on the specified image. + id: name/identifier for the container + image: image to base the container on + present: whether the container should be present or absent **Example:** .. code:: python lxd.container( name="Add an ubuntu container", id="ubuntu19", image="ubuntu:19.10", ) """ current_containers = host.get_fact(LxdContainers) container = get_container_named(id, current_containers) # Container exists and we don't want it if not present: if container: if container["status"] == "Running": yield "lxc stop {0}".format(id) # Command to remove the container: yield "lxc delete {0}".format(id) current_containers.remove(container) else: host.noop("container {0} does not exist".format(id)) # Container doesn't exist and we want it if present: if not container: # Command to create the container: yield "lxc launch {image} {id} < /dev/null".format(id=id, image=image) current_containers.append({ "name": id, "image": image, }, ) else: host.noop("container {0} exists".format(id))
def config(key, value, repo=None): """ Manage git config for a repository or globally. + key: the key of the config to ensure + value: the value this key should have + repo: specify the git repo path to edit local config (defaults to global) **Example:** .. code:: python git.config( name="Ensure user name is set for a repo", key="user.name", value="Anon E. Mouse", repo="/usr/local/src/pyinfra", ) """ existing_config = {} if not repo: existing_config = host.get_fact(GitConfig) # Only get the config if the repo exists at this stage elif host.get_fact(Directory, path=unix_path_join(repo, ".git")): existing_config = host.get_fact(GitConfig, repo=repo) if existing_config.get(key) != value: if repo is None: yield 'git config --global {0} "{1}"'.format(key, value) else: yield 'cd {0} && git config --local {1} "{2}"'.format( repo, key, value) existing_config[key] = value else: host.noop("git config {0} is set to {1}".format(key, value))
def packages( packages=None, present=True, latest=False, update=False, upgrade=False, ): """ Add/remove/update pkgin packages. + packages: list of packages to ensure + present: whether the packages should be installed + latest: whether to upgrade packages without a specified version + update: run ``pkgin update`` before installing packages + upgrade: run ``pkgin upgrade`` before installing packages **Examples:** .. code:: python # Update package list and install packages pkgin.packages( name="Install tmux and Vim", packages=["tmux", "vim"], update=True, ) # Install the latest versions of packages (always check) pkgin.packages( name="Install latest Vim", packages=["vim"], latest=True, ) """ if update: yield from _update() if upgrade: yield from _upgrade() # TODO support glob for specific versions (it isn't as simple # as apt-s, as pkgin supports something like 'mysql-server>=5.6<5.7') yield from ensure_packages( host, packages, host.get_fact(PkginPackages), present, install_command="pkgin -y install", uninstall_command="pkgin -y remove", upgrade_command="pkgin -y upgrade", latest=latest, )
def modprobe(module, present=True, force=False): """ Load/unload kernel modules. + module: name of the module to manage + present: whether the module should be loaded or not + force: whether to force any add/remove modules **Example:** .. code:: python server.modprobe( name="Silly example for modprobe", module="floppy", ) """ list_value = [module] if isinstance(module, str) else module # NOTE: https://docs.python.org/3/library/itertools.html#itertools-recipes def partition(predicate, iterable): t1, t2 = tee(iterable) return list(filter(predicate, t2)), list(filterfalse(predicate, t1)) modules = host.get_fact(KernelModules) present_mods, missing_mods = partition(lambda mod: mod in modules, list_value) args = "" if force: args = " -f" # Module is loaded and we don't want it? if not present and present_mods: yield "modprobe{0} -r -a {1}".format(args, " ".join(present_mods)) for mod in present_mods: modules.pop(mod) # Module isn't loaded and we want it? elif present and missing_mods: yield "modprobe{0} -a {1}".format(args, " ".join(missing_mods)) for mod in missing_mods: modules[mod] = {} else: host.noop( "{0} {1} {2} {3}".format( "modules" if len(list_value) > 1 else "module", "/".join(list_value), "are" if len(list_value) > 1 else "is", "loaded" if present else "not loaded", ), )
def service( service, running=True, restarted=False, reloaded=False, command=None, enabled=None, ): """ Manage the state of upstart managed services. + service: name of the service to manage + running: whether the service should be running + restarted: whether the service should be restarted + reloaded: whether the service should be reloaded + command: custom command to pass like: ``/etc/rc.d/<service> <command>`` + enabled: whether this service should be enabled/disabled on boot Enabling/disabling services: Upstart jobs define runlevels in their config files - as such there is no way to edit/list these without fiddling with the config. So pyinfra simply manages the existence of a ``/etc/init/<service>.override`` file, and sets its content to "manual" to disable automatic start of services. """ yield from handle_service_control( host, service, host.get_fact(UpstartStatus), "initctl {1} {0}", running, restarted, reloaded, command, ) # Upstart jobs are setup w/runlevels etc in their config files, so here we just check # there's no override file. if enabled is True: yield from files.file( "/etc/init/{0}.override".format(service), present=False, ) # Set the override file to "manual" to disable automatic start elif enabled is False: file = StringIO("manual\n") yield from files.put( src=file, dest="/etc/init/{0}.override".format(service), )
def bare_repo( path, user=None, group=None, present=True, ): """ Create bare git repositories. + path: path to the folder + present: whether the bare repository should exist + user: chown files to this user after + group: chown files to this group after **Example:** .. code:: python git.bare_repo( name="Create bare repo", path="/home/git/test.git", ) """ yield from files.directory(path, present=present) if present: head_filename = unix_path_join(path, "HEAD") head_file = host.get_fact(File, path=head_filename) if not head_file: yield "git init --bare {0}".format(path) if user or group: yield chown(path, user, group, recursive=True) else: if (user and head_file["user"] != user) or ( group and head_file["group"] != group): yield chown(path, user, group, recursive=True) host.create_fact( File, kwargs={"path": head_filename}, data={ "user": user, "group": group, "mode": None }, )
def sysctl( key, value, persist=False, persist_file="/etc/sysctl.conf", ): """ Edit sysctl configuration. + key: name of the sysctl setting to ensure + value: the value or list of values the sysctl should be + persist: whether to write this sysctl to the config + persist_file: file to write the sysctl to persist on reboot **Example:** .. code:: python server.sysctl( name="Change the fs.file-max value", key="fs.file-max", value=100000, persist=True, ) """ string_value = " ".join(["{0}".format(v) for v in value]) if isinstance( value, list) else value value = [try_int(v) for v in value] if isinstance(value, list) else try_int(value) existing_sysctls = host.get_fact(Sysctl) existing_value = existing_sysctls.get(key) if not existing_value or existing_value != value: yield "sysctl {0}='{1}'".format(key, string_value) existing_sysctls[key] = value else: host.noop("sysctl {0} is set to {1}".format(key, string_value)) if persist: yield from files.line( path=persist_file, line="{0}[[:space:]]*=[[:space:]]*{1}".format(key, string_value), replace="{0} = {1}".format(key, string_value), )
def packages(packages=None, present=True, pkg_path=None): """ Install/remove/update pkg packages. This will use ``pkg ...`` where available (FreeBSD) and the ``pkg_*`` variants elsewhere. + packages: list of packages to ensure + present: whether the packages should be installed + pkg_path: the PKG_PATH environment variable to set pkg_path: By default this is autogenerated as follows (tested/working for OpenBSD): ``http://ftp.<OS>.org/pub/<OS>/<VERSION>/packages/<ARCH>/``. Note that OpenBSD's official mirrors only hold the latest two versions packages. NetBSD/FreeBSD helpfully use their own directory structures, so the default won't work. **Example:** .. code:: python pkg.packages( name="Install Vim and Vim Addon Manager", packages=["vim-addon-manager", "vim"], ) """ if present is True: if not pkg_path and not host.get_fact(File, path="/etc/installurl"): host_os = host.get_fact(Os) or "" pkg_path = "http://ftp.{http}.org/pub/{os}/{version}/packages/{arch}/".format( http=host_os.lower(), os=host_os, version=host.get_fact(OsVersion), arch=host.get_fact(Arch), ) # FreeBSD used "pkg ..." and OpenBSD uses "pkg_[add|delete]" is_pkg = host.get_fact(Which, command="pkg") install_command = "pkg install -y" if is_pkg else "pkg_add" uninstall_command = "pkg delete -y" if is_pkg else "pkg_delete" if pkg_path: install_command = "PKG_PATH={0} {1}".format(pkg_path, install_command) yield from ensure_packages( host, packages, host.get_fact(PkgPackages), present, install_command=install_command, uninstall_command=uninstall_command, )
def casks( casks=None, present=True, latest=False, upgrade=False, ): """ Add/remove/update brew casks. + casks: list of casks to ensure + present: whether the casks should be installed + latest: whether to upgrade casks without a specified version + upgrade: run brew cask upgrade before installing casks Versions: Cask versions can be pinned like brew: ``<pkg>@<version>``. **Example:** .. code:: python brew.casks( name='Upgrade and install the latest cask', casks=["godot"], upgrade=True, latest=True, ) """ if upgrade: yield from cask_upgrade() args = cask_args(host) yield from ensure_packages( host, casks, host.get_fact(BrewCasks), present, install_command="brew %sinstall%s" % args, uninstall_command="brew %suninstall%s" % args, upgrade_command="brew %supgrade%s" % args, version_join="@", latest=latest, )
def create(ctid, template=None): """ Create OpenVZ containers. + ctid: CTID of the container to create """ # Check we don't already have a container with this CTID current_containers = host.get_fact(OpenvzContainers) if ctid in current_containers: raise OperationError( "An OpenVZ container with CTID {0} already exists".format(ctid), ) args = ["{0}".format(ctid)] if template: args.append("--ostemplate {0}".format(template)) yield "vzctl create {0}".format(" ".join(args))
def tap(src, present=True): """ Add/remove brew taps. + src: the name of the tap + present: whether this tap should be present or not **Examples:** .. code:: python brew.tap( name="Add a brew tap", src="includeos/includeos", ) # Multiple taps for tap in ["includeos/includeos", "ktr0731/evans"]: brew.tap( name={f"Add brew tap {tap}"}, src=tap, ) """ taps = host.get_fact(BrewTaps) is_tapped = src in taps if present: if is_tapped: host.noop("tap {0} already exists".format(src)) else: yield "brew tap {0}".format(src) taps.append(src) elif not present: if is_tapped: yield "brew untap {0}".format(src) taps.remove(src) else: host.noop("tap {0} does not exist".format(src))
def packages( packages=None, present=True, update=False, upgrade=False, ): """ Install/remove/update XBPS packages. + packages: list of packages to ensure + present: whether the packages should be installed + update: run ``xbps-install -S`` before installing packages + upgrade: run ``xbps-install -y -u`` before installing packages **Example:** .. code:: python xbps.packages( name="Install Vim and Vim Pager", packages=["vimpager", "vim"], ) """ if update: yield from _update() if upgrade: yield from _upgrade() yield from ensure_packages( host, packages, host.get_fact(XbpsPackages), present, install_command="xbps-install -y -u", uninstall_command="xbps-remove -y", )