D7net
Home
Console
Upload
information
Create File
Create Folder
About
Tools
:
/
opt
/
saltstack
/
salt
/
lib
/
python3.10
/
site-packages
/
salt
/
modules
/
Filename :
win_wusa.py
back
Copy
""" Microsoft Update files management via wusa.exe :maintainer: Thomas Lemarchand :platform: Windows :depends: PowerShell .. versionadded:: 2018.3.4 """ import logging import os import salt.utils.platform from salt.exceptions import CommandExecutionError log = logging.getLogger(__name__) # Define the module's virtual name __virtualname__ = "wusa" def __virtual__(): """ Load only on Windows """ if not salt.utils.platform.is_windows(): return False, "Only available on Windows systems" powershell_info = __salt__["cmd.shell_info"](shell="powershell", list_modules=False) if not powershell_info["installed"]: return False, "PowerShell not available" return __virtualname__ def _pshell_json(cmd, cwd=None): """ Execute the desired powershell command and ensure that it returns data in JSON format and load that into python """ if "convertto-json" not in cmd.lower(): cmd = "{} | ConvertTo-Json".format(cmd) log.debug("PowerShell: %s", cmd) ret = __salt__["cmd.run_all"](cmd, shell="powershell", cwd=cwd) if "pid" in ret: del ret["pid"] if ret.get("stderr", ""): error = ret["stderr"].splitlines()[0] raise CommandExecutionError(error, info=ret) if "retcode" not in ret or ret["retcode"] != 0: # run_all logs an error to log.error, fail hard back to the user raise CommandExecutionError( "Issue executing PowerShell {}".format(cmd), info=ret ) # Sometimes Powershell returns an empty string, which isn't valid JSON if ret["stdout"] == "": ret["stdout"] = "{}" try: ret = salt.utils.json.loads(ret["stdout"], strict=False) except ValueError: raise CommandExecutionError("No JSON results from PowerShell", info=ret) return ret def is_installed(name): """ Check if a specific KB is installed. Args: name (str): The name of the KB to check Returns: bool: ``True`` if installed, otherwise ``False`` CLI Example: .. code-block:: bash salt '*' wusa.is_installed KB123456 """ return ( __salt__["cmd.retcode"]( cmd="Get-HotFix -Id {}".format(name), shell="powershell", ignore_retcode=True, ) == 0 ) def install(path, restart=False): """ Install a KB from a .msu file. Args: path (str): The full path to the msu file to install restart (bool): ``True`` to force a restart if required by the installation. Adds the ``/forcerestart`` switch to the ``wusa.exe`` command. ``False`` will add the ``/norestart`` switch instead. Default is ``False`` Returns: bool: ``True`` if successful, otherwise ``False`` Raise: CommandExecutionError: If the package is already installed or an error is encountered CLI Example: .. code-block:: bash salt '*' wusa.install C:/temp/KB123456.msu """ # Build the command cmd = ["wusa.exe", path, "/quiet"] if restart: cmd.append("/forcerestart") else: cmd.append("/norestart") # Run the command ret_code = __salt__["cmd.retcode"](cmd, ignore_retcode=True) # Check the ret_code file_name = os.path.basename(path) errors = { 2359302: "{} is already installed".format(file_name), 3010: ( "{} correctly installed but server reboot is needed to complete" " installation".format(file_name) ), 87: "Unknown error", } if ret_code in errors: raise CommandExecutionError(errors[ret_code], ret_code) elif ret_code: raise CommandExecutionError("Unknown error: {}".format(ret_code)) return True def uninstall(path, restart=False): """ Uninstall a specific KB. Args: path (str): The full path to the msu file to uninstall. This can also be just the name of the KB to uninstall restart (bool): ``True`` to force a restart if required by the installation. Adds the ``/forcerestart`` switch to the ``wusa.exe`` command. ``False`` will add the ``/norestart`` switch instead. Default is ``False`` Returns: bool: ``True`` if successful, otherwise ``False`` Raises: CommandExecutionError: If an error is encountered CLI Example: .. code-block:: bash salt '*' wusa.uninstall KB123456 # or salt '*' wusa.uninstall C:/temp/KB123456.msu """ # Build the command cmd = ["wusa.exe", "/uninstall", "/quiet"] kb = os.path.splitext(os.path.basename(path))[0] if os.path.exists(path): cmd.append(path) else: cmd.append("/kb:{}".format(kb[2:] if kb.lower().startswith("kb") else kb)) if restart: cmd.append("/forcerestart") else: cmd.append("/norestart") # Run the command ret_code = __salt__["cmd.retcode"](cmd, ignore_retcode=True) # Check the ret_code # If you pass /quiet and specify /kb, you'll always get retcode 87 if there # is an error. Use the actual file to get a more descriptive error errors = { -2145116156: "{} does not support uninstall".format(kb), 2359303: "{} not installed".format(kb), 87: "Unknown error. Try specifying an .msu file", } if ret_code in errors: raise CommandExecutionError(errors[ret_code], ret_code) elif ret_code: raise CommandExecutionError("Unknown error: {}".format(ret_code)) return True def list(): """ Get a list of updates installed on the machine Returns: list: A list of installed updates CLI Example: .. code-block:: bash salt '*' wusa.list """ kbs = [] ret = _pshell_json("Get-HotFix | Select HotFixID") for item in ret: kbs.append(item["HotFixID"]) return kbs