mirror of
https://github.com/saltstack/salt.git
synced 2025-04-17 10:10:20 +00:00
Merge pull request #56637 from twangboy/win_update_installed
Add `win_wua.installed` to the win_wua module
This commit is contained in:
commit
efa84d18f9
5 changed files with 533 additions and 158 deletions
14
CHANGELOG.md
14
CHANGELOG.md
|
@ -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
|
||||
|
||||
|
|
|
@ -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:
|
||||
|
||||
|
|
|
@ -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:
|
||||
|
||||
|
|
96
tests/unit/modules/test_win_wua.py
Normal file
96
tests/unit/modules/test_win_wua.py
Normal 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)
|
92
tests/unit/utils/test_win_update.py
Normal file
92
tests/unit/utils/test_win_update.py
Normal 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
|
Loading…
Add table
Reference in a new issue