mirror of
https://github.com/valitydev/salt.git
synced 2024-11-06 08:35:21 +00:00
Merge branch '2018.3' into 2018.3
This commit is contained in:
commit
faa3471c05
5
Gemfile
5
Gemfile
@ -3,7 +3,7 @@
|
||||
source 'https://rubygems.org'
|
||||
|
||||
gem 'test-kitchen', '~>1.23.3'
|
||||
gem 'kitchen-salt', '~>0.4.1'
|
||||
gem 'kitchen-salt', :git => 'https://github.com/s0undt3ch/kitchen-salt.git', :branch => 'features/nox'
|
||||
gem 'kitchen-sync'
|
||||
gem 'git'
|
||||
|
||||
@ -13,7 +13,8 @@ end
|
||||
|
||||
group :windows do
|
||||
gem 'winrm', '~>2.0'
|
||||
gem 'winrm-fs', '~>1.3.1'
|
||||
# gem 'winrm-fs', '~>1.3.1'
|
||||
gem 'winrm-fs', :git => 'https://github.com/s0undt3ch/winrm-fs.git', :branch => 'hotfix/saltstack-ci'
|
||||
end
|
||||
|
||||
group :ec2 do
|
||||
|
@ -340,8 +340,17 @@ Building the documentation
|
||||
4. A useful method of viewing the HTML documentation locally is to start
|
||||
Python's built-in HTTP server:
|
||||
|
||||
Python 3:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
cd /path/to/salt/doc/_build/html
|
||||
python3 -m http.server
|
||||
|
||||
Python 2:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
cd /path/to/salt/doc/_build/html
|
||||
python -m SimpleHTTPServer
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
Salt Release Process
|
||||
====================
|
||||
|
||||
The goal for Salt projects is to cut a new feature release every four to six
|
||||
The goal for Salt projects is to cut a new feature release every six
|
||||
months. This document outlines the process for these releases, and the
|
||||
subsequent bug fix releases which follow.
|
||||
|
||||
@ -11,29 +11,66 @@ Feature Release Process
|
||||
=======================
|
||||
|
||||
When a new release is ready to be cut, the person responsible for cutting the
|
||||
release will follow the following steps (written using the 0.16 release as an
|
||||
release will follow the following steps (written using the 2019.2.0 release as an
|
||||
example):
|
||||
|
||||
#. All open issues on the release milestone should be moved to the next release
|
||||
milestone. (e.g. from the ``0.16`` milestone to the ``0.17`` milestone)
|
||||
#. Release notes should be created documenting the major new features and
|
||||
bugfixes in the release.
|
||||
#. Create an annotated tag with only the major and minor version numbers,
|
||||
preceded by the letter ``v``. (e.g. ``v0.16``) This tag will reside on the
|
||||
``develop`` branch.
|
||||
#. Create a branch for the new release, using only the major and minor version
|
||||
numbers. (e.g. ``0.16``)
|
||||
#. On this new branch, create an annotated tag for the first revision release,
|
||||
which is generally a release candidate. It should be preceded by the letter
|
||||
``v``. (e.g. ``v0.16.0rc1``)
|
||||
#. The release should be packaged from this annotated tag and uploaded to PyPI
|
||||
as well as the GitHub releases page for this tag.
|
||||
#. The packagers should be notified on the `salt-packagers`_ mailing list so
|
||||
they can create packages for all the major operating systems. (note that
|
||||
release candidates should go in the testing repositories)
|
||||
#. After the packagers have been given a few days to compile the packages, the
|
||||
release is announced on the `salt-users`_ mailing list.
|
||||
#. Log into RTD and add the new release there. (Have to do it manually)
|
||||
#. Create first public draft of release notes with major features.
|
||||
#. Remove any deprecations for the upcoming release.
|
||||
#. Notify salt-users and salt-announce google groups when the feature freeze
|
||||
branch creation will occur so they can try to get their work merged.
|
||||
#. Create QA test plan. Review features planned for the release and determine if
|
||||
there is sufficient test coverage.
|
||||
#. Ensure all required features are merged.
|
||||
#. Complete one last merge forward from the previous branch.
|
||||
#. Create feature release branch with the name of the release. (ex. fluorine)
|
||||
#. Create issue to start the process of deprecating for the next feature release.
|
||||
#. Create jenkins jobs to test the new feature release branch.
|
||||
#. Inform salt-users and salt-announce google groups feature branch and
|
||||
freeze is complete.
|
||||
#. Add new feature branch to salt-jenkins repo and the kitchen yaml file.
|
||||
#. Fix tests failing in jenkins test runs.
|
||||
#. Finalize QA test plan and add all required tests.
|
||||
#. Run through a manual test run based off of the head of the feature branch.
|
||||
#. Convert the feature release branch to the version number. For example (v2019.2)
|
||||
This is based off of the year and month that is planned to release.
|
||||
#. Migrate both the jenkins test jobs and salt-jenkins repo to the new branch number.
|
||||
#. Notify salt-users and salt-announce google groups of the new version branch
|
||||
number and migrate any PRs to the new branch.
|
||||
#. Delete old feature release branch name (ex. fluorine)
|
||||
#. Update all name references to version number in the docs. For example
|
||||
all fluorine references in the docs needs to be moved to v2019.2.0
|
||||
#. Create RC release branch. (ex. 2019.2.0.rc)
|
||||
#. Create new jenkins test jobs with new RC release branch
|
||||
#. Notify salt-users and salt-announce google groups of the new RC branch.
|
||||
#. Fix tests failing in jenkins test runs.
|
||||
#. Review the release notes with major features.
|
||||
#. Generate the new man pages for the release.
|
||||
#. Create internal RC tag for testing.
|
||||
#. Build latest windows, mac, ubuntu, debian and redhat packages.
|
||||
#. Run manual and package tests against new RC packages.
|
||||
#. Update release candidate docs with the new version. (ex. 2019.2.0rc1)
|
||||
#. Push the internal tag live to salt's repo.
|
||||
#. Publish release archive to pypi based off tag.
|
||||
#. Push the RC packages live.
|
||||
#. Announce new RC to salt-users and salt-announce google groups.
|
||||
#. Triage incoming issues based on the new RC release.
|
||||
#. Fix RC issues once they are categorized as a release blocker.
|
||||
#. Depending on the issues found during the RC process make a decesion
|
||||
on whether to release based off the RC or go through another RC process,
|
||||
repeating the steps starting at ensuring the tests are not failing.
|
||||
#. If a RC is categorized as stable, build all required packages.
|
||||
#. Test all release packages.
|
||||
#. Test links from `repo.saltstack.com`_.
|
||||
#. Update installation instructions with new release number at `repo.saltstack.com`_.
|
||||
#. Update and build docs to include new version (2019.2) as the latest.
|
||||
#. Pre-announce on salt-users google group that we are about to update our repo.
|
||||
#. Publish release (v2019.2.0) archive to pypi based off tag.
|
||||
#. Publish all packages live to repo.
|
||||
#. Publish the docs.
|
||||
#. Create release at `github`_
|
||||
#. Update win-repo-ng with new salt versions.
|
||||
#. Announce release is live to irc, salt-users, salt-announce and release slack
|
||||
community channel.
|
||||
|
||||
|
||||
Maintenance and Bugfix Releases
|
||||
@ -44,16 +81,34 @@ into a "feature freeze" state. The new release branch enters the ``merge-forward
|
||||
chain and only bugfixes should be applied against the new branch. Once major bugs
|
||||
have been fixed, a bugfix release can be cut:
|
||||
|
||||
#. On the release branch (i.e. ``0.16``), create an annotated tag for the
|
||||
revision release. It should be preceded by the letter ``v``. (e.g.
|
||||
``v0.16.2``) Release candidates are unnecessary for bugfix releases.
|
||||
#. The release should be packaged from this annotated tag and uploaded to PyPI.
|
||||
#. The packagers should be notified on the `salt-packagers`_ mailing list so
|
||||
they can create packages for all the major operating systems.
|
||||
#. After the packagers have been given a few days to compile the packages, the
|
||||
release is announced on the `salt-users`_ mailing list.
|
||||
#. Ensure all required bug fixes are merged.
|
||||
#. Inform salt-users and salt-announce we are going to branch for the release.
|
||||
#. Complete one last merge forward from the previous branch.
|
||||
#. Create release branch with the version of the release. (ex. 2019.2.1)
|
||||
#. Create jenkins jobs that test the new release branch.
|
||||
#. Fix tests failing in jeknins test runs.
|
||||
#. Run through a manual test run based off of the head of the branch.
|
||||
#. Generate the new man pages for the release.
|
||||
#. Create internal tag for testing.(ex v2019.2.1)
|
||||
#. Build all release packages.
|
||||
#. Run manual and package tests against new packages.
|
||||
#. Update installation instructions with new release number at `repo.saltstack.com`_.
|
||||
#. Update and build docs to include new version. (ex. 2019.2.1)
|
||||
#. Pre-announce on salt-users google groups that we are about to update our repo.
|
||||
#. Push the internal tag live to salt's repo.
|
||||
#. Publish release archive to pypi based off tag.
|
||||
#. Push the packages live.
|
||||
#. Publish release (v2019.2.1) archive to pypi based off tag.
|
||||
#. Publish all packages live to repo.
|
||||
#. Publish the docs.
|
||||
#. Create release at `github`_
|
||||
#. Update win-repo-ng with new salt versions.
|
||||
#. Announce release is live to irc, salt-users, salt-announce and release slack channel.
|
||||
|
||||
For more information about the difference between the ``develop`` branch and
|
||||
bugfix release branches, please refer to the :ref:`Which Salt Branch?
|
||||
<which-salt-branch>` section of Salt's :ref:`Contributing <contributing>`
|
||||
documentation.
|
||||
|
||||
.. _`github`: https://github.com/saltstack/salt/releases
|
||||
.. _`repo.saltstack.com`: https://repo.saltstack.com
|
||||
|
@ -68,11 +68,10 @@ related release branches.
|
||||
For more information, please see the :ref:`Which Salt Branch? <which-salt-branch>`
|
||||
section of Salt's :ref:`Contributing <contributing>` documentation.
|
||||
|
||||
Determining when a point release is going to be made is up to the project
|
||||
leader (Thomas Hatch). Generally point releases are made every 2-4 weeks or
|
||||
if there is a security fix they can be made sooner.
|
||||
Generally point releases are made every 2 months or if there is a security fix
|
||||
they can be made sooner.
|
||||
|
||||
The point release is only designated by tagging the commit on the release
|
||||
branch with a release number using the existing convention (version 2015.8.1
|
||||
is tagged with v2015.8.1). From the tag point a new source tarball is generated
|
||||
and published to PyPI, and a release announcement is made.
|
||||
The point release is designated by branching (ex 2019.2.1) and then tagging (v2019.2.1)
|
||||
from that newly created release branch when its determined the release is stable.
|
||||
From the tag point a new source tarball is generated and published to PyPI,
|
||||
and a release announcement is made.
|
||||
|
6
doc/topics/releases/2018.3.5.rst
Normal file
6
doc/topics/releases/2018.3.5.rst
Normal file
@ -0,0 +1,6 @@
|
||||
========================================
|
||||
In Progress: Salt 2018.3.5 Release Notes
|
||||
========================================
|
||||
|
||||
Version 2018.3.5 is an **unreleased** bugfix release for :ref:`2018.3.0 <release-2018-3-0>`.
|
||||
This release is still in progress and has not been released yet.
|
19
noxfile.py
19
noxfile.py
@ -23,6 +23,10 @@ import nox
|
||||
REPO_ROOT = os.path.abspath(os.path.dirname(__file__))
|
||||
SITECUSTOMIZE_DIR = os.path.join(REPO_ROOT, 'tests', 'support', 'coverage')
|
||||
|
||||
# We can't just import salt because if this is running under a frozen nox, there
|
||||
# will be no salt to import
|
||||
IS_WINDOWS = sys.platform.lower().startswith('win')
|
||||
|
||||
# Python versions to run against
|
||||
_PYTHON_VERSIONS = ('2', '2.7', '3', '3.4', '3.5', '3.6')
|
||||
|
||||
@ -41,7 +45,9 @@ def _create_ci_directories():
|
||||
|
||||
def _install_requirements(session, *extra_requirements):
|
||||
# Install requirements
|
||||
_requirements_files = []
|
||||
_requirements_files = [
|
||||
os.path.join(REPO_ROOT, 'requirements', 'pytest.txt')
|
||||
]
|
||||
if sys.platform.startswith('linux'):
|
||||
requirements_files = [
|
||||
os.path.join(REPO_ROOT, 'requirements', 'tests.txt')
|
||||
@ -49,7 +55,6 @@ def _install_requirements(session, *extra_requirements):
|
||||
elif sys.platform.startswith('win'):
|
||||
requirements_files = [
|
||||
os.path.join(REPO_ROOT, 'pkg', 'windows', 'req.txt'),
|
||||
os.path.join(REPO_ROOT, 'pkg', 'windows', 'req_testing.txt'),
|
||||
]
|
||||
elif sys.platform.startswith('darwin'):
|
||||
requirements_files = [
|
||||
@ -61,6 +66,10 @@ def _install_requirements(session, *extra_requirements):
|
||||
if not requirements_files:
|
||||
break
|
||||
requirements_file = requirements_files.pop(0)
|
||||
|
||||
if requirements_file not in _requirements_files:
|
||||
_requirements_files.append(requirements_file)
|
||||
|
||||
session.log('Processing {}'.format(requirements_file))
|
||||
with open(requirements_file) as rfh: # pylint: disable=resource-leakage
|
||||
for line in rfh:
|
||||
@ -80,8 +89,14 @@ def _install_requirements(session, *extra_requirements):
|
||||
if extra_requirements:
|
||||
session.install(*extra_requirements)
|
||||
|
||||
if IS_WINDOWS:
|
||||
# Windows hacks :/
|
||||
nox_windows_setup = os.path.join(REPO_ROOT, 'tests', 'support', 'nox-windows-setup.py')
|
||||
session.run('python', nox_windows_setup)
|
||||
|
||||
|
||||
def _run_with_coverage(session, *test_cmd):
|
||||
session.install('coverage')
|
||||
session.run('coverage', 'erase')
|
||||
python_path_env_var = os.environ.get('PYTHONPATH') or None
|
||||
if python_path_env_var is None:
|
||||
|
@ -495,8 +495,6 @@ If Exist "%BinDir%\Lib\site-packages\salt\modules\xfs.py"^
|
||||
del /Q "%BinDir%\Lib\site-packages\salt\modules\xfs.*" 1>nul
|
||||
If Exist "%BinDir%\Lib\site-packages\salt\modules\yumpkg.py"^
|
||||
del /Q "%BinDir%\Lib\site-packages\salt\modules\yum.*" 1>nul
|
||||
If Exist "%BinDir%\Lib\site-packages\salt\modules\zabbix.py"^
|
||||
del /Q "%BinDir%\Lib\site-packages\salt\modules\zabbix.*" 1>nul
|
||||
If Exist "%BinDir%\Lib\site-packages\salt\modules\zfs.py"^
|
||||
del /Q "%BinDir%\Lib\site-packages\salt\modules\zfs.*" 1>nul
|
||||
If Exist "%BinDir%\Lib\site-packages\salt\modules\znc.py"^
|
||||
@ -601,8 +599,6 @@ If Exist "%BinDir%\Lib\site-packages\salt\states\vbox_guest.py"^
|
||||
del /Q "%BinDir%\Lib\site-packages\salt\states\vbox_guest.*" 1>nul
|
||||
If Exist "%BinDir%\Lib\site-packages\salt\states\virt.py"^
|
||||
del /Q "%BinDir%\Lib\site-packages\salt\states\virt.*" 1>nul
|
||||
If Exist "%BinDir%\Lib\site-packages\salt\states\zabbix*"^
|
||||
del /Q "%BinDir%\Lib\site-packages\salt\states\zabbix*" 1>nul
|
||||
If Exist "%BinDir%\Lib\site-packages\salt\states\zfs.py"^
|
||||
del /Q "%BinDir%\Lib\site-packages\salt\states\zfs.*" 1>nul
|
||||
If Exist "%BinDir%\Lib\site-packages\salt\states\zpool.py"^
|
||||
|
@ -97,7 +97,13 @@ def beacon(config):
|
||||
mount_re = '{0}$'.format(mount)
|
||||
|
||||
if salt.utils.platform.is_windows():
|
||||
mount_re = re.sub('\\$', '\\\\', mount_re)
|
||||
# mount_re comes in formatted with a $ at the end
|
||||
# can be `C:\\$` or `C:\\\\$`
|
||||
# re string must be like `C:\\\\` regardless of \\ or \\\\
|
||||
# also, psutil returns uppercase
|
||||
mount_re = re.sub(r':\\\$', r':\\\\', mount_re)
|
||||
mount_re = re.sub(r':\\\\\$', r':\\\\', mount_re)
|
||||
mount_re = mount_re.upper()
|
||||
|
||||
for part in parts:
|
||||
if re.match(mount_re, part.mountpoint):
|
||||
|
@ -238,21 +238,21 @@ def beacon(config):
|
||||
break
|
||||
path = os.path.dirname(path)
|
||||
|
||||
for path in _config.get('files', {}):
|
||||
excludes = _config['files'][path].get('exclude', '')
|
||||
excludes = _config['files'][path].get('exclude', '')
|
||||
|
||||
if excludes and isinstance(excludes, list):
|
||||
for exclude in excludes:
|
||||
if isinstance(exclude, dict):
|
||||
if exclude.values()[0].get('regex', False):
|
||||
_exclude = next(iter(exclude))
|
||||
if exclude[_exclude].get('regex', False):
|
||||
try:
|
||||
if re.search(list(exclude)[0], event.pathname):
|
||||
if re.search(_exclude, event.pathname):
|
||||
_append = False
|
||||
except Exception:
|
||||
log.warning('Failed to compile regex: %s',
|
||||
list(exclude)[0])
|
||||
_exclude)
|
||||
else:
|
||||
exclude = list(exclude)[0]
|
||||
exclude = _exclude
|
||||
elif '*' in exclude:
|
||||
if fnmatch.fnmatch(event.pathname, exclude):
|
||||
_append = False
|
||||
@ -312,7 +312,7 @@ def beacon(config):
|
||||
excl = []
|
||||
for exclude in excludes:
|
||||
if isinstance(exclude, dict):
|
||||
excl.append(exclude.keys()[0])
|
||||
excl.append(list(exclude)[0])
|
||||
else:
|
||||
excl.append(exclude)
|
||||
excl = pyinotify.ExcludeFilter(excl)
|
||||
|
@ -1208,13 +1208,15 @@ def mount(name, device, mkmnt=False, fstype='', opts='defaults', user=None, util
|
||||
lopts = ','.join(opts)
|
||||
args = '-o {0}'.format(lopts)
|
||||
|
||||
# use of fstype on AIX differs from typical Linux use of -t functionality
|
||||
# AIX uses -v vfsname, -t fstype mounts all with fstype in /etc/filesystems
|
||||
if 'AIX' in __grains__['os']:
|
||||
if fstype:
|
||||
if fstype:
|
||||
# use of fstype on AIX differs from typical Linux use of -t
|
||||
# functionality AIX uses -v vfsname, -t fstype mounts all with
|
||||
# fstype in /etc/filesystems
|
||||
if 'AIX' in __grains__['os']:
|
||||
args += ' -v {0}'.format(fstype)
|
||||
else:
|
||||
args += ' -t {0}'.format(fstype)
|
||||
else:
|
||||
args += ' -t {0}'.format(fstype)
|
||||
|
||||
cmd = 'mount {0} {1} {2} '.format(args, device, name)
|
||||
out = __salt__['cmd.run_all'](cmd, runas=user, python_shell=False)
|
||||
if out['retcode']:
|
||||
|
@ -1265,7 +1265,12 @@ def user_exists(user,
|
||||
salt '*' mysql.user_exists 'username' password_column='authentication_string'
|
||||
'''
|
||||
run_verify = False
|
||||
server_version = version(**connection_args)
|
||||
server_version = salt.utils.data.decode(version(**connection_args))
|
||||
if not server_version:
|
||||
last_err = __context__['mysql.error']
|
||||
err = 'MySQL Error: Unable to fetch current server version. Last error was: "{}"'.format(last_err)
|
||||
log.error(err)
|
||||
return False
|
||||
compare_version = '10.2.0' if 'MariaDB' in server_version else '8.0.11'
|
||||
dbc = _connect(**connection_args)
|
||||
# Did we fail to connect with the user we are checking
|
||||
@ -1404,7 +1409,12 @@ def user_create(user,
|
||||
salt '*' mysql.user_create 'username' 'hostname' password_hash='hash'
|
||||
salt '*' mysql.user_create 'username' 'hostname' allow_passwordless=True
|
||||
'''
|
||||
server_version = version(**connection_args)
|
||||
server_version = salt.utils.data.decode(version(**connection_args))
|
||||
if not server_version:
|
||||
last_err = __context__['mysql.error']
|
||||
err = 'MySQL Error: Unable to fetch current server version. Last error was: "{}"'.format(last_err)
|
||||
log.error(err)
|
||||
return False
|
||||
compare_version = '10.2.0' if 'MariaDB' in server_version else '8.0.11'
|
||||
if user_exists(user, host, **connection_args):
|
||||
log.info('User \'%s\'@\'%s\' already exists', user, host)
|
||||
@ -1509,7 +1519,12 @@ def user_chpass(user,
|
||||
salt '*' mysql.user_chpass frank localhost password_hash='hash'
|
||||
salt '*' mysql.user_chpass frank localhost allow_passwordless=True
|
||||
'''
|
||||
server_version = version(**connection_args)
|
||||
server_version = salt.utils.data.decode(version(**connection_args))
|
||||
if not server_version:
|
||||
last_err = __context__['mysql.error']
|
||||
err = 'MySQL Error: Unable to fetch current server version. Last error was: "{}"'.format(last_err)
|
||||
log.error(err)
|
||||
return False
|
||||
compare_version = '10.2.0' if 'MariaDB' in server_version else '8.0.11'
|
||||
args = {}
|
||||
if password is not None:
|
||||
@ -1865,7 +1880,12 @@ def grant_exists(grant,
|
||||
'SELECT,INSERT,UPDATE,...' 'database.*' 'frank' 'localhost'
|
||||
'''
|
||||
|
||||
server_version = version(**connection_args)
|
||||
server_version = salt.utils.data.decode(version(**connection_args))
|
||||
if not server_version:
|
||||
last_err = __context__['mysql.error']
|
||||
err = 'MySQL Error: Unable to fetch current server version. Last error was: "{}"'.format(last_err)
|
||||
log.error(err)
|
||||
return False
|
||||
if 'ALL' in grant:
|
||||
if salt.utils.versions.version_cmp(server_version, '8.0') >= 0 and \
|
||||
'MariaDB' not in server_version:
|
||||
|
@ -13,11 +13,11 @@ from __future__ import absolute_import, unicode_literals, print_function
|
||||
# Import Salt libs
|
||||
import salt.utils.platform
|
||||
import salt.utils.win_functions
|
||||
import salt.utils.winapi
|
||||
|
||||
|
||||
try:
|
||||
import win32com.client
|
||||
import pythoncom
|
||||
import pywintypes
|
||||
HAS_DEPENDENCIES = True
|
||||
except ImportError:
|
||||
@ -43,8 +43,8 @@ def _get_computer_object():
|
||||
Returns:
|
||||
object: Returns the computer object for the local machine
|
||||
'''
|
||||
pythoncom.CoInitialize()
|
||||
nt = win32com.client.Dispatch('AdsNameSpaces')
|
||||
with salt.utils.winapi.Com():
|
||||
nt = win32com.client.Dispatch('AdsNameSpaces')
|
||||
return nt.GetObject('', 'WinNT://.,computer')
|
||||
|
||||
|
||||
@ -59,8 +59,8 @@ def _get_group_object(name):
|
||||
Returns:
|
||||
object: The specified group object
|
||||
'''
|
||||
pythoncom.CoInitialize()
|
||||
nt = win32com.client.Dispatch('AdsNameSpaces')
|
||||
with salt.utils.winapi.Com():
|
||||
nt = win32com.client.Dispatch('AdsNameSpaces')
|
||||
return nt.GetObject('', 'WinNT://./' + name + ',group')
|
||||
|
||||
|
||||
@ -72,8 +72,8 @@ def _get_all_groups():
|
||||
Returns:
|
||||
iter: A list of objects for all groups on the machine
|
||||
'''
|
||||
pythoncom.CoInitialize()
|
||||
nt = win32com.client.Dispatch('AdsNameSpaces')
|
||||
with salt.utils.winapi.Com():
|
||||
nt = win32com.client.Dispatch('AdsNameSpaces')
|
||||
results = nt.GetObject('', 'WinNT://.')
|
||||
results.Filter = ['group']
|
||||
return results
|
||||
|
@ -3,7 +3,6 @@
|
||||
Module for managing windows systems.
|
||||
|
||||
:depends:
|
||||
- pythoncom
|
||||
- pywintypes
|
||||
- win32api
|
||||
- win32con
|
||||
@ -24,12 +23,12 @@ from datetime import datetime
|
||||
import salt.utils.functools
|
||||
import salt.utils.locales
|
||||
import salt.utils.platform
|
||||
import salt.utils.winapi
|
||||
from salt.exceptions import CommandExecutionError
|
||||
|
||||
# Import 3rd-party Libs
|
||||
from salt.ext import six
|
||||
try:
|
||||
import pythoncom
|
||||
import wmi
|
||||
import win32net
|
||||
import win32api
|
||||
@ -516,8 +515,8 @@ def get_system_info():
|
||||
os_type = {1: 'Work Station',
|
||||
2: 'Domain Controller',
|
||||
3: 'Server'}
|
||||
pythoncom.CoInitialize()
|
||||
conn = wmi.WMI()
|
||||
with salt.utils.winapi.Com():
|
||||
conn = wmi.WMI()
|
||||
system = conn.Win32_OperatingSystem()[0]
|
||||
ret = {'name': get_computer_name(),
|
||||
'description': system.Description,
|
||||
@ -756,8 +755,8 @@ def _join_domain(domain,
|
||||
if not account_exists:
|
||||
join_options |= NETSETUP_ACCOUNT_CREATE
|
||||
|
||||
pythoncom.CoInitialize()
|
||||
conn = wmi.WMI()
|
||||
with salt.utils.winapi.Com():
|
||||
conn = wmi.WMI()
|
||||
comp = conn.Win32_ComputerSystem()[0]
|
||||
|
||||
# Return the results of the command as an error
|
||||
@ -848,8 +847,8 @@ def unjoin_domain(username=None,
|
||||
if disable:
|
||||
unjoin_options |= NETSETUP_ACCT_DELETE
|
||||
|
||||
pythoncom.CoInitialize()
|
||||
conn = wmi.WMI()
|
||||
with salt.utils.winapi.Com():
|
||||
conn = wmi.WMI()
|
||||
comp = conn.Win32_ComputerSystem()[0]
|
||||
err = comp.UnjoinDomainOrWorkgroup(Password=password,
|
||||
UserName=username,
|
||||
@ -892,8 +891,8 @@ def get_domain_workgroup():
|
||||
|
||||
salt 'minion-id' system.get_domain_workgroup
|
||||
'''
|
||||
pythoncom.CoInitialize()
|
||||
conn = wmi.WMI()
|
||||
with salt.utils.winapi.Com():
|
||||
conn = wmi.WMI()
|
||||
for computer in conn.Win32_ComputerSystem():
|
||||
if computer.PartOfDomain:
|
||||
return {'Domain': computer.Domain}
|
||||
|
@ -17,6 +17,7 @@ from datetime import datetime
|
||||
|
||||
# Import Salt libs
|
||||
import salt.utils.platform
|
||||
import salt.utils.winapi
|
||||
|
||||
# Import 3rd-party libraries
|
||||
try:
|
||||
@ -333,8 +334,8 @@ def list_tasks(location='\\'):
|
||||
salt 'minion-id' task.list_tasks
|
||||
'''
|
||||
# Create the task service object
|
||||
pythoncom.CoInitialize()
|
||||
task_service = win32com.client.Dispatch("Schedule.Service")
|
||||
with salt.utils.winapi.Com():
|
||||
task_service = win32com.client.Dispatch("Schedule.Service")
|
||||
task_service.Connect()
|
||||
|
||||
# Get the folder to list tasks from
|
||||
@ -366,8 +367,8 @@ def list_folders(location='\\'):
|
||||
salt 'minion-id' task.list_folders
|
||||
'''
|
||||
# Create the task service object
|
||||
pythoncom.CoInitialize()
|
||||
task_service = win32com.client.Dispatch("Schedule.Service")
|
||||
with salt.utils.winapi.Com():
|
||||
task_service = win32com.client.Dispatch("Schedule.Service")
|
||||
task_service.Connect()
|
||||
|
||||
# Get the folder to list folders from
|
||||
@ -401,8 +402,8 @@ def list_triggers(name, location='\\'):
|
||||
salt 'minion-id' task.list_triggers <task_name>
|
||||
'''
|
||||
# Create the task service object
|
||||
pythoncom.CoInitialize()
|
||||
task_service = win32com.client.Dispatch("Schedule.Service")
|
||||
with salt.utils.winapi.Com():
|
||||
task_service = win32com.client.Dispatch("Schedule.Service")
|
||||
task_service.Connect()
|
||||
|
||||
# Get the folder to list folders from
|
||||
@ -437,8 +438,8 @@ def list_actions(name, location='\\'):
|
||||
salt 'minion-id' task.list_actions <task_name>
|
||||
'''
|
||||
# Create the task service object
|
||||
pythoncom.CoInitialize()
|
||||
task_service = win32com.client.Dispatch("Schedule.Service")
|
||||
with salt.utils.winapi.Com():
|
||||
task_service = win32com.client.Dispatch("Schedule.Service")
|
||||
task_service.Connect()
|
||||
|
||||
# Get the folder to list folders from
|
||||
@ -499,8 +500,8 @@ def create_task(name,
|
||||
return '{0} already exists'.format(name)
|
||||
|
||||
# connect to the task scheduler
|
||||
pythoncom.CoInitialize()
|
||||
task_service = win32com.client.Dispatch("Schedule.Service")
|
||||
with salt.utils.winapi.Com():
|
||||
task_service = win32com.client.Dispatch("Schedule.Service")
|
||||
task_service.Connect()
|
||||
|
||||
# Create a new task definition
|
||||
@ -584,8 +585,8 @@ def create_task_from_xml(name,
|
||||
return 'Must specify either xml_text or xml_path'
|
||||
|
||||
# Create the task service object
|
||||
pythoncom.CoInitialize()
|
||||
task_service = win32com.client.Dispatch("Schedule.Service")
|
||||
with salt.utils.winapi.Com():
|
||||
task_service = win32com.client.Dispatch("Schedule.Service")
|
||||
task_service.Connect()
|
||||
|
||||
# Load xml from file, overrides xml_text
|
||||
@ -664,8 +665,8 @@ def create_folder(name, location='\\'):
|
||||
return '{0} already exists'.format(name)
|
||||
|
||||
# Create the task service object
|
||||
pythoncom.CoInitialize()
|
||||
task_service = win32com.client.Dispatch("Schedule.Service")
|
||||
with salt.utils.winapi.Com():
|
||||
task_service = win32com.client.Dispatch("Schedule.Service")
|
||||
task_service.Connect()
|
||||
|
||||
# Get the folder to list folders from
|
||||
@ -879,8 +880,8 @@ def edit_task(name=None,
|
||||
if name in list_tasks(location):
|
||||
|
||||
# Connect to the task scheduler
|
||||
pythoncom.CoInitialize()
|
||||
task_service = win32com.client.Dispatch("Schedule.Service")
|
||||
with salt.utils.winapi.Com():
|
||||
task_service = win32com.client.Dispatch("Schedule.Service")
|
||||
task_service.Connect()
|
||||
|
||||
# get the folder to create the task in
|
||||
@ -1045,8 +1046,8 @@ def delete_task(name, location='\\'):
|
||||
return '{0} not found in {1}'.format(name, location)
|
||||
|
||||
# connect to the task scheduler
|
||||
pythoncom.CoInitialize()
|
||||
task_service = win32com.client.Dispatch("Schedule.Service")
|
||||
with salt.utils.winapi.Com():
|
||||
task_service = win32com.client.Dispatch("Schedule.Service")
|
||||
task_service.Connect()
|
||||
|
||||
# get the folder to delete the task from
|
||||
@ -1085,8 +1086,8 @@ def delete_folder(name, location='\\'):
|
||||
return '{0} not found in {1}'.format(name, location)
|
||||
|
||||
# connect to the task scheduler
|
||||
pythoncom.CoInitialize()
|
||||
task_service = win32com.client.Dispatch("Schedule.Service")
|
||||
with salt.utils.winapi.Com():
|
||||
task_service = win32com.client.Dispatch("Schedule.Service")
|
||||
task_service.Connect()
|
||||
|
||||
# get the folder to delete the folder from
|
||||
@ -1126,8 +1127,8 @@ def run(name, location='\\'):
|
||||
return '{0} not found in {1}'.format(name, location)
|
||||
|
||||
# connect to the task scheduler
|
||||
pythoncom.CoInitialize()
|
||||
task_service = win32com.client.Dispatch("Schedule.Service")
|
||||
with salt.utils.winapi.Com():
|
||||
task_service = win32com.client.Dispatch("Schedule.Service")
|
||||
task_service.Connect()
|
||||
|
||||
# get the folder to delete the folder from
|
||||
@ -1165,8 +1166,8 @@ def run_wait(name, location='\\'):
|
||||
return '{0} not found in {1}'.format(name, location)
|
||||
|
||||
# connect to the task scheduler
|
||||
pythoncom.CoInitialize()
|
||||
task_service = win32com.client.Dispatch("Schedule.Service")
|
||||
with salt.utils.winapi.Com():
|
||||
task_service = win32com.client.Dispatch("Schedule.Service")
|
||||
task_service.Connect()
|
||||
|
||||
# get the folder to delete the folder from
|
||||
@ -1222,8 +1223,8 @@ def stop(name, location='\\'):
|
||||
return '{0} not found in {1}'.format(name, location)
|
||||
|
||||
# connect to the task scheduler
|
||||
pythoncom.CoInitialize()
|
||||
task_service = win32com.client.Dispatch("Schedule.Service")
|
||||
with salt.utils.winapi.Com():
|
||||
task_service = win32com.client.Dispatch("Schedule.Service")
|
||||
task_service.Connect()
|
||||
|
||||
# get the folder to delete the folder from
|
||||
@ -1268,8 +1269,8 @@ def status(name, location='\\'):
|
||||
return '{0} not found in {1}'.format(name, location)
|
||||
|
||||
# connect to the task scheduler
|
||||
pythoncom.CoInitialize()
|
||||
task_service = win32com.client.Dispatch("Schedule.Service")
|
||||
with salt.utils.winapi.Com():
|
||||
task_service = win32com.client.Dispatch("Schedule.Service")
|
||||
task_service.Connect()
|
||||
|
||||
# get the folder where the task is defined
|
||||
@ -1303,8 +1304,8 @@ def info(name, location='\\'):
|
||||
return '{0} not found in {1}'.format(name, location)
|
||||
|
||||
# connect to the task scheduler
|
||||
pythoncom.CoInitialize()
|
||||
task_service = win32com.client.Dispatch("Schedule.Service")
|
||||
with salt.utils.winapi.Com():
|
||||
task_service = win32com.client.Dispatch("Schedule.Service")
|
||||
task_service.Connect()
|
||||
|
||||
# get the folder to delete the folder from
|
||||
@ -1493,8 +1494,8 @@ def add_action(name=None,
|
||||
if name in list_tasks(location):
|
||||
|
||||
# Connect to the task scheduler
|
||||
pythoncom.CoInitialize()
|
||||
task_service = win32com.client.Dispatch("Schedule.Service")
|
||||
with salt.utils.winapi.Com():
|
||||
task_service = win32com.client.Dispatch("Schedule.Service")
|
||||
task_service.Connect()
|
||||
|
||||
# get the folder to create the task in
|
||||
@ -1600,8 +1601,8 @@ def _clear_actions(name, location='\\'):
|
||||
return '{0} not found in {1}'.format(name, location)
|
||||
|
||||
# Create the task service object
|
||||
pythoncom.CoInitialize()
|
||||
task_service = win32com.client.Dispatch("Schedule.Service")
|
||||
with salt.utils.winapi.Com():
|
||||
task_service = win32com.client.Dispatch("Schedule.Service")
|
||||
task_service.Connect()
|
||||
|
||||
# Get the actions from the task
|
||||
@ -1992,8 +1993,8 @@ def add_trigger(name=None,
|
||||
if name in list_tasks(location):
|
||||
|
||||
# Connect to the task scheduler
|
||||
pythoncom.CoInitialize()
|
||||
task_service = win32com.client.Dispatch("Schedule.Service")
|
||||
with salt.utils.winapi.Com():
|
||||
task_service = win32com.client.Dispatch("Schedule.Service")
|
||||
task_service.Connect()
|
||||
|
||||
# get the folder to create the task in
|
||||
@ -2169,8 +2170,8 @@ def clear_triggers(name, location='\\'):
|
||||
return '{0} not found in {1}'.format(name, location)
|
||||
|
||||
# Create the task service object
|
||||
pythoncom.CoInitialize()
|
||||
task_service = win32com.client.Dispatch("Schedule.Service")
|
||||
with salt.utils.winapi.Com():
|
||||
task_service = win32com.client.Dispatch("Schedule.Service")
|
||||
task_service.Connect()
|
||||
|
||||
# Get the triggers from the task
|
||||
|
@ -9,7 +9,6 @@ Module for managing Windows Users
|
||||
<module-provider-override>`.
|
||||
|
||||
:depends:
|
||||
- pythoncom
|
||||
- pywintypes
|
||||
- win32api
|
||||
- win32con
|
||||
@ -38,6 +37,7 @@ except Exception:
|
||||
import salt.utils.args
|
||||
import salt.utils.dateutils
|
||||
import salt.utils.platform
|
||||
import salt.utils.winapi
|
||||
from salt.ext import six
|
||||
from salt.ext.six import string_types
|
||||
from salt.exceptions import CommandExecutionError
|
||||
@ -47,7 +47,6 @@ log = logging.getLogger(__name__)
|
||||
try:
|
||||
import pywintypes
|
||||
import wmi
|
||||
import pythoncom
|
||||
import win32api
|
||||
import win32con
|
||||
import win32net
|
||||
@ -989,8 +988,8 @@ def rename(name, new_name):
|
||||
|
||||
# Rename the user account
|
||||
# Connect to WMI
|
||||
pythoncom.CoInitialize()
|
||||
c = wmi.WMI(find_classes=0)
|
||||
with salt.utils.winapi.Com():
|
||||
c = wmi.WMI(find_classes=0)
|
||||
|
||||
# Get the user object
|
||||
try:
|
||||
|
@ -63,12 +63,12 @@ import logging
|
||||
import salt.utils.platform
|
||||
import salt.utils.versions
|
||||
import salt.utils.win_update
|
||||
import salt.utils.winapi
|
||||
from salt.exceptions import CommandExecutionError
|
||||
|
||||
# Import 3rd-party libs
|
||||
from salt.ext import six
|
||||
try:
|
||||
import pythoncom
|
||||
import win32com.client
|
||||
HAS_PYWIN32 = True
|
||||
except ImportError:
|
||||
@ -1057,6 +1057,7 @@ def set_wu_settings(level=None,
|
||||
# work on Windows 10 / Server 2016. It is called in throughout this function
|
||||
# like this:
|
||||
#
|
||||
# with salt.utils.winapi.Com():
|
||||
# obj_au = win32com.client.Dispatch('Microsoft.Update.AutoUpdate')
|
||||
# obj_au_settings = obj_au.Settings
|
||||
# obj_au_settings.Save()
|
||||
@ -1077,10 +1078,10 @@ def set_wu_settings(level=None,
|
||||
ret = {'Success': True}
|
||||
|
||||
# Initialize the PyCom system
|
||||
pythoncom.CoInitialize()
|
||||
with salt.utils.winapi.Com():
|
||||
|
||||
# Create an AutoUpdate object
|
||||
obj_au = win32com.client.Dispatch('Microsoft.Update.AutoUpdate')
|
||||
# Create an AutoUpdate object
|
||||
obj_au = win32com.client.Dispatch('Microsoft.Update.AutoUpdate')
|
||||
|
||||
# Create an AutoUpdate Settings Object
|
||||
obj_au_settings = obj_au.Settings
|
||||
@ -1174,7 +1175,8 @@ def set_wu_settings(level=None,
|
||||
if msupdate is not None:
|
||||
# Microsoft Update requires special handling
|
||||
# First load the MS Update Service Manager
|
||||
obj_sm = win32com.client.Dispatch('Microsoft.Update.ServiceManager')
|
||||
with salt.utils.winapi.Com():
|
||||
obj_sm = win32com.client.Dispatch('Microsoft.Update.ServiceManager')
|
||||
|
||||
# Give it a bogus name
|
||||
obj_sm.ClientApplicationID = "My App"
|
||||
@ -1275,10 +1277,9 @@ def get_wu_settings():
|
||||
'Saturday']
|
||||
|
||||
# Initialize the PyCom system
|
||||
pythoncom.CoInitialize()
|
||||
|
||||
# Create an AutoUpdate object
|
||||
obj_au = win32com.client.Dispatch('Microsoft.Update.AutoUpdate')
|
||||
with salt.utils.winapi.Com():
|
||||
# Create an AutoUpdate object
|
||||
obj_au = win32com.client.Dispatch('Microsoft.Update.AutoUpdate')
|
||||
|
||||
# Create an AutoUpdate Settings Object
|
||||
obj_au_settings = obj_au.Settings
|
||||
@ -1312,8 +1313,10 @@ def _get_msupdate_status():
|
||||
'''
|
||||
# To get the status of Microsoft Update we actually have to check the
|
||||
# Microsoft Update Service Manager
|
||||
# Create a ServiceManager Object
|
||||
obj_sm = win32com.client.Dispatch('Microsoft.Update.ServiceManager')
|
||||
# Initialize the PyCom system
|
||||
with salt.utils.winapi.Com():
|
||||
# Create a ServiceManager Object
|
||||
obj_sm = win32com.client.Dispatch('Microsoft.Update.ServiceManager')
|
||||
|
||||
# Return a collection of loaded Services
|
||||
col_services = obj_sm.Services
|
||||
|
@ -298,6 +298,7 @@ from salt.state import get_accumulator_dir as _get_accumulator_dir
|
||||
if salt.utils.platform.is_windows():
|
||||
import salt.utils.win_dacl
|
||||
import salt.utils.win_functions
|
||||
import salt.utils.winapi
|
||||
|
||||
# Import 3rd-party libs
|
||||
from salt.ext import six
|
||||
@ -1207,7 +1208,8 @@ def _shortcut_check(name,
|
||||
), pchanges
|
||||
|
||||
if os.path.isfile(name):
|
||||
shell = win32com.client.Dispatch("WScript.Shell")
|
||||
with salt.utils.winapi.Com():
|
||||
shell = win32com.client.Dispatch("WScript.Shell")
|
||||
scut = shell.CreateShortcut(name)
|
||||
state_checks = [scut.TargetPath.lower() == target.lower()]
|
||||
if arguments is not None:
|
||||
@ -6851,7 +6853,8 @@ def shortcut(
|
||||
|
||||
# This will just load the shortcut if it already exists
|
||||
# It won't create the file until calling scut.Save()
|
||||
shell = win32com.client.Dispatch("WScript.Shell")
|
||||
with salt.utils.winapi.Com():
|
||||
shell = win32com.client.Dispatch("WScript.Shell")
|
||||
scut = shell.CreateShortcut(name)
|
||||
|
||||
# The shortcut target will automatically be created with its
|
||||
|
@ -14,12 +14,17 @@ import re
|
||||
|
||||
# Import Salt libs
|
||||
import salt.utils.path
|
||||
import salt.modules.cmdmod
|
||||
|
||||
__salt__ = {
|
||||
'cmd.run_all': salt.modules.cmdmod.run_all
|
||||
}
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def get_certs_path():
|
||||
icinga2_output = __salt__['cmd.run_all']([salt.utils.path.which('icinga2'), "--version"], python_shell=False)
|
||||
icinga2_output = __salt__['cmd.run_all']([salt.utils.path.which('icinga2'),
|
||||
"--version"], python_shell=False)
|
||||
version = re.search(r'r\d+\.\d+', icinga2_output['stdout']).group(0)
|
||||
# Return new certs path for icinga2 >= 2.8
|
||||
if int(version.split('.')[1]) >= 8:
|
||||
|
@ -56,6 +56,13 @@ def _init_libcrypto():
|
||||
'''
|
||||
libcrypto = _load_libcrypto()
|
||||
|
||||
try:
|
||||
libcrypto.OPENSSL_init_crypto()
|
||||
except AttributeError:
|
||||
# Support for OpenSSL < 1.1 (OPENSSL_API_COMPAT < 0x10100000L)
|
||||
libcrypto.OPENSSL_no_config()
|
||||
libcrypto.OPENSSL_add_all_algorithms_noconf()
|
||||
|
||||
libcrypto.RSA_new.argtypes = ()
|
||||
libcrypto.RSA_new.restype = c_void_p
|
||||
libcrypto.RSA_free.argtypes = (c_void_p, )
|
||||
@ -70,13 +77,6 @@ def _init_libcrypto():
|
||||
libcrypto.RSA_private_encrypt.argtypes = (c_int, c_char_p, c_char_p, c_void_p, c_int)
|
||||
libcrypto.RSA_public_decrypt.argtypes = (c_int, c_char_p, c_char_p, c_void_p, c_int)
|
||||
|
||||
try:
|
||||
libcrypto.OPENSSL_init_crypto()
|
||||
except AttributeError:
|
||||
# Support for OpenSSL < 1.1 (OPENSSL_API_COMPAT < 0x10100000L)
|
||||
libcrypto.OPENSSL_no_config()
|
||||
libcrypto.OPENSSL_add_all_algorithms_noconf()
|
||||
|
||||
return libcrypto
|
||||
|
||||
|
||||
|
@ -10,6 +10,7 @@ import subprocess
|
||||
# Import Salt libs
|
||||
import salt.utils.args
|
||||
import salt.utils.data
|
||||
import salt.utils.winapi
|
||||
from salt.ext import six
|
||||
from salt.ext.six.moves import range
|
||||
from salt.exceptions import CommandExecutionError
|
||||
@ -17,7 +18,6 @@ from salt.exceptions import CommandExecutionError
|
||||
# Import 3rd-party libs
|
||||
try:
|
||||
import win32com.client
|
||||
import pythoncom
|
||||
import pywintypes
|
||||
HAS_PYWIN32 = True
|
||||
except ImportError:
|
||||
@ -25,6 +25,16 @@ except ImportError:
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
__virtualname__ = 'win_update'
|
||||
|
||||
|
||||
def __virtual__():
|
||||
if not salt.utils.platform.is_windows():
|
||||
return False, 'win_update: Not available on Windows'
|
||||
if not HAS_PYWIN32:
|
||||
return False, 'win_update: Missing pywin32'
|
||||
return __virtualname__
|
||||
|
||||
|
||||
class Updates(object):
|
||||
'''
|
||||
@ -68,7 +78,8 @@ class Updates(object):
|
||||
Initialize the updates collection. Can be accessed via
|
||||
``Updates.updates``
|
||||
'''
|
||||
self.updates = win32com.client.Dispatch('Microsoft.Update.UpdateColl')
|
||||
with salt.utils.winapi.Com():
|
||||
self.updates = win32com.client.Dispatch('Microsoft.Update.UpdateColl')
|
||||
|
||||
def count(self):
|
||||
'''
|
||||
@ -274,13 +285,13 @@ class WindowsUpdateAgent(object):
|
||||
Need to look at the possibility of loading this into ``__context__``
|
||||
'''
|
||||
# Initialize the PyCom system
|
||||
pythoncom.CoInitialize()
|
||||
with salt.utils.winapi.Com():
|
||||
|
||||
# Create a session with the Windows Update Agent
|
||||
self._session = win32com.client.Dispatch('Microsoft.Update.Session')
|
||||
# Create a session with the Windows Update Agent
|
||||
self._session = win32com.client.Dispatch('Microsoft.Update.Session')
|
||||
|
||||
# Create Collection for Updates
|
||||
self._updates = win32com.client.Dispatch('Microsoft.Update.UpdateColl')
|
||||
# Create Collection for Updates
|
||||
self._updates = win32com.client.Dispatch('Microsoft.Update.UpdateColl')
|
||||
|
||||
self.refresh()
|
||||
|
||||
@ -572,7 +583,8 @@ class WindowsUpdateAgent(object):
|
||||
# Initialize the downloader object and list collection
|
||||
downloader = self._session.CreateUpdateDownloader()
|
||||
self._session.ClientApplicationID = 'Salt: Download Update'
|
||||
download_list = win32com.client.Dispatch('Microsoft.Update.UpdateColl')
|
||||
with salt.utils.winapi.Com():
|
||||
download_list = win32com.client.Dispatch('Microsoft.Update.UpdateColl')
|
||||
|
||||
ret = {'Updates': {}}
|
||||
|
||||
@ -683,7 +695,8 @@ class WindowsUpdateAgent(object):
|
||||
|
||||
installer = self._session.CreateUpdateInstaller()
|
||||
self._session.ClientApplicationID = 'Salt: Install Update'
|
||||
install_list = win32com.client.Dispatch('Microsoft.Update.UpdateColl')
|
||||
with salt.utils.winapi.Com():
|
||||
install_list = win32com.client.Dispatch('Microsoft.Update.UpdateColl')
|
||||
|
||||
ret = {'Updates': {}}
|
||||
|
||||
@ -802,7 +815,8 @@ class WindowsUpdateAgent(object):
|
||||
|
||||
installer = self._session.CreateUpdateInstaller()
|
||||
self._session.ClientApplicationID = 'Salt: Install Update'
|
||||
uninstall_list = win32com.client.Dispatch('Microsoft.Update.UpdateColl')
|
||||
with salt.utils.winapi.Com():
|
||||
uninstall_list = win32com.client.Dispatch('Microsoft.Update.UpdateColl')
|
||||
|
||||
ret = {'Updates': {}}
|
||||
|
||||
@ -999,8 +1013,7 @@ def needs_reboot():
|
||||
|
||||
'''
|
||||
# Initialize the PyCom system
|
||||
pythoncom.CoInitialize()
|
||||
|
||||
# Create an AutoUpdate object
|
||||
obj_sys = win32com.client.Dispatch('Microsoft.Update.SystemInfo')
|
||||
with salt.utils.winapi.Com():
|
||||
# Create an AutoUpdate object
|
||||
obj_sys = win32com.client.Dispatch('Microsoft.Update.SystemInfo')
|
||||
return salt.utils.data.is_true(obj_sys.RebootRequired)
|
||||
|
@ -651,14 +651,18 @@ def _fetch_events(q):
|
||||
|
||||
atexit.register(_clean_queue)
|
||||
a_config = AdaptedConfigurationTestCaseMixin()
|
||||
event = salt.utils.event.get_event('minion', sock_dir=a_config.get_config('minion')['sock_dir'], opts=a_config.get_config('minion'))
|
||||
event = salt.utils.event.get_event(
|
||||
'minion',
|
||||
sock_dir=a_config.get_config('minion')['sock_dir'],
|
||||
opts=a_config.get_config('minion'),
|
||||
)
|
||||
while True:
|
||||
try:
|
||||
events = event.get_event(full=False)
|
||||
except Exception:
|
||||
except Exception as exc:
|
||||
# This is broad but we'll see all kinds of issues right now
|
||||
# if we drop the proc out from under the socket while we're reading
|
||||
pass
|
||||
log.exception("Exception caught while getting events %r", exc)
|
||||
q.put(events)
|
||||
|
||||
|
||||
|
102
tests/support/nox-windows-setup.py
Normal file
102
tests/support/nox-windows-setup.py
Normal file
@ -0,0 +1,102 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
'''
|
||||
tests.support.nox-windows-setup
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
This script is meant to run under the nox virtualenv to take care of required
|
||||
windows procedures
|
||||
'''
|
||||
# pylint: disable=resource-leakage
|
||||
|
||||
from __future__ import absolute_import, print_function, unicode_literals
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
import site
|
||||
import shutil
|
||||
|
||||
try:
|
||||
import site
|
||||
SITE_PACKAGES = site.getsitepackages()
|
||||
PYTHON_EXECUTABLE_DIRECTORY = os.path.dirname(sys.executable)
|
||||
PYTHON_SCRIPTS_DIR = os.path.join(PYTHON_EXECUTABLE_DIRECTORY, 'Scripts')
|
||||
except AttributeError:
|
||||
# The site module does not have the getsitepackages function when running within a virtualenv
|
||||
# But the site-packages directory WILL be on sys.path
|
||||
SITE_PACKAGES = None
|
||||
for entry in sys.path:
|
||||
if 'site-packages' in entry:
|
||||
SITE_PACKAGES = entry
|
||||
break
|
||||
# Under a virtualenv, the python "binary" is under Scripts already.
|
||||
# Well, not the binary, but the Python DLLs
|
||||
PYTHON_EXECUTABLE_DIRECTORY = PYTHON_SCRIPTS_DIR = os.path.dirname(sys.executable)
|
||||
|
||||
# Requests is a Salt dependency, it's safe to import, but...
|
||||
try:
|
||||
import requests
|
||||
HAS_REQUESTS = True
|
||||
except ImportError:
|
||||
HAS_REQUESTS = False
|
||||
|
||||
IS_64_BITS = sys.maxsize > 2**32
|
||||
SALT_REPO_URL = 'https://repo.saltstack.com/windows/dependencies/{}'.format(IS_64_BITS and 64 or 32)
|
||||
DLLS = ("libeay32.dll", "ssleay32.dll", "OpenSSL_License.txt", "msvcr120.dll", "libsodium.dll")
|
||||
|
||||
for dll in DLLS:
|
||||
outfile = os.path.join(PYTHON_EXECUTABLE_DIRECTORY, dll)
|
||||
if os.path.exists(outfile):
|
||||
continue
|
||||
src_url = '{}/{}'.format(SALT_REPO_URL, dll)
|
||||
if HAS_REQUESTS:
|
||||
print('Downloading {} to {}'.format(src_url, outfile))
|
||||
request = requests.get(src_url, allow_redirects=True)
|
||||
with open(outfile, 'wb') as wfh:
|
||||
wfh.write(request.content)
|
||||
else:
|
||||
print('ATTENTION: The python requests package is not installed, can\'t download {}'.format(src_url))
|
||||
|
||||
PYWIN32_SYSTEM32_DIR = os.path.join(SITE_PACKAGES, 'pywin32_system32')
|
||||
if os.path.exists(PYWIN32_SYSTEM32_DIR):
|
||||
for fname in os.listdir(PYWIN32_SYSTEM32_DIR):
|
||||
if not fname.endswith('.dll'):
|
||||
continue
|
||||
spath = os.path.join(PYWIN32_SYSTEM32_DIR, fname)
|
||||
dpath = spath.replace('pywin32_system32', 'win32')
|
||||
print('Moving {} to {}'.format(spath, dpath))
|
||||
shutil.move(spath, dpath)
|
||||
|
||||
print('Deleting {}'.format(PYWIN32_SYSTEM32_DIR))
|
||||
shutil.rmtree(PYWIN32_SYSTEM32_DIR, ignore_errors=True)
|
||||
|
||||
|
||||
if os.path.exists(PYTHON_SCRIPTS_DIR):
|
||||
print('Searching for pywin32 scripts to delete')
|
||||
for fname in os.listdir(PYTHON_SCRIPTS_DIR):
|
||||
if not fname.startswith('pywin32_'):
|
||||
continue
|
||||
fpath = os.path.join(PYTHON_SCRIPTS_DIR, fname)
|
||||
print('Deleting {}'.format(fpath))
|
||||
os.unlink(fpath)
|
||||
|
||||
|
||||
PYTHONWIN_DIR = os.path.join(SITE_PACKAGES, 'pythonwin')
|
||||
if os.path.exists(PYTHONWIN_DIR):
|
||||
print('Deleting {}'.format(PYTHONWIN_DIR))
|
||||
shutil.rmtree(PYTHONWIN_DIR, ignore_errors=True)
|
||||
|
||||
PYCRPTO_NT_FILE = os.path.join(SITE_PACKAGES, 'Crypto', 'Random', 'OSRNG', 'nt.py')
|
||||
if os.path.exists(PYCRPTO_NT_FILE):
|
||||
with open(PYCRPTO_NT_FILE, 'r') as rfh:
|
||||
contents = rfh.read()
|
||||
new_contents = re.sub(
|
||||
r'^import winrandom$',
|
||||
'from Crypto.Random.OSRNG import winrandom',
|
||||
contents,
|
||||
count=1,
|
||||
flags=re.MULTILINE
|
||||
)
|
||||
if contents != new_contents:
|
||||
print('Patching {}'.format(PYCRPTO_NT_FILE))
|
||||
with open(PYCRPTO_NT_FILE, 'w') as wfh:
|
||||
wfh.write(new_contents)
|
@ -120,7 +120,10 @@ class DiskUsageBeaconTestCase(TestCase, LoaderModuleMockMixin):
|
||||
ret = diskusage.beacon(config)
|
||||
self.assertEqual(ret, [{'diskusage': 50, 'mount': '/'}])
|
||||
|
||||
def test_diskusage_windows(self):
|
||||
def test_diskusage_windows_single_slash(self):
|
||||
r'''
|
||||
This tests new behavior (C:\)
|
||||
'''
|
||||
disk_usage_mock = Mock(return_value=WINDOWS_STUB_DISK_USAGE)
|
||||
with patch('salt.utils.platform.is_windows',
|
||||
MagicMock(return_value=True)):
|
||||
@ -136,6 +139,44 @@ class DiskUsageBeaconTestCase(TestCase, LoaderModuleMockMixin):
|
||||
ret = diskusage.beacon(config)
|
||||
self.assertEqual(ret, [{'diskusage': 50, 'mount': 'C:\\'}])
|
||||
|
||||
def test_diskusage_windows_double_slash(self):
|
||||
'''
|
||||
This tests original behavior (C:\\)
|
||||
'''
|
||||
disk_usage_mock = Mock(return_value=WINDOWS_STUB_DISK_USAGE)
|
||||
with patch('salt.utils.platform.is_windows',
|
||||
MagicMock(return_value=True)):
|
||||
with patch('psutil.disk_partitions',
|
||||
MagicMock(return_value=WINDOWS_STUB_DISK_PARTITION)), \
|
||||
patch('psutil.disk_usage', disk_usage_mock):
|
||||
config = [{'C:\\\\': '50%'}]
|
||||
|
||||
ret = diskusage.validate(config)
|
||||
|
||||
self.assertEqual(ret, (True, 'Valid beacon configuration'))
|
||||
|
||||
ret = diskusage.beacon(config)
|
||||
self.assertEqual(ret, [{'diskusage': 50, 'mount': 'C:\\'}])
|
||||
|
||||
def test_diskusage_windows_lowercase(self):
|
||||
r'''
|
||||
This tests lowercase drive letter (c:\)
|
||||
'''
|
||||
disk_usage_mock = Mock(return_value=WINDOWS_STUB_DISK_USAGE)
|
||||
with patch('salt.utils.platform.is_windows',
|
||||
MagicMock(return_value=True)):
|
||||
with patch('psutil.disk_partitions',
|
||||
MagicMock(return_value=WINDOWS_STUB_DISK_PARTITION)), \
|
||||
patch('psutil.disk_usage', disk_usage_mock):
|
||||
config = [{'c:\\': '50%'}]
|
||||
|
||||
ret = diskusage.validate(config)
|
||||
|
||||
self.assertEqual(ret, (True, 'Valid beacon configuration'))
|
||||
|
||||
ret = diskusage.beacon(config)
|
||||
self.assertEqual(ret, [{'diskusage': 50, 'mount': 'C:\\'}])
|
||||
|
||||
def test_diskusage_windows_match_regex(self):
|
||||
disk_usage_mock = Mock(return_value=WINDOWS_STUB_DISK_USAGE)
|
||||
with patch('salt.utils.platform.is_windows',
|
||||
|
@ -156,3 +156,43 @@ class INotifyBeaconTestCase(TestCase, LoaderModuleMockMixin):
|
||||
self.assertEqual(len(ret), 1)
|
||||
self.assertEqual(ret[0]['path'], fp)
|
||||
self.assertEqual(ret[0]['change'], 'IN_DELETE')
|
||||
|
||||
def test_multi_files_exclude(self):
|
||||
dp1 = os.path.join(self.tmpdir, 'subdir1')
|
||||
dp2 = os.path.join(self.tmpdir, 'subdir2')
|
||||
os.mkdir(dp1)
|
||||
os.mkdir(dp2)
|
||||
_exclude1 = '{0}/subdir1/*tmpfile*$'.format(self.tmpdir)
|
||||
_exclude2 = '{0}/subdir2/*filetmp*$'.format(self.tmpdir)
|
||||
config = [{'files': {dp1: {'mask': ['create', 'delete'],
|
||||
'recurse': True,
|
||||
'exclude': [{_exclude1: {'regex': True}}],
|
||||
'auto_add': True}}},
|
||||
{'files': {dp2: {'mask': ['create', 'delete'],
|
||||
'recurse': True,
|
||||
'exclude': [{_exclude2: {'regex': True}}],
|
||||
'auto_add': True}}}]
|
||||
ret = inotify.validate(config)
|
||||
self.assertEqual(ret, (True, 'Valid beacon configuration'))
|
||||
|
||||
fp = os.path.join(dp1, 'tmpfile')
|
||||
with salt.utils.files.fopen(fp, 'w') as f:
|
||||
pass
|
||||
ret = inotify.beacon(config)
|
||||
self.assertEqual(len(ret), 0)
|
||||
os.remove(fp)
|
||||
ret = inotify.beacon(config)
|
||||
self.assertEqual(len(ret), 0)
|
||||
|
||||
fp = os.path.join(dp2, 'tmpfile')
|
||||
with salt.utils.files.fopen(fp, 'w') as f:
|
||||
pass
|
||||
ret = inotify.beacon(config)
|
||||
self.assertEqual(len(ret), 1)
|
||||
self.assertEqual(ret[0]['path'], fp)
|
||||
self.assertEqual(ret[0]['change'], 'IN_CREATE')
|
||||
os.remove(fp)
|
||||
ret = inotify.beacon(config)
|
||||
self.assertEqual(len(ret), 1)
|
||||
self.assertEqual(ret[0]['path'], fp)
|
||||
self.assertEqual(ret[0]['change'], 'IN_DELETE')
|
||||
|
@ -297,6 +297,13 @@ class MountTestCase(TestCase, LoaderModuleMockMixin):
|
||||
'stderr': True})
|
||||
with patch.dict(mount.__salt__, {'cmd.run_all': mock}):
|
||||
self.assertTrue(mount.mount('name', 'device'))
|
||||
mock.assert_called_with('mount device name ',
|
||||
python_shell=False, runas=None)
|
||||
|
||||
with patch.dict(mount.__salt__, {'cmd.run_all': mock}):
|
||||
self.assertTrue(mount.mount('name', 'device', fstype='fstype'))
|
||||
mock.assert_called_with('mount -t fstype device name ',
|
||||
python_shell=False, runas=None)
|
||||
|
||||
mock = MagicMock(return_value={'retcode': False,
|
||||
'stderr': False})
|
||||
@ -312,6 +319,35 @@ class MountTestCase(TestCase, LoaderModuleMockMixin):
|
||||
'stderr': True})
|
||||
with patch.dict(mount.__salt__, {'cmd.run_all': mock}):
|
||||
self.assertTrue(mount.mount('name', 'device'))
|
||||
mock.assert_called_with('mount device name ',
|
||||
python_shell=False, runas=None)
|
||||
|
||||
with patch.dict(mount.__salt__, {'cmd.run_all': mock}):
|
||||
self.assertTrue(mount.mount('name', 'device', fstype='fstype'))
|
||||
mock.assert_called_with('mount -v fstype device name ',
|
||||
python_shell=False, runas=None)
|
||||
|
||||
mock = MagicMock(return_value={'retcode': False,
|
||||
'stderr': False})
|
||||
with patch.dict(mount.__salt__, {'cmd.run_all': mock}):
|
||||
self.assertTrue(mount.mount('name', 'device'))
|
||||
|
||||
with patch.dict(mount.__grains__, {'os': 'Linux'}):
|
||||
mock = MagicMock(return_value=True)
|
||||
with patch.object(os.path, 'exists', mock):
|
||||
mock = MagicMock(return_value=None)
|
||||
with patch.dict(mount.__salt__, {'file.mkdir': None}):
|
||||
mock = MagicMock(return_value={'retcode': True,
|
||||
'stderr': True})
|
||||
with patch.dict(mount.__salt__, {'cmd.run_all': mock}):
|
||||
self.assertTrue(mount.mount('name', 'device'))
|
||||
mock.assert_called_with('mount -o defaults device name ',
|
||||
python_shell=False, runas=None)
|
||||
|
||||
with patch.dict(mount.__salt__, {'cmd.run_all': mock}):
|
||||
self.assertTrue(mount.mount('name', 'device', fstype='fstype'))
|
||||
mock.assert_called_with('mount -o defaults -t fstype device name ',
|
||||
python_shell=False, runas=None)
|
||||
|
||||
mock = MagicMock(return_value={'retcode': False,
|
||||
'stderr': False})
|
||||
|
@ -38,6 +38,11 @@ class WinSystemTestCase(TestCase, LoaderModuleMockMixin):
|
||||
now.day, now.hour, now.minute,
|
||||
now.second, now.microsecond])
|
||||
modules_globals['win32api'] = win32api
|
||||
win32net = types.ModuleType(str('win32net')) # future lint: disable=blacklisted-function
|
||||
win32net.NetServerGetInfo = MagicMock()
|
||||
win32net.NetServerSetInfo = MagicMock()
|
||||
modules_globals['win32net'] = win32net
|
||||
|
||||
return {win_system: modules_globals}
|
||||
|
||||
def test_halt(self):
|
||||
@ -177,14 +182,15 @@ class WinSystemTestCase(TestCase, LoaderModuleMockMixin):
|
||||
'''
|
||||
Test to set the Windows computer description
|
||||
'''
|
||||
mock = MagicMock(return_value=True)
|
||||
with patch.dict(win_system.__salt__, {'cmd.run': mock}):
|
||||
mock = MagicMock(return_value="Salt's comp")
|
||||
with patch.object(win_system, 'get_computer_desc', mock):
|
||||
self.assertDictEqual(win_system.set_computer_desc(
|
||||
"Salt's comp"
|
||||
),
|
||||
{'Computer Description': "Salt's comp"})
|
||||
mock = MagicMock()
|
||||
mock_get_info = MagicMock(return_value={'comment': ''})
|
||||
mock_get_desc = MagicMock(return_value="Salt's comp")
|
||||
with patch('salt.modules.win_system.win32net.NetServerGetInfo', mock_get_info), \
|
||||
patch('salt.modules.win_system.win32net.NetServerSetInfo', mock), \
|
||||
patch.object(win_system, 'get_computer_desc', mock_get_desc):
|
||||
self.assertDictEqual(
|
||||
win_system.set_computer_desc("Salt's comp"),
|
||||
{'Computer Description': "Salt's comp"})
|
||||
|
||||
@skipIf(not win_system.HAS_WIN32NET_MODS, 'this test needs the w32net library')
|
||||
def test_get_computer_desc(self):
|
||||
|
71
tests/unit/test__compat.py
Normal file
71
tests/unit/test__compat.py
Normal file
@ -0,0 +1,71 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
'''
|
||||
Unit tests for salt._compat
|
||||
'''
|
||||
|
||||
# Import Python libs
|
||||
from __future__ import absolute_import, print_function, unicode_literals
|
||||
import logging
|
||||
import sys
|
||||
|
||||
# Import Salt Testing libs
|
||||
from tests.support.unit import TestCase
|
||||
|
||||
# Import Salt libs
|
||||
import salt._compat as compat
|
||||
|
||||
# Import 3rd Party libs
|
||||
from salt.ext.six import binary_type, text_type
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
PY3 = sys.version_info.major == 3
|
||||
|
||||
|
||||
class CompatTestCase(TestCase):
|
||||
def test_text(self):
|
||||
ret = compat.text_('test string')
|
||||
self.assertTrue(isinstance(ret, text_type))
|
||||
|
||||
def test_text_binary(self):
|
||||
ret = compat.text_(b'test string')
|
||||
self.assertTrue(isinstance(ret, text_type))
|
||||
|
||||
def test_bytes(self):
|
||||
ret = compat.bytes_('test string')
|
||||
self.assertTrue(isinstance(ret, binary_type))
|
||||
|
||||
def test_bytes_binary(self):
|
||||
ret = compat.bytes_(b'test string')
|
||||
self.assertTrue(isinstance(ret, binary_type))
|
||||
|
||||
def test_ascii_native(self):
|
||||
ret = compat.ascii_native_('test string')
|
||||
self.assertTrue(isinstance(ret, str))
|
||||
|
||||
def test_ascii_native_binary(self):
|
||||
ret = compat.ascii_native_(b'test string')
|
||||
self.assertTrue(isinstance(ret, str))
|
||||
|
||||
def test_native(self):
|
||||
ret = compat.native_('test string')
|
||||
self.assertTrue(isinstance(ret, str))
|
||||
|
||||
def test_native_binary(self):
|
||||
ret = compat.native_(b'test string')
|
||||
self.assertTrue(isinstance(ret, str))
|
||||
|
||||
def test_string_io(self):
|
||||
ret = compat.string_io('test string')
|
||||
if PY3:
|
||||
expected = 'io.StringIO object'
|
||||
else:
|
||||
expected = 'cStringIO.StringI object'
|
||||
self.assertTrue(expected in repr(ret))
|
||||
|
||||
def test_string_io_unicode(self):
|
||||
ret = compat.string_io(u'test string \xf8')
|
||||
if PY3:
|
||||
expected = 'io.StringIO object'
|
||||
else:
|
||||
expected = 'StringIO.StringIO instance'
|
||||
self.assertTrue(expected in repr(ret))
|
Loading…
Reference in New Issue
Block a user