Merge branch 'develop' into state-deprecations

This commit is contained in:
Nicole Thomas 2018-03-20 09:18:53 -04:00 committed by GitHub
commit a7c93fdd63
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 192 additions and 1846 deletions

4
.github/stale.yml vendored
View File

@ -1,8 +1,8 @@
# Probot Stale configuration file
# Number of days of inactivity before an issue becomes stale
# 780 is approximately 2 years and 2 months
daysUntilStale: 780
# 770 is approximately 2 years and 1 month
daysUntilStale: 770
# Number of days of inactivity before a stale issue is closed
daysUntilClose: 7

View File

@ -83,8 +83,8 @@ Glossary
to the system. State module functions should be idempotent. Some
state module functions, such as :mod:`cmd.run <salt.states.cmd.run>`
are not idempotent by default but can be made idempotent with the
proper use of requisites such as :ref:```unless`` <unless-requisite>`
and :ref:```onlyif`` <onlyif-requisite>`. For more information, *see*
proper use of requisites such as :ref:`unless <unless-requisite>`
and :ref:`onlyif <onlyif-requisite>`. For more information, *see*
`wikipedia <https://en.wikipedia.org/wiki/Idempotent>`_.
Jinja

View File

@ -334,6 +334,7 @@ execution modules
publish
puppet
purefa
purefb
pushbullet
pushover_notify
pw_group

View File

@ -0,0 +1,6 @@
===================
salt.modules.purefb
===================
.. automodule:: salt.modules.purefb
:members:

View File

@ -409,7 +409,7 @@ module. This module includes several functions, each of them with their own
use. These functions include:
- :py:func:`pillar.item <salt.modules.pillar.item>` - Retrieves the value of
one or more keys from the :ref:`in-memory pillar datj <pillar-in-memory>`.
one or more keys from the :ref:`in-memory pillar data <pillar-in-memory>`.
- :py:func:`pillar.items <salt.modules.pillar.items>` - Compiles a fresh pillar
dictionary and returns it, leaving the :ref:`in-memory pillar data
<pillar-in-memory>` untouched. If pillar keys are passed to this function

View File

@ -27,6 +27,35 @@ syndic respects :conf_minion:`enable_legacy_startup_events` as well.
Deprecations
------------
Module Deprecations
===================
The ``win_update`` module has been removed. It has been replaced by ``win_wua``
module.
The ``win_wua`` module had the following changes:
- Support for the ``download_update`` function has been removed. Please use the
``download`` function instead.
- Support for the ``download_updates`` function has been removed. Please use the
``download`` function instead.
- Support for the ``install_update`` function has been removed. Please use the
``install`` function instead.
- Support for the ``install_updates`` function has been removed. Please use the
``install`` function instead.
- Support for the ``list_update`` function has been removed. Please use the
``get`` function instead.
- Support for the ``list_updates`` function has been removed. Please use the
``list`` function instead.
Pillar Deprecations
===================
The ``vault`` pillar had the following changes:
- Support for the ``profile`` argument was removed. Any options passed up until
and following the first ``path=`` are discarded.
Roster Deprecations
===================
@ -84,3 +113,5 @@ instead:
files to use the ``kubernetes.node_label_present`` function instead.
- The ``k8s.label_folder_absent`` function was removed. Please update applicable
SLS files to use the ``kubernetes.node_label_folder_absent`` function instead.
The ``win_update`` state has been removed. Please use the ``win_wua`` state instead.

View File

@ -1929,7 +1929,7 @@ def fqdns():
socket.gaierror, socket.timeout) as e:
log.error("Exception during resolving address: " + str(e))
grains['fqdns'] = list(fqdns)
grains['fqdns'] = sorted(list(fqdns))
return grains

View File

@ -2604,7 +2604,7 @@ class Minion(MinionBase):
def ping_master():
try:
def ping_timeout_handler(*_):
if not self.opts.get('auth_safemode', True):
if self.opts.get('auth_safemode', False):
log.error('** Master Ping failed. Attempting to restart minion**')
delay = self.opts.get('random_reauth_delay', 5)
log.info('delaying random_reauth_delay %ss', delay)

View File

@ -829,6 +829,7 @@ def get_instances(name, lifecycle_state="InService", health_status="Healthy",
while True:
try:
asgs = conn.get_all_groups(names=[name])
break
except boto.exception.BotoServerError as e:
if retries and e.code == 'Throttling':
log.debug('Throttled by AWS API, retrying in 5 seconds...')

View File

@ -1,750 +0,0 @@
# -*- coding: utf-8 -*-
'''
Module for running windows updates.
This module is being deprecated and will be removed in Salt Fluorine. Please use
the ``win_wua`` module instead.
:depends: - win32com
- win32con
- win32api
- pywintypes
.. versionadded:: 2014.7.0
Set windows updates to run by category. Default behavior is to install
all updates that do not require user interaction to complete.
Optionally set ``categories`` to a category of your choice to only
install certain updates. Default is to set to install all available but driver updates.
The following example will install all Security and Critical Updates,
and download but not install standard updates.
.. code-block:: bash
salt '*' win_update.install_updates categories="['Critical Updates', 'Security Updates']"
You can also specify a number of features about the update to have a
fine grain approach to specific types of updates. These are the following
features/states of updates available for configuring:
.. code-block:: text
'UI' - User interaction required, skipped by default
'downloaded' - Already downloaded, included by default
'present' - Present on computer, included by default
'installed' - Already installed, skipped by default
'reboot' - Reboot required, included by default
'hidden' - Skip hidden updates, skipped by default
'software' - Software updates, included by default
'driver' - Driver updates, included by default
The following example installs all updates that don't require a reboot:
.. code-block:: bash
salt '*' win_update.install_updates skips="[{'reboot':True}]"
Once installed Salt will return a similar output:
.. code-block:: bash
2 : Windows Server 2012 Update (KB123456)
4 : Internet Explorer Security Update (KB098765)
2 : Malware Definition Update (KB321456)
...
The number at the beginning of the line is an OperationResultCode from the Windows Update Agent,
it's enumeration is described here: https://msdn.microsoft.com/en-us/library/windows/desktop/aa387095(v=vs.85).aspx.
The result code is then followed by the update name and its KB identifier.
'''
# pylint: disable=invalid-name,missing-docstring
# Import Python libs
from __future__ import absolute_import, unicode_literals, print_function
import logging
# Import 3rd-party libs
# pylint: disable=import-error
from salt.ext import six
from salt.ext.six.moves import range # pylint: disable=no-name-in-module,redefined-builtin
try:
import win32com.client
import pythoncom
HAS_DEPENDENCIES = True
except ImportError:
HAS_DEPENDENCIES = False
# pylint: enable=import-error
# Import Salt libs
import salt.utils.platform
import salt.utils.locales
import salt.utils.versions
log = logging.getLogger(__name__)
def __virtual__():
'''
Only works on Windows systems
'''
if salt.utils.platform.is_windows() and HAS_DEPENDENCIES:
salt.utils.versions.warn_until(
'Fluorine',
'The \'win_update\' module is being deprecated and will be removed '
'in Salt {version}. Please use the \'win_wua\' module instead.'
)
return True
return (False, "Module win_update: module has failed dependencies or is not on Windows client")
def _gather_update_categories(updateCollection):
'''
this is a convenience method to gather what categories of updates are available in any update
collection it is passed. Typically though, the download_collection.
Some known categories:
Updates
Windows 7
Critical Updates
Security Updates
Update Rollups
'''
categories = []
for i in range(updateCollection.Count):
update = updateCollection.Item(i)
for j in range(update.Categories.Count):
name = update.Categories.Item(j).Name
if name not in categories:
log.debug('found category: %s', name)
categories.append(name)
return categories
class PyWinUpdater(object):
def __init__(self, categories=None, skipUI=True, skipDownloaded=False,
skipInstalled=True, skipReboot=False, skipPresent=False,
skipSoftwareUpdates=False, skipDriverUpdates=False, skipHidden=True):
log.debug('CoInitializing the pycom system')
pythoncom.CoInitialize()
self.skipUI = skipUI
self.skipDownloaded = skipDownloaded
self.skipInstalled = skipInstalled
self.skipReboot = skipReboot
self.skipPresent = skipPresent
self.skipHidden = skipHidden
self.skipSoftwareUpdates = skipSoftwareUpdates
self.skipDriverUpdates = skipDriverUpdates
# the list of categories that the user wants to be searched for.
self.categories = categories
# the list of categories that are present in the updates found.
self.foundCategories = []
# careful not to get those two confused.
log.debug('dispatching update_session to keep the session object.')
self.update_session = win32com.client.Dispatch('Microsoft.Update.Session')
log.debug('update_session got. Now creating a win_searcher to seek out the updates')
self.win_searcher = self.update_session.CreateUpdateSearcher()
# list of updates that are applicable by current settings.
self.download_collection = win32com.client.Dispatch('Microsoft.Update.UpdateColl')
# list of updates to be installed.
self.install_collection = win32com.client.Dispatch('Microsoft.Update.UpdateColl')
# the object responsible for fetching the actual downloads.
self.win_downloader = self.update_session.CreateUpdateDownloader()
self.win_downloader.Updates = self.download_collection
# the object responsible for the installing of the updates.
self.win_installer = self.update_session.CreateUpdateInstaller()
self.win_installer.Updates = self.install_collection
# the results of the download process
self.download_results = None
# the results of the installation process
self.install_results = None
# search results from CreateUpdateSearcher()
self.search_results = None
def Search(self, searchString):
try:
log.debug('beginning search of the passed string: %s', searchString)
self.search_results = self.win_searcher.Search(searchString)
log.debug('search completed successfully.')
except Exception as exc:
log.info('search for updates failed. %s', exc)
return exc
log.debug('parsing results. %s updates were found.',
self.search_results.Updates.Count)
try:
# step through the list of the updates to ensure that the updates match the
# features desired.
for update in self.search_results.Updates:
# this skipps an update if UI updates are not desired.
if update.InstallationBehavior.CanRequestUserInput:
log.debug(U'Skipped update {0} - requests user input'.format(update.title))
continue
# if this update is already downloaded, it doesn't need to be in
# the download_collection. so skipping it unless the user mandates re-download.
if self.skipDownloaded and update.IsDownloaded:
log.debug(
'Skipped update %s - already downloaded',
update.title
)
continue
# check this update's categories against the ones desired.
for category in update.Categories:
# this is a zero guard. these tests have to be in this order
# or it will error out when the user tries to search for
# updates with out specifying categories.
if self.categories is None or category.Name in self.categories:
# adds it to the list to be downloaded.
self.download_collection.Add(update)
log.debug('added update %s', update.title)
# ever update has 2 categories. this prevents the
# from being added twice.
break
log.debug('download_collection made. gathering found categories.')
# gets the categories of the updates available in this collection of updates
self.foundCategories = _gather_update_categories(self.download_collection)
log.debug('found categories: %s',
six.text_type(self.foundCategories))
return True
except Exception as exc:
log.info('parsing updates failed. %s', exc)
return exc
def AutoSearch(self):
'''
this function generates a search string. simplifying the search function while
still providing as many features as possible.
'''
search_string = ''
searchParams = []
if self.skipInstalled:
searchParams.append('IsInstalled=0')
else:
searchParams.append('IsInstalled=1')
if self.skipHidden:
searchParams.append('IsHidden=0')
else:
searchParams.append('IsHidden=1')
if self.skipReboot:
searchParams.append('RebootRequired=0')
else:
searchParams.append('RebootRequired=1')
if self.skipPresent:
searchParams.append('IsPresent=0')
else:
searchParams.append('IsPresent=1')
for i in searchParams:
search_string += '{0} and '.format(i)
if not self.skipSoftwareUpdates and not self.skipDriverUpdates:
search_string += 'Type=\'Software\' or Type=\'Driver\''
elif not self.skipSoftwareUpdates:
search_string += 'Type=\'Software\''
elif not self.skipDriverUpdates:
search_string += 'Type=\'Driver\''
else:
return False
# if there is no type, the is nothing to search.
log.debug('generated search string: %s', search_string)
return self.Search(search_string)
def Download(self):
# chase the download_collection! do the actual download process.
try:
# if the download_collection is empty. no need to download things.
if self.download_collection.Count != 0:
self.download_results = self.win_downloader.Download()
else:
log.debug('Skipped downloading, all updates were already cached.')
return True
except Exception as exc:
log.debug('failed in the downloading %s.', exc)
return exc
def Install(self):
# beat those updates into place!
try:
# this does not draw from the download_collection. important thing to know.
# the blugger is created regardless of what the download_collection has done. but it
# will only download those updates which have been downloaded and are ready.
for update in self.search_results.Updates:
if update.IsDownloaded:
self.install_collection.Add(update)
log.debug('Updates prepared. beginning installation')
except Exception as exc:
log.info('Preparing install list failed: %s', exc)
return exc
# accept eula if not accepted
try:
for update in self.search_results.Updates:
if not update.EulaAccepted:
log.debug('Accepting EULA: %s', update.Title)
update.AcceptEula()
except Exception as exc:
log.info('Accepting Eula failed: %s', exc)
return exc
# if the blugger is empty. no point it starting the install process.
if self.install_collection.Count != 0:
log.debug('Install list created, about to install')
try:
# the call to install.
self.install_results = self.win_installer.Install()
log.info('Installation of updates complete')
return True
except Exception as exc:
log.info('Installation failed: %s', exc)
return exc
else:
log.info('no new updates.')
return True
def GetInstallationResults(self):
'''
this gets results of installation process.
'''
# if the blugger is empty, the results are nil.
log.debug('blugger has {0} updates in it'.format(self.install_collection.Count))
if self.install_collection.Count == 0:
return {}
updates = []
log.debug('repairing update list')
for i in range(self.install_collection.Count):
# this gets the result from install_results, but the title comes from the update
# collection install_collection.
updates.append('{0}: {1}'.format(
self.install_results.GetUpdateResult(i).ResultCode,
self.install_collection.Item(i).Title))
log.debug('Update results enumerated, now making a library to pass back')
results = {}
# translates the list of update results into a library that salt expects.
for i, update in enumerate(updates):
results['update {0}'.format(i)] = update
log.debug('Update information complied. returning')
return results
def GetInstallationResultsPretty(self):
'''
converts the installation results into a pretty print.
'''
updates = self.GetInstallationResults()
ret = 'The following are the updates and their return codes.\n'
for i in updates:
ret += '\t{0}\n'.format(updates[i])
return ret
def GetDownloadResults(self):
updates = []
for i in range(self.download_collection.Count):
updates.append('{0}: {1}'.format(
six.text_type(self.download_results.GetUpdateResult(i).ResultCode),
six.text_type(self.download_collection.Item(i).Title)))
results = {}
for i, update in enumerate(updates):
results['update {0}'.format(i)] = update
return results
def GetSearchResultsVerbose(self):
updates = []
log.debug('parsing results. %s updates were found.',
self.download_collection.count)
for update in self.download_collection:
if update.InstallationBehavior.CanRequestUserInput:
log.debug('Skipped update %s', update.title)
continue
# More fields can be added from https://msdn.microsoft.com/en-us/library/windows/desktop/aa386099(v=vs.85).aspx
update_com_fields = ['Categories', 'Deadline', 'Description',
'Identity', 'IsMandatory',
'KBArticleIDs', 'MaxDownloadSize', 'MinDownloadSize',
'MoreInfoUrls', 'MsrcSeverity', 'ReleaseNotes',
'SecurityBulletinIDs', 'SupportUrl', 'Title']
simple_enums = ['KBArticleIDs', 'MoreInfoUrls', 'SecurityBulletinIDs']
# update_dict = {k: getattr(update, k) for k in update_com_fields}
update_dict = {}
for f in update_com_fields:
v = getattr(update, f)
if not any([isinstance(v, bool), isinstance(v, six.string_types)]):
# Fields that require special evaluation.
if f in simple_enums:
v = [x for x in v]
elif f == 'Categories':
v = [{'Name': cat.Name, 'Description': cat.Description} for cat in v]
elif f == 'Deadline':
# Deadline will be useful and should be added.
# However, until it can be tested with a date object
# as returned by the COM, it is unclear how to
# handle this field.
continue
elif f == 'Identity':
v = {'RevisionNumber': v.RevisionNumber,
'UpdateID': v.UpdateID}
update_dict[f] = v
updates.append(update_dict)
log.debug('added update %s', update.title)
return updates
def GetSearchResults(self, fields=None):
"""Reduce full updates information to the most important information."""
updates_verbose = self.GetSearchResultsVerbose()
if fields is not None:
updates = [dict((k, v) for k, v in update.items() if k in fields)
for update in updates_verbose]
return updates
# Return list of titles.
return [update['Title'] for update in updates_verbose]
def SetCategories(self, categories):
self.categories = categories
def GetCategories(self):
return self.categories
def GetAvailableCategories(self):
return self.foundCategories
def SetSkips(self, skips):
if skips:
for i in skips:
value = i[next(six.iterkeys(i))]
skip = next(six.iterkeys(i))
self.SetSkip(skip, value)
log.debug('was asked to set %s to %s', skip, value)
def SetSkip(self, skip, state):
if skip == 'UI':
self.skipUI = state
elif skip == 'downloaded':
self.skipDownloaded = state
elif skip == 'installed':
self.skipInstalled = state
elif skip == 'reboot':
self.skipReboot = state
elif skip == 'present':
self.skipPresent = state
elif skip == 'hidden':
self.skipHidden = state
elif skip == 'software':
self.skipSoftwareUpdates = state
elif skip == 'driver':
self.skipDriverUpdates = state
log.debug('new search state: \n\tUI: %s\n\tDownload: %s\n\tInstalled: %s\n\treboot :%s\n\tPresent: %s\n\thidden: %s\n\tsoftware: %s\n\tdriver: %s',
self.skipUI, self.skipDownloaded, self.skipInstalled, self.skipReboot,
self.skipPresent, self.skipHidden, self.skipSoftwareUpdates, self.skipDriverUpdates)
def __str__(self):
results = 'There are {0} updates, by category there are:\n'.format(
self.download_collection.count)
for category in self.foundCategories:
count = 0
for update in self.download_collection:
for cat in update.Categories:
if category == cat.Name:
count += 1
results += '\t{0}: {1}\n'.format(category, count)
return results
def _search(quidditch, retries=5):
'''
a wrapper method for the pywinupdater class. I might move this into the class, but right now,
that is to much for one class I think.
'''
passed = False
clean = True
comment = ''
while not passed:
log.debug('Searching. tries left: %s', retries)
# let the updater make its own search string. MORE POWER this way.
passed = quidditch.AutoSearch()
log.debug('Done searching: %s', passed)
if isinstance(passed, Exception):
clean = False
comment += 'Failed in the seeking/parsing process:\n\t\t{0}\n'.format(passed)
retries -= 1
if retries:
comment += '{0} tries to go. retrying\n'.format(str(retries))
else:
comment += 'out of retries. this update round failed.\n'
return (comment, True, retries)
passed = False
if clean:
# bragging rights.
comment += 'Search was done without error.\n'
return (comment, True, retries)
def _download(quidditch, retries=5):
'''
another wrapper method.
'''
passed = False
clean = True
comment = ''
while not passed:
log.debug('Downloading. tries left: %s', retries)
passed = quidditch.Download()
log.debug('Done downloading: %s', passed)
if isinstance(passed, Exception):
clean = False
comment += 'Failed while trying to download updates:\n\t\t{0}\n'.format(str(passed))
retries -= 1
if retries:
comment += '{0} tries to go. retrying\n'.format(str(retries))
passed = False
else:
comment += 'out of retries. this update round failed.\n'
return (comment, False, retries)
if clean:
comment += 'Download was done without error.\n'
return (comment, True, retries)
def _install(quidditch, retries=5):
'''
and the last wrapper method. keeping things simple.
'''
passed = False
clean = True
comment = ''
while not passed:
log.debug('download_collection is this long: %s',
quidditch.install_collection.Count)
log.debug('Installing. tries left: %s', retries)
passed = quidditch.Install()
log.info('Done installing: %s', passed)
if isinstance(passed, Exception):
clean = False
comment += 'Failed while trying to install the updates.\n\t\t{0}\n'.format(str(passed))
retries -= 1
if retries:
comment += '{0} tries to go. retrying\n'.format(str(retries))
passed = False
else:
comment += 'out of retries. this update round failed.\n'
return (comment, False, retries)
if clean:
comment += 'Install was done without error.\n'
return (comment, True, retries)
# this is where the actual functions available to salt begin.
def list_updates(verbose=False, fields=None, skips=None, retries=5, categories=None):
'''
Returns a summary of available updates, grouped into their non-mutually
exclusive categories.
verbose
Return full set of results, including several fields from the COM.
fields
Return a list of specific fields for each update. The optional
values here are those at the root level of the verbose list. This
is superseded by the verbose option.
retries
Number of retries to make before giving up. This is total, not per
step.
categories
Specify the categories to list. Must be passed as a list.
.. code-block:: bash
salt '*' win_update.list_updates categories="['Updates']"
Categories include, but are not limited to, the following:
* Updates
* Windows 7
* Critical Updates
* Security Updates
* Update Rollups
CLI Examples:
.. code-block:: bash
# Normal Usage
salt '*' win_update.list_updates
# Specific Fields
salt '*' win_update.list_updates fields="['Title', 'Description']"
# List all critical updates list in verbose detail
salt '*' win_update.list_updates categories="['Critical Updates']" verbose=True
'''
log.debug('categories to search for are: %s', categories)
updates = PyWinUpdater()
if categories:
updates.SetCategories(categories)
updates.SetSkips(skips)
# this is where we be seeking the things! yar!
comment, passed, retries = _search(updates, retries)
if not passed:
return (comment, str(passed))
log.debug('verbose: %s', verbose)
if verbose:
return updates.GetSearchResultsVerbose()
return updates.GetSearchResults(fields=fields)
def download_updates(skips=None, retries=5, categories=None):
'''
Downloads all available updates, skipping those that require user
interaction.
Various aspects of the updates can be included or excluded. this feature is
still in development.
retries
Number of retries to make before giving up. This is total, not per
step.
categories
Specify the categories to update. Must be passed as a list.
.. code-block:: bash
salt '*' win_update.download_updates categories="['Updates']"
Categories include the following:
* Updates
* Windows 7
* Critical Updates
* Security Updates
* Update Rollups
CLI Examples:
.. code-block:: bash
# Normal Usage
salt '*' win_update.download_updates
# Download critical updates only
salt '*' win_update.download_updates categories="['Critical Updates']"
'''
log.debug('categories to search for are: %s', categories)
quidditch = PyWinUpdater(skipDownloaded=True)
quidditch.SetCategories(categories)
quidditch.SetSkips(skips)
# this is where we be seeking the things! yar!
comment, passed, retries = _search(quidditch, retries)
if not passed:
return (comment, str(passed))
# this is where we get all the things! i.e. download updates.
comment, passed, retries = _download(quidditch, retries)
if not passed:
return (comment, str(passed))
try:
comment = quidditch.GetDownloadResults()
except Exception as exc:
comment = 'could not get results, but updates were installed. {0}'.format(exc)
return 'Windows is up to date. \n{0}'.format(comment)
def install_updates(skips=None, retries=5, categories=None):
'''
Downloads and installs all available updates, skipping those that require
user interaction.
Add ``cached`` to only install those updates which have already been downloaded.
you can set the maximum number of retries to ``n`` in the search process by
adding: ``retries=n``
various aspects of the updates can be included or excluded. This function is
still under development.
retries
Number of retries to make before giving up. This is total, not per
step.
categories
Specify the categories to install. Must be passed as a list.
.. code-block:: bash
salt '*' win_update.install_updates categories="['Updates']"
Categories include the following:
* Updates
* Windows 7
* Critical Updates
* Security Updates
* Update Rollups
CLI Examples:
.. code-block:: bash
# Normal Usage
salt '*' win_update.install_updates
# Install all critical updates
salt '*' win_update.install_updates categories="['Critical Updates']"
'''
log.debug('categories to search for are: %s', categories)
quidditch = PyWinUpdater()
quidditch.SetCategories(categories)
quidditch.SetSkips(skips)
# this is where we be seeking the things! yar!
comment, passed, retries = _search(quidditch, retries)
if not passed:
return (comment, str(passed))
# this is where we get all the things! i.e. download updates.
comment, passed, retries = _download(quidditch, retries)
if not passed:
return (comment, str(passed))
# this is where we put things in their place!
comment, passed, retries = _install(quidditch, retries)
if not passed:
return (comment, str(passed))
try:
comment = quidditch.GetInstallationResultsPretty()
except Exception as exc:
comment = 'Could not get results, but updates were installed. {0}'.format(exc)
return 'Windows is up to date. \n{0}'.format(comment)

View File

@ -56,7 +56,6 @@ import logging
# Import Salt libs
import salt.utils.platform
import salt.utils.versions
import salt.utils.win_update
from salt.exceptions import CommandExecutionError
@ -228,87 +227,6 @@ def available(software=True,
return updates.summary() if summary else updates.list()
def list_update(name, download=False, install=False):
'''
.. deprecated:: 2017.7.0
Use :func:`get` instead
Returns details for all updates that match the search criteria
Args:
name (str):
The name of the update you're searching for. This can be the GUID, a
KB number, or any part of the name of the update. GUIDs and KBs are
preferred. Run ``list_updates`` to get the GUID for the update
you're looking for.
download (bool):
Download the update returned by this function. Run this function
first to see if the update exists, then set ``download=True`` to
download the update.
install (bool):
Install the update returned by this function. Run this function
first to see if the update exists, then set ``install=True`` to
install the update.
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.
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>',
...]
}
}
CLI Examples:
.. code-block:: bash
# Recommended Usage using GUID without braces
# Use this to find the status of a specific update
salt '*' win_wua.list_update 12345678-abcd-1234-abcd-1234567890ab
# Use the following if you don't know the GUID:
# Using a KB number (could possibly return multiple results)
# Not all updates have an associated KB
salt '*' win_wua.list_update KB3030298
# Using part or all of the name of the update
# Could possibly return multiple results
# Not all updates have an associated KB
salt '*' win_wua.list_update 'Microsoft Camera Codec Pack'
'''
salt.utils.versions.warn_until(
'Fluorine',
'This function is replaced by \'get\' as of Salt 2017.7.0. This '
'warning will be removed in Salt Fluorine.')
return get(name, download, install)
def get(name, download=False, install=False):
'''
.. versionadded:: 2017.7.0
@ -401,142 +319,6 @@ def get(name, download=False, install=False):
return ret if ret else updates.list()
def list_updates(software=True,
drivers=False,
summary=False,
skip_installed=True,
categories=None,
severities=None,
download=False,
install=False):
'''
.. deprecated:: 2017.7.0
Use :func:`list` instead
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:
software (bool):
Include software updates in the results (default is True)
drivers (bool):
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.
skip_installed (bool):
Skip installed updates in the results (default is False)
download (bool):
(Overrides reporting functionality) Download the list of updates
returned by this function. Run this function first with
``download=False`` to see what will be downloaded, then set
``download=True`` to download the updates.
install (bool):
(Overrides reporting functionality) Install the list of updates
returned by this function. Run this function first with
``install=False`` to see what will be installed, then set
``install=True`` to install the updates.
categories (list):
Specify the categories to list. Must be passed as a list. All
categories returned by default.
Categories include the following:
* Critical Updates
* Definition Updates
* Drivers (make sure you set drivers=True)
* Feature Packs
* Security Updates
* Update Rollups
* Updates
* Update Rollups
* Windows 7
* Windows 8.1
* Windows 8.1 drivers
* Windows 8.1 and later drivers
* Windows Defender
severities (list):
Specify the severities to include. Must be passed as a list. All
severities returned by default.
Severities include the following:
* Critical
* Important
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>',
...]
}
}
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>,
... }
}
CLI Examples:
.. code-block:: bash
# Normal Usage (list all software updates)
salt '*' win_wua.list_updates
# List all updates with categories of Critical Updates and Drivers
salt '*' win_wua.list_updates categories=['Critical Updates','Drivers']
# List all Critical Security Updates
salt '*' win_wua.list_updates categories=['Security Updates'] severities=['Critical']
# List all updates with a severity of Critical
salt '*' win_wua.list_updates severities=['Critical']
# A summary of all available updates
salt '*' win_wua.list_updates summary=True
# A summary of all Feature Packs and Windows 8.1 Updates
salt '*' win_wua.list_updates categories=['Feature Packs','Windows 8.1'] summary=True
'''
salt.utils.versions.warn_until(
'Fluorine',
'This function is replaced by \'list\' as of Salt 2017.7.0. This '
'warning will be removed in Salt Fluorine.')
return list(software, drivers, summary, skip_installed, categories,
severities, download, install)
def list(software=True,
drivers=False,
summary=False,
@ -688,74 +470,6 @@ def list(software=True,
return ret
def download_update(name):
'''
.. deprecated:: 2017.7.0
Use :func:`download` instead
Downloads a single update.
Args:
name (str):
The name of the update to download. This can be a GUID, a KB number,
or any part of the name. To ensure a single item is matched the GUID
is preferred.
.. note::
If more than one result is returned an error will be raised.
Returns:
dict: A dictionary containing the results of the download
CLI Examples:
.. code-block:: bash
salt '*' win_wua.download_update 12345678-abcd-1234-abcd-1234567890ab
salt '*' win_wua.download_update KB12312321
'''
salt.utils.versions.warn_until(
'Fluorine',
'This function is replaced by \'download\' as of Salt 2017.7.0. This '
'warning will be removed in Salt Fluorine.')
return download(name)
def download_updates(names):
'''
.. deprecated:: 2017.7.0
Use :func:`download` instead
Downloads updates that match the list of passed identifiers. It's easier to
use this function by using list_updates and setting install=True.
Args:
names (list):
A list of updates to download. This can be any combination of GUIDs,
KB numbers, or names. GUIDs or KBs are preferred.
Returns:
dict: A dictionary containing the details about the downloaded updates
CLI Examples:
.. code-block:: bash
# Normal Usage
salt '*' win_wua.download_updates guid=['12345678-abcd-1234-abcd-1234567890ab', 'KB2131233']
'''
salt.utils.versions.warn_until(
'Fluorine',
'This function is replaced by \'download\' as of Salt 2017.7.0. This '
'warning will be removed in Salt Fluorine.')
return download(names)
def download(names):
'''
.. versionadded:: 2017.7.0
@ -808,73 +522,6 @@ def download(names):
return wua.download(updates)
def install_update(name):
'''
.. deprecated:: 2017.7.0
Use :func:`install` instead
Installs a single update
Args:
name (str): The name of the update to install. This can be a GUID, a KB
number, or any part of the name. To ensure a single item is matched the
GUID is preferred.
.. note::
If no results or more than one result is returned an error will be
raised.
Returns:
dict: A dictionary containing the results of the install
CLI Examples:
.. code-block:: bash
salt '*' win_wua.install_update 12345678-abcd-1234-abcd-1234567890ab
salt '*' win_wua.install_update KB12312231
'''
salt.utils.versions.warn_until(
'Fluorine',
'This function is replaced by \'install\' as of Salt 2017.7.0. This '
'warning will be removed in Salt Fluorine.')
return install(name)
def install_updates(names):
'''
.. deprecated:: 2017.7.0
Use :func:`install` instead
Installs updates that match the list of identifiers. It may be easier to use
the list_updates function and set install=True.
Args:
names (list): A list of updates to install. This can be any combination
of GUIDs, KB numbers, or names. GUIDs or KBs are preferred.
Returns:
dict: A dictionary containing the details about the installed updates
CLI Examples:
.. code-block:: bash
# Normal Usage
salt '*' win_wua.install_updates guid=['12345678-abcd-1234-abcd-1234567890ab', 'KB12323211']
'''
salt.utils.versions.warn_until(
'Fluorine',
'This function is replaced by \'install\' as of Salt 2017.7.0. This '
'warning will be removed in Salt Fluorine.')
return install(names)
def install(names):
'''
.. versionadded:: 2017.7.0

View File

@ -58,8 +58,9 @@ The ``it-admins`` configuration below returns the Pillar ``it-admins`` by:
- filtering for:
- members of the group ``it-admins``
- objects with ``objectclass=user``
- returning the data of users (``mode: map``), where each user is a dictionary
containing the configured string or list attributes.
- returning the data of users (``mode: map``) as a list of dictionaries, where
each user is a dictionary containing the configured string or list attributes,
and the user dictionaries are combined to a list.
**Configuration:**
@ -106,6 +107,118 @@ The ``it-admins`` configuration below returns the Pillar ``it-admins`` by:
- cn=team02,ou=groups,dc=company
Dict Mode
---------
The ``it-admins`` configuration below returns the Pillar ``it-admins`` by:
- filtering for:
- members of the group ``it-admins``
- objects with ``objectclass=user``
- returning the data of users (``mode: dict``), where each user is a dictionary
containing the configured string or list attributes, and the user dictionaries
are combined to a dictionary using the value of the LDAP attribute defined in the
``dict_key_attr`` configuration option (defaults to ``dn`` or ``distinguishedName``)
as the key.
**Configuration:**
.. code-block:: yaml
salt-users:
server: ldap.company.tld
port: 389
tls: true
dn: 'dc=company,dc=tld'
binddn: 'cn=salt-pillars,ou=users,dc=company,dc=tld'
bindpw: bi7ieBai5Ano
referrals: false
anonymous: false
mode: dict
dn: 'ou=users,dc=company,dc=tld'
filter: '(&(memberof=cn=it-admins,ou=groups,dc=company,dc=tld)(objectclass=user))'
attrs:
- cn
- displayName
- givenName
- sn
lists:
- memberOf
**Result:**
.. code-block:: yaml
salt-users:
cn=johndoe,ou=users,dc=company,dc=tld:
- cn: cn=johndoe,ou=users,dc=company,dc=tld
displayName: John Doe
givenName: John
sn: Doe
memberOf:
- cn=it-admins,ou=groups,dc=company,dc=tld
- cn=team01,ou=groups,dc=company
cn=janedoe,ou=users,dc=company,dc=tld:
- cn: cn=janedoe,ou=users,dc=company,dc=tld
displayName: Jane Doe
givenName: Jane
sn: Doe
memberOf:
- cn=it-admins,ou=groups,dc=company,dc=tld
- cn=team02,ou=groups,dc=company
**Configuration:**
.. code-block:: yaml
salt-users:
server: ldap.company.tld
port: 389
tls: true
dn: 'dc=company,dc=tld'
binddn: 'cn=salt-pillars,ou=users,dc=company,dc=tld'
bindpw: bi7ieBai5Ano
referrals: false
anonymous: false
mode: dict
dict_key_attr: displayName
dn: 'ou=users,dc=company,dc=tld'
filter: '(&(memberof=cn=it-admins,ou=groups,dc=company,dc=tld)(objectclass=user))'
attrs:
- dn
- cn
- givenName
- sn
lists:
- memberOf
**Result:**
.. code-block:: yaml
salt-users:
John Doe:
- dn: cn=johndoe,ou=users,dc=company,dc=tld
cn: cn=johndoe,ou=users,dc=company,dc=tld
givenName: John
sn: Doe
memberOf:
- cn=it-admins,ou=groups,dc=company,dc=tld
- cn=team01,ou=groups,dc=company
Jane Doe:
- dn: cn=janedoe,ou=users,dc=company,dc=tld
cn: cn=janedoe,ou=users,dc=company,dc=tld
givenName: Jane
sn: Doe
memberOf:
- cn=it-admins,ou=groups,dc=company,dc=tld
- cn=team02,ou=groups,dc=company
List Mode
---------
@ -193,6 +306,7 @@ def _result_to_dict(data, result, conf, source):
'''
attrs = _config('attrs', conf) or []
lists = _config('lists', conf) or []
dict_key_attr = _config('dict_key_attr', conf) or 'dn'
# TODO:
# deprecate the default 'mode: split' and make the more
# straightforward 'mode: dict' the new default
@ -213,6 +327,30 @@ def _result_to_dict(data, result, conf, source):
if key in lists:
ret[key] = record.get(key)
data[source].append(ret)
elif mode == 'dict':
data[source] = {}
for record in result:
ret = {}
distinguished_name = record[0]
log.debug('dn: %s', distinguished_name)
if 'dn' in attrs or 'distinguishedName' in attrs:
ret['dn'] = distinguished_name
record = record[1]
log.debug('record: %s', record)
for key in record:
if key in attrs:
for item in record.get(key):
ret[key] = item
if key in lists:
ret[key] = record.get(key)
if dict_key_attr in ['dn', 'distinguishedName']:
dict_key = distinguished_name
else:
dict_key = ','.join(sorted(record.get(dict_key_attr, [])))
try:
data[source][dict_key].append(ret)
except KeyError:
data[source][dict_key] = [ret]
elif mode == 'split':
for key in result[0][1]:
if key in attrs:
@ -250,7 +388,8 @@ def _do_search(conf):
scope = _config('scope', conf)
_lists = _config('lists', conf) or []
_attrs = _config('attrs', conf) or []
attrs = _lists + _attrs
_dict_key_attr = _config('dict_key_attr', conf) or 'dn'
attrs = _lists + _attrs + [_dict_key_attr]
if not attrs:
attrs = None
# Perform the search

View File

@ -52,9 +52,6 @@ Multiple Vault sources may also be used:
from __future__ import absolute_import, print_function, unicode_literals
import logging
# Import Salt libs
import salt.utils.versions
log = logging.getLogger(__name__)
__func_alias__ = {
@ -77,12 +74,6 @@ def ext_pillar(minion_id, # pylint: disable=W0613
'''
comps = conf.split()
if not comps[0].startswith('path='):
salt.utils.versions.warn_until(
'Fluorine',
'The \'profile\' argument has been deprecated. Any parts up until '
'and following the first "path=" are discarded'
)
paths = [comp for comp in comps if comp.startswith('path=')]
if not paths:
log.error('"%s" is not a valid Vault ext_pillar config', conf)

View File

@ -1,587 +0,0 @@
# -*- coding: utf-8 -*-
'''
Management of the windows update agent
======================================
This module is being deprecated and will be removed in Salt Fluorine. Please use
the ``win_wua`` state module instead.
.. versionadded:: 2014.7.0
Set windows updates to run by category. Default behavior is to install
all updates that do not require user interaction to complete.
Optionally set ``category`` to a category of your choice to only
install certain updates. Default is to set to install all available updates.
The following example will install all Security and Critical Updates,
and download but not install standard updates.
.. code-block:: yaml
updates:
win_update.installed:
- categories:
- 'Critical Updates'
- 'Security Updates'
- skips:
- downloaded
win_update.downloaded:
- categories:
- 'Updates'
- skips:
- downloaded
You can also specify a number of features about the update to have a
fine grain approach to specific types of updates. These are the following
features/states of updates available for configuring:
.. code-block:: text
'UI' - User interaction required, skipped by default
'downloaded' - Already downloaded, included by default
'present' - Present on computer, skipped by default
'installed' - Already installed, skipped by default
'reboot' - Reboot required, included by default
'hidden' - Skip updates that have been hidden, skipped by default
'software' - Software updates, included by default
'driver' - driver updates, included by default
The following example installs all driver updates that don't require a reboot:
.. code-block:: yaml
gryffindor:
win_update.installed:
- skips:
- driver: True
- software: False
- reboot: False
To just update your windows machine, add this your sls:
.. code-block:: yaml
updates:
win_update.installed
'''
# Import Python libs
from __future__ import absolute_import, unicode_literals, print_function
import logging
# Import 3rd-party libs
# pylint: disable=import-error
from salt.ext import six
from salt.ext.six.moves import range # pylint: disable=redefined-builtin
try:
import win32com.client
import pythoncom
HAS_DEPENDENCIES = True
except ImportError:
HAS_DEPENDENCIES = False
# pylint: enable=import-error
# Import Salt libs
import salt.utils.platform
import salt.utils.versions
log = logging.getLogger(__name__)
def __virtual__():
'''
Only works on Windows systems
'''
if salt.utils.platform.is_windows() and HAS_DEPENDENCIES:
return True
return False
def _gather_update_categories(updateCollection):
'''
this is a convenience method to gather what categories of updates are available in any update
collection it is passed. Typically though, the download_collection.
Some known categories:
Updates
Windows 7
Critical Updates
Security Updates
Update Rollups
'''
categories = []
for i in range(updateCollection.Count):
update = updateCollection.Item(i)
for j in range(update.Categories.Count):
name = update.Categories.Item(j).Name
if name not in categories:
log.debug('found category: {0}'.format(name))
categories.append(name)
return categories
class PyWinUpdater(object):
def __init__(self, categories=None, skipUI=True, skipDownloaded=False,
skipInstalled=True, skipReboot=False, skipPresent=False,
skipSoftwareUpdates=False, skipDriverUpdates=False, skipHidden=True):
log.debug('CoInitializing the pycom system')
pythoncom.CoInitialize()
# pylint: disable=invalid-name
self.skipUI = skipUI
self.skipDownloaded = skipDownloaded
self.skipInstalled = skipInstalled
self.skipReboot = skipReboot
self.skipPresent = skipPresent
self.skipHidden = skipHidden
self.skipSoftwareUpdates = skipSoftwareUpdates
self.skipDriverUpdates = skipDriverUpdates
self.categories = categories
self.foundCategories = None
# pylint: enable=invalid-name
log.debug('dispatching update_session to keep the session object.')
self.update_session = win32com.client.Dispatch('Microsoft.Update.Session')
log.debug('update_session got. Now creating a win_searcher to seek out the updates')
self.win_searcher = self.update_session.CreateUpdateSearcher()
# list of updates that are applicable by current settings.
self.download_collection = win32com.client.Dispatch('Microsoft.Update.UpdateColl')
# list of updates to be installed.
self.install_collection = win32com.client.Dispatch('Microsoft.Update.UpdateColl')
# the object responsible for fetching the actual downloads.
self.win_downloader = self.update_session.CreateUpdateDownloader()
self.win_downloader.Updates = self.download_collection
# the object responsible for the installing of the updates.
self.win_installer = self.update_session.CreateUpdateInstaller()
self.win_installer.Updates = self.install_collection
# the results of the download process
self.download_results = None
# the results of the installation process
self.install_results = None
def Search(self, searchString):
try:
log.debug('beginning search of the passed string: %s',
searchString)
self.search_results = self.win_searcher.Search(searchString)
log.debug('search completed successfully.')
except Exception as exc:
log.info('search for updates failed. %s', exc)
return exc
log.debug('parsing results. %s updates were found.',
self.search_results.Updates.Count)
try:
for update in self.search_results.Updates:
if update.InstallationBehavior.CanRequestUserInput:
log.debug('Skipped update %s', update.title)
continue
for category in update.Categories:
if self.skipDownloaded and update.IsDownloaded:
continue
if self.categories is None or category.Name in self.categories:
self.download_collection.Add(update)
log.debug('added update %s', update.title)
self.foundCategories = _gather_update_categories(self.download_collection)
return True
except Exception as exc:
log.info('parsing updates failed. %s', exc)
return exc
def AutoSearch(self):
search_string = ''
searchParams = []
if self.skipInstalled:
searchParams.append('IsInstalled=0')
else:
searchParams.append('IsInstalled=1')
if self.skipHidden:
searchParams.append('IsHidden=0')
else:
searchParams.append('IsHidden=1')
if self.skipReboot:
searchParams.append('RebootRequired=0')
else:
searchParams.append('RebootRequired=1')
if self.skipPresent:
searchParams.append('IsPresent=0')
else:
searchParams.append('IsPresent=1')
if len(searchParams) > 1:
for i in searchParams:
search_string += '{0} and '.format(i)
else:
search_string += '{0} and '.format(searchParams[1])
if not self.skipSoftwareUpdates and not self.skipDriverUpdates:
search_string += 'Type=\'Software\' or Type=\'Driver\''
elif not self.skipSoftwareUpdates:
search_string += 'Type=\'Software\''
elif not self.skipDriverUpdates:
search_string += 'Type=\'Driver\''
else:
return False
# if there is no type, the is nothing to search.
log.debug('generated search string: %s', search_string)
return self.Search(search_string)
def Download(self):
try:
if self.download_collection.Count != 0:
self.download_results = self.win_downloader.Download()
else:
log.debug('Skipped downloading, all updates were already cached.')
return True
except Exception as exc:
log.debug('failed in the downloading %s.', exc)
return exc
def Install(self):
try:
for update in self.search_results.Updates:
if update.IsDownloaded:
self.install_collection.Add(update)
log.debug('Updates prepared. beginning installation')
except Exception as exc:
log.info('Preparing install list failed: %s', exc)
return exc
# accept eula if not accepted
try:
for update in self.search_results.Updates:
if not update.EulaAccepted:
log.debug('Accepting EULA: %s', update.Title)
update.AcceptEula()
except Exception as exc:
log.info('Accepting Eula failed: %s', exc)
return exc
if self.install_collection.Count != 0:
log.debug('Install list created, about to install')
updates = []
try:
self.install_results = self.win_installer.Install()
log.info('Installation of updates complete')
return True
except Exception as exc:
log.info('Installation failed: %s', exc)
return exc
else:
log.info('no new updates.')
return True
def GetInstallationResults(self):
log.debug('bluger has %s updates in it', self.install_collection.Count)
updates = []
if self.install_collection.Count == 0:
return {}
for i in range(self.install_collection.Count):
updates.append('{0}: {1}'.format(
self.install_results.GetUpdateResult(i).ResultCode,
self.install_collection.Item(i).Title))
log.debug('Update results enumerated, now making a list to pass back')
results = {}
for i, update in enumerate(updates):
results['update {0}'.format(i)] = update
log.debug('Update information complied. returning')
return results
def GetDownloadResults(self):
updates = []
for i in range(self.download_collection.Count):
updates.append('{0}: {1}'.format(
self.download_results.GetUpdateResult(i).ResultCode,
self.download_collection.Item(i).Title))
results = {}
for i, update in enumerate(updates):
results['update {0}'.format(i)] = update
return results
def SetCategories(self, categories):
self.categories = categories
def GetCategories(self):
return self.categories
def GetAvailableCategories(self):
return self.foundCategories
def SetSkips(self, skips):
if skips:
for i in skips:
value = i[next(six.iterkeys(i))]
skip = next(six.iterkeys(i))
self.SetSkip(skip, value)
log.debug('was asked to set %s to %s', skip, value)
def SetSkip(self, skip, state):
if skip == 'UI':
self.skipUI = state
elif skip == 'downloaded':
self.skipDownloaded = state
elif skip == 'installed':
self.skipInstalled = state
elif skip == 'reboot':
self.skipReboot = state
elif skip == 'present':
self.skipPresent = state
elif skip == 'hidden':
self.skipHidden = state
elif skip == 'software':
self.skipSoftwareUpdates = state
elif skip == 'driver':
self.skipDriverUpdates = state
log.debug('new search state: \n\tUI: %s\n\tDownload: %s\n'
'\tInstalled: %s\n\treboot :%s\n\tPresent: %s\n'
'\thidden: %s\n\tsoftware: %s\n\tdriver: %s',
self.skipUI, self.skipDownloaded, self.skipInstalled,
self.skipReboot, self.skipPresent, self.skipHidden,
self.skipSoftwareUpdates, self.skipDriverUpdates)
def _search(win_updater, retries=5):
passed = False
clean = True
comment = ''
while not passed:
log.debug('Searching. tries left: %s', retries)
passed = win_updater.AutoSearch()
log.debug('Done searching: %s', passed)
if isinstance(passed, Exception):
clean = False
comment += 'Failed in the seeking/parsing process:\n\t\t{0}\n'.format(passed)
retries -= 1
if retries:
comment += '{0} tries to go. retrying\n'.format(retries)
passed = False
else:
comment += 'out of retries. this update round failed.\n'
return (comment, True, retries)
passed = False
if clean:
comment += 'Search was done without error.\n'
return (comment, True, retries)
def _download(win_updater, retries=5):
passed = False
clean = True
comment = ''
while not passed:
log.debug('Downloading. tries left: %s', retries)
passed = win_updater.Download()
log.debug('Done downloading: %s', passed)
if isinstance(passed, Exception):
clean = False
comment += 'Failed while trying to download updates:\n\t\t{0}\n'.format(passed)
retries -= 1
if retries:
comment += '{0} tries to go. retrying\n'.format(retries)
passed = False
else:
comment += 'out of retries. this update round failed.\n'
return (comment, False, retries)
if clean:
comment += 'Download was done without error.\n'
return (comment, True, retries)
def _install(win_updater, retries=5):
passed = False
clean = True
comment = ''
while not passed:
log.debug('download_collection is this long: %s',
win_updater.install_collection.Count)
log.debug('Installing. tries left: %s', retries)
passed = win_updater.Install()
log.info('Done installing: %s', passed)
if isinstance(passed, Exception):
clean = False
comment += 'Failed while trying to install the updates.\n\t\t{0}\n'.format(passed)
retries -= 1
if retries:
comment += '{0} tries to go. retrying\n'.format(retries)
passed = False
else:
comment += 'out of retries. this update round failed.\n'
return (comment, False, retries)
if clean:
comment += 'Install was done without error.\n'
return (comment, True, retries)
def installed(name, categories=None, skips=None, retries=10):
'''
Install specified windows updates.
name:
if categories is left empty, it will be assumed that you are passing the category option
through the name. These are separate because you can only have one name, but can have
multiple categories.
categories:
the list of categories to be downloaded. These are simply strings in the update's
information, so there is no enumeration of the categories available. Some known categories:
.. code-block:: text
Updates
Windows 7
Critical Updates
Security Updates
Update Rollups
skips:
a list of features of the updates to cull by. Available features:
.. code-block:: text
'UI' - User interaction required, skipped by default
'downloaded' - Already downloaded, skipped by default (downloading)
'present' - Present on computer, included by default (installing)
'installed' - Already installed, skipped by default
'reboot' - Reboot required, included by default
'hidden' - skip those updates that have been hidden.
'software' - Software updates, included by default
'driver' - driver updates, skipped by default
retries
Number of retries to make before giving up. This is total, not per
step.
'''
ret = {'name': name,
'result': True,
'changes': {},
'comment': ''}
deprecation_msg = 'The \'win_update\' module is deprecated, and will be ' \
'removed in Salt Fluorine. Please use the \'win_wua\' ' \
'module instead.'
salt.utils.versions.warn_until('Fluorine', deprecation_msg)
ret.setdefault('warnings', []).append(deprecation_msg)
if not categories:
categories = [name]
log.debug('categories to search for are: %s', categories)
win_updater = PyWinUpdater()
win_updater.SetCategories(categories)
win_updater.SetSkips(skips)
# this is where we be seeking the things! yar!
comment, passed, retries = _search(win_updater, retries)
ret['comment'] += comment
if not passed:
ret['result'] = False
return ret
# this is where we get all the things! i.e. download updates.
comment, passed, retries = _download(win_updater, retries)
ret['comment'] += comment
if not passed:
ret['result'] = False
return ret
# this is where we put things in their place!
comment, passed, retries = _install(win_updater, retries)
ret['comment'] += comment
if not passed:
ret['result'] = False
return ret
try:
ret['changes'] = win_updater.GetInstallationResults()
except Exception:
ret['comment'] += 'could not get results, but updates were installed.'
return ret
def downloaded(name, categories=None, skips=None, retries=10):
'''
Cache updates for later install.
name:
if categories is left empty, it will be assumed that you are passing the category option
through the name. These are separate because you can only have one name, but can have
multiple categories.
categories:
the list of categories to be downloaded. These are simply strings in the update's
information, so there is no enumeration of the categories available. Some known categories:
.. code-block:: text
Updates
Windows 7
Critical Updates
Security Updates
Update Rollups
skips:
a list of features of the updates to cull by. Available features:
.. code-block:: text
'UI' - User interaction required, skipped by default
'downloaded' - Already downloaded, skipped by default (downloading)
'present' - Present on computer, included by default (installing)
'installed' - Already installed, skipped by default
'reboot' - Reboot required, included by default
'hidden' - skip those updates that have been hidden.
'software' - Software updates, included by default
'driver' - driver updates, skipped by default
retries
Number of retries to make before giving up. This is total, not per
step.
'''
ret = {'name': name,
'result': True,
'changes': {},
'comment': ''}
deprecation_msg = 'The \'win_update\' module is deprecated, and will be ' \
'removed in Salt Fluorine. Please use the \'win_wua\' ' \
'module instead.'
salt.utils.versions.warn_until('Fluorine', deprecation_msg)
ret.setdefault('warnings', []).append(deprecation_msg)
if not categories:
categories = [name]
log.debug('categories to search for are: %s', categories)
win_updater = PyWinUpdater()
win_updater.SetCategories(categories)
win_updater.SetSkips(skips)
# this is where we be seeking the things! yar!
comment, passed, retries = _search(win_updater, retries)
ret['comment'] += comment
if not passed:
ret['result'] = False
return ret
# this is where we get all the things! i.e. download updates.
comment, passed, retries = _download(win_updater, retries)
ret['comment'] += comment
if not passed:
ret['result'] = False
return ret
try:
ret['changes'] = win_updater.GetDownloadResults()
except Exception:
ret['comment'] += 'could not get results, but updates were downloaded.'
return ret

View File

@ -193,7 +193,7 @@ def present(host, groups, interfaces, **kwargs):
host_exists = __salt__['zabbix.host_exists'](host, **connection_args)
if host_exists:
host = __salt__['zabbix.host_get'](name=host, **connection_args)[0]
host = __salt__['zabbix.host_get'](host=host, **connection_args)[0]
hostid = host['hostid']
update_proxy = False
@ -457,7 +457,7 @@ def assign_templates(host, templates, **kwargs):
ret['comment'] = comment_host_templ_notupdated
return ret
host_info = __salt__['zabbix.host_get'](name=host, **connection_args)[0]
host_info = __salt__['zabbix.host_get'](host=host, **connection_args)[0]
hostid = host_info['hostid']
if not templates:

View File

@ -1411,7 +1411,7 @@ class ExecutionOptionsMixIn(six.with_metaclass(MixInMeta, object)):
nargs=2,
default=None,
metavar='<FUNC-NAME> <PROVIDER>',
help='Perform an function that may be specific to this cloud '
help='Perform a function that may be specific to this cloud '
'provider, that does not apply to an instance. This '
'argument requires a provider to be specified (i.e.: nova).'
)

View File

@ -927,7 +927,7 @@ SwapTotal: 4789244 kB'''
('rinzler.evil-corp.com', [], ['5.6.7.8']),
('foo.bar.baz', [], ['fe80::a8b2:93ff:fe00:0']),
('bluesniff.foo.bar', [], ['fe80::a8b2:93ff:dead:beef'])]
ret = {'fqdns': ['rinzler.evil-corp.com', 'foo.bar.baz', 'bluesniff.foo.bar']}
ret = {'fqdns': ['bluesniff.foo.bar', 'foo.bar.baz', 'rinzler.evil-corp.com']}
self._run_fqdns_test(reverse_resolv_mock, ret)
def _run_fqdns_test(self, reverse_resolv_mock, ret):

View File

@ -1,133 +0,0 @@
# -*- coding: utf-8 -*-
'''
:codeauthor: :email:`Rahul Handay <rahulha@saltstack.com>`
'''
# Import Python Libs
from __future__ import absolute_import, unicode_literals, print_function
# Import Salt Testing Libs
from tests.support.mixins import LoaderModuleMockMixin
from tests.support.unit import TestCase, skipIf
from tests.support.mock import (
MagicMock,
patch,
NO_MOCK,
NO_MOCK_REASON
)
# Import Salt Libs
import salt.states.win_update as win_update
class MockPyWinUpdater(object):
'''
Mock PyWinUpdater class
'''
def __init__(self):
pass
@staticmethod
def SetCategories(arg):
'''
Mock SetCategories
'''
return arg
@staticmethod
def SetIncludes(arg):
'''
Mock SetIncludes
'''
return arg
@staticmethod
def GetInstallationResults():
'''
Mock GetInstallationResults
'''
return True
@staticmethod
def GetDownloadResults():
'''
Mock GetDownloadResults
'''
return True
@staticmethod
def SetSkips(arg):
return True
@skipIf(NO_MOCK, NO_MOCK_REASON)
class WinUpdateTestCase(TestCase, LoaderModuleMockMixin):
'''
Validate the win_update state
'''
def setup_loader_modules(self):
return {win_update: {'PyWinUpdater': MockPyWinUpdater}}
def test_installed(self):
'''
Test to install specified windows updates
'''
ret = {'name': 'salt',
'changes': {},
'result': False,
'comment': '',
'warnings': ["The 'win_update' module is deprecated, and will "
"be removed in Salt Fluorine. Please use the "
"'win_wua' module instead."]}
mock = MagicMock(side_effect=[['Saltstack', False, 5],
['Saltstack', True, 5],
['Saltstack', True, 5],
['Saltstack', True, 5]])
with patch.object(win_update, '_search', mock):
ret.update({'comment': 'Saltstack'})
self.assertDictEqual(win_update.installed('salt'), ret)
mock = MagicMock(side_effect=[['dude', False, 5],
['dude', True, 5],
['dude', True, 5]])
with patch.object(win_update, '_download', mock):
ret.update({'comment': 'Saltstackdude'})
self.assertDictEqual(win_update.installed('salt'), ret)
mock = MagicMock(side_effect=[['@Me', False, 5],
['@Me', True, 5]])
with patch.object(win_update, '_install', mock):
ret.update({'comment': 'Saltstackdude@Me'})
self.assertDictEqual(win_update.installed('salt'), ret)
ret.update({'changes': True, 'result': True})
self.assertDictEqual(win_update.installed('salt'), ret)
def test_downloaded(self):
'''
Test to cache updates for later install.
'''
ret = {'name': 'salt',
'changes': {},
'result': False,
'comment': '',
'warnings': ["The 'win_update' module is deprecated, and will "
"be removed in Salt Fluorine. Please use the "
"'win_wua' module instead."]}
mock = MagicMock(side_effect=[['Saltstack', False, 5],
['Saltstack', True, 5],
['Saltstack', True, 5]])
with patch.object(win_update, '_search', mock):
ret.update({'comment': 'Saltstack'})
self.assertDictEqual(win_update.downloaded('salt'), ret)
mock = MagicMock(side_effect=[['dude', False, 5],
['dude', True, 5]])
with patch.object(win_update, '_download', mock):
ret.update({'comment': 'Saltstackdude'})
self.assertDictEqual(win_update.downloaded('salt'), ret)
ret.update({'changes': True, 'result': True})
self.assertDictEqual(win_update.downloaded('salt'), ret)