Source code for idem_posix.states.cmdmod

"""State module for executing commands on a posix systems."""
import copy
import os
from typing import Any
from typing import Dict
from typing import List

__virtualname__ = "cmd"


def __virtual__(hub):
    return os.name == "posix", "idem-posix only runs on posix systems"


[docs]async def run( hub, ctx, name: str, cmd: str or List[str], cwd: str = None, shell: bool = False, env: Dict[str, Any] = None, umask: str = None, timeout: int or float = None, render_pipe: str = None, is_string_output: bool = False, success_retcodes: List[int] = None, **kwargs, ) -> Dict[str, Any]: """Execute the passed command and return the output as a string Args: name(str): The state name. cmd(str or list[str]): The command to run. ex: ``ls -lart /home`` cwd(str, Optional): The directory from which to execute the command. Defaults to the home directory of the user specified by ``runas`` (or the user under which Salt is running if ``runas`` is not specified). shell(bool): If ``False``, let python handle the positional arguments. Set to ``True`` to use shell features, such as pipes or redirection. Defaults to False. env(dict[str], Optional): Environment variables to be set prior to execution. Defaults to None. .. note:: When passing environment variables on the CLI, they should be passed as the string representation of a dictionary. .. code-block:: bash idem exec cmd.run 'some command' env='{"FOO": "bar"}' umask(str, Optional): The umask (in octal) to use when running the command. timeout(int or float, Optional): A timeout in seconds for the executed process to return. Defaults to None. render_pipe(str, Optional): The render pipe to use on the output. Defaults to None. is_string_output(bool): Give the output in string format irrespective of format the command executed returns. Defaults to False. success_retcodes(list[int], Optional): The result will be True if the command's return code is in this list. Defaults to [0]. kwargs: kwargs that will be forwarded to subprocess. Returns: Dict[str, Any] Example: .. code-block:: sls # Execute "ls -l" command my_state_name: cmd.run: - cmd: ls -l - cwd: / - shell: False - env: ENV_VAR_1: ENV_VAL_1 ENV_VAR_2: ENV_VAL_2 - timeout: 100 - render_pipe: - kwargs: The "new_state" will have the following keys: "stdout": The plaintext output of the command "stderr": The plaintext error/logging output of the command "retcode": The return code from the command "state": The output as rendered from the render_pipe (if one was given), for use in arg_binding """ ret = { "name": name, "result": True, "comment": "", "old_state": ctx.get("old_state", {}), "new_state": {}, } # Need the check for None here, if env is not provided then it falls back # to None and it is assumed that the environment is not being overridden. if env is not None and not isinstance(env, (list, dict)): ret["result"] = False ret["comment"] = "Invalidly-formatted 'env' parameter. See " "documentation." return ret cmd_kwargs = copy.deepcopy(kwargs) cmd_kwargs.update( { "cwd": cwd, "shell": shell, "env": env, "umask": umask, } ) if cwd and not os.path.isdir(cwd): ret["result"] = False ret["comment"] = f'Desired working directory "{cwd}" ' "is not available" return ret if ctx["test"]: if kwargs and kwargs.get("ignore_test", False): hub.log.debug( "Invoking cmd.run even when ctx.test is enabled due to ignore_test flag." ) else: ret["comment"] = f"The cmd.run does not run when ctx.test is enabled" return ret cmd_ret = await hub.exec.cmd.run( cmd=cmd, timeout=timeout, python_shell=True, render_pipe=render_pipe, is_string_output=is_string_output, success_retcodes=success_retcodes, **cmd_kwargs, ) ret["new_state"] = cmd_ret.ret ret["result"] = cmd_ret.result ret["comment"] = ( f"stdout: {cmd_ret.ret['stdout']}", f"stderr: {cmd_ret.ret['stderr']}", f"retcode: {cmd_ret.ret['retcode']}", ) if cmd_ret.comment: ret["comment"] = (cmd_ret.comment,) + ret["comment"] return ret