Merge branch '2017.7' into improve-async-operation-handling-in-kubernetes-module

This commit is contained in:
Jochen Breuer 2017-08-29 19:13:51 +02:00 committed by GitHub
commit dcd8d4f639
41 changed files with 1265 additions and 711 deletions

View file

@ -59,15 +59,14 @@
# Directory for custom modules. This directory can contain subdirectories for
# each of Salt's module types such as "runners", "output", "wheel", "modules",
# "states", "returners", etc.
#extension_modules: <no default>
# "states", "returners", "engines", "utils", etc.
#extension_modules: /var/cache/salt/master/extmods
# Directory for custom modules. This directory can contain subdirectories for
# each of Salt's module types such as "runners", "output", "wheel", "modules",
# "states", "returners", "engines", etc.
# "states", "returners", "engines", "utils", etc.
# Like 'extension_modules' but can take an array of paths
#module_dirs: <no default>
# - /var/cache/salt/minion/extmods
#module_dirs: []
# Verify and set permissions on configuration directories at startup:
#verify_env: True

View file

@ -183,8 +183,8 @@ The directory to store the pki authentication keys.
Directory for custom modules. This directory can contain subdirectories for
each of Salt's module types such as ``runners``, ``output``, ``wheel``,
``modules``, ``states``, ``returners``, ``engines``, etc. This path is appended to
:conf_master:`root_dir`.
``modules``, ``states``, ``returners``, ``engines``, ``utils``, etc.
This path is appended to :conf_master:`root_dir`.
.. code-block:: yaml

View file

@ -260,6 +260,13 @@ The Salt development team will back-port bug fixes made to ``develop`` to the
current release branch if the contributor cannot create the pull request
against that branch.
Release Branches
----------------
For each release a branch will be created when we are ready to tag. The branch will be the same name as the tag minus the v. For example, the v2017.7.1 release was created from the 2017.7.1 branch. This branching strategy will allow for more stability when there is a need for a re-tag during the testing phase of our releases.
Once the branch is created, the fixes required for a given release, as determined by the SaltStack release team, will be added to this branch. All commits in this branch will be merged forward into the parent branch as well.
Keeping Salt Forks in Sync
==========================

View file

@ -87,8 +87,8 @@ Also you could even write your utility modules in object oriented fashion:
# -*- coding: utf-8 -*-
'''
My utils module
---------------
My OOP-style utils module
-------------------------
This module contains common functions for use in my other custom types.
'''

View file

@ -15,91 +15,119 @@
# This script is run as a part of the macOS Salt Installation
#
###############################################################################
echo "Post install started on:" > /tmp/postinstall.txt
date >> /tmp/postinstall.txt
###############################################################################
# Define Variables
###############################################################################
# Get Minor Version
OSX_VERSION=$(sw_vers | grep ProductVersion | cut -f 2 -d: | tr -d '[:space:]')
MINOR=$(echo ${OSX_VERSION} | cut -f 2 -d.)
# Path Variables
INSTALL_DIR="/opt/salt"
BIN_DIR="$INSTALL_DIR/bin"
CONFIG_DIR="/etc/salt"
TEMP_DIR="/tmp"
SBIN_DIR="/usr/local/sbin"
###############################################################################
# Set up logging and error handling
###############################################################################
echo "Post install script started on:" > "$TEMP_DIR/postinstall.txt"
date "+%Y/%m/%d %H:%m:%S" >> "$TEMP_DIR/postinstall.txt"
trap 'quit_on_error $LINENO $BASH_COMMAND' ERR
quit_on_error() {
echo "$(basename $0) caught error on line : $1 command was: $2" >> /tmp/postinstall.txt
echo "$(basename $0) caught error on line : $1 command was: $2" >> "$TEMP_DIR/postinstall.txt"
exit -1
}
###############################################################################
# Check for existing minion config, copy if it doesn't exist
###############################################################################
if [ ! -f /etc/salt/minion ]; then
echo "Config copy: Started..." >> /tmp/postinstall.txt
cp /etc/salt/minion.dist /etc/salt/minion
echo "Config copy: Successful" >> /tmp/postinstall.txt
if [ ! -f "$CONFIG_DIR/minion" ]; then
echo "Config: Copy Started..." >> "$TEMP_DIR/postinstall.txt"
cp "$CONFIG_DIR/minion.dist" "$CONFIG_DIR/minion"
echo "Config: Copied Successfully" >> "$TEMP_DIR/postinstall.txt"
fi
###############################################################################
# Create symlink to salt-config.sh
###############################################################################
# echo "Symlink: Creating symlink for salt-config..." >> /tmp/postinstall.txt
if [ ! -d "/usr/local/sbin" ]; then
mkdir /usr/local/sbin
if [ ! -d "$SBIN_DIR" ]; then
echo "Symlink: Creating $SBIN_DIR..." >> "$TEMP_DIR/postinstall.txt"
mkdir "$SBIN_DIR"
echo "Symlink: Created Successfully" >> "$TEMP_DIR/postinstall.txt"
fi
ln -sf /opt/salt/bin/salt-config.sh /usr/local/sbin/salt-config
echo "Symlink: Creating symlink for salt-config..." >> "$TEMP_DIR/postinstall.txt"
ln -sf "$BIN_DIR/salt-config.sh" "$SBIN_DIR/salt-config"
echo "Symlink: Created Successfully" >> "$TEMP_DIR/postinstall.txt"
###############################################################################
# Add salt to paths.d
###############################################################################
# echo "Path: Adding salt to the path..." >> /tmp/postinstall.txt
if [ ! -d "/etc/paths.d" ]; then
echo "Path: Creating paths.d directory..." >> "$TEMP_DIR/postinstall.txt"
mkdir /etc/paths.d
echo "Path: Created Successfully" >> "$TEMP_DIR/postinstall.txt"
fi
sh -c 'echo "/opt/salt/bin" > /etc/paths.d/salt'
sh -c 'echo "/usr/local/sbin" >> /etc/paths.d/salt'
echo "Path: Adding salt to the path..." >> "$TEMP_DIR/postinstall.txt"
sh -c "echo \"$BIN_DIR\" > /etc/paths.d/salt"
sh -c "echo \"$SBIN_DIR\" >> /etc/paths.d/salt"
echo "Path: Added Successfully" >> "$TEMP_DIR/postinstall.txt"
###############################################################################
# Register Salt as a service
###############################################################################
setup_services_maverick() {
echo "Using old (< 10.10) launchctl interface" >> /tmp/postinstall.txt
echo "Service: Using old (< 10.10) launchctl interface" >> "$TEMP_DIR/postinstall.txt"
if /bin/launchctl list "com.saltstack.salt.minion" &> /dev/null; then
echo "Stop running service..." >> /tmp/postinstall.txt
echo "Service: Stopping salt-minion..." >> "$TEMP_DIR/postinstall.txt"
launchctl unload -w /Library/LaunchDaemons/com.saltstack.salt.minion.plist
echo "Service: Stopped Successfully" >> "$TEMP_DIR/postinstall.txt"
fi;
echo "Service: Starting salt-minion..." >> "$TEMP_DIR/postinstall.txt"
launchctl load -w /Library/LaunchDaemons/com.saltstack.salt.minion.plist || return 1
echo "Service: Started Successfully" >> "$TEMP_DIR/postinstall.txt"
echo "Service start: Successful" >> /tmp/postinstall.txt
echo "Service disable: Disabling Master, Syndic, and API" >> /tmp/postinstall.txt
echo "Service: Disabling Master, Syndic, and API services..." >> "$TEMP_DIR/postinstall.txt"
launchctl unload -w /Library/LaunchDaemons/com.saltstack.salt.api.plist
launchctl unload -w /Library/LaunchDaemons/com.saltstack.salt.master.plist
launchctl unload -w /Library/LaunchDaemons/com.saltstack.salt.syndic.plist
echo "Service: Disabled Successfully" >> "$TEMP_DIR/postinstall.txt"
return 0
}
setup_services_yosemite_and_later() {
echo "Using new (>= 10.10) launchctl interface" >> /tmp/postinstall.txt
echo "Service: Using new (>= 10.10) launchctl interface" >> "$TEMP_DIR/postinstall.txt"
echo "Service: Enabling salt-minion..." >> "$TEMP_DIR/postinstall.txt"
launchctl enable system/com.saltstack.salt.minion
echo "Service start: Bootstrapping service..." >> /tmp/postinstall.txt
echo "Service: Enabled Successfully" >> "$TEMP_DIR/postinstall.txt"
echo "Service: Bootstrapping salt-minion..." >> "$TEMP_DIR/postinstall.txt"
launchctl bootstrap system /Library/LaunchDaemons/com.saltstack.salt.minion.plist
echo "Service: Bootstrapped Successfully" >> "$TEMP_DIR/postinstall.txt"
if /bin/launchctl list "com.saltstack.salt.minion" &> /dev/null; then
echo "Service is running" >> /tmp/postinstall.txt
echo "Service: Service Running" >> "$TEMP_DIR/postinstall.txt"
else
echo "Service start: Kickstarting service..." >> /tmp/postinstall.txt
echo "Service: Kickstarting Service..." >> "$TEMP_DIR/postinstall.txt"
launchctl kickstart -kp system/com.saltstack.salt.minion
echo "Service: Kickstarted Successfully" >> "$TEMP_DIR/postinstall.txt"
fi
echo "Service start: Successful" >> /tmp/postinstall.txt
echo "Service disable: Disabling Master, Syndic, and API" >> /tmp/postinstall.txt
echo "Service: Started Successfully" >> "$TEMP_DIR/postinstall.txt"
echo "Service: Disabling Master, Syndic, and API services" >> "$TEMP_DIR/postinstall.txt"
launchctl disable system/com.saltstack.salt.master
launchctl disable system/com.saltstack.salt.syndic
launchctl disable system/com.saltstack.salt.api
echo "Service: Disabled Successfully" >> "$TEMP_DIR/postinstall.txt"
return 0
}
OSX_VERSION=$(sw_vers | grep ProductVersion | cut -f 2 -d: | tr -d '[:space:]')
MINOR=$(echo ${OSX_VERSION} | cut -f 2 -d.)
echo "Service start: Enabling service..." >> /tmp/postinstall.txt
echo "Service: Configuring..." >> "$TEMP_DIR/postinstall.txt"
case $MINOR in
9 )
setup_services_maverick;
@ -108,7 +136,9 @@ case $MINOR in
setup_services_yosemite_and_later;
;;
esac
echo "Service: Configured Successfully" >> "$TEMP_DIR/postinstall.txt"
echo "Post install completed successfully" >> /tmp/postinstall.txt
echo "Post install completed successfully on:" >> "$TEMP_DIR/postinstall.txt"
date "+%Y/%m/%d %H:%m:%S" >> "$TEMP_DIR/postinstall.txt"
exit 0

