mirror of
https://github.com/valitydev/salt.git
synced 2024-11-07 08:58:59 +00:00
Merge branch 'develop' into state-deprecations
This commit is contained in:
commit
a7c93fdd63
4
.github/stale.yml
vendored
4
.github/stale.yml
vendored
@ -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
|
||||
|
@ -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
|
||||
|
@ -334,6 +334,7 @@ execution modules
|
||||
publish
|
||||
puppet
|
||||
purefa
|
||||
purefb
|
||||
pushbullet
|
||||
pushover_notify
|
||||
pw_group
|
||||
|
6
doc/ref/modules/all/salt.modules.purefb.rst
Normal file
6
doc/ref/modules/all/salt.modules.purefb.rst
Normal file
@ -0,0 +1,6 @@
|
||||
===================
|
||||
salt.modules.purefb
|
||||
===================
|
||||
|
||||
.. automodule:: salt.modules.purefb
|
||||
:members:
|
@ -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
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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...')
|
||||
|
@ -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)
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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
|
@ -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:
|
||||
|
@ -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).'
|
||||
)
|
||||
|
@ -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):
|
||||
|
@ -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)
|
Loading…
Reference in New Issue
Block a user