Merge pull request #56637 from twangboy/win_update_installed

Add `win_wua.installed` to the win_wua module
This commit is contained in:
Daniel Wozniak 2020-04-23 00:52:03 -07:00 committed by GitHub
commit efa84d18f9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 533 additions and 158 deletions

View file

@ -7,6 +7,20 @@ This project versioning is _similar_ to [Semantic Versioning](https://semver.org
Versions are `MAJOR.PATCH`.
## 3001 - Sodium
## 3001 - Sodium [yyyy-mm-dd]
### Removed
### Deprecated
### Changed
### Fixed
### Added
- [#56637](https://github.com/saltstack/salt/pull/56637) - Add ``win_wua.installed`` to the ``win_wua`` execution module
## 3000.1
### Removed

View file

@ -109,6 +109,7 @@ def available(
skip_reboot=False,
categories=None,
severities=None,
online=True,
):
"""
.. versionadded:: 2017.7.0
@ -125,8 +126,8 @@ def available(
Include driver updates in the results. Default is ``True``
summary (bool):
- True: Return a summary of updates available for each category.
- False (default): Return a detailed list of available updates.
- ``True``: Return a summary of updates available for each category.
- ``False`` (default): Return a detailed list of available updates.
skip_installed (bool):
Skip updates that are already installed. Default is ``True``
@ -148,7 +149,7 @@ def available(
* Critical Updates
* Definition Updates
* Drivers (make sure you set drivers=True)
* Drivers (make sure you set ``drivers=True``)
* Feature Packs
* Security Updates
* Update Rollups
@ -169,39 +170,48 @@ def available(
* Critical
* Important
online (bool):
Tells the Windows Update Agent go online to update its local update
database. ``True`` will go online. ``False`` will use the local
update database as is. Default is ``True``
.. versionadded:: Sodium
Returns:
dict: Returns a dict containing either a summary or a list of updates:
.. code-block:: cfg
List of Updates:
{'<GUID>': {'Title': <title>,
'KB': <KB>,
'GUID': <the globally unique identifier for the update>
'Description': <description>,
'Downloaded': <has the update been downloaded>,
'Installed': <has the update been installed>,
'Mandatory': <is the update mandatory>,
'UserInput': <is user input required>,
'EULAAccepted': <has the EULA been accepted>,
'Severity': <update severity>,
'NeedsReboot': <is the update installed and awaiting reboot>,
'RebootBehavior': <will the update require a reboot>,
'Categories': [ '<category 1>',
'<category 2>',
...]
}
}
Dict of Updates:
{'<GUID>': {
'Title': <title>,
'KB': <KB>,
'GUID': <the globally unique identifier for the update>,
'Description': <description>,
'Downloaded': <has the update been downloaded>,
'Installed': <has the update been installed>,
'Mandatory': <is the update mandatory>,
'UserInput': <is user input required>,
'EULAAccepted': <has the EULA been accepted>,
'Severity': <update severity>,
'NeedsReboot': <is the update installed and awaiting reboot>,
'RebootBehavior': <will the update require a reboot>,
'Categories': [
'<category 1>',
'<category 2>',
... ]
}}
Summary of Updates:
{'Total': <total number of updates returned>,
'Available': <updates that are not downloaded or installed>,
'Downloaded': <updates that are downloaded but not installed>,
'Installed': <updates installed (usually 0 unless installed=True)>,
'Categories': { <category 1>: <total for that category>,
<category 2>: <total for category 2>,
... }
'Categories': {
<category 1>: <total for that category>,
<category 2>: <total for category 2>,
... }
}
CLI Examples:
@ -228,7 +238,7 @@ def available(
"""
# Create a Windows Update Agent instance
wua = salt.utils.win_update.WindowsUpdateAgent()
wua = salt.utils.win_update.WindowsUpdateAgent(online=online)
# Look for available
updates = wua.available(
@ -246,7 +256,7 @@ def available(
return updates.summary() if summary else updates.list()
def get(name, download=False, install=False):
def get(name, download=False, install=False, online=True):
"""
.. versionadded:: 2017.7.0
@ -270,35 +280,44 @@ def get(name, download=False, install=False):
first to see if the update exists, then set ``install=True`` to
install the update.
online (bool):
Tells the Windows Update Agent go online to update its local update
database. ``True`` will go online. ``False`` will use the local
update database as is. Default is ``True``
.. versionadded:: Sodium
Returns:
dict: Returns a dict containing a list of updates that match the name if
download and install are both set to False. Should usually be a single
update, but can return multiple if a partial name is given.
dict:
Returns a dict containing a list of updates that match the name if
download and install are both set to False. Should usually be a
single update, but can return multiple if a partial name is given.
If download or install is set to true it will return the results of the
operation.
.. code-block:: cfg
List of Updates:
{'<GUID>': {'Title': <title>,
'KB': <KB>,
'GUID': <the globally unique identifier for the update>
'Description': <description>,
'Downloaded': <has the update been downloaded>,
'Installed': <has the update been installed>,
'Mandatory': <is the update mandatory>,
'UserInput': <is user input required>,
'EULAAccepted': <has the EULA been accepted>,
'Severity': <update severity>,
'NeedsReboot': <is the update installed and awaiting reboot>,
'RebootBehavior': <will the update require a reboot>,
'Categories': [ '<category 1>',
'<category 2>',
...]
}
}
Dict of Updates:
{'<GUID>': {
'Title': <title>,
'KB': <KB>,
'GUID': <the globally unique identifier for the update>,
'Description': <description>,
'Downloaded': <has the update been downloaded>,
'Installed': <has the update been installed>,
'Mandatory': <is the update mandatory>,
'UserInput': <is user input required>,
'EULAAccepted': <has the EULA been accepted>,
'Severity': <update severity>,
'NeedsReboot': <is the update installed and awaiting reboot>,
'RebootBehavior': <will the update require a reboot>,
'Categories': [
'<category 1>',
'<category 2>',
... ]
}}
CLI Examples:
@ -320,7 +339,7 @@ def get(name, download=False, install=False):
salt '*' win_wua.get 'Microsoft Camera Codec Pack'
"""
# Create a Windows Update Agent instance
wua = salt.utils.win_update.WindowsUpdateAgent()
wua = salt.utils.win_update.WindowsUpdateAgent(online=online)
# Search for Update
updates = wua.search(name)
@ -347,12 +366,14 @@ def list(
severities=None,
download=False,
install=False,
online=True,
):
"""
.. versionadded:: 2017.7.0
Returns a detailed list of available updates or a summary. If download or
install is True the same list will be downloaded and/or installed.
Returns a detailed list of available updates or a summary. If ``download``
or ``install`` is ``True`` the same list will be downloaded and/or
installed.
Args:
@ -363,8 +384,8 @@ def list(
Include driver updates in the results. Default is ``False``
summary (bool):
- True: Return a summary of updates available for each category.
- False (default): Return a detailed list of available updates.
- ``True``: Return a summary of updates available for each category.
- ``False`` (default): Return a detailed list of available updates.
skip_installed (bool):
Skip installed updates in the results. Default is ``True``
@ -389,7 +410,7 @@ def list(
* Critical Updates
* Definition Updates
* Drivers (make sure you set drivers=True)
* Drivers (make sure you set ``drivers=True``)
* Feature Packs
* Security Updates
* Update Rollups
@ -410,39 +431,48 @@ def list(
* Critical
* Important
online (bool):
Tells the Windows Update Agent go online to update its local update
database. ``True`` will go online. ``False`` will use the local
update database as is. Default is ``True``
.. versionadded:: Sodium
Returns:
dict: Returns a dict containing either a summary or a list of updates:
.. code-block:: cfg
List of Updates:
{'<GUID>': {'Title': <title>,
'KB': <KB>,
'GUID': <the globally unique identifier for the update>
'Description': <description>,
'Downloaded': <has the update been downloaded>,
'Installed': <has the update been installed>,
'Mandatory': <is the update mandatory>,
'UserInput': <is user input required>,
'EULAAccepted': <has the EULA been accepted>,
'Severity': <update severity>,
'NeedsReboot': <is the update installed and awaiting reboot>,
'RebootBehavior': <will the update require a reboot>,
'Categories': [ '<category 1>',
'<category 2>',
...]
}
}
Dict of Updates:
{'<GUID>': {
'Title': <title>,
'KB': <KB>,
'GUID': <the globally unique identifier for the update>,
'Description': <description>,
'Downloaded': <has the update been downloaded>,
'Installed': <has the update been installed>,
'Mandatory': <is the update mandatory>,
'UserInput': <is user input required>,
'EULAAccepted': <has the EULA been accepted>,
'Severity': <update severity>,
'NeedsReboot': <is the update installed and awaiting reboot>,
'RebootBehavior': <will the update require a reboot>,
'Categories': [
'<category 1>',
'<category 2>',
... ]
}}
Summary of Updates:
{'Total': <total number of updates returned>,
'Available': <updates that are not downloaded or installed>,
'Downloaded': <updates that are downloaded but not installed>,
'Installed': <updates installed (usually 0 unless installed=True)>,
'Categories': { <category 1>: <total for that category>,
<category 2>: <total for category 2>,
... }
'Categories': {
<category 1>: <total for that category>,
<category 2>: <total for category 2>,
... }
}
CLI Examples:
@ -468,7 +498,7 @@ def list(
salt '*' win_wua.list categories=['Feature Packs','Windows 8.1'] summary=True
"""
# Create a Windows Update Agent instance
wua = salt.utils.win_update.WindowsUpdateAgent()
wua = salt.utils.win_update.WindowsUpdateAgent(online=online)
# Search for Update
updates = wua.available(
@ -495,12 +525,77 @@ def list(
return ret
def installed(summary=False, kbs_only=False):
"""
.. versionadded:: Sodium
Get a list of all updates that are currently installed on the system.
.. note::
This list may not necessarily match the Update History on the machine.
This will only show the updates that apply to the current build of
Windows. So, for example, the system may have shipped with Windows 10
Build 1607. That machine received updates to the 1607 build. Later the
machine was upgraded to a newer feature release, 1803 for example. Then
more updates were applied. This will only return the updates applied to
the 1803 build and not those applied when the system was at the 1607
build.
Args:
summary (bool):
Return a summary instead of a detailed list of updates. ``True``
will return a Summary, ``False`` will return a detailed list of
installed updates. Default is ``False``
kbs_only (bool):
Only return a list of KBs installed on the system. If this parameter
is passed, the ``summary`` parameter will be ignored. Default is
``False``
Returns:
dict:
Returns a dictionary of either a Summary or a detailed list of
updates installed on the system when ``kbs_only=False``
list:
Returns a list of KBs installed on the system when ``kbs_only=True``
CLI Examples:
.. code-block:: bash
# Get a detailed list of all applicable updates installed on the system
salt '*' win_wua.installed
# Get a summary of all applicable updates installed on the system
salt '*' win_wua.installed summary=True
# Get a simple list of KBs installed on the system
salt '*' win_wua.installed kbs_only=True
"""
# Create a Windows Update Agent instance. Since we're only listing installed
# updates, there's no need to go online to update the Windows Update db
wua = salt.utils.win_update.WindowsUpdateAgent(online=False)
updates = wua.installed() # Get installed Updates objects
results = updates.list() # Convert to list
if kbs_only:
list_kbs = set()
for item in results:
list_kbs.update(results[item]["KBs"])
return sorted(list_kbs)
return updates.summary() if summary else results
def download(names):
"""
.. versionadded:: 2017.7.0
Downloads updates that match the list of passed identifiers. It's easier to
use this function by using list_updates and setting install=True.
use this function by using list_updates and setting ``download=True``.
Args:
@ -509,15 +604,16 @@ def download(names):
combination of GUIDs, KB numbers, or names. GUIDs or KBs are
preferred.
.. note::
An error will be raised if there are more results than there are items
in the names parameter
.. note::
An error will be raised if there are more results than there are
items in the names parameter
Returns:
dict: A dictionary containing the details about the downloaded updates
CLI Examples:
CLI Example:
.. code-block:: bash
@ -542,7 +638,7 @@ def download(names):
if updates.count() > len(names):
raise CommandExecutionError(
"Multiple updates found, names need to be " "more specific"
"Multiple updates found, names need to be more specific"
)
return wua.download(updates)
@ -553,7 +649,7 @@ def install(names):
.. versionadded:: 2017.7.0
Installs updates that match the list of identifiers. It may be easier to use
the list_updates function and set install=True.
the list_updates function and set ``install=True``.
Args:
@ -563,6 +659,7 @@ def install(names):
preferred.
.. note::
An error will be raised if there are more results than there are items
in the names parameter
@ -595,7 +692,7 @@ def install(names):
if updates.count() > len(names):
raise CommandExecutionError(
"Multiple updates found, names need to be " "more specific"
"Multiple updates found, names need to be more specific"
)
return wua.install(updates)
@ -672,7 +769,8 @@ def set_wu_settings(
Number from 1 to 4 indicating the update level:
1. Never check for updates
2. Check for updates but let me choose whether to download and install them
2. Check for updates but let me choose whether to download and
install them
3. Download updates but let me choose whether to install them
4. Install updates automatically
@ -804,7 +902,7 @@ def set_wu_settings(
}
if day not in days:
ret["Comment"] = (
"Day needs to be one of the following: Everyday,"
"Day needs to be one of the following: Everyday, "
"Monday, Tuesday, Wednesday, Thursday, Friday, "
"Saturday"
)
@ -824,15 +922,15 @@ def set_wu_settings(
# treat it as an integer
if not isinstance(time, six.string_types):
ret["Comment"] = (
"Time argument needs to be a string; it may need to"
"Time argument needs to be a string; it may need to "
"be quoted. Passed {0}. Time not set.".format(time)
)
ret["Success"] = False
# Check for colon in the time
elif ":" not in time:
ret["Comment"] = (
"Time argument needs to be in 00:00 format."
" Passed {0}. Time not set.".format(time)
"Time argument needs to be in 00:00 format. "
"Passed {0}. Time not set.".format(time)
)
ret["Success"] = False
else:
@ -906,35 +1004,46 @@ def get_wu_settings():
Featured Updates:
Boolean value that indicates whether to display notifications for
featured updates.
Group Policy Required (Read-only):
Boolean value that indicates whether Group Policy requires the
Automatic Updates service.
Microsoft Update:
Boolean value that indicates whether to turn on Microsoft Update for
other Microsoft Products
Needs Reboot:
Boolean value that indicates whether the machine is in a reboot
pending state.
Non Admins Elevated:
Boolean value that indicates whether non-administrators can perform
some update-related actions without administrator approval.
Notification Level:
Number 1 to 4 indicating the update level:
1. Never check for updates
2. Check for updates but let me choose whether to download and
install them
3. Download updates but let me choose whether to install them
4. Install updates automatically
Read Only (Read-only):
Boolean value that indicates whether the Automatic Update
settings are read-only.
Recommended Updates:
Boolean value that indicates whether to include optional or
recommended updates when a search for updates and installation of
updates is performed.
Scheduled Day:
Days of the week on which Automatic Updates installs or uninstalls
updates.
Scheduled Time:
Time at which Automatic Updates installs or uninstalls updates.
@ -1019,7 +1128,7 @@ def get_needs_reboot():
Returns:
bool: True if the system requires a reboot, otherwise False
bool: ``True`` if the system requires a reboot, otherwise ``False``
CLI Examples:

View file

@ -32,7 +32,7 @@ __virtualname__ = "win_update"
def __virtual__():
if not salt.utils.platform.is_windows():
return False, "win_update: Not available on Windows"
return False, "win_update: Only available on Windows"
if not HAS_PYWIN32:
return False, "win_update: Missing pywin32"
return __virtualname__
@ -43,7 +43,8 @@ class Updates(object):
Wrapper around the 'Microsoft.Update.UpdateColl' instance
Adds the list and summary functions. For use by the WindowUpdateAgent class.
Usage:
Code Example:
.. code-block:: python
# Create an instance
@ -110,24 +111,25 @@ class Updates(object):
.. code-block:: cfg
List of Updates:
{'<GUID>': {'Title': <title>,
'KB': <KB>,
'GUID': <the globally unique identifier for the update>
'Description': <description>,
'Downloaded': <has the update been downloaded>,
'Installed': <has the update been installed>,
'Mandatory': <is the update mandatory>,
'UserInput': <is user input required>,
'EULAAccepted': <has the EULA been accepted>,
'Severity': <update severity>,
'NeedsReboot': <is the update installed and awaiting reboot>,
'RebootBehavior': <will the update require a reboot>,
'Categories': [ '<category 1>',
'<category 2>',
...]
}
}
Dict of Updates:
{'<GUID>': {
'Title': <title>,
'KB': <KB>,
'GUID': <the globally unique identifier for the update>,
'Description': <description>,
'Downloaded': <has the update been downloaded>,
'Installed': <has the update been installed>,
'Mandatory': <is the update mandatory>,
'UserInput': <is user input required>,
'EULAAccepted': <has the EULA been accepted>,
'Severity': <update severity>,
'NeedsReboot': <is the update installed and awaiting reboot>,
'RebootBehavior': <will the update require a reboot>,
'Categories': [
'<category 1>',
'<category 2>',
... ]
}}
Code Example:
@ -182,10 +184,12 @@ class Updates(object):
'Available': <updates that are not downloaded or installed>,
'Downloaded': <updates that are downloaded but not installed>,
'Installed': <updates installed (usually 0 unless installed=True)>,
'Categories': { <category 1>: <total for that category>,
<category 2>: <total for category 2>,
... }
'Categories': {
<category 1>: <total for that category>,
<category 2>: <total for category 2>,
... }
}
Code Example:
.. code-block:: python
@ -251,7 +255,6 @@ class Updates(object):
class WindowsUpdateAgent(object):
"""
Class for working with the Windows update agent
"""
# Error codes found at the following site:
@ -285,12 +288,21 @@ class WindowsUpdateAgent(object):
-4292607995: "Reboot required: 0x00240005",
}
def __init__(self):
def __init__(self, online=True):
"""
Initialize the session and load all updates into the ``_updates``
collection. This collection is used by the other class functions instead
of querying Windows update (expensive).
Args:
online (bool):
Tells the Windows Update Agent go online to update its local
update database. ``True`` will go online. ``False`` will use the
local update database as is. Default is ``True``
.. versionadded:: Sodium
Need to look at the possibility of loading this into ``__context__``
"""
# Initialize the PyCom system
@ -302,7 +314,7 @@ class WindowsUpdateAgent(object):
# Create Collection for Updates
self._updates = win32com.client.Dispatch("Microsoft.Update.UpdateColl")
self.refresh()
self.refresh(online=online)
def updates(self):
"""
@ -310,8 +322,12 @@ class WindowsUpdateAgent(object):
Updates class to expose the list and summary functions.
Returns:
Updates: An instance of the Updates class with all updates for the
system.
Updates:
An instance of the Updates class with all updates for the
system.
Code Example:
.. code-block:: python
@ -333,12 +349,21 @@ class WindowsUpdateAgent(object):
return updates
def refresh(self):
def refresh(self, online=True):
"""
Refresh the contents of the ``_updates`` collection. This gets all
updates in the Windows Update system and loads them into the collection.
This is the part that is slow.
Args:
online (bool):
Tells the Windows Update Agent go online to update its local
update database. ``True`` will go online. ``False`` will use the
local update database as is. Default is ``True``
.. versionadded:: Sodium
Code Example:
.. code-block:: python
@ -352,6 +377,7 @@ class WindowsUpdateAgent(object):
# Create searcher object
searcher = self._session.CreateUpdateSearcher()
searcher.Online = online
self._session.ClientApplicationID = "Salt: Load Updates"
# Load all updates into the updates collection
@ -373,6 +399,32 @@ class WindowsUpdateAgent(object):
self._updates = results.Updates
def installed(self):
"""
Gets a list of all updates available on the system that have the
``IsInstalled`` attribute set to ``True``.
Returns:
Updates: An instance of Updates with the results.
Code Example:
.. code-block:: python
import salt.utils.win_update
wua = salt.utils.win_update.WindowsUpdateAgent(online=False)
installed_updates = wua.installed()
"""
# https://msdn.microsoft.com/en-us/library/windows/desktop/aa386099(v=vs.85).aspx
updates = Updates()
for update in self._updates:
if salt.utils.data.is_true(update.IsInstalled):
updates.updates.Add(update)
return updates
def available(
self,
skip_hidden=True,
@ -390,23 +442,27 @@ class WindowsUpdateAgent(object):
Args:
skip_hidden (bool): Skip hidden updates. Default is True
skip_hidden (bool):
Skip hidden updates. Default is ``True``
skip_installed (bool): Skip installed updates. Default is True
skip_installed (bool):
Skip installed updates. Default is ``True``
skip_mandatory (bool): Skip mandatory updates. Default is False
skip_mandatory (bool):
Skip mandatory updates. Default is ``False``
skip_reboot (bool): Skip updates that can or do require reboot.
Default is False
skip_reboot (bool):
Skip updates that can or do require reboot. Default is ``False``
software (bool): Include software updates. Default is True
software (bool):
Include software updates. Default is ``True``
drivers (bool): Include driver updates. Default is True
drivers (bool):
Include driver updates. Default is ``True``
categories (list): Include updates that have these categories.
Default is none (all categories).
Categories include the following:
categories (list):
Include updates that have these categories. Default is none
(all categories). Categories include the following:
* Critical Updates
* Definition Updates
@ -422,16 +478,17 @@ class WindowsUpdateAgent(object):
* Windows 8.1 and later drivers
* Windows Defender
severities (list): Include updates that have these severities.
Default is none (all severities).
Severities include the following:
severities (list):
Include updates that have these severities. Default is none
(all severities). Severities include the following:
* Critical
* Important
.. note:: All updates are either software or driver updates. If both
``software`` and ``drivers`` is False, nothing will be returned.
.. note::
All updates are either software or driver updates. If both
``software`` and ``drivers`` is ``False``, nothing will be returned.
Returns:
@ -445,7 +502,7 @@ class WindowsUpdateAgent(object):
wua = salt.utils.win_update.WindowsUpdateAgent()
# Gets all updates and shows a summary
updates = wua.available
updates = wua.available()
updates.summary()
# Get a list of Critical updates
@ -502,11 +559,11 @@ class WindowsUpdateAgent(object):
Args:
search_string (str, list): The search string to use to find the
update. This can be the GUID or KB of the update (preferred). It can
also be the full Title of the update or any part of the Title. A
partial Title search is less specific and can return multiple
results.
search_string (str, list):
The search string to use to find the update. This can be the
GUID or KB of the update (preferred). It can also be the full
Title of the update or any part of the Title. A partial Title
search is less specific and can return multiple results.
Returns:
Updates: An instance of Updates with the results of the search
@ -568,8 +625,9 @@ class WindowsUpdateAgent(object):
Args:
updates (Updates): An instance of the Updates class containing a
the updates to be downloaded.
updates (Updates):
An instance of the Updates class containing a the updates to be
downloaded.
Returns:
dict: A dictionary containing the results of the download
@ -681,8 +739,9 @@ class WindowsUpdateAgent(object):
Args:
updates (Updates): An instance of the Updates class containing a
the updates to be installed.
updates (Updates):
An instance of the Updates class containing a the updates to be
installed.
Returns:
dict: A dictionary containing the results of the installation
@ -789,19 +848,23 @@ class WindowsUpdateAgent(object):
Uninstall the updates passed in the updates collection. Load the updates
collection using the ``search`` or ``available`` functions.
.. note:: Starting with Windows 10 the Windows Update Agent is unable to
uninstall updates. An ``Uninstall Not Allowed`` error is returned. If
this error is encountered this function will instead attempt to use
``dism.exe`` to perform the uninstallation. ``dism.exe`` may fail to
to find the KB number for the package. In that case, removal will fail.
.. note::
Starting with Windows 10 the Windows Update Agent is unable to
uninstall updates. An ``Uninstall Not Allowed`` error is returned.
If this error is encountered this function will instead attempt to
use ``dism.exe`` to perform the un-installation. ``dism.exe`` may
fail to to find the KB number for the package. In that case, removal
will fail.
Args:
updates (Updates): An instance of the Updates class containing a
the updates to be uninstalled.
updates (Updates):
An instance of the Updates class containing a the updates to be
uninstalled.
Returns:
dict: A dictionary containing the results of the uninstallation
dict: A dictionary containing the results of the un-installation
Code Example:
@ -825,7 +888,7 @@ class WindowsUpdateAgent(object):
return ret
installer = self._session.CreateUpdateInstaller()
self._session.ClientApplicationID = "Salt: Install Update"
self._session.ClientApplicationID = "Salt: Uninstall Update"
with salt.utils.winapi.Com():
uninstall_list = win32com.client.Dispatch("Microsoft.Update.UpdateColl")
@ -920,7 +983,7 @@ class WindowsUpdateAgent(object):
log.debug("NeedsReboot: %s", ret["NeedsReboot"])
# Refresh the Updates Table
self.refresh()
self.refresh(online=False)
reboot = {0: "Never Reboot", 1: "Always Reboot", 2: "Poss Reboot"}
@ -941,7 +1004,7 @@ class WindowsUpdateAgent(object):
return ret
# Found a differenct exception, Raise error
# Found a different exception, Raise error
log.error("Uninstall Failed: %s", failure_code)
raise CommandExecutionError(failure_code)
@ -984,7 +1047,8 @@ class WindowsUpdateAgent(object):
Internal function for running commands. Used by the uninstall function.
Args:
cmd (str, list): The command to run
cmd (str, list):
The command to run
Returns:
str: The stdout of the command
@ -1012,7 +1076,7 @@ def needs_reboot():
Returns:
bool: True if the system requires a reboot, False if not
bool: ``True`` if the system requires a reboot, ``False`` if not
CLI Examples:

View file

@ -0,0 +1,96 @@
# -*- coding: utf-8 -*-
"""
Test the win_wua execution module
"""
# Import Python Libs
from __future__ import absolute_import, print_function, unicode_literals
# Import Salt Libs
import salt.modules.win_wua as win_wua
import salt.utils.platform
import salt.utils.win_update
# Import Salt Testing Libs
from tests.support.mock import patch
from tests.support.unit import TestCase, skipIf
UPDATES_LIST = {
"ca3bb521-a8ea-4e26-a563-2ad6e3108b9a": {"KBs": ["KB4481252"]},
"07609d43-d518-4e77-856e-d1b316d1b8a8": {"KBs": ["KB925673"]},
"fbaa5360-a440-49d8-a3b6-0c4fc7ecaa19": {"KBs": ["KB4481252"]},
"a873372b-7a5c-443c-8022-cd59a550bef4": {"KBs": ["KB3193497"]},
"14075cbe-822e-4004-963b-f50e08d45563": {"KBs": ["KB4540723"]},
"d931e99c-4dda-4d39-9905-0f6a73f7195f": {"KBs": ["KB3193497"]},
"afda9e11-44a0-4602-9e9b-423af11ecaed": {"KBs": ["KB4541329"]},
"a0f997b1-1abe-4a46-941f-b37f732f9fbd": {"KBs": ["KB3193497"]},
"eac02b09-d745-4891-b80f-400e0e5e4b6d": {"KBs": ["KB4052623"]},
"0689e74b-54d1-4f55-a916-96e3c737db90": {"KBs": ["KB890830"]},
}
UPDATES_SUMMARY = {"Installed": 10}
class Updates(object):
@staticmethod
def list():
return UPDATES_LIST
@staticmethod
def summary():
return UPDATES_SUMMARY
@skipIf(not salt.utils.platform.is_windows(), "System is not Windows")
class WinWuaInstalledTestCase(TestCase):
"""
Test the functions in the win_wua.installed function
"""
def test_installed(self):
"""
Test installed function default
"""
expected = UPDATES_LIST
with patch("salt.utils.winapi.Com", autospec=True), patch(
"win32com.client.Dispatch", autospec=True
), patch.object(
salt.utils.win_update.WindowsUpdateAgent, "refresh", autospec=True
), patch.object(
salt.utils.win_update, "Updates", autospec=True, return_value=Updates()
):
result = win_wua.installed()
self.assertDictEqual(result, expected)
def test_installed_summary(self):
"""
Test installed function with summary=True
"""
expected = UPDATES_SUMMARY
# Remove all updates that are not installed
with patch("salt.utils.winapi.Com", autospec=True), patch(
"win32com.client.Dispatch", autospec=True
), patch.object(
salt.utils.win_update.WindowsUpdateAgent, "refresh", autospec=True
), patch.object(
salt.utils.win_update, "Updates", autospec=True, return_value=Updates()
):
result = win_wua.installed(summary=True)
self.assertDictEqual(result, expected)
def test_installed_kbs_only(self):
"""
Test installed function with kbs_only=True
"""
expected = set()
for update in UPDATES_LIST:
expected.update(UPDATES_LIST[update]["KBs"])
expected = sorted(expected)
# Remove all updates that are not installed
with patch("salt.utils.winapi.Com", autospec=True), patch(
"win32com.client.Dispatch", autospec=True
), patch.object(
salt.utils.win_update.WindowsUpdateAgent, "refresh", autospec=True
), patch.object(
salt.utils.win_update, "Updates", autospec=True, return_value=Updates()
):
result = win_wua.installed(kbs_only=True)
self.assertListEqual(result, expected)

View file

@ -0,0 +1,92 @@
# -*- coding: utf-8 -*-
# Import Python Libs
from __future__ import absolute_import, print_function, unicode_literals
# Import Salt Libs
import salt.utils.platform
import salt.utils.win_update as win_update
# Import Salt Testing Libs
from tests.support.mock import MagicMock, patch
from tests.support.unit import TestCase, skipIf
@skipIf(not salt.utils.platform.is_windows(), "System is not Windows")
class WinUpdateTestCase(TestCase):
"""
Test cases for salt.utils.win_update
"""
def test_installed_no_updates(self):
"""
Test installed when there are no updates on the system
"""
with patch("salt.utils.winapi.Com", autospec=True), patch(
"win32com.client.Dispatch", autospec=True
), patch.object(win_update.WindowsUpdateAgent, "refresh", autospec=True):
wua = win_update.WindowsUpdateAgent(online=False)
wua._updates = []
installed_updates = wua.installed()
assert installed_updates.updates.Add.call_count == 0
def test_installed_no_updates_installed(self):
"""
Test installed when there are no Installed updates on the system
"""
with patch("salt.utils.winapi.Com", autospec=True), patch(
"win32com.client.Dispatch", autospec=True
), patch.object(win_update.WindowsUpdateAgent, "refresh", autospec=True):
wua = win_update.WindowsUpdateAgent(online=False)
wua._updates = [
MagicMock(IsInstalled=False),
MagicMock(IsInstalled=False),
MagicMock(IsInstalled=False),
]
installed_updates = wua.installed()
assert installed_updates.updates.Add.call_count == 0
def test_installed_updates_all_installed(self):
"""
Test installed when all updates on the system are Installed
"""
with patch("salt.utils.winapi.Com", autospec=True), patch(
"win32com.client.Dispatch", autospec=True
), patch.object(win_update.WindowsUpdateAgent, "refresh", autospec=True):
wua = win_update.WindowsUpdateAgent(online=False)
wua._updates = [
MagicMock(IsInstalled=True),
MagicMock(IsInstalled=True),
MagicMock(IsInstalled=True),
]
installed_updates = wua.installed()
assert installed_updates.updates.Add.call_count == 3
def test_installed_updates_some_installed(self):
"""
Test installed when some updates are installed on the system
"""
with patch("salt.utils.winapi.Com", autospec=True), patch(
"win32com.client.Dispatch", autospec=True
), patch.object(win_update.WindowsUpdateAgent, "refresh", autospec=True):
wua = win_update.WindowsUpdateAgent(online=False)
wua._updates = [
MagicMock(IsInstalled=True),
MagicMock(IsInstalled=False),
MagicMock(IsInstalled=True),
MagicMock(IsInstalled=False),
MagicMock(IsInstalled=True),
]
installed_updates = wua.installed()
assert installed_updates.updates.Add.call_count == 3