View file

@ -6,7 +6,8 @@
# Date: December 2015
#
# Description: This script stops the salt minion service before attempting to
# install Salt on macOS
# install Salt on macOS. It also removes the /opt/salt/bin
# directory, symlink to salt-config, and salt from paths.d.
#
# Requirements:
# - None
@ -15,12 +16,29 @@
# This script is run as a part of the macOS Salt Installation
#
###############################################################################
echo "Preinstall started on:" > /tmp/preinstall.txt
date >> /tmp/preinstall.txt
###############################################################################
# Define Variables
###############################################################################
# Get Minor Version
OSX_VERSION=$(sw_vers | grep ProductVersion | cut -f 2 -d: | tr -d '[:space:]')
MINOR=$(echo ${OSX_VERSION} | cut -f 2 -d.)
# Path Variables
INSTALL_DIR="/opt/salt"
BIN_DIR="$INSTALL_DIR/bin"
CONFIG_DIR="/etc/salt"
TEMP_DIR="/tmp"
SBIN_DIR="/usr/local/sbin"
###############################################################################
# Set up logging and error handling
###############################################################################
echo "Preinstall started on:" > "$TEMP_DIR/preinstall.txt"
date "+%Y/%m/%d %H:%m:%S" >> "$TEMP_DIR/preinstall.txt"
trap 'quit_on_error $LINENO $BASH_COMMAND' ERR
quit_on_error() {
echo "$(basename $0) caught error on line : $1 command was: $2" >> /tmp/preinstall.txt
echo "$(basename $0) caught error on line : $1 command was: $2" >> "$TEMP_DIR/preinstall.txt"
exit -1
}
@ -31,24 +49,58 @@ MINOR=$(echo ${OSX_VERSION} | cut -f 2 -d.)
# Stop the service
###############################################################################
stop_service_maverick() {
echo "Using old (< 10.10) launchctl interface" >> /tmp/preinstall.txt
echo "Service: Using old (< 10.10) launchctl interface" >> "$TEMP_DIR/preinstall.txt"
if /bin/launchctl list "com.saltstack.salt.minion" &> /dev/null; then
echo "Stop service: Started..." >> /tmp/preinstall.txt
echo "Service: Unloading minion..." >> "$TEMP_DIR/preinstall.txt"
launchctl unload -w /Library/LaunchDaemons/com.saltstack.salt.minion.plist
echo "Stop service: Successful" >> /tmp/preinstall.txt
echo "Service: Unloaded Successfully" >> "$TEMP_DIR/preinstall.txt"
fi
if /bin/launchctl list "com.saltstack.salt.master" &> /dev/null; then
echo "Service: Unloading master..." >> "$TEMP_DIR/preinstall.txt"
launchctl unload -w /Library/LaunchDaemons/com.saltstack.salt.master.plist
echo "Service: Unloaded Successfully" >> "$TEMP_DIR/preinstall.txt"
fi
if /bin/launchctl list "com.saltstack.salt.syndic" &> /dev/null; then
echo "Service: Unloading syndic..." >> "$TEMP_DIR/preinstall.txt"
launchctl unload -w /Library/LaunchDaemons/com.saltstack.salt.syndic.plist
echo "Service: Unloaded Successfully" >> "$TEMP_DIR/preinstall.txt"
fi
if /bin/launchctl list "com.saltstack.salt.api" &> /dev/null; then
echo "Service: Unloading api..." >> "$TEMP_DIR/preinstall.txt"
launchctl unload -w /Library/LaunchDaemons/com.saltstack.salt.api.plist
echo "Service: Unloaded Successfully" >> "$TEMP_DIR/preinstall.txt"
fi
}
stop_service_yosemite_and_later() {
echo "Using new (>= 10.10) launchctl interface" >> /tmp/preinstall.txt
echo "Service: Using new (>= 10.10) launchctl interface" >> "$TEMP_DIR/preinstall.txt"
if /bin/launchctl list "com.saltstack.salt.minion" &> /dev/null; then
echo "Stop service: Started..." >> /tmp/preinstall.txt
echo "Service: Stopping minion..." >> "$TEMP_DIR/preinstall.txt"
launchctl disable system/com.saltstack.salt.minion
launchctl bootout system /Library/LaunchDaemons/com.saltstack.salt.minion.plist
echo "Stop service: Successful" >> /tmp/preinstall.txt
echo "Service: Stopped Successfully" >> "$TEMP_DIR/preinstall.txt"
fi
if /bin/launchctl list "com.saltstack.salt.master" &> /dev/null; then
echo "Service: Stopping master..." >> "$TEMP_DIR/preinstall.txt"
launchctl disable system/com.saltstack.salt.master
launchctl bootout system /Library/LaunchDaemons/com.saltstack.salt.master.plist
echo "Service: Stopped Successfully" >> "$TEMP_DIR/preinstall.txt"
fi
if /bin/launchctl list "com.saltstack.salt.syndic" &> /dev/null; then
echo "Service: Stopping syndic..." >> "$TEMP_DIR/preinstall.txt"
launchctl disable system/com.saltstack.salt.syndic
launchctl bootout system /Library/LaunchDaemons/com.saltstack.salt.syndic.plist
echo "Service: Stopped Successfully" >> "$TEMP_DIR/preinstall.txt"
fi
if /bin/launchctl list "com.saltstack.salt.api" &> /dev/null; then
echo "Service: Stopping api..." >> "$TEMP_DIR/preinstall.txt"
launchctl disable system/com.saltstack.salt.api
launchctl bootout system /Library/LaunchDaemons/com.saltstack.salt.api.plist
echo "Service: Stopped Successfully" >> "$TEMP_DIR/preinstall.txt"
fi
}
echo "Service: Configuring..." >> "$TEMP_DIR/preinstall.txt"
case $MINOR in
9 )
stop_service_maverick;
@ -57,6 +109,36 @@ case $MINOR in
stop_service_yosemite_and_later;
;;
esac
echo "Preinstall Completed Successfully" >> /tmp/preinstall.txt
echo "Service: Configured Successfully" >> "$TEMP_DIR/preinstall.txt"
###############################################################################
# Remove the Symlink to salt-config.sh
###############################################################################
if [ -L "$SBIN_DIR/salt-config" ]; then
echo "Cleanup: Removing Symlink $BIN_DIR/salt-config" >> "$TEMP_DIR/preinstall.txt"
rm "$SBIN_DIR/salt-config"
echo "Cleanup: Removed Successfully" >> "$TEMP_DIR/preinstall.txt"
fi
###############################################################################
# Remove the $INSTALL_DIR directory
###############################################################################
if [ -d "$INSTALL_DIR" ]; then
echo "Cleanup: Removing $INSTALL_DIR" >> "$TEMP_DIR/preinstall.txt"
rm -rf "$INSTALL_DIR"
echo "Cleanup: Removed Successfully" >> "$TEMP_DIR/preinstall.txt"
fi
###############################################################################
# Remove the salt from the paths.d
###############################################################################
if [ ! -f "/etc/paths.d/salt" ]; then
echo "Path: Removing salt from the path..." >> "$TEMP_DIR/preinstall.txt"
rm "/etc/paths.d/salt"
echo "Path: Removed Successfully" >> "$TEMP_DIR/preinstall.txt"
fi
echo "Preinstall Completed Successfully on:" >> "$TEMP_DIR/preinstall.txt"
date "+%Y/%m/%d %H:%m:%S" >> "$TEMP_DIR/preinstall.txt"
exit 0

View file

@ -730,18 +730,9 @@ class Cloud(object):
continue
for vm_name, details in six.iteritems(vms):
# If VM was created with use_fqdn with either of the softlayer drivers,
# we need to strip the VM name and only search for the short hostname.
if driver == 'softlayer' or driver == 'softlayer_hw':
ret = []
for name in names:
name = name.split('.')[0]
ret.append(name)
if vm_name not in ret:
continue
# XXX: The logic below can be removed once the aws driver
# is removed
elif vm_name not in names:
if vm_name not in names:
continue
elif driver == 'ec2' and 'aws' in handled_drivers and \

View file

@ -508,7 +508,7 @@ def list_nodes_full(mask='mask[id]', call=None):
conn = get_conn(service='SoftLayer_Account')
response = conn.getVirtualGuests()
for node_id in response:
hostname = node_id['hostname'].split('.')[0]
hostname = node_id['hostname']
ret[hostname] = node_id
__utils__['cloud.cache_node_list'](ret, __active_provider_name__.split(':')[0], __opts__)
return ret
@ -594,9 +594,6 @@ def destroy(name, call=None):
transport=__opts__['transport']
)
# If the VM was created with use_fqdn, the short hostname will be used instead.
name = name.split('.')[0]
node = show_instance(name, call='action')
conn = get_conn()
response = conn.deleteObject(id=node['id'])

View file

@ -526,9 +526,6 @@ def destroy(name, call=None):
transport=__opts__['transport']
)
# If the VM was created with use_fqdn, the short hostname will be used instead.
name = name.split('.')[0]
node = show_instance(name, call='action')
conn = get_conn(service='SoftLayer_Ticket')
response = conn.createCancelServerTicket(

View file

@ -792,6 +792,8 @@ def _virtual(osdata):
grains['virtual_subtype'] = 'ovirt'
elif 'Google' in output:
grains['virtual'] = 'gce'
elif 'BHYVE' in output:
grains['virtual'] = 'bhyve'
except IOError:
pass
elif osdata['kernel'] == 'FreeBSD':

View file

@ -139,19 +139,6 @@ def _reconstruct_ppa_name(owner_name, ppa_name):
return 'ppa:{0}/{1}'.format(owner_name, ppa_name)
def _get_repo(**kwargs):
'''
Check the kwargs for either 'fromrepo' or 'repo' and return the value.
'fromrepo' takes precedence over 'repo'.
'''
for key in ('fromrepo', 'repo'):
try:
return kwargs[key]
except KeyError:
pass
return ''
def _check_apt():
'''
Abort if python-apt is not installed
@ -246,18 +233,11 @@ def latest_version(*names, **kwargs):
'''
refresh = salt.utils.is_true(kwargs.pop('refresh', True))
show_installed = salt.utils.is_true(kwargs.pop('show_installed', False))
if 'repo' in kwargs:
# Remember to kill _get_repo() too when removing this warning.
salt.utils.warn_until(
'Hydrogen',
'The \'repo\' argument to apt.latest_version is deprecated, and '
'will be removed in Salt {version}. Please use \'fromrepo\' '
'instead.'
raise SaltInvocationError(
'The \'repo\' argument is invalid, use \'fromrepo\' instead'
)
fromrepo = _get_repo(**kwargs)
kwargs.pop('fromrepo', None)
kwargs.pop('repo', None)
fromrepo = kwargs.pop('fromrepo', None)
cache_valid_time = kwargs.pop('cache_valid_time', 0)
if len(names) == 0:
@ -1402,9 +1382,10 @@ def _get_upgradable(dist_upgrade=True, **kwargs):
cmd.append('dist-upgrade')
else:
cmd.append('upgrade')
fromrepo = _get_repo(**kwargs)
if fromrepo:
cmd.extend(['-o', 'APT::Default-Release={0}'.format(fromrepo)])
try:
cmd.extend(['-o', 'APT::Default-Release={0}'.format(kwargs['fromrepo'])])
except KeyError:
pass
call = __salt__['cmd.run_all'](cmd,
python_shell=False,

View file

@ -202,45 +202,48 @@ def _get_snapshot_url(artifactory_url, repository, group_id, artifact_id, versio
has_classifier = classifier is not None and classifier != ""
if snapshot_version is None:
snapshot_version_metadata = _get_snapshot_version_metadata(artifactory_url=artifactory_url, repository=repository, group_id=group_id, artifact_id=artifact_id, version=version, headers=headers)
try:
snapshot_version_metadata = _get_snapshot_version_metadata(artifactory_url=artifactory_url, repository=repository, group_id=group_id, artifact_id=artifact_id, version=version, headers=headers)
if packaging not in snapshot_version_metadata['snapshot_versions']:
error_message = '''Cannot find requested packaging '{packaging}' in the snapshot version metadata.
artifactory_url: {artifactory_url}
repository: {repository}
group_id: {group_id}
artifact_id: {artifact_id}
packaging: {packaging}
classifier: {classifier}
version: {version}'''.format(
artifactory_url=artifactory_url,
repository=repository,
group_id=group_id,
artifact_id=artifact_id,
packaging=packaging,
classifier=classifier,
version=version)
raise ArtifactoryError(error_message)
if packaging not in snapshot_version_metadata['snapshot_versions']:
error_message = '''Cannot find requested packaging '{packaging}' in the snapshot version metadata.
artifactory_url: {artifactory_url}
repository: {repository}
group_id: {group_id}
artifact_id: {artifact_id}
packaging: {packaging}
classifier: {classifier}
version: {version}'''.format(
artifactory_url=artifactory_url,
repository=repository,
group_id=group_id,
artifact_id=artifact_id,
packaging=packaging,
classifier=classifier,
version=version)
raise ArtifactoryError(error_message)
if has_classifier and classifier not in snapshot_version_metadata['snapshot_versions']:
error_message = '''Cannot find requested classifier '{classifier}' in the snapshot version metadata.
artifactory_url: {artifactory_url}
repository: {repository}
group_id: {group_id}
artifact_id: {artifact_id}
packaging: {packaging}
classifier: {classifier}
version: {version}'''.format(
artifactory_url=artifactory_url,
repository=repository,
group_id=group_id,
artifact_id=artifact_id,
packaging=packaging,
classifier=classifier,
version=version)
raise ArtifactoryError(error_message)
if has_classifier and classifier not in snapshot_version_metadata['snapshot_versions']:
error_message = '''Cannot find requested classifier '{classifier}' in the snapshot version metadata.
artifactory_url: {artifactory_url}
repository: {repository}
group_id: {group_id}
artifact_id: {artifact_id}
packaging: {packaging}
classifier: {classifier}
version: {version}'''.format(
artifactory_url=artifactory_url,
repository=repository,
group_id=group_id,
artifact_id=artifact_id,
packaging=packaging,
classifier=classifier,
version=version)
raise ArtifactoryError(error_message)
snapshot_version = snapshot_version_metadata['snapshot_versions'][packaging]
snapshot_version = snapshot_version_metadata['snapshot_versions'][packaging]
except CommandExecutionError as err:
log.error('Could not fetch maven-metadata.xml. Assuming snapshot_version=%s.', version)
snapshot_version = version
group_url = __get_group_id_subpath(group_id)

View file

@ -199,7 +199,7 @@ def execute(context=None, lens=None, commands=(), load_path=None):
method = METHOD_MAP[cmd]
nargs = arg_map[method]
parts = salt.utils.shlex_split(arg, posix=False)
parts = salt.utils.shlex_split(arg)
if len(parts) not in nargs:
err = '{0} takes {1} args: {2}'.format(method, nargs, parts)

View file

@ -22,6 +22,10 @@ import salt.utils.decorators as decorators
from salt.utils.decorators import depends
from salt.exceptions import CommandExecutionError
__func_alias__ = {
'format_': 'format'
}
log = logging.getLogger(__name__)
HAS_HDPARM = salt.utils.which('hdparm') is not None

View file

@ -899,9 +899,14 @@ def compare_container(first, second, ignore=None):
continue
val1 = result1[conf_dict][item]
val2 = result2[conf_dict].get(item)
if item in ('OomKillDisable',):
if item in ('OomKillDisable',) or (val1 is None or val2 is None):
if bool(val1) != bool(val2):
ret.setdefault(conf_dict, {})[item] = {'old': val1, 'new': val2}
elif item == 'Image':
image1 = inspect_image(val1)['Id']
image2 = inspect_image(val2)['Id']
if image1 != image2:
ret.setdefault(conf_dict, {})[item] = {'old': image1, 'new': image2}
else:
if item == 'Links':
val1 = _scrub_links(val1, first)
@ -917,9 +922,14 @@ def compare_container(first, second, ignore=None):
continue
val1 = result1[conf_dict].get(item)
val2 = result2[conf_dict][item]
if item in ('OomKillDisable',):
if item in ('OomKillDisable',) or (val1 is None or val2 is None):
if bool(val1) != bool(val2):
ret.setdefault(conf_dict, {})[item] = {'old': val1, 'new': val2}
elif item == 'Image':
image1 = inspect_image(val1)['Id']
image2 = inspect_image(val2)['Id']
if image1 != image2:
ret.setdefault(conf_dict, {})[item] = {'old': image1, 'new': image2}
else:
if item == 'Links':
val1 = _scrub_links(val1, first)
@ -3880,8 +3890,9 @@ def save(name,
saved_path = salt.utils.files.mkstemp()
else:
saved_path = path
cmd = ['docker', 'save', '-o', saved_path, inspect_image(name)['Id']]
# use the image name if its valid if not use the image id
image_to_save = name if name in inspect_image(name)['RepoTags'] else inspect_image(name)['Id']
cmd = ['docker', 'save', '-o', saved_path, image_to_save]
time_started = time.time()
result = __salt__['cmd.run_all'](cmd, python_shell=False)
if result['retcode'] != 0:

View file

@ -24,6 +24,8 @@ import salt.utils.kickstart
import salt.syspaths
from salt.exceptions import SaltInvocationError
# Import 3rd-party libs
from salt.ext import six
log = logging.getLogger(__name__)
@ -325,6 +327,8 @@ def _bootstrap_yum(
'''
if pkgs is None:
pkgs = []
elif isinstance(pkgs, six.string_types):
pkgs = pkgs.split(',')
default_pkgs = ('yum', 'centos-release', 'iputils')
for pkg in default_pkgs:
@ -333,6 +337,8 @@ def _bootstrap_yum(
if exclude_pkgs is None:
exclude_pkgs = []
elif isinstance(exclude_pkgs, six.string_types):
exclude_pkgs = exclude_pkgs.split(',')
for pkg in exclude_pkgs:
pkgs.remove(pkg)
@ -393,15 +399,27 @@ def _bootstrap_deb(
if repo_url is None:
repo_url = 'http://ftp.debian.org/debian/'
if not salt.utils.which('debootstrap'):
log.error('Required tool debootstrap is not installed.')
return False
if isinstance(pkgs, (list, tuple)):
pkgs = ','.join(pkgs)
if isinstance(exclude_pkgs, (list, tuple)):
exclude_pkgs = ','.join(exclude_pkgs)
deb_args = [
'debootstrap',
'--foreign',
'--arch',
_cmd_quote(arch),
'--include',
] + pkgs + [
'--exclude',
] + exclude_pkgs + [
_cmd_quote(arch)]
if pkgs:
deb_args += ['--include', _cmd_quote(pkgs)]
if exclude_pkgs:
deb_args += ['--exclude', _cmd_quote(exclude_pkgs)]
deb_args += [
_cmd_quote(flavor),
_cmd_quote(root),
_cmd_quote(repo_url),
@ -469,6 +487,8 @@ def _bootstrap_pacman(
if pkgs is None:
pkgs = []
elif isinstance(pkgs, six.string_types):
pkgs = pkgs.split(',')
default_pkgs = ('pacman', 'linux', 'systemd-sysvcompat', 'grub')
for pkg in default_pkgs:
@ -477,6 +497,8 @@ def _bootstrap_pacman(
if exclude_pkgs is None:
exclude_pkgs = []
elif isinstance(exclude_pkgs, six.string_types):
exclude_pkgs = exclude_pkgs.split(',')
for pkg in exclude_pkgs:
pkgs.remove(pkg)

View file

@ -493,8 +493,11 @@ def build_rule(table='filter', chain=None, command=None, position='', full=None,
after_jump.append('--{0} {1}'.format(after_jump_argument, value))
del kwargs[after_jump_argument]
for key, value in kwargs.items():
for key in kwargs:
negation = maybe_add_negation(key)
# don't use .items() since maybe_add_negation removes the prefix from
# the value in the kwargs, thus we need to fetch it after that has run
value = kwargs[key]
flag = '-' if len(key) == 1 else '--'
value = '' if value in (None, '') else ' {0}'.format(value)
rule.append('{0}{1}{2}{3}'.format(negation, flag, key, value))

View file

@ -403,7 +403,7 @@ def _context_string_to_dict(context):
return ret
def _filetype_id_to_string(filetype='a'):
def filetype_id_to_string(filetype='a'):
'''
Translates SELinux filetype single-letter representation
to a more human-readable version (which is also used in `semanage fcontext -l`).
@ -444,7 +444,7 @@ def fcontext_get_policy(name, filetype=None, sel_type=None, sel_user=None, sel_l
'sel_role': '[^:]+', # se_role for file context is always object_r
'sel_type': sel_type or '[^:]+',
'sel_level': sel_level or '[^:]+'}
cmd_kwargs['filetype'] = '[[:alpha:] ]+' if filetype is None else _filetype_id_to_string(filetype)
cmd_kwargs['filetype'] = '[[:alpha:] ]+' if filetype is None else filetype_id_to_string(filetype)
cmd = 'semanage fcontext -l | egrep ' + \
"'^{filespec}{spacer}{filetype}{spacer}{sel_user}:{sel_role}:{sel_type}:{sel_level}$'".format(**cmd_kwargs)
current_entry_text = __salt__['cmd.shell'](cmd)

View file

@ -12,6 +12,7 @@ from __future__ import absolute_import
# Import salt libs
import salt.utils
import salt.utils.win_functions
try:
@ -35,10 +36,18 @@ def __virtual__():
return (False, "Module win_groupadd: module only works on Windows systems")
def add(name, gid=None, system=False):
def add(name, **kwargs):
'''
Add the specified group
Args:
name (str):
The name of the group to add
Returns:
dict: A dictionary of results
CLI Example:
.. code-block:: bash
@ -57,29 +66,32 @@ def add(name, gid=None, system=False):
compObj = nt.GetObject('', 'WinNT://.,computer')
newGroup = compObj.Create('group', name)
newGroup.SetInfo()
ret['changes'].append((
'Successfully created group {0}'
).format(name))
ret['changes'].append('Successfully created group {0}'.format(name))
except pywintypes.com_error as com_err:
ret['result'] = False
if len(com_err.excepinfo) >= 2:
friendly_error = com_err.excepinfo[2].rstrip('\r\n')
ret['comment'] = (
'Failed to create group {0}. {1}'
).format(name, friendly_error)
ret['comment'] = 'Failed to create group {0}. {1}' \
''.format(name, friendly_error)
else:
ret['result'] = None
ret['comment'] = (
'The group {0} already exists.'
).format(name)
ret['comment'] = 'The group {0} already exists.'.format(name)
return ret
def delete(name):
def delete(name, **kwargs):
'''
Remove the named group
Args:
name (str):
The name of the group to remove
Returns:
dict: A dictionary of results
CLI Example:
.. code-block:: bash
@ -118,6 +130,14 @@ def info(name):
'''
Return information about a group
Args:
name (str):
The name of the group for which to get information
Returns:
dict: A dictionary of information about the group
CLI Example:
.. code-block:: bash
@ -151,6 +171,17 @@ def getent(refresh=False):
'''
Return info on all groups
Args:
refresh (bool):
Refresh the info for all groups in ``__context__``. If False only
the groups in ``__context__`` wil be returned. If True the
``__context__`` will be refreshed with current data and returned.
Default is False
Returns:
A list of groups and their information
CLI Example:
.. code-block:: bash
@ -182,16 +213,26 @@ def getent(refresh=False):
return ret
def adduser(name, username):
def adduser(name, username, **kwargs):
'''
add a user to a group
Add a user to a group
Args:
name (str):
The name of the group to modify
username (str):
The name of the user to add to the group
Returns:
dict: A dictionary of results
CLI Example:
.. code-block:: bash
salt '*' group.adduser foo username
'''
ret = {'name': name,
@ -209,7 +250,7 @@ def adduser(name, username):
'/', '\\').encode('ascii', 'backslashreplace').lower())
try:
if __fixlocaluser(username.lower()) not in existingMembers:
if salt.utils.win_functions.get_sam_name(username) not in existingMembers:
if not __opts__['test']:
groupObj.Add('WinNT://' + username.replace('\\', '/'))
@ -231,16 +272,26 @@ def adduser(name, username):
return ret
def deluser(name, username):
def deluser(name, username, **kwargs):
'''
remove a user from a group
Remove a user from a group
Args:
name (str):
The name of the group to modify
username (str):
The name of the user to remove from the group
Returns:
dict: A dictionary of results
CLI Example:
.. code-block:: bash
salt '*' group.deluser foo username
'''
ret = {'name': name,
@ -258,7 +309,7 @@ def deluser(name, username):
'/', '\\').encode('ascii', 'backslashreplace').lower())
try:
if __fixlocaluser(username.lower()) in existingMembers:
if salt.utils.win_functions.get_sam_name(username) in existingMembers:
if not __opts__['test']:
groupObj.Remove('WinNT://' + username.replace('\\', '/'))
@ -280,16 +331,27 @@ def deluser(name, username):
return ret
def members(name, members_list):
def members(name, members_list, **kwargs):
'''
remove a user from a group
Ensure a group contains only the members in the list
Args:
name (str):
The name of the group to modify
members_list (str):
A single user or a comma separated list of users. The group will
contain only the users specified in this list.
Returns:
dict: A dictionary of results
CLI Example:
.. code-block:: bash
salt '*' group.members foo 'user1,user2,user3'
'''
ret = {'name': name,
@ -297,7 +359,7 @@ def members(name, members_list):
'changes': {'Users Added': [], 'Users Removed': []},
'comment': []}
members_list = [__fixlocaluser(thisMember) for thisMember in members_list.lower().split(",")]
members_list = [salt.utils.win_functions.get_sam_name(m) for m in members_list.split(",")]
if not isinstance(members_list, list):
ret['result'] = False
ret['comment'].append('Members is not a list object')
@ -364,27 +426,26 @@ def members(name, members_list):
return ret
def __fixlocaluser(username):
'''
prefixes a username w/o a backslash with the computername
i.e. __fixlocaluser('Administrator') would return 'computername\administrator'
'''
if '\\' not in username:
username = ('{0}\\{1}').format(__salt__['grains.get']('host'), username)
return username.lower()
def list_groups(refresh=False):
'''
Return a list of groups
Args:
refresh (bool):
Refresh the info for all groups in ``__context__``. If False only
the groups in ``__context__`` wil be returned. If True, the
``__context__`` will be refreshed with current data and returned.
Default is False
Returns:
list: A list of groups on the machine
CLI Example:
.. code-block:: bash
salt '*' group.getent
salt '*' group.list_groups
'''
if 'group.list_groups' in __context__ and not refresh:
return __context__['group.getent']

File diff suppressed because it is too large Load diff

View file

@ -35,8 +35,10 @@ try:
import yum
HAS_YUM = True
except ImportError:
from salt.ext.six.moves import configparser
HAS_YUM = False
from salt.ext.six.moves import configparser
# pylint: enable=import-error,redefined-builtin
# Import salt libs
@ -2708,41 +2710,32 @@ def _parse_repo_file(filename):
'''
Turn a single repo file into a dict
'''
repos = {}
header = ''
repo = ''
with salt.utils.fopen(filename, 'r') as rfile:
for line in rfile:
if line.startswith('['):
repo = line.strip().replace('[', '').replace(']', '')
repos[repo] = {}
parsed = configparser.ConfigParser()
config = {}
# Even though these are essentially uselss, I want to allow the
# user to maintain their own comments, etc
if not line:
if not repo:
header += line
if line.startswith('#'):
if not repo:
header += line
else:
if 'comments' not in repos[repo]:
repos[repo]['comments'] = []
repos[repo]['comments'].append(line.strip())
continue
try:
parsed.read(filename)
except configparser.MissingSectionHeaderError as err:
log.error(
'Failed to parse file {0}, error: {1}'.format(filename, err.message)
)
return ('', {})
# These are the actual configuration lines that matter
if '=' in line:
try:
comps = line.strip().split('=')
repos[repo][comps[0].strip()] = '='.join(comps[1:])
except KeyError:
log.error(
'Failed to parse line in %s, offending line was '
'\'%s\'', filename, line.rstrip()
)
for section in parsed._sections:
section_dict = dict(parsed._sections[section])
section_dict.pop('__name__')
config[section] = section_dict
return (header, repos)
# Try to extract leading comments
headers = ''
with salt.utils.fopen(filename, 'r') as rawfile:
for line in rawfile:
if line.strip().startswith('#'):
headers += '{0}\n'.format(line.strip())
else:
break
return (headers, config)
def file_list(*packages):

View file

@ -561,7 +561,7 @@ def ext_pillar(minion_id, repo, pillar_dirs):
)
for pillar_dir, env in six.iteritems(pillar.pillar_dirs):
# If pillarenv is set, only grab pillars with that match pillarenv
if opts['pillarenv'] and env != opts['pillarenv']:
if opts['pillarenv'] and env != opts['pillarenv'] and env != '__env__':
log.debug(
'env \'%s\' for pillar dir \'%s\' does not match '
'pillarenv \'%s\', skipping',

View file

@ -2122,11 +2122,14 @@ class State(object):
reqs[r_state].append(chunk)
continue
try:
if (fnmatch.fnmatch(chunk['name'], req_val) or
fnmatch.fnmatch(chunk['__id__'], req_val)):
if req_key == 'id' or chunk['state'] == req_key:
found = True
reqs[r_state].append(chunk)
if isinstance(req_val, six.string_types):
if (fnmatch.fnmatch(chunk['name'], req_val) or
fnmatch.fnmatch(chunk['__id__'], req_val)):
if req_key == 'id' or chunk['state'] == req_key:
found = True
reqs[r_state].append(chunk)
else:
raise KeyError
except KeyError as exc:
raise SaltRenderError(
'Could not locate requisite of [{0}] present in state with name [{1}]'.format(
@ -2302,13 +2305,17 @@ class State(object):
req_val = lreq[req_key]
comment += \
'{0}{1}: {2}\n'.format(' ' * 23, req_key, req_val)
running[tag] = {'changes': {},
'result': False,
'comment': comment,
'__run_num__': self.__run_num,
'__sls__': low['__sls__']}
if low.get('__prereq__'):
run_dict = self.pre
else:
run_dict = running
run_dict[tag] = {'changes': {},
'result': False,
'comment': comment,
'__run_num__': self.__run_num,
'__sls__': low['__sls__']}
self.__run_num += 1
self.event(running[tag], len(chunks), fire_event=low.get('fire_event'))
self.event(run_dict[tag], len(chunks), fire_event=low.get('fire_event'))
return running
for chunk in reqs:
# Check to see if the chunk has been run, only run it if

View file

@ -622,7 +622,11 @@ def _clean_dir(root, keep, exclude_pat):
while True:
fn_ = os.path.dirname(fn_)
real_keep.add(fn_)
if fn_ in ['/', ''.join([os.path.splitdrive(fn_)[0], '\\\\'])]:
if fn_ in [
os.sep,
''.join([os.path.splitdrive(fn_)[0], os.sep]),
''.join([os.path.splitdrive(fn_)[0], os.sep, os.sep])
]:
break
def _delete_not_kept(nfn):

View file

@ -1301,6 +1301,23 @@ def latest(name,
'if it does not already exist).',
comments
)
remote_tags = set([
x.replace('refs/tags/', '') for x in __salt__['git.ls_remote'](
cwd=target,
remote=remote,
opts="--tags",
user=user,
password=password,
identity=identity,
saltenv=__env__,
ignore_retcode=True,
).keys() if '^{}' not in x
])
if set(all_local_tags) != remote_tags:
has_remote_rev = False
ret['changes']['new_tags'] = list(remote_tags.symmetric_difference(
all_local_tags
))
if not has_remote_rev:
try:
@ -1456,8 +1473,6 @@ def latest(name,
user=user,
password=password,
ignore_retcode=True):
merge_rev = remote_rev if rev == 'HEAD' \
else desired_upstream
if git_ver >= _LooseVersion('1.8.1.6'):
# --ff-only added in version 1.8.1.6. It's not
@ -1474,7 +1489,7 @@ def latest(name,
__salt__['git.merge'](
target,
rev=merge_rev,
rev=remote_rev,
opts=merge_opts,
user=user,
password=password)
@ -2238,13 +2253,18 @@ def detached(name,
local_commit_id = _get_local_rev_and_branch(target, user, password)[0]
if remote_rev_type is 'hash' \
and __salt__['git.describe'](target,
rev,
user=user,
password=password):
# The rev is a hash and it exists locally so skip to checkout
hash_exists_locally = True
if remote_rev_type is 'hash':
try:
__salt__['git.describe'](target,
rev,
user=user,
password=password,
ignore_retcode=True)
except CommandExecutionError:
hash_exists_locally = False
else:
# The rev is a hash and it exists locally so skip to checkout
hash_exists_locally = True
else:
# Check that remote is present and set to correct url
remotes = __salt__['git.remotes'](target,

View file

@ -1,10 +1,15 @@
# -*- coding: utf-8 -*-
'''
r'''
Management of user groups
=========================
The group module is used to create and manage unix group settings, groups
can be either present or absent:
The group module is used to create and manage group settings, groups can be
either present or absent. User/Group names can be passed to the ``adduser``,
``deluser``, and ``members`` parameters. ``adduser`` and ``deluser`` can be used
together but not with ``members``.
In Windows, if no domain is specified in the user or group name (ie:
`DOMAIN\username``) the module will assume a local user or group.
.. code-block:: yaml
@ -36,6 +41,10 @@ import sys
# Import 3rd-party libs
import salt.ext.six as six
# Import Salt libs
import salt.utils
import salt.utils.win_functions
def _changes(name,
gid=None,
@ -50,6 +59,18 @@ def _changes(name,
if not lgrp:
return False
# User and Domain names are not case sensitive in Windows. Let's make them
# all lower case so we can compare properly
if salt.utils.is_windows():
if lgrp['members']:
lgrp['members'] = [user.lower() for user in lgrp['members']]
if members:
members = [salt.utils.win_functions.get_sam_name(user) for user in members]
if addusers:
addusers = [salt.utils.win_functions.get_sam_name(user) for user in addusers]
if delusers:
delusers = [salt.utils.win_functions.get_sam_name(user) for user in delusers]
change = {}
if gid:
if lgrp['gid'] != gid:
@ -57,7 +78,7 @@ def _changes(name,
if members:
# -- if new member list if different than the current
if set(lgrp['members']) ^ set(members):
if set(lgrp['members']).symmetric_difference(members):
change['members'] = members
if addusers:
@ -79,31 +100,58 @@ def present(name,
addusers=None,
delusers=None,
members=None):
'''
r'''
Ensure that a group is present
name
The name of the group to manage
Args:
gid
The group id to assign to the named group; if left empty, then the next
available group id will be assigned
name (str):
The name of the group to manage
system
Whether or not the named group is a system group. This is essentially
the '-r' option of 'groupadd'.
gid (str):
The group id to assign to the named group; if left empty, then the
next available group id will be assigned. Ignored on Windows
addusers
List of additional users to be added as a group members.
system (bool):
Whether or not the named group is a system group. This is essentially
the '-r' option of 'groupadd'. Ignored on Windows
delusers
Ensure these user are removed from the group membership.
addusers (list):
List of additional users to be added as a group members. Cannot
conflict with names in delusers. Cannot be used in conjunction with
members.
members
Replace existing group members with a list of new members.
delusers (list):
Ensure these user are removed from the group membership. Cannot
conflict with names in addusers. Cannot be used in conjunction with
members.
Note: Options 'members' and 'addusers/delusers' are mutually exclusive and
can not be used together.
members (list):
Replace existing group members with a list of new members. Cannot be
used in conjunction with addusers or delusers.
Example:
.. code-block:: yaml
# Adds DOMAIN\db_admins and Administrators to the local db_admin group
# Removes Users
db_admin:
group.present:
- addusers:
- DOMAIN\db_admins
- Administrators
- delusers:
- Users
# Ensures only DOMAIN\domain_admins and the local Administrator are
# members of the local Administrators group. All other users are
# removed
Administrators:
group.present:
- members:
- DOMAIN\domain_admins
- Administrator
'''
ret = {'name': name,
'changes': {},
@ -233,8 +281,17 @@ def absent(name):
'''
Ensure that the named group is absent
name
The name of the group to remove
Args:
name (str):
The name of the group to remove
Example:
.. code-block:: yaml
# Removes the local group `db_admin`
db_admin:
group.absent
'''
ret = {'name': name,
'changes': {},

View file

@ -92,6 +92,13 @@ def managed(name,
Use certain profiles to generate the config.
If not specified, will use the platform default profile(s).
compliance_report: ``False``
Return the compliance report in the comment.
The compliance report structured object can be found however
in the ``pchanges`` field of the output (not displayed on the CLI).
.. versionadded:: 2017.7.3
test: ``False``
Dry run? If set as ``True``, will apply the config, discard
and return the changes. Default: ``False`` and will commit
@ -140,6 +147,7 @@ def managed(name,
debug = kwargs.get('debug', False) or __opts__.get('debug', False)
commit = kwargs.get('commit', True) or __opts__.get('commit', True)
replace = kwargs.get('replace', False) or __opts__.get('replace', False)
return_compliance_report = kwargs.get('compliance_report', False) or __opts__.get('compliance_report', False)
profiles = kwargs.get('profiles', [])
temp_file = __salt__['temp.file']()
log.debug('Creating temp file: {0}'.format(temp_file))
@ -180,7 +188,13 @@ def managed(name,
log.debug('Loaded config result:')
log.debug(loaded_changes)
__salt__['file.remove'](temp_file)
return salt.utils.napalm.loaded_ret(ret, loaded_changes, test, debug)
loaded_changes['compliance_report'] = compliance_report
return salt.utils.napalm.loaded_ret(ret,
loaded_changes,
test,
debug,
opts=__opts__,
compliance_report=return_compliance_report)
def configured(name,

View file

@ -1,6 +1,11 @@
# -*- coding: utf-8 -*-
'''
Some of the utils used by salt
NOTE: The dev team is working on splitting up this file for the Oxygen release.
Please do not add any new functions to this file. New functions should be
organized in other files under salt/utils/. Please consult the dev team if you
are unsure where a new function should go.
'''
# Import python libs

View file

@ -7,9 +7,11 @@ import contextlib
import errno
import logging
import os
import re
import shutil
import subprocess
import time
import urllib
# Import salt libs
import salt.utils
@ -258,3 +260,39 @@ def set_umask(mask):
yield
finally:
os.umask(orig_mask)
def safe_filename_leaf(file_basename):
'''
Input the basename of a file, without the directory tree, and returns a safe name to use
i.e. only the required characters are converted by urllib.quote
If the input is a PY2 String, output a PY2 String. If input is Unicode output Unicode.
For consistency all platforms are treated the same. Hard coded to utf8 as its ascii compatible
windows is \\ / : * ? " < > | posix is /
.. versionadded:: 2017.7.2
'''
def _replace(re_obj):
return urllib.quote(re_obj.group(0), safe=u'')
if not isinstance(file_basename, six.text_type):
# the following string is not prefixed with u
return re.sub('[\\\\:/*?"<>|]',
_replace,
six.text_type(file_basename, 'utf8').encode('ascii', 'backslashreplace'))
# the following string is prefixed with u
return re.sub(u'[\\\\:/*?"<>|]', _replace, file_basename, flags=re.UNICODE)
def safe_filepath(file_path_name):
'''
Input the full path and filename, splits on directory separator and calls safe_filename_leaf for
each part of the path.
.. versionadded:: 2017.7.2
'''
(drive, path) = os.path.splitdrive(file_path_name)
path = os.sep.join([safe_filename_leaf(file_section) for file_section in file_path_name.rsplit(os.sep)])
if drive:
return os.sep.join([drive, path])
else:
return path

View file

@ -24,6 +24,7 @@ from functools import wraps
log = logging.getLogger(__file__)
import salt.utils
import salt.output
# Import third party lib
try:
@ -432,58 +433,58 @@ def default_ret(name):
return ret
def loaded_ret(ret, loaded, test, debug):
def loaded_ret(ret, loaded, test, debug, compliance_report=False, opts=None):
'''
Return the final state output.
ret
The initial state output structure.
loaded
The loaded dictionary.
'''
# Always get the comment
ret.update({
'comment': loaded.get('comment', '')
})
changes = {}
pchanges = {}
ret['comment'] = loaded['comment']
if 'diff' in loaded:
changes['diff'] = loaded['diff']
pchanges['diff'] = loaded['diff']
if 'compliance_report' in loaded:
if compliance_report:
changes['compliance_report'] = loaded['compliance_report']
pchanges['compliance_report'] = loaded['compliance_report']
if debug and 'loaded_config' in loaded:
changes['loaded_config'] = loaded['loaded_config']
pchanges['loaded_config'] = loaded['loaded_config']
ret['pchanges'] = pchanges
if changes.get('diff'):
ret['comment'] = '{comment_base}\n\nConfiguration diff:\n\n{diff}'.format(comment_base=ret['comment'],
diff=changes['diff'])
if changes.get('loaded_config'):
ret['comment'] = '{comment_base}\n\nLoaded config:\n\n{loaded_cfg}'.format(
comment_base=ret['comment'],
loaded_cfg=changes['loaded_config'])
if changes.get('compliance_report'):
ret['comment'] = '{comment_base}\n\nCompliance report:\n\n{compliance}'.format(
comment_base=ret['comment'],
compliance=salt.output.string_format(changes['compliance_report'], 'nested', opts=opts))
if not loaded.get('result', False):
# Failure of some sort
return ret
if debug:
# Always check for debug
pchanges.update({
'loaded_config': loaded.get('loaded_config', '')
})
ret.update({
"pchanges": pchanges
})
if not loaded.get('already_configured', True):
# We're making changes
pchanges.update({
"diff": loaded.get('diff', '')
})
ret.update({
'pchanges': pchanges
})
if test:
for k, v in pchanges.items():
ret.update({
"comment": "{}:\n{}\n\n{}".format(k, v, ret.get("comment", ''))
})
ret.update({
'result': None,
})
ret['result'] = None
return ret
# Not test, changes were applied
ret.update({
'result': True,
'changes': pchanges,
'comment': "Configuration changed!\n{}".format(ret.get('comment', ''))
'changes': changes,
'comment': "Configuration changed!\n{}".format(loaded['comment'])
})
return ret
# No changes
ret.update({
'result': True
'result': True,
'changes': {}
})
return ret

View file

@ -15,6 +15,7 @@ import contextlib
import subprocess
import multiprocessing
import multiprocessing.util
import socket
# Import salt libs
@ -55,7 +56,20 @@ def notify_systemd():
import systemd.daemon
except ImportError:
if salt.utils.which('systemd-notify') and systemd_notify_call('--booted'):
return systemd_notify_call('--ready')
# Notify systemd synchronously
notify_socket = os.getenv('NOTIFY_SOCKET')
if notify_socket:
# Handle abstract namespace socket
if notify_socket.startswith('@'):
notify_socket = '\0{0}'.format(notify_socket[1:])
try:
sock = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM)
sock.connect(notify_socket)
sock.sendall('READY=1'.encode())
sock.close()
except socket.error:
return systemd_notify_call('--ready')
return True
return False
if systemd.daemon.booted():

View file

@ -60,15 +60,15 @@ def is_escaped(url):
'''
test whether `url` is escaped with `|`
'''
if salt.utils.is_windows():
return False
scheme = urlparse(url).scheme
if not scheme:
return url.startswith('|')
elif scheme == 'salt':
path, saltenv = parse(url)
return path.startswith('|')
if salt.utils.is_windows() and '|' in url:
return path.startswith('_')
else:
return path.startswith('|')
else:
return False
@ -100,15 +100,15 @@ def unescape(url):
'''
remove escape character `|` from `url`
'''
if salt.utils.is_windows():
return url
scheme = urlparse(url).scheme
if not scheme:
return url.lstrip('|')
elif scheme == 'salt':
path, saltenv = parse(url)
return create(path.lstrip('|'), saltenv)
if salt.utils.is_windows() and '|' in url:
return create(path.lstrip('_'), saltenv)
else:
return create(path.lstrip('|'), saltenv)
else:
return url

View file

@ -4,6 +4,9 @@ Various functions to be used by windows during start up and to monkey patch
missing functions in other modules
'''
from __future__ import absolute_import
import platform
# Import Salt Libs
from salt.exceptions import CommandExecutionError
# Import 3rd Party Libs
@ -138,3 +141,19 @@ def get_current_user():
return False
return user_name
def get_sam_name(username):
'''
Gets the SAM name for a user. It basically prefixes a username without a
backslash with the computer name. If the username contains a backslash, it
is returned as is.
Everything is returned lower case
i.e. salt.utils.fix_local_user('Administrator') would return 'computername\administrator'
'''
if '\\' not in username:
username = '{0}\\{1}'.format(platform.node(), username)
return username.lower()

View file

@ -42,7 +42,7 @@ class NpmStateTest(ModuleCase, SaltReturnAssertsMixin):
'''
Determine if URL-referenced NPM module can be successfully installed.
'''
ret = self.run_state('npm.installed', name='git://github.com/request/request')
ret = self.run_state('npm.installed', name='request/request#v2.81.1')
self.assertSaltTrueReturn(ret)
ret = self.run_state('npm.removed', name='git://github.com/request/request')
self.assertSaltTrueReturn(ret)

View file

@ -697,3 +697,30 @@ class DockerTestCase(TestCase, LoaderModuleMockMixin):
result = docker_mod.images()
self.assertEqual(result,
{'sha256:abcdefg': {'RepoTags': ['image:latest']}})
def test_compare_container_image_id_resolution(self):
'''
Test comparing two containers when one's inspect output is an ID and
not formatted in image:tag notation.
'''
def _inspect_container_effect(id_):
return {
'container1': {'Config': {'Image': 'realimage:latest'},
'HostConfig': {}},
'container2': {'Config': {'Image': 'image_id'},
'HostConfig': {}},
}[id_]
def _inspect_image_effect(id_):
return {
'realimage:latest': {'Id': 'image_id'},
'image_id': {'Id': 'image_id'},
}[id_]
inspect_container_mock = MagicMock(side_effect=_inspect_container_effect)
inspect_image_mock = MagicMock(side_effect=_inspect_image_effect)
with patch.object(docker_mod, 'inspect_container', inspect_container_mock):
with patch.object(docker_mod, 'inspect_image', inspect_image_mock):
ret = docker_mod.compare_container('container1', 'container2')
self.assertEqual(ret, {})

View file

@ -46,12 +46,57 @@ class GenesisTestCase(TestCase, LoaderModuleMockMixin):
with patch.dict(genesis.__salt__, {'disk.blkid': MagicMock(return_value={})}):
self.assertEqual(genesis.bootstrap('rpm', 'root', 'dir'), None)
with patch.object(genesis, '_bootstrap_deb', return_value='A'):
common_parms = {'platform': 'deb',
'root': 'root',
'img_format': 'dir',
'arch': 'amd64',
'flavor': 'stable',
'static_qemu': 'qemu'}
param_sets = [
{'params': {},
'cmd': ['debootstrap', '--foreign', '--arch', 'amd64',
'stable', 'root', 'http://ftp.debian.org/debian/']
},
{'params': {'pkgs': 'vim'},
'cmd': ['debootstrap', '--foreign', '--arch', 'amd64',
'--include', 'vim',
'stable', 'root', 'http://ftp.debian.org/debian/']
},
{'params': {'pkgs': 'vim,emacs'},
'cmd': ['debootstrap', '--foreign', '--arch', 'amd64',
'--include', 'vim,emacs',
'stable', 'root', 'http://ftp.debian.org/debian/']
},
{'params': {'pkgs': ['vim', 'emacs']},
'cmd': ['debootstrap', '--foreign', '--arch', 'amd64',
'--include', 'vim,emacs',
'stable', 'root', 'http://ftp.debian.org/debian/']
},
{'params': {'pkgs': ['vim', 'emacs'], 'exclude_pkgs': ['vim', 'foo']},
'cmd': ['debootstrap', '--foreign', '--arch', 'amd64',
'--include', 'vim,emacs', '--exclude', 'vim,foo',
'stable', 'root', 'http://ftp.debian.org/debian/']
},
]
for param_set in param_sets:
with patch.dict(genesis.__salt__, {'mount.umount': MagicMock(),
'file.rmdir': MagicMock(),
'file.directory_exists': MagicMock()}):
with patch.dict(genesis.__salt__, {'disk.blkid': MagicMock(return_value={})}):
self.assertEqual(genesis.bootstrap('deb', 'root', 'dir'), None)
'file.directory_exists': MagicMock(),
'cmd.run': MagicMock(),
'disk.blkid': MagicMock(return_value={})}):
with patch('salt.modules.genesis.salt.utils.which', return_value=True):
param_set['params'].update(common_parms)
self.assertEqual(genesis.bootstrap(**param_set['params']), None)
genesis.__salt__['cmd.run'].assert_any_call(param_set['cmd'], python_shell=False)
with patch.object(genesis, '_bootstrap_pacman', return_value='A') as pacman_patch:
with patch.dict(genesis.__salt__, {'mount.umount': MagicMock(),

View file

@ -60,6 +60,9 @@ class IptablesTestCase(TestCase, LoaderModuleMockMixin):
self.assertEqual(iptables.build_rule(**{'if': 'not eth0'}),
'! -i eth0')
self.assertEqual(iptables.build_rule(**{'proto': 'tcp', 'syn': '!'}),
'-p tcp ! --syn')
self.assertEqual(iptables.build_rule(dports=[80, 443], proto='tcp'),
'-p tcp -m multiport --dports 80,443')

View file

@ -6,6 +6,7 @@
# Import Python libs
from __future__ import absolute_import
import os
# Import Salt Testing libs
from tests.support.unit import TestCase
@ -13,6 +14,7 @@ from tests.support.unit import TestCase
# Import Salt libs
import tests.integration as integration
import salt.modules.cmdmod
import salt.utils
class DocTestCase(TestCase):
@ -32,8 +34,15 @@ class DocTestCase(TestCase):
https://github.com/saltstack/salt/issues/12788
'''
salt_dir = integration.CODE_DIR
salt_dir += '/'
cmd = 'grep -r :doc: ' + salt_dir
if salt.utils.is_windows():
# No grep in Windows, use findstr
# findstr in windows doesn't prepend 'Binary` to binary files, so
# use the '/P' switch to skip files with unprintable characters
cmd = 'findstr /C:":doc:" /S /P {0}\\*'.format(salt_dir)
else:
salt_dir += '/'
cmd = 'grep -r :doc: ' + salt_dir
grep_call = salt.modules.cmdmod.run_stdout(cmd=cmd).split('\n')
@ -43,25 +52,30 @@ class DocTestCase(TestCase):
if line.startswith('Binary'):
continue
key, val = line.split(':', 1)
if salt.utils.is_windows():
# Need the space after the colon so it doesn't split the drive
# letter
key, val = line.split(': ', 1)
else:
key, val = line.split(':', 1)
# Don't test man pages, this file,
# the page that documents to not use ":doc:", or
# the doc/conf.py file
if 'man' in key \
or key.endswith('test_doc.py') \
or key.endswith('doc/conf.py') \
or key.endswith('/conventions/documentation.rst') \
or key.endswith('doc/topics/releases/2016.11.2.rst') \
or key.endswith('doc/topics/releases/2016.11.3.rst') \
or key.endswith('doc/topics/releases/2016.3.5.rst'):
or key.endswith(os.sep.join(['doc', 'conf.py'])) \
or key.endswith(os.sep.join(['conventions', 'documentation.rst'])) \
or key.endswith(os.sep.join(['doc', 'topics', 'releases', '2016.11.2.rst'])) \
or key.endswith(os.sep.join(['doc', 'topics', 'releases', '2016.11.3.rst'])) \
or key.endswith(os.sep.join(['doc', 'topics', 'releases', '2016.3.5.rst'])):
continue
# Set up test return dict
if test_ret.get(key) is None:
test_ret[key] = [val.lstrip()]
test_ret[key] = [val.strip()]
else:
test_ret[key].append(val.lstrip())
test_ret[key].append(val.strip())
# Allow test results to show files with :doc: ref, rather than truncating
self.maxDiff = None

View file

@ -13,33 +13,33 @@ from tests.support.unit import TestCase
from tests.support.paths import CODE_DIR
EXCLUDED_DIRS = [
'tests/pkg',
'tests/perf',
'tests/support',
'tests/unit/utils/cache_mods',
'tests/unit/modules/inspectlib',
'tests/unit/modules/zypp/',
'tests/unit/templates/files',
'tests/integration/files/',
'tests/integration/cloud/helpers',
os.path.join('tests', 'pkg'),
os.path.join('tests', 'perf'),
os.path.join('tests', 'support'),
os.path.join('tests', 'unit', 'utils', 'cache_mods'),
os.path.join('tests', 'unit', 'modules', 'inspectlib'),
os.path.join('tests', 'unit', 'modules', 'zypp'),
os.path.join('tests', 'unit', 'templates', 'files'),
os.path.join('tests', 'integration', 'files'),
os.path.join('tests', 'integration', 'cloud', 'helpers'),
]
EXCLUDED_FILES = [
'tests/eventlisten.py',
'tests/buildpackage.py',
'tests/saltsh.py',
'tests/minionswarm.py',
'tests/wheeltest.py',
'tests/runtests.py',
'tests/jenkins.py',
'tests/salt-tcpdump.py',
'tests/conftest.py',
'tests/packdump.py',
'tests/consist.py',
'tests/modparser.py',
'tests/committer_parser.py',
'tests/zypp_plugin.py',
'tests/unit/transport/mixins.py',
'tests/integration/utils/testprogram.py',
os.path.join('tests', 'eventlisten.py'),
os.path.join('tests', 'buildpackage.py'),
os.path.join('tests', 'saltsh.py'),
os.path.join('tests', 'minionswarm.py'),
os.path.join('tests', 'wheeltest.py'),
os.path.join('tests', 'runtests.py'),
os.path.join('tests', 'jenkins.py'),
os.path.join('tests', 'salt-tcpdump.py'),
os.path.join('tests', 'conftest.py'),
os.path.join('tests', 'packdump.py'),
os.path.join('tests', 'consist.py'),
os.path.join('tests', 'modparser.py'),
os.path.join('tests', 'committer_parser.py'),
os.path.join('tests', 'zypp_plugin.py'),
os.path.join('tests', 'unit', 'transport', 'mixins.py'),
os.path.join('tests', 'integration', 'utils', 'testprogram.py'),
]

View file

@ -38,6 +38,8 @@ class UrlTestCase(TestCase):
'''
path = '?funny/path with {interesting|chars}'
url = 'salt://' + path
if salt.utils.is_windows():
path = '_funny/path with {interesting_chars}'
self.assertEqual(salt.utils.url.parse(url), (path, None))
@ -48,6 +50,8 @@ class UrlTestCase(TestCase):
saltenv = 'ambience'
path = '?funny/path&with {interesting|chars}'
url = 'salt://' + path + '?saltenv=' + saltenv
if salt.utils.is_windows():
path = '_funny/path&with {interesting_chars}'
self.assertEqual(salt.utils.url.parse(url), (path, saltenv))
@ -59,6 +63,8 @@ class UrlTestCase(TestCase):
'''
path = '? interesting/&path.filetype'
url = 'salt://' + path
if salt.utils.is_windows():
url = 'salt://_ interesting/&path.filetype'
self.assertEqual(salt.utils.url.create(path), url)
@ -68,6 +74,8 @@ class UrlTestCase(TestCase):
'''
saltenv = 'raumklang'
path = '? interesting/&path.filetype'
if salt.utils.is_windows():
path = '_ interesting/&path.filetype'
url = 'salt://' + path + '?saltenv=' + saltenv
@ -149,6 +157,8 @@ class UrlTestCase(TestCase):
'''
path = 'dir/file.conf'
escaped_path = '|' + path
if salt.utils.is_windows():
escaped_path = path
self.assertEqual(salt.utils.url.escape(path), escaped_path)
@ -167,6 +177,8 @@ class UrlTestCase(TestCase):
path = 'dir/file.conf'
url = 'salt://' + path
escaped_url = 'salt://|' + path
if salt.utils.is_windows():
escaped_url = url
self.assertEqual(salt.utils.url.escape(url), escaped_url)

View file

@ -44,18 +44,21 @@ class TestWhich(TestCase):
# The second, iterating through $PATH, should also return False,
# still checking for Linux
False,
# We will now also return False once so we get a .EXE back from
# the function, see PATHEXT below.
False,
# Lastly return True, this is the windows check.
True
]
# Let's patch os.environ to provide a custom PATH variable
with patch.dict(os.environ, {'PATH': '/bin'}):
with patch.dict(os.environ, {'PATH': '/bin',
'PATHEXT': '.COM;.EXE;.BAT;.CMD'}):
# Let's also patch is_windows to return True
with patch('salt.utils.is_windows', lambda: True):
with patch('os.path.isfile', lambda x: True):
self.assertEqual(
salt.utils.which('this-binary-exists-under-windows'),
# The returned path should return the .exe suffix
'/bin/this-binary-exists-under-windows.EXE'
os.path.join('/bin', 'this-binary-exists-under-windows.EXE')
)
def test_missing_binary_in_windows(self):
@ -106,6 +109,5 @@ class TestWhich(TestCase):
with patch('os.path.isfile', lambda x: True):
self.assertEqual(
salt.utils.which('this-binary-exists-under-windows'),
# The returned path should return the .exe suffix
'/bin/this-binary-exists-under-windows.CMD'
os.path.join('/bin', 'this-binary-exists-under-windows.CMD')
